From c06cb760c89993747d0d56dcae77983433790869 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Fri, 19 Apr 2024 14:10:23 -0700 Subject: [PATCH 01/12] Remote CLI command --- pgml-sdks/pgml/Cargo.lock | 3 +- pgml-sdks/pgml/Cargo.toml | 1 + pgml-sdks/pgml/src/cli.rs | 55 ++++++++++++++++++++++++++++++- pgml-sdks/pgml/src/sql/remote.sql | 31 +++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 pgml-sdks/pgml/src/sql/remote.sql diff --git a/pgml-sdks/pgml/Cargo.lock b/pgml-sdks/pgml/Cargo.lock index 11128b907..c9d5723db 100644 --- a/pgml-sdks/pgml/Cargo.lock +++ b/pgml-sdks/pgml/Cargo.lock @@ -1531,7 +1531,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pgml" -version = "1.0.0" +version = "1.0.2" dependencies = [ "anyhow", "async-trait", @@ -1562,6 +1562,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "url", "uuid", "walkdir", ] diff --git a/pgml-sdks/pgml/Cargo.toml b/pgml-sdks/pgml/Cargo.toml index e78e7413a..ba4037bce 100644 --- a/pgml-sdks/pgml/Cargo.toml +++ b/pgml-sdks/pgml/Cargo.toml @@ -45,6 +45,7 @@ ctrlc = "3" inquire = "0.6" parking_lot = "0.12.1" once_cell = "1.19.0" +url = "2.5.0" [features] default = [] diff --git a/pgml-sdks/pgml/src/cli.rs b/pgml-sdks/pgml/src/cli.rs index 709e5c1ab..63f694fc6 100644 --- a/pgml-sdks/pgml/src/cli.rs +++ b/pgml-sdks/pgml/src/cli.rs @@ -10,7 +10,7 @@ use pyo3::prelude::*; use sqlx::{Acquire, Executor}; use std::io::Write; -/// PostgresML CLI +/// PostgresML CLI: configure your PostgresML deployments & create connections to remote data sources. #[cfg(feature = "python")] #[derive(Parser, Debug, Clone)] #[command(author, version, about, long_about = None, name = "pgml", bin_name = "pgml")] @@ -97,6 +97,12 @@ enum Subcommands { #[arg(long)] database_url: Option, }, + + /// Connect your database to PostgresML via dblink. + Remote { + #[arg(long)] + database_url: Option, + }, } enum Level { @@ -212,6 +218,10 @@ async fn cli_internal() -> anyhow::Result<()> { ) .await?; } + + Subcommands::Remote { database_url } => { + remote(database_url).await?; + } }; Ok(()) @@ -326,6 +336,49 @@ async fn connect( Ok(()) } +async fn remote(database_url: Option) -> anyhow::Result<()> { + let database_url = user_input!(database_url, "PostgresML DATABASE_URL"); + let database_url = url::Url::parse(&database_url)?; + let user = database_url.username(); + if user.is_empty() { + anyhow::bail!("user not found in DATABASE_URL"); + } + + let password = database_url.password(); + let password = if password.is_none() { + anyhow::bail!("password not found in DATABASE_URL"); + } else { + password.unwrap() + }; + + let host = database_url.host_str(); + let host = if host.is_none() { + anyhow::bail!("host not found in DATABASE_URL"); + } else { + host.unwrap() + }; + + let port = database_url.port(); + let port = if port.is_none() { + "6432".to_string() + } else { + port.unwrap().to_string() + }; + + let database = database_url.path().replace("/", ""); + + let sql = include_str!("sql/remote.sql") + .replace("{user}", user) + .replace("{password}", password) + .replace("{host}", host) + .replace("{db_name}", "postgresml") + .replace("{database_name}", &database) + .replace("{port}", &port); + + println!("{}", syntax_highlight(&sql)); + Ok(()) +} + fn syntax_highlight(text: &str) -> String { if !std::io::stdout().is_terminal() { return text.to_owned(); diff --git a/pgml-sdks/pgml/src/sql/remote.sql b/pgml-sdks/pgml/src/sql/remote.sql new file mode 100644 index 000000000..d44b7b84f --- /dev/null +++ b/pgml-sdks/pgml/src/sql/remote.sql @@ -0,0 +1,31 @@ + + CREATE EXTENSION IF NOT EXISTS postgres_fdw; + CREATE EXTENSION IF NOT EXISTS dblink; + + CREATE SERVER "{db_name}" + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS ( + host '{host}', + port '{port}', + dbname '{database_name}' + ); + + CREATE USER MAPPING + FOR CURRENT_USER + SERVER "{db_name}" + OPTIONS ( + user '{user}', + password '{password}' + ); + + SELECT * FROM dblink( + '{db_name}', + 'SELECT pgml.embed(''intfloat/e5-small'', ''test postgresml embedding'') AS embedding' + ) AS t(embedding real[386]); + + CREATE FUNCTION pgml_embed_e5_small(text) RETURNS real[386] AS $$ + SELECT * FROM dblink( + '{db_name}', + 'SELECT pgml.embed(''intfloat/e5-small'', ''' || $1 || ''') AS embedding' + ) AS t(embedding real[386]); + $$ LANGUAGE SQL; From dfd5d2980ad1932c2015ab383b16f64354cb6b52 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Fri, 19 Apr 2024 15:27:44 -0700 Subject: [PATCH 02/12] Generate dblink and pgcat config --- pgml-sdks/pgml/Cargo.lock | 65 +++++++++++++++++++++++++++++++++++++++ pgml-sdks/pgml/Cargo.toml | 2 ++ pgml-sdks/pgml/src/cli.rs | 51 +++++++++++++++++++++++++++--- 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/pgml-sdks/pgml/Cargo.lock b/pgml-sdks/pgml/Cargo.lock index c9d5723db..abc7b5c37 100644 --- a/pgml-sdks/pgml/Cargo.lock +++ b/pgml-sdks/pgml/Cargo.lock @@ -1529,6 +1529,17 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pgcat_config" +version = "2.0.0-alpha3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6d1904cc180d4021ce8acd54eb92074ed10b34bc531faa418ea3d69e1d30b94" +dependencies = [ + "serde", + "socket2", + "toml", +] + [[package]] name = "pgml" version = "1.0.2" @@ -1549,6 +1560,7 @@ dependencies = [ "neon", "once_cell", "parking_lot", + "pgcat_config", "pyo3", "pyo3-asyncio", "regex", @@ -1560,6 +1572,7 @@ dependencies = [ "serde_json", "sqlx", "tokio", + "toml", "tracing", "tracing-subscriber", "url", @@ -2123,6 +2136,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2715,6 +2737,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -3197,6 +3253,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/pgml-sdks/pgml/Cargo.toml b/pgml-sdks/pgml/Cargo.toml index ba4037bce..612a3eb92 100644 --- a/pgml-sdks/pgml/Cargo.toml +++ b/pgml-sdks/pgml/Cargo.toml @@ -46,6 +46,8 @@ inquire = "0.6" parking_lot = "0.12.1" once_cell = "1.19.0" url = "2.5.0" +pgcat_config = "2.0.0-alpha3" +toml = "0.8.12" [features] default = [] diff --git a/pgml-sdks/pgml/src/cli.rs b/pgml-sdks/pgml/src/cli.rs index 63f694fc6..513894890 100644 --- a/pgml-sdks/pgml/src/cli.rs +++ b/pgml-sdks/pgml/src/cli.rs @@ -10,6 +10,9 @@ use pyo3::prelude::*; use sqlx::{Acquire, Executor}; use std::io::Write; +use tokio::fs::File; +use tokio::io::AsyncWriteExt; + /// PostgresML CLI: configure your PostgresML deployments & create connections to remote data sources. #[cfg(feature = "python")] #[derive(Parser, Debug, Clone)] @@ -100,8 +103,11 @@ enum Subcommands { /// Connect your database to PostgresML via dblink. Remote { - #[arg(long)] + #[arg(long, short)] database_url: Option, + + #[arg(long, short)] + pgcat_config_file: Option, }, } @@ -219,8 +225,11 @@ async fn cli_internal() -> anyhow::Result<()> { .await?; } - Subcommands::Remote { database_url } => { - remote(database_url).await?; + Subcommands::Remote { + database_url, + pgcat_config_file, + } => { + remote(database_url, pgcat_config_file).await?; } }; @@ -336,7 +345,12 @@ async fn connect( Ok(()) } -async fn remote(database_url: Option) -> anyhow::Result<()> { +async fn remote( + database_url: Option, + pgcat_config_file: Option, +) -> anyhow::Result<()> { + use pgcat_config::{Config, Database, Server, ServerRole, User}; + let database_url = user_input!(database_url, "PostgresML DATABASE_URL"); let database_url = url::Url::parse(&database_url)?; let user = database_url.username(); @@ -375,6 +389,35 @@ async fn remote(database_url: Option) -> anyhow::Result<()> { .replace("{database_name}", &database) .replace("{port}", &port); + let mut pgcat_config = Config::default(); + let mut pgcat_database = Database::default(); + let mut pgcat_user = User::default(); + let mut pgcat_server = Server::default(); + pgcat_user.password = password.to_owned(); + pgcat_database.database_name = database.clone(); + + pgcat_database.users.clear(); + pgcat_database.users.insert(user.into(), pgcat_user); + let servers_entry = pgcat_database.shards.get_mut("0").unwrap(); + + servers_entry.servers.clear(); + + pgcat_server.host = host.to_owned(); + pgcat_server.port = port.parse().unwrap(); + pgcat_server.role = ServerRole::Primary; + + servers_entry + .servers + .insert("postgresml".into(), pgcat_server); + + pgcat_config.databases.clear(); + pgcat_config.databases.insert(database, pgcat_database); + + File::create(pgcat_config_file.unwrap_or("pgcat.toml".to_string())) + .await? + .write_all(toml::to_string(&pgcat_config)?.as_bytes()) + .await?; + println!("{}", syntax_highlight(&sql)); Ok(()) } From ed7e8e86fbe4b7138a91d3eb40bdd08209ab4291 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Fri, 19 Apr 2024 15:53:03 -0700 Subject: [PATCH 03/12] blog --- .../blog/using-postgresml-with-aws-rds.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 pgml-cms/blog/using-postgresml-with-aws-rds.md diff --git a/pgml-cms/blog/using-postgresml-with-aws-rds.md b/pgml-cms/blog/using-postgresml-with-aws-rds.md new file mode 100644 index 000000000..1a3db50f2 --- /dev/null +++ b/pgml-cms/blog/using-postgresml-with-aws-rds.md @@ -0,0 +1,25 @@ +--- +description: >- + Integrating PostgresML with other managed PostgreSQL services like AWS RDS. +--- + +# Using PostgresML with AWS RSD + +
+ +
Author
+ +
+ +Lev Kokotov + +April 19, 2024 + +## Introduction + +PostgresML is an Postgres extension that enables you to run machine learning models and LLMs inside your database. Our managed cloud provides PostgresML deployments on GPUs, but other cloud providers like AWS RDS do not. In an effort to make PostgresML available to everyone, +we're adding support for using PostgresML with AWS RDS and other cloud providers. + +## Getting started + +Make sure to create an account on our cloud and create a serverless AI engine. From 47b18e7dfe3a6e38928f9661dbfbc6c439635ecc Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Sun, 21 Apr 2024 13:53:54 -0700 Subject: [PATCH 04/12] RDS proxy --- packages/pgml-rds-proxy/Dockerfile | 9 ++++ packages/pgml-rds-proxy/build.sh | 23 +++++++++ packages/pgml-rds-proxy/ec2.tf | 47 +++++++++++++++++++ .../pgml-rds-proxy/pgml-rds-proxy.service | 0 packages/pgml-rds-proxy/run.sh | 5 ++ 5 files changed, 84 insertions(+) create mode 100644 packages/pgml-rds-proxy/Dockerfile create mode 100644 packages/pgml-rds-proxy/build.sh create mode 100644 packages/pgml-rds-proxy/ec2.tf create mode 100644 packages/pgml-rds-proxy/pgml-rds-proxy.service create mode 100644 packages/pgml-rds-proxy/run.sh diff --git a/packages/pgml-rds-proxy/Dockerfile b/packages/pgml-rds-proxy/Dockerfile new file mode 100644 index 000000000..fc65a8228 --- /dev/null +++ b/packages/pgml-rds-proxy/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:22.04 +RUN apt update && \ + apt install -y curl postgresql-client-common postgresql-client-14 && \ + apt clean +WORKDIR /pgml-rds-proxy +COPY --chown=root:root build.sh build.sh +COPY --chown=root:root run.sh run.sh +RUN bash build.sh +ENTRYPOINT ["bash", "run.sh"] diff --git a/packages/pgml-rds-proxy/build.sh b/packages/pgml-rds-proxy/build.sh new file mode 100644 index 000000000..1dc4a4c6a --- /dev/null +++ b/packages/pgml-rds-proxy/build.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# pgml-rds-proxy docker entrypoint +# +# Author: PostgresML +# License: MIT +# +architecture=$(arch) +name=$(uname) +url="https://static.postgresml.org/packages/pgcat" + +if [[ "$architecture" == "aarch64" && "$name" == "Linux" ]]; then + url="${url}/arm64/pgcat" +elif [[ "$architecture" == "x86_64" && "$name" == "Linux" ]]; then + url="${url}/amd64/pgcat" +else + echo "Unsupported platform: ${name} ${architecture}" + exit 1 +fi + +echo "Downloading pgcat from $url" +curl -L -o /usr/local/bin/pgcat ${url} +chmod +x /usr/local/bin/pgcat diff --git a/packages/pgml-rds-proxy/ec2.tf b/packages/pgml-rds-proxy/ec2.tf new file mode 100644 index 000000000..a6b519579 --- /dev/null +++ b/packages/pgml-rds-proxy/ec2.tf @@ -0,0 +1,47 @@ +# +# Terraform configuration for pgml-rds-proxy running on EC2. +# +# Author: PostgresML +# License: MIT +# +provider "aws" { + region = "us-west-2" +} + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.46.0" + } + } + required_version = ">= 1.2.0" +} + +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + owners = ["099720109477"] # Canonical +} + +provider "aws" { + region = "us-west-2" +} + +resource "aws_instance" "example_server" { + ami = data.aws_ami.ubuntu.id + instance_type = "t2.micro" + tags = { + Name = "pgml-rds-proxy" + } +} diff --git a/packages/pgml-rds-proxy/pgml-rds-proxy.service b/packages/pgml-rds-proxy/pgml-rds-proxy.service new file mode 100644 index 000000000..e69de29bb diff --git a/packages/pgml-rds-proxy/run.sh b/packages/pgml-rds-proxy/run.sh new file mode 100644 index 000000000..e540d7383 --- /dev/null +++ b/packages/pgml-rds-proxy/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# +# +# +exec /usr/local/bin/pgcat --database-url ${DATABASE_URL} From eb212ba09ca5d52d4f23b894d48255db0f4c9b26 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Sun, 21 Apr 2024 13:56:55 -0700 Subject: [PATCH 05/12] no config generation --- pgml-sdks/pgml/Cargo.lock | 65 --------------------------------------- pgml-sdks/pgml/Cargo.toml | 2 -- pgml-sdks/pgml/src/cli.rs | 50 +++--------------------------- 3 files changed, 4 insertions(+), 113 deletions(-) diff --git a/pgml-sdks/pgml/Cargo.lock b/pgml-sdks/pgml/Cargo.lock index abc7b5c37..c9d5723db 100644 --- a/pgml-sdks/pgml/Cargo.lock +++ b/pgml-sdks/pgml/Cargo.lock @@ -1529,17 +1529,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pgcat_config" -version = "2.0.0-alpha3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d1904cc180d4021ce8acd54eb92074ed10b34bc531faa418ea3d69e1d30b94" -dependencies = [ - "serde", - "socket2", - "toml", -] - [[package]] name = "pgml" version = "1.0.2" @@ -1560,7 +1549,6 @@ dependencies = [ "neon", "once_cell", "parking_lot", - "pgcat_config", "pyo3", "pyo3-asyncio", "regex", @@ -1572,7 +1560,6 @@ dependencies = [ "serde_json", "sqlx", "tokio", - "toml", "tracing", "tracing-subscriber", "url", @@ -2136,15 +2123,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2737,40 +2715,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -3253,15 +3197,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "winnow" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" diff --git a/pgml-sdks/pgml/Cargo.toml b/pgml-sdks/pgml/Cargo.toml index 612a3eb92..ba4037bce 100644 --- a/pgml-sdks/pgml/Cargo.toml +++ b/pgml-sdks/pgml/Cargo.toml @@ -46,8 +46,6 @@ inquire = "0.6" parking_lot = "0.12.1" once_cell = "1.19.0" url = "2.5.0" -pgcat_config = "2.0.0-alpha3" -toml = "0.8.12" [features] default = [] diff --git a/pgml-sdks/pgml/src/cli.rs b/pgml-sdks/pgml/src/cli.rs index 513894890..c77c9d5b0 100644 --- a/pgml-sdks/pgml/src/cli.rs +++ b/pgml-sdks/pgml/src/cli.rs @@ -10,9 +10,6 @@ use pyo3::prelude::*; use sqlx::{Acquire, Executor}; use std::io::Write; -use tokio::fs::File; -use tokio::io::AsyncWriteExt; - /// PostgresML CLI: configure your PostgresML deployments & create connections to remote data sources. #[cfg(feature = "python")] #[derive(Parser, Debug, Clone)] @@ -103,11 +100,9 @@ enum Subcommands { /// Connect your database to PostgresML via dblink. Remote { + /// DATABASE_URL. #[arg(long, short)] database_url: Option, - - #[arg(long, short)] - pgcat_config_file: Option, }, } @@ -225,11 +220,8 @@ async fn cli_internal() -> anyhow::Result<()> { .await?; } - Subcommands::Remote { - database_url, - pgcat_config_file, - } => { - remote(database_url, pgcat_config_file).await?; + Subcommands::Remote { database_url } => { + remote(database_url).await?; } }; @@ -345,12 +337,7 @@ async fn connect( Ok(()) } -async fn remote( - database_url: Option, - pgcat_config_file: Option, -) -> anyhow::Result<()> { - use pgcat_config::{Config, Database, Server, ServerRole, User}; - +async fn remote(database_url: Option) -> anyhow::Result<()> { let database_url = user_input!(database_url, "PostgresML DATABASE_URL"); let database_url = url::Url::parse(&database_url)?; let user = database_url.username(); @@ -389,35 +376,6 @@ async fn remote( .replace("{database_name}", &database) .replace("{port}", &port); - let mut pgcat_config = Config::default(); - let mut pgcat_database = Database::default(); - let mut pgcat_user = User::default(); - let mut pgcat_server = Server::default(); - pgcat_user.password = password.to_owned(); - pgcat_database.database_name = database.clone(); - - pgcat_database.users.clear(); - pgcat_database.users.insert(user.into(), pgcat_user); - let servers_entry = pgcat_database.shards.get_mut("0").unwrap(); - - servers_entry.servers.clear(); - - pgcat_server.host = host.to_owned(); - pgcat_server.port = port.parse().unwrap(); - pgcat_server.role = ServerRole::Primary; - - servers_entry - .servers - .insert("postgresml".into(), pgcat_server); - - pgcat_config.databases.clear(); - pgcat_config.databases.insert(database, pgcat_database); - - File::create(pgcat_config_file.unwrap_or("pgcat.toml".to_string())) - .await? - .write_all(toml::to_string(&pgcat_config)?.as_bytes()) - .await?; - println!("{}", syntax_highlight(&sql)); Ok(()) } From cbb9f088693955c0cd4c3a0aaedf6b86cafc5e19 Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 13:44:46 -0700 Subject: [PATCH 06/12] Build image --- .github/workflows/pgml-rds-proxy.yaml | 27 +++++++ packages/pgml-rds-proxy/Dockerfile | 5 +- packages/pgml-rds-proxy/README.md | 73 +++++++++++++++++++ packages/pgml-rds-proxy/build-docker-image.sh | 17 +++++ .../{build.sh => download-pgcat.sh} | 8 +- packages/pgml-rds-proxy/ec2.tf | 47 ------------ .../pgml-rds-proxy/pgml-rds-proxy.service | 0 packages/pgml-rds-proxy/run.sh | 6 ++ 8 files changed, 131 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/pgml-rds-proxy.yaml create mode 100644 packages/pgml-rds-proxy/README.md create mode 100644 packages/pgml-rds-proxy/build-docker-image.sh rename packages/pgml-rds-proxy/{build.sh => download-pgcat.sh} (70%) delete mode 100644 packages/pgml-rds-proxy/ec2.tf delete mode 100644 packages/pgml-rds-proxy/pgml-rds-proxy.service diff --git a/.github/workflows/pgml-rds-proxy.yaml b/.github/workflows/pgml-rds-proxy.yaml new file mode 100644 index 000000000..12f0424d0 --- /dev/null +++ b/.github/workflows/pgml-rds-proxy.yaml @@ -0,0 +1,27 @@ +name: Build and release pgml-rds-proxy Docker image + +on: + workflow_dispatch: +jobs: + # + # PostgresML Docker image. + # + publish-proxy-docker-image: + strategy: + matrix: + os: ["buildjet-4vcpu-ubuntu-2204"] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: packages/pgml-rds-proxy + steps: + - uses: actions/checkout@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push Docker image + run: | + bash build-docker-image.sh diff --git a/packages/pgml-rds-proxy/Dockerfile b/packages/pgml-rds-proxy/Dockerfile index fc65a8228..b3feea9b7 100644 --- a/packages/pgml-rds-proxy/Dockerfile +++ b/packages/pgml-rds-proxy/Dockerfile @@ -3,7 +3,8 @@ RUN apt update && \ apt install -y curl postgresql-client-common postgresql-client-14 && \ apt clean WORKDIR /pgml-rds-proxy -COPY --chown=root:root build.sh build.sh +ARG PGCAT_VERSION +COPY --chown=root:root download-pgcat.sh download-pgcat.sh COPY --chown=root:root run.sh run.sh -RUN bash build.sh +RUN bash download-pgcat.sh ENTRYPOINT ["bash", "run.sh"] diff --git a/packages/pgml-rds-proxy/README.md b/packages/pgml-rds-proxy/README.md new file mode 100644 index 000000000..aece1cb68 --- /dev/null +++ b/packages/pgml-rds-proxy/README.md @@ -0,0 +1,73 @@ +# pgml-rds-proxy + +A pgcat-based PostgreSQL proxy that allows to use PostgresML functions on managed PostgreSQL databases that may not have Internet access, like AWS RDS. + +## Getting started + +A Docker image is provided and is the easiest way to get started. To run the image, you can simply: + +```bash +docker run \ + -e DATABASE_URL=postgres://pg:ml@sql.cloud.postgresml.org:38042/pgml \ + -p 6432:6432 \ + ghcr.io/postgresml/pgml-rds-proxy:latest +``` + +**Note:** Replace the `DATABASE_URL` above with the `DATABASE_URL` of your own PostgresML database. + +If you're running this on EC2, make sure the instance is placed inside the same VPC as your RDS database and that the RDS database is allowed to make outbound connections to the EC2 instance. +The example above starts the proxy process on port 6432, so for your security group configuration, make sure the database can make outbound connections to the EC2 instance using TCP on port 6432. + +### Configure FDW + +We'll be using the Foreign Data Wrapper extension to connect fromn your RDS database to PostgresML, forwarding the connection through the proxy. If you're running the proxy on EC2, take note of the private IP +or DNS entry of the instance. + +Before proceeding, make sure you have the following extensions installed into your RDS database: + +```postgresql +CREATE EXTENSION IF NOT EXISTS dblink; +CREATE EXTENSION IF NOT EXISTS postgres_fdw; +``` + +Both of these require superuser, so make sure you're running these commands with a user that has the `rds_superuser` role. + +To create a foreign data wrapper connection, take your PostgresML host and port and replace the host with the private IP or DNS entry of the instance. + +```postgresql +CREATE SERVER postgresml +FOREIGN DATA WRAPPER postgres_fdw +OPTIONS ( + host '127.0.0.1', + port '6432', + dbname 'pgml' +); +``` + +Replace the value for `host` with the private IP or DNS entry of the EC2 instance running the proxy. Replace the `dbname` with the name of the database from your PostgresML database `DATABASE_URL`. + +#### User mapping + +PostgresML and the proxy requires authentication. For each user that will use the connection, create a user mappping, like so: + +```postgresql +CREATE USER MAPPING +FOR CURRENT_USER +SERVER postgresml +OPTIONS ( + user 'pg', + password 'ml' +); +``` + +Replace the values for `user` and `password` with the values from your PostgresML database `DATABASE_URL`. This example contains values that will only work with our demo server and aren't suitable for production. `CURRENT_USER` is a special PostgreSQL variable that's replaced by the name of the user running the command. If you want to create this mapping for other users, replace it with the name of the user/role. + +### Test the connection + +To test the connection, you can use `dblink`: + +``` +SELECT * FROM dblink('postgresml', 'SELECT * FROM pgml.embed(''intfloat/e5-small'', ''embed this text'') AS embedding') AS t1(embedding real[386]); +``` + +If everything is configured correctly, you should see an array of 386 floating points, your first embedding generated using PostgresML on AWS RDS. Both dblink and the proxy makes efficient use of connections, so queries will be executed as fast as the network connection allows. diff --git a/packages/pgml-rds-proxy/build-docker-image.sh b/packages/pgml-rds-proxy/build-docker-image.sh new file mode 100644 index 000000000..0bc4700f9 --- /dev/null +++ b/packages/pgml-rds-proxy/build-docker-image.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# +# +# +set -ex + +version="${1:-2.0.0-alpha18}" +image="ghcr.io/postgresml/pgml-rds-proxy" +image_with_version="$image:$version" + +docker run --privileged --rm tonistiigi/binfmt --install all +docker buildx create --use --name mybuilder || true +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${image_with_version} \ + --push \ + . diff --git a/packages/pgml-rds-proxy/build.sh b/packages/pgml-rds-proxy/download-pgcat.sh similarity index 70% rename from packages/pgml-rds-proxy/build.sh rename to packages/pgml-rds-proxy/download-pgcat.sh index 1dc4a4c6a..883234671 100644 --- a/packages/pgml-rds-proxy/build.sh +++ b/packages/pgml-rds-proxy/download-pgcat.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# pgml-rds-proxy docker entrypoint +# Download the right version of pgcat for the architecture. # # Author: PostgresML # License: MIT @@ -8,11 +8,13 @@ architecture=$(arch) name=$(uname) url="https://static.postgresml.org/packages/pgcat" +version="${PGCAT_VERSION:-2.0.0-alpha16}" +bin_name="pgcat2-$version.bin" if [[ "$architecture" == "aarch64" && "$name" == "Linux" ]]; then - url="${url}/arm64/pgcat" + url="${url}/arm64/$bin_name" elif [[ "$architecture" == "x86_64" && "$name" == "Linux" ]]; then - url="${url}/amd64/pgcat" + url="${url}/amd64/$bin_name" else echo "Unsupported platform: ${name} ${architecture}" exit 1 diff --git a/packages/pgml-rds-proxy/ec2.tf b/packages/pgml-rds-proxy/ec2.tf deleted file mode 100644 index a6b519579..000000000 --- a/packages/pgml-rds-proxy/ec2.tf +++ /dev/null @@ -1,47 +0,0 @@ -# -# Terraform configuration for pgml-rds-proxy running on EC2. -# -# Author: PostgresML -# License: MIT -# -provider "aws" { - region = "us-west-2" -} - -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.46.0" - } - } - required_version = ">= 1.2.0" -} - -data "aws_ami" "ubuntu" { - most_recent = true - - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] - } - - filter { - name = "virtualization-type" - values = ["hvm"] - } - - owners = ["099720109477"] # Canonical -} - -provider "aws" { - region = "us-west-2" -} - -resource "aws_instance" "example_server" { - ami = data.aws_ami.ubuntu.id - instance_type = "t2.micro" - tags = { - Name = "pgml-rds-proxy" - } -} diff --git a/packages/pgml-rds-proxy/pgml-rds-proxy.service b/packages/pgml-rds-proxy/pgml-rds-proxy.service deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/pgml-rds-proxy/run.sh b/packages/pgml-rds-proxy/run.sh index e540d7383..0df30c75e 100644 --- a/packages/pgml-rds-proxy/run.sh +++ b/packages/pgml-rds-proxy/run.sh @@ -1,5 +1,11 @@ #!/bin/bash # +# Configure pgcat from a DATABASE_URL environment variable and run it as PID 1. +# This will regenerate the configuration file every time so modifications to it won't be saved. # +# If you want to modify the configuration file, generate it first and then run pgcat with `--config ` instead. +# +# Author: PostgresML +# License: MIT # exec /usr/local/bin/pgcat --database-url ${DATABASE_URL} From c0a7dd31a34934205b9fcaf3ca222cf6e81ed2f7 Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 13:46:48 -0700 Subject: [PATCH 07/12] remove post --- .../blog/using-postgresml-with-aws-rds.md | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 pgml-cms/blog/using-postgresml-with-aws-rds.md diff --git a/pgml-cms/blog/using-postgresml-with-aws-rds.md b/pgml-cms/blog/using-postgresml-with-aws-rds.md deleted file mode 100644 index 1a3db50f2..000000000 --- a/pgml-cms/blog/using-postgresml-with-aws-rds.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -description: >- - Integrating PostgresML with other managed PostgreSQL services like AWS RDS. ---- - -# Using PostgresML with AWS RSD - -
- -
Author
- -
- -Lev Kokotov - -April 19, 2024 - -## Introduction - -PostgresML is an Postgres extension that enables you to run machine learning models and LLMs inside your database. Our managed cloud provides PostgresML deployments on GPUs, but other cloud providers like AWS RDS do not. In an effort to make PostgresML available to everyone, -we're adding support for using PostgresML with AWS RDS and other cloud providers. - -## Getting started - -Make sure to create an account on our cloud and create a serverless AI engine. From 2096722d972da831d0b75c5c836f7a4b034187b8 Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 13:50:42 -0700 Subject: [PATCH 08/12] spell --- packages/pgml-rds-proxy/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pgml-rds-proxy/README.md b/packages/pgml-rds-proxy/README.md index aece1cb68..fa6035813 100644 --- a/packages/pgml-rds-proxy/README.md +++ b/packages/pgml-rds-proxy/README.md @@ -20,7 +20,7 @@ The example above starts the proxy process on port 6432, so for your security gr ### Configure FDW -We'll be using the Foreign Data Wrapper extension to connect fromn your RDS database to PostgresML, forwarding the connection through the proxy. If you're running the proxy on EC2, take note of the private IP +We'll be using the Foreign Data Wrapper extension to connect from your RDS database to PostgresML, forwarding the connection through the proxy. If you're running the proxy on EC2, take note of the private IP or DNS entry of the instance. Before proceeding, make sure you have the following extensions installed into your RDS database: @@ -48,7 +48,7 @@ Replace the value for `host` with the private IP or DNS entry of the EC2 instan #### User mapping -PostgresML and the proxy requires authentication. For each user that will use the connection, create a user mappping, like so: +PostgresML and the proxy requires authentication. For each user that will use the connection, create a user mapping, like so: ```postgresql CREATE USER MAPPING From 8f0036a865c4619e3f389ab2d89ec847b53e342c Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 14:04:34 -0700 Subject: [PATCH 09/12] version --- packages/pgml-rds-proxy/Dockerfile | 2 +- packages/pgml-rds-proxy/build-docker-image.sh | 8 +++----- packages/pgml-rds-proxy/download-pgcat.sh | 7 ++++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/pgml-rds-proxy/Dockerfile b/packages/pgml-rds-proxy/Dockerfile index b3feea9b7..dadef5791 100644 --- a/packages/pgml-rds-proxy/Dockerfile +++ b/packages/pgml-rds-proxy/Dockerfile @@ -1,9 +1,9 @@ FROM ubuntu:22.04 +ENV PGCAT_VERSION=2.0.0-alpha18 RUN apt update && \ apt install -y curl postgresql-client-common postgresql-client-14 && \ apt clean WORKDIR /pgml-rds-proxy -ARG PGCAT_VERSION COPY --chown=root:root download-pgcat.sh download-pgcat.sh COPY --chown=root:root run.sh run.sh RUN bash download-pgcat.sh diff --git a/packages/pgml-rds-proxy/build-docker-image.sh b/packages/pgml-rds-proxy/build-docker-image.sh index 0bc4700f9..ff78af0f4 100644 --- a/packages/pgml-rds-proxy/build-docker-image.sh +++ b/packages/pgml-rds-proxy/build-docker-image.sh @@ -4,14 +4,12 @@ # set -ex -version="${1:-2.0.0-alpha18}" -image="ghcr.io/postgresml/pgml-rds-proxy" -image_with_version="$image:$version" - docker run --privileged --rm tonistiigi/binfmt --install all docker buildx create --use --name mybuilder || true docker buildx build \ --platform linux/amd64,linux/arm64 \ - --tag ${image_with_version} \ + --tag ghcr.io/postgresml/pgml-rds-proxy:latest \ + --progress plain \ + --no-cache \ --push \ . diff --git a/packages/pgml-rds-proxy/download-pgcat.sh b/packages/pgml-rds-proxy/download-pgcat.sh index 883234671..26cb609e7 100644 --- a/packages/pgml-rds-proxy/download-pgcat.sh +++ b/packages/pgml-rds-proxy/download-pgcat.sh @@ -8,9 +8,14 @@ architecture=$(arch) name=$(uname) url="https://static.postgresml.org/packages/pgcat" -version="${PGCAT_VERSION:-2.0.0-alpha16}" +version="$PGCAT_VERSION" bin_name="pgcat2-$version.bin" +if [[ -z "$version" ]]; then + echo "PGCAT_VERSION environment variable is not set" + exit 1 +fi + if [[ "$architecture" == "aarch64" && "$name" == "Linux" ]]; then url="${url}/arm64/$bin_name" elif [[ "$architecture" == "x86_64" && "$name" == "Linux" ]]; then From f154127f3eea6b00741a61ac38e531dbc78e2f4e Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 14:04:55 -0700 Subject: [PATCH 10/12] bad comment --- .github/workflows/pgml-rds-proxy.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pgml-rds-proxy.yaml b/.github/workflows/pgml-rds-proxy.yaml index 12f0424d0..cfffc4482 100644 --- a/.github/workflows/pgml-rds-proxy.yaml +++ b/.github/workflows/pgml-rds-proxy.yaml @@ -3,9 +3,6 @@ name: Build and release pgml-rds-proxy Docker image on: workflow_dispatch: jobs: - # - # PostgresML Docker image. - # publish-proxy-docker-image: strategy: matrix: From 5ef7b5d2d368942cbbfaac8935dc525d7a424ff8 Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 14:05:49 -0700 Subject: [PATCH 11/12] save --- packages/pgml-rds-proxy/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/pgml-rds-proxy/README.md b/packages/pgml-rds-proxy/README.md index fa6035813..a29700273 100644 --- a/packages/pgml-rds-proxy/README.md +++ b/packages/pgml-rds-proxy/README.md @@ -67,7 +67,13 @@ Replace the values for `user` and `password` with the values from your PostgresM To test the connection, you can use `dblink`: ``` -SELECT * FROM dblink('postgresml', 'SELECT * FROM pgml.embed(''intfloat/e5-small'', ''embed this text'') AS embedding') AS t1(embedding real[386]); +SELECT + * +FROM + dblink( + 'postgresml', + 'SELECT * FROM pgml.embed(''intfloat/e5-small'', ''embed this text'') AS embedding' +) AS t1(embedding real[386]); ``` If everything is configured correctly, you should see an array of 386 floating points, your first embedding generated using PostgresML on AWS RDS. Both dblink and the proxy makes efficient use of connections, so queries will be executed as fast as the network connection allows. From 3bfe9513cdc11fd1c424340365ab3c78242a279f Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 22 Apr 2024 14:06:14 -0700 Subject: [PATCH 12/12] space --- packages/pgml-rds-proxy/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/pgml-rds-proxy/README.md b/packages/pgml-rds-proxy/README.md index a29700273..6cf35569b 100644 --- a/packages/pgml-rds-proxy/README.md +++ b/packages/pgml-rds-proxy/README.md @@ -8,9 +8,9 @@ A Docker image is provided and is the easiest way to get started. To run the ima ```bash docker run \ - -e DATABASE_URL=postgres://pg:ml@sql.cloud.postgresml.org:38042/pgml \ - -p 6432:6432 \ - ghcr.io/postgresml/pgml-rds-proxy:latest + -e DATABASE_URL=postgres://pg:ml@sql.cloud.postgresml.org:38042/pgml \ + -p 6432:6432 \ + ghcr.io/postgresml/pgml-rds-proxy:latest ``` **Note:** Replace the `DATABASE_URL` above with the `DATABASE_URL` of your own PostgresML database. @@ -38,9 +38,9 @@ To create a foreign data wrapper connection, take your PostgresML host and port CREATE SERVER postgresml FOREIGN DATA WRAPPER postgres_fdw OPTIONS ( - host '127.0.0.1', - port '6432', - dbname 'pgml' + host '127.0.0.1', + port '6432', + dbname 'pgml' ); ``` @@ -55,8 +55,8 @@ CREATE USER MAPPING FOR CURRENT_USER SERVER postgresml OPTIONS ( - user 'pg', - password 'ml' + user 'pg', + password 'ml' ); ``` @@ -68,11 +68,11 @@ To test the connection, you can use `dblink`: ``` SELECT - * + * FROM - dblink( - 'postgresml', - 'SELECT * FROM pgml.embed(''intfloat/e5-small'', ''embed this text'') AS embedding' + dblink( + 'postgresml', + 'SELECT * FROM pgml.embed(''intfloat/e5-small'', ''embed this text'') AS embedding' ) AS t1(embedding real[386]); ```