这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

应用模板

使用开箱即用的配置,拉起使用 PostgreSQL 作为核心数据库的应用业务软件。

PostgreSQL 是世界上最流行的数据库,有无数的经典的软件构建于 PostgreSQL 之上。Pigsty 为其提供了开箱即用的置备模板。

Pigsty 的模板会使用由 Pigsty 创建管理的外部 PostgreSQL,MinIO,Etcd 服务,包含了完整的备份恢复,高可用,监控日志告警,IaC,连接池,负载均衡等功能。 并一并解决了基础设施,Nginx转发,证书申请等“最后一公里”问题。相比使用 Docker 拉起包括数据库在内的整套软件的 “玩具模式”,提供了“企业级”所需的能力。


1 - Dify:自建AI工作流平台

如何使用 Pigsty 自建 AI Workflow LLMOps 平台 —— Dify,并使用外部 PostgreSQL,PGVector,Redis 作为存储?

Dify 是一个生成式 AI 应用创新引擎,开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力,帮助用户轻松构建和运营生成式 AI 原生应用。

Pigsty 提供对自建 Dify 的支持,您可以一键拉起 Dify ,并将关键状态保存于外部由 Pigsty 管理的 PostgreSQL ,并使用同一个 PG 中的 pgvector 作为向量数据库,进一步简化版部署。


快速上手

curl -fsSL https://repo.pigsty.io/get | bash; cd ~/pigsty 
./bootstrap                # 安装 Pigsty 依赖
./configure -c app/dify    # 使用 Dify 配置模板 (请在这一步修改生成配置文件 pigsty.yml 中的各种密码!)
./install.yml              # 安装 Pigsty
./docker.yml               # 安装 Docker 模块
./app.yml                  # 拉起 Dify

Dify 默认监听于 5001 端口,你可以通过浏览器访问 http://<ip>:5001,并设置初始用户与密码。

请注意,Odoo 无状态部分使用 Docker 拉起,然而中国大陆 DockerHub 被墙, 你可能需要参考教程 来配置 镜像站代理服务器 方可顺利完成最后一步。 在 Pigsty 商业版 中,我们可以帮您丝滑解决这个问题。

请注意,Redis 在 Dify 中作为“缓存”使用,因此默认同样使用容器拉起。但您同样可以使用容器外部由 Pigsty 所托管的 Redis 作为 Dify 的缓存


配置文件

conf/app/dify.yml 中有一个模板配置文件,定义了单机 Dify 所需的资源。

configure 之后,您应该根据自己的实际需求,修改这里的密码类参数。请注意修改密码务必匹配:例如你如果在 pg_users 中修改了 dify 数据库用户的密码, 那么也同样要修改 all.children.odoo.vars.apps.<dify>.conf.PG_PASSWORD 参数,以确保 Odoo 与 PostgreSQL 数据库的连接正常。

这里需要特别注意的是,请务必使用 openssl rand -base64 42 生成一个你自己的随机密钥,替换 SECRET_KEY,并相应更改其他密码类参数。

all:
  children:

    # the dify application (default username & password: admin/admin)
    dify:
      hosts: { 10.10.10.10: {} }
      vars:
        app: dify   # specify app name to be installed (in the apps)
        apps:       # define all applications
          dify:     # app name, should have corresponding ~/app/dify folder
            conf:   # override /opt/dify/.env config file
              # A secret key for signing and encryption, gen with `openssl rand -base64 42` (CHANGE PASSWORD!)
              SECRET_KEY: sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U
              DB_USERNAME: dify
              DB_PASSWORD: difyai123456
              DB_HOST: 10.10.10.10
              DB_PORT: 5432
              DB_DATABASE: dify
              VECTOR_STORE: pgvector
              PGVECTOR_HOST: 10.10.10.10
              PGVECTOR_PORT: 5432
              PGVECTOR_USER: dify
              PGVECTOR_PASSWORD: difyai123456
              PGVECTOR_DATABASE: dify
              PGVECTOR_MIN_CONNECTION: 2
              PGVECTOR_MAX_CONNECTION: 10
              NGINX_SERVER_NAME: localhost
              DIFY_PORT: 5001 # expose DIFY nginx service with port 5001 by default
              #STORAGE_TYPE: s3
              #S3_ENDPOINT: 'https://sss.pigsty'
              #S3_BUCKET_NAME: 'dify'
              #S3_ACCESS_KEY: 'dify'
              #S3_SECRET_KEY: 'S3User.Dify'
              #S3_REGION: 'us-east-1'

    pg-meta:
      hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
      vars:
        pg_cluster: pg-meta
        pg_users:
          - { name: dify ,password: difyai123456 ,pgbouncer: true ,roles: [ dbrole_admin ] ,superuser: true ,comment: dify superuser }
        pg_databases:
          - { name: dify ,owner: dify ,revokeconn: true ,comment: dify main database  }
        pg_hba_rules:
          - { user: dify ,db: all ,addr: 172.17.0.0/16  ,auth: pwd ,title: 'allow dify access from local docker network' }
          - { user: dbuser_view , db: all ,addr: infra ,auth: pwd ,title: 'allow grafana dashboard access cmdb from infra nodes' }

    infra: { hosts: { 10.10.10.10: { infra_seq: 1 } } }
    etcd:  { hosts: { 10.10.10.10: { etcd_seq: 1 } }, vars: { etcd_cluster: etcd } }
    minio: { hosts: { 10.10.10.10: { minio_seq: 1 } }, vars: { minio_cluster: minio } }

  vars:                               # global variables
    version: v3.3.0                   # pigsty version string
    admin_ip: 10.10.10.10             # admin node ip address
    region: default                   # upstream mirror region: default|china|europe
    node_tune: oltp                   # node tuning specs: oltp,olap,tiny,crit
    pg_conf: oltp.yml                 # pgsql tuning specs: {oltp,olap,tiny,crit}.yml

    docker_enabled: true              # enable docker on app group
    #docker_registry_mirrors: ["https://docker.m.daocloud.io"] # use dao cloud mirror in mainland china
    proxy_env:                        # global proxy env when downloading packages & pull docker images
      no_proxy: "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.tsinghua.edu.cn"
      #http_proxy:  127.0.0.1:12345 # add your proxy env here for downloading packages or pull images
      #https_proxy: 127.0.0.1:12345 # usually the proxy is format as http://user:pass@proxy.xxx.com
      #all_proxy:   127.0.0.1:12345

    infra_portal: # domain names and upstream servers
      home         : { domain: h.pigsty }
      grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
      prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
      alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
      blackbox     : { endpoint: "${admin_ip}:9115" }
      loki         : { endpoint: "${admin_ip}:3100" }
      minio        : { domain: m.pigsty    ,endpoint: "${admin_ip}:9001" ,scheme: https ,websocket: true }
      dify: { domain: dify.pigsty ,endpoint: "10.10.10.10:5001" ,websocket: true }
      # setup your own domain name here ^^^, or use default domain name, or ip + 5001 port direct access
      # certbot --nginx --agree-tos --email your@email.com -n -d dify.your.domain    # replace with your email & dify domain

    #----------------------------------#
    # Credential: CHANGE THESE PASSWORDS
    #----------------------------------#
    #grafana_admin_username: admin
    grafana_admin_password: pigsty
    #pg_admin_username: dbuser_dba
    pg_admin_password: DBUser.DBA
    #pg_monitor_username: dbuser_monitor
    pg_monitor_password: DBUser.Monitor
    #pg_replication_username: replicator
    pg_replication_password: DBUser.Replicator
    #patroni_username: postgres
    patroni_password: Patroni.API
    #haproxy_admin_username: admin
    haproxy_admin_password: pigsty
    #minio_access_key: minioadmin
    minio_secret_key: minioadmin      # minio root secret key, `minioadmin` by default

    # use minio as supabase file storage, single node single driver mode for demonstration purpose
    minio_buckets: [ { name: pgsql }, { name: supa } ]
    minio_users:
      - { access_key: dba , secret_key: S3User.DBA, policy: consoleAdmin }
      - { access_key: pgbackrest , secret_key: S3User.Backup ,policy: readwrite }
      - { access_key: dify       , secret_key: S3User.Dify   ,policy: readwrite }   # <----- Change These Too!
    minio_endpoint: https://sss.pigsty:9000    # explicit overwrite minio endpoint with haproxy port
    node_etc_hosts: ["10.10.10.10 sss.pigsty"] # domain name to access minio from all nodes (required)

    # use minio as default backup repo for PostgreSQL
    pgbackrest_method: minio          # pgbackrest repo method: local,minio,[user-defined...]
    pgbackrest_repo:                  # pgbackrest repo: https://pgbackrest.org/configuration.html#section-repository
      local:                          # default pgbackrest repo with local posix fs
        path: /pg/backup              # local backup directory, `/pg/backup` by default
        retention_full_type: count    # retention full backups by count
        retention_full: 2             # keep 2, at most 3 full backup when using local fs repo
      minio:                          # optional minio repo for pgbackrest
        type: s3                      # minio is s3-compatible, so s3 is used
        s3_endpoint: sss.pigsty       # minio endpoint domain name, `sss.pigsty` by default
        s3_region: us-east-1          # minio region, us-east-1 by default, useless for minio
        s3_bucket: pgsql              # minio bucket name, `pgsql` by default
        s3_key: pgbackrest            # minio user access key for pgbackrest
        s3_key_secret: S3User.Backup  # minio user secret key for pgbackrest
        s3_uri_style: path            # use path style uri for minio rather than host style
        path: /pgbackrest             # minio backup path, default is `/pgbackrest`
        storage_port: 9000            # minio port, 9000 by default
        storage_ca_file: /pg/cert/ca.crt  # minio ca file path, `/pg/cert/ca.crt` by default
        bundle: y                     # bundle small files into a single file
        cipher_type: aes-256-cbc      # enable AES encryption for remote backup repo
        cipher_pass: pgBackRest       # AES encryption password, default is 'pgBackRest'
        retention_full_type: time     # retention full backup by time on minio repo
        retention_full: 14            # keep full backup for last 14 days

    repo_modules: infra,node,pgsql,docker
    repo_packages: [ node-bootstrap, infra-package, infra-addons, node-package1, node-package2, pgsql-utility, docker ]
    repo_extra_packages: [ pg17-main ]
    pg_version: 17

Pigsty的准备工作

我们用 单机安装 的 Pigsty 为例,假设你有一台 IP 地址为 10.10.10.10 的机器,已经 安装好了单机 Pigsty

当然,我们需要在 Pigsty 配置文件 pigsty.yml 中定义一下我们所需的数据库集群。 这里定义了一个名为 pg-meta 的集群,其中有一个名为 dify 的超级业务用户, 一个安装了 pgvector 扩展插件的数据库 dify,以及一条特定的防火墙规则,允许用户通过密码从任何地方访问数据库(你也可以将其限制为docker的网段 172.0.0.0/8 之类更精确的范围)。

pg-meta:
  hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
  vars:
    pg_cluster: pg-meta
    pg_users:
      - { name: dify ,password: difyai123456 ,pgbouncer: true ,roles: [ dbrole_admin ] ,superuser: true ,comment: dify superuser }
    pg_databases:
      - { name: dify ,owner: dify ,revokeconn: true ,comment: dify main database  }
    pg_hba_rules:
      - { user: dify ,db: all ,addr: 172.17.0.0/16  ,auth: pwd ,title: 'allow dify access from local docker network' }
      - { user: dbuser_view , db: all ,addr: infra ,auth: pwd ,title: 'allow grafana dashboard access cmdb from infra nodes' }

这里出于演示目的,我们全部使用单实例配置,你可以参考 Pigsty 文档部署 高可用 的 PG 集群。

bin/pgsql-add  pg-meta                # create the dify database cluster

当然,您也可以在现有的 PostgreSQL 集群,例如 pg-meta 上新定义业务用户与业务数据库,并通过以下命令创建:

bin/pgsql-user pg-meta dify           # create dify biz user
bin/pgsql-db   pg-meta dify           # create dify biz database

您应该可以通过以下的连接串,访问到 PostgreSQL 与 Redis,当然连接信息请根据实际情况进行修改。

psql postgres://dify:difyai123456@10.10.10.10:5432/dify -c 'SELECT 1'

当你确认这个连接串可用后,大功告成,你可以开始部署 Dify 了。

这里出于演示方便的原因,使用IP直连的土办法,如果是多节点的高可用 PG 集群,请参考 接入 一节。

当然,上面的部分是假设你已经是 Pigsty 用户,了解如何部署 PostgreSQL 与 Redis 集群。你可以直接跳过下一节,查看 Dify 如何配置


从零开始的一些说明

如果您已经了解如何配置使用 Pigsty,可以略过本节。

从零安装 Pigsty 需要 准备 一台符合要求的机器节点: Linux / x86_64,静态 IP,使用带有免密 sudo 权限的用户,执行以下命令:

curl -fsSL https://repo.pigsty.io/get | bash

然后依次完成以下步骤:

cd ~/pigsty      # 下载源码包解压后进入 Pigsty 源码目录,完成后续 准备、配置、安装 三个步骤
./bootstrap      # 【可选项】用于确保 Ansible 正常安装,如果 /tmp/pkg.tgz 离线包则使用它
./configure      # 【可选项】执行环境检测,并生成相应的推荐配置文件,如果知道如何配置可以跳过

# …… 这里请修改自动生成的配置 pigsty.yml ,将上面的集群定义填入 all.children 部分内

./install.yml    # 根据生成的配置文件开始在当前节点上执行安装,使用离线安装包大概需要10分钟完成

您应当将上面的 PostgreSQL 集群与 Redis 集群定义填入 pigsty.yml 文件中,然后执行 install.yml 完成安装。

Redis安装问题

Pigsty 默认不会安装 Redis,所以您需要使用 redis.yml 剧本显式完成 Redis 安装:

./redis.yml

Docker安装问题

Pigsty 默认不会在当前节点安装 Docker,所以您需要使用 docker.yml 剧本安装 Docker。

./docker.yml

Docker Hub 被墙问题

请注意,对于中国大陆用户来说,Docker Hub 与各镜像站点目前出于封锁状态,需要 “科学上网” 才能拉取 Dify 所需的镜像,您可以考虑 docker save|load,或者为 Docker Daemon 配置代理。

要为 Docker Daemon 配置代理,您需要在 proxy_env 中指定 http_proxyhttps_proxy 环境变量,该参数会在 docker_config 任务中被写入 /etc/docker/daemon.json 中:

{
  "proxies": {
    "http-proxy": "http://192.168.x.x:8118",
    "https-proxy": "http://192.168.x.x:8118",
    "no-proxy": "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.myqcloud.com,*.tsinghua.edu.cn"
  }
}

当然您也可以直接在配置文件中填入您的 HTTP/HTTPS 代理地址,并使用 systemctl restart docker 重启生效。

$ docker compose pull
[+] Pulling 5/5
✔ worker Skipped - Image is already being pulled by api
✔ web Pulled
✔ api Pulled
✔ ssrf_proxy Pulled
✔ nginx Pulled

配置代理后,镜像都可以成功拉取了。当然您也可以使用其他可用的镜像站点,例如 quay.io 等。


Dify的配置工作

Dify 的配置参数一如往常地放在 .env 文件中,内容如下所示:

所有参数都顾名思义,已经填入了在 Pigsty默认沙箱环境 中可以直接工作的默认值,数据库连接信息请根据您的真实配置,与上面 PG / Redis 集群配置保持一致即可。 我们建议你随便改一下这个 SECRET_KEY 字段,可以使用 openssl rand -base64 42 生成一个强密钥。

# meta parameter
DIFY_PORT=8001 # expose dify nginx service with port 8001 by default
LOG_LEVEL=INFO # The log level for the application. Supported values are `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
SECRET_KEY=sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U # A secret key for signing and encryption, gen with `openssl rand -base64 42`

# postgres credential
PG_USERNAME=dbuser_dify
PG_PASSWORD=DBUser.Dify
PG_HOST=10.10.10.10
PG_PORT=5432
PG_DATABASE=dify

# redis credential
REDIS_HOST=10.10.10.10
REDIS_PORT=6379
REDIS_USERNAME=''
REDIS_PASSWORD=redis.dify

# minio/s3 [OPTIONAL] when STORAGE_TYPE=s3
STORAGE_TYPE=local
S3_ENDPOINT='https://sss.pigsty'
S3_BUCKET_NAME='infra'
S3_ACCESS_KEY='dba'
S3_SECRET_KEY='S3User.DBA'
S3_REGION='us-east-1'

填好连接信息后,我们就可以使用 Docker Compose 拉起 Dify 服务了:

cd pigsty/app/dify && make up

使用Nginx暴露Web服务

Dify 的 Docker Compose 模板里面已经包含了一个 Nginx Server,占据了宿主机的 80 端口。如果你的这台机器就是拿来专门跑 Dify 的那没问题。如果你用的是 Pigsty 单机安装,那么这台宿主机上的 80 端口已经被 Pigsty 部署的 Nginx Web Portal 占据了。

所以,Pigsty 提供的模板中,DIFY_PORT 默认使用了 8001,并通过宿主机上 Pigsty 部署的 Nginx 转发至此端口。当然我们也提供选项B,你也可以直接在 /etc/nginx/conf.d/dify.conf 里使用样例配置,直接指向 Dify 的 webapi 端口。

pigsty.yml 配置文件中的 infra_portal 参数中新增一行 Dify 的配置

infra_portal:                     # domain names and upstream servers
  home         : { domain: h.pigsty }
  grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
  prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
  alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
  blackbox     : { endpoint: "${admin_ip}:9115" }
  loki         : { endpoint: "${admin_ip}:3100" }
  
  dify         : { domain: dify.pigsty ,endpoint: "10.10.10.10:8001", websocket: true }

执行以下剧本,重新生成 Nginx 配置、证书并应用:

./infra.yml -t nginx

当然如果要通过域名访问,你要把自己的域名 dify.pigsty 添加到域名服务器,或者简单地写入:/etc/hostsC:\Windows\System32\drivers\etc\hosts 之类的静态域名解析文件。

然后,你就可以从浏览器中,通过 http://dify.pigsty 访问 Dify IDE 了。

2 - Odoo:自建开源ERP系统

如何拉起开箱即用的企业级应用全家桶 Odoo,并使用 Pigsty 管理其后端 PostgreSQL 数据库。

Odoo 是一个开源的企业级 ERP 系统,提供了从 CRM、销售、采购、库存、生产、财务等全方位的企业管理功能。Odoo 也是一个典型的 Web 应用,底层使用 PostgreSQL 数据库作为存储。

将你所有的业务都汇总入一个平台,简单,高效,省钱,你自己的 ERP!


快速上手

在 “网络条件良好” 的情况下,你可以通过以下命令快速拉起一个 Odoo 实例,使用由 Pigsty 管理的外部 PostgreSQL 数据库:

curl -fsSL https://repo.pigsty.io/get | bash; cd ~/pigsty 
pig sty init               # 安装 Pigsty
./bootstrap                # 安装 Pigsty 依赖
./configure -c app/odoo    # 使用 Odoo 配置模板 (请在这一步修改生成配置文件 pigsty.yml 中的各种密码!)
./install.yml              # 安装 Pigsty
./docker.yml               # 安装 Docker 模块
./app.yml                  # 拉起 Odoo

Odoo 默认监听在 8069 端口,你可以通过浏览器访问 http://<ip>:8069。默认的用户名和密码都是: admin

请注意,Odoo 无状态部分使用 Docker 拉起,然而中国大陆 DockerHub 被墙, 你可能需要参考教程 来配置 镜像站代理服务器 方可顺利完成最后一步。 在 Pigsty 商业版 中,我们可以帮您丝滑解决这个问题。


配置文件

conf/app/odoo.yml 中有一个模板配置文件,定义了单机 Odoo 所需的资源。

configure 之后,您应该根据自己的实际需求,修改这里的密码类参数。请注意修改密码务必匹配:例如你如果在 pg_users 中修改了 odoo 数据库用户的密码, 那么也同样要修改 all.children.odoo.vars.apps.<odoo>.conf.PG_PASSWORD 参数,以确保 Odoo 与 PostgreSQL 数据库的连接正常。

all:
  children:

    # the odoo application (default username & password: admin/admin)
    odoo:
      hosts: { 10.10.10.10: {} }
      vars:
        app: odoo   # specify app name to be installed (in the apps)
        apps:       # define all applications
          odoo:     # app name, should have corresponding ~/app/odoo folder
            file:   # optional directory to be created
              - { path: /data/odoo         ,state: directory, owner: 100, group: 101 }
              - { path: /data/odoo/webdata ,state: directory, owner: 100, group: 101 }
              - { path: /data/odoo/addons  ,state: directory, owner: 100, group: 101 }
            conf:   # override /opt/<app>/.env config file
              PG_HOST: 10.10.10.10            # postgres host
              PG_PORT: 5432                   # postgres port
              PG_USERNAME: odoo               # postgres user
              PG_PASSWORD: DBUser.Odoo        # postgres password
              ODOO_PORT: 8069                 # odoo app port
              ODOO_DATA: /data/odoo/webdata   # odoo webdata
              ODOO_ADDONS: /data/odoo/addons  # odoo plugins
              ODOO_DBNAME: odoo               # odoo database name
              ODOO_VERSION: 18.0              # odoo image version

    # the odoo database
    pg-odoo:
      hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
      vars:
        pg_cluster: pg-odoo
        pg_users:
          - { name: odoo    ,password: DBUser.Odoo ,pgbouncer: true ,roles: [ dbrole_admin ] ,createdb: true ,comment: admin user for odoo service }
          - { name: odoo_ro ,password: DBUser.Odoo ,pgbouncer: true ,roles: [ dbrole_readonly ]  ,comment: read only user for odoo service  }
          - { name: odoo_rw ,password: DBUser.Odoo ,pgbouncer: true ,roles: [ dbrole_readwrite ] ,comment: read write user for odoo service }
        pg_databases:
          - { name: odoo ,owner: odoo ,revokeconn: true ,comment: odoo main database  }
        pg_hba_rules:
          - { user: all ,db: all ,addr: 172.17.0.0/16  ,auth: pwd ,title: 'allow access from local docker network' }
          - { user: dbuser_view , db: all ,addr: infra ,auth: pwd ,title: 'allow grafana dashboard access cmdb from infra nodes' }

    infra: { hosts: { 10.10.10.10: { infra_seq: 1 } } }
    etcd:  { hosts: { 10.10.10.10: { etcd_seq: 1 } }, vars: { etcd_cluster: etcd } }
    #minio: { hosts: { 10.10.10.10: { minio_seq: 1 } }, vars: { minio_cluster: minio } }

  vars:                               # global variables
    version: v3.3.0                   # pigsty version string
    admin_ip: 10.10.10.10             # admin node ip address
    region: default                   # upstream mirror region: default|china|europe
    node_tune: oltp                   # node tuning specs: oltp,olap,tiny,crit
    pg_conf: oltp.yml                 # pgsql tuning specs: {oltp,olap,tiny,crit}.yml

    docker_enabled: true              # enable docker on app group
    #docker_registry_mirrors: ["https://docker.m.daocloud.io"] # use dao cloud mirror in mainland china
    proxy_env:                        # global proxy env when downloading packages & pull docker images
      no_proxy: "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.tsinghua.edu.cn"
      #http_proxy:  127.0.0.1:12345 # add your proxy env here for downloading packages or pull images
      #https_proxy: 127.0.0.1:12345 # usually the proxy is format as http://user:pass@proxy.xxx.com
      #all_proxy:   127.0.0.1:12345

    infra_portal: # domain names and upstream servers
      home         : { domain: h.pigsty }
      grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
      prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
      alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
      blackbox     : { endpoint: "${admin_ip}:9115" }
      loki         : { endpoint: "${admin_ip}:3100" }
      minio        : { domain: m.pigsty    ,endpoint: "${admin_ip}:9001" ,scheme: https ,websocket: true }
      odoo         : { domain: odoo.pigsty, endpoint: "127.0.0.1:8069"   ,websocket: true }  #cert: /path/to/crt ,key: /path/to/key
      # setup your own domain name here ^^^, or use default domain name, or ip + 8069 port direct access
      # certbot --nginx --agree-tos --email your@email.com -n -d odoo.your.domain    # replace with your email & odoo domain

    #----------------------------------#
    # Credential: CHANGE THESE PASSWORDS
    #----------------------------------#
    #grafana_admin_username: admin
    grafana_admin_password: pigsty
    #pg_admin_username: dbuser_dba
    pg_admin_password: DBUser.DBA
    #pg_monitor_username: dbuser_monitor
    pg_monitor_password: DBUser.Monitor
    #pg_replication_username: replicator
    pg_replication_password: DBUser.Replicator
    #patroni_username: postgres
    patroni_password: Patroni.API
    #haproxy_admin_username: admin
    haproxy_admin_password: pigsty

    repo_modules: infra,node,pgsql,docker
    repo_packages: [ node-bootstrap, infra-package, infra-addons, node-package1, node-package2, pgsql-utility, docker ]
    repo_extra_packages: [ pg17-main ]
    pg_version: 17

Odoo 扩展插件

社区中有很多可用的 Odoo 模块,你可以通过下载并将它们放置在 addons 文件夹中来安装它们。

在上面的配置文件中,addons 目录默认为 /data/odoo/addons,把扩展 zip 包放进该目录即可将其“安装”。

要启用这些模块,首先在 Odoo 中进入 开发者模式

Settings -> Generic Settings -> Developer Tools -> Activate the developer Mode

然后,转到 > Apps -> Update Apps List, 然后你可以在面板中找到这些额外的模块并安装它们。

经常使用的免费模块请 参考这里 ,当然以及大家最需要的 Accounting Kit 模块。


对外服务

您可以直接用 IP 地址访问目标服务器上的 8069 端口,访问 Odoo Web 界面,但显然这样的方式对于严肃的场景过于儿戏。 以下是如何使用域名访问 Odoo 的说明与 Pigsty 配置方法:

在上面的配置文件中,已经为 Odoo 设置了在 Infra Nginx 上的反向代理,因此您可以通过 odoo.pigsty 的域名访问 Odoo 网络界面。

infra_portal: # 定义 Nginx 服务器配置
  # ...
  odoo : { domain: odoo.pigsty, endpoint: "127.0.0.1:8069" ,websocket: true }  #cert: /path/to/crt ,key: /path/to/key

如果您想要使用其他域名,请相应修改 domain 参数,如果您的 Odoo 部署在其他服务器上,请相应修改 endpoint 参数。 然后执行 ./infra.yml -t nginx_config,nginx_reload 生效。

在任何情况下通过域名访问 Odoo 都需要配置 DNS 解析,有三种典型的配置方式:

  • 使用真域名,通过云厂商/DNS服务商的解析服务,指向你的服务器公网IP
  • 使用内网 DNS,在你的内网 DNS 服务上添加指向你服务器的内网IP地址
  • 使用本地静态 DNS,在你览器所在主机(/etc/hosts)添加一条静态解析记录

HTTPS证书

如果您想要通过 HTTPS 访问 Odoo 服务,则需要申请 HTTPS 证书。

Pigsty 默认会为你的 Odoo 服务生成一个 “自签名CA” 生成的证书,这个证书是不被浏览器信任的,因此在浏览器中会提示不安全。你可以选择:

  • “我知道不安全,继续访问”
  • 使用 Chrome 浏览器时,你也可以使用敲击键入 thisisunsafe 来绕过证书验证
  • 将 Pigsty 创建的 pigsty-ca 加入信任的根 CA 列表。
  • 花钱当大冤种去买 HTTPS 证书
  • 使用 certbot 申请免费的 HTTPS 证书 (正规且推荐!)

如果你已经有 HTTPS 证书,你可以在 infra_portal 中指定 certkey

infra_portal:
  # ...
  odoo : { domain: odoo.pigsty.cc, endpoint: "127.0.0.1:8069" ,websocket: true ,cert: /etc/cert/odoo.pigsty.cc.crt   ,key: /etc/cert/odoo.pigsty.cc.key  }

然后使用 ./infra.yml -t nginx_config,nginx_launch 更新服务器配置并使其生效 。


免费HTTPS证书

如果你不想当大冤种花钱去买 HTTPS 证书,最简单的办法是使用 Let’s Encrypt 的免费 HTTPS 证书。

Pigsty 默认集成了 certbot,这里是 详细的教程,核心就是以下这行命令:

certbot --nginx --agree-tos --email your@email.com -n -d odoo.pigsty.cc

把上面的 email 换成你自己的邮件地址,域名换成你的域名,然后按照提示操作即可,全自动申请与配置。

请注意,使用 certbot 申请免费的 HTTPS 证书需要:

  • 你的服务器有网络访问,且可以通过公网访问(80/443端口)。
  • 你的域名正确指向这台服务器的公网IP地址,即在域名服务商处配置了正确的 A 记录

使用 Certbot 申请完证书后,默认会修改 Nginx 的配置文件,将 HTTP 服务器重定向到 HTTPS 服务器,而这可能并非你想要的。 你可以通过修改 Pigsty 配置文件中的 infra_portal 参数,将 Certbot 已经 成功签发证书的域名配置到 Nginx 的配置文件中。

infra_portal:
  # ...
  odoo : { domain: odoo.pigsty.cc, endpoint: "127.0.0.1:8069" ,websocket: true ,cert: /etc/cert/odoo.pigsty.cc.crt ,key: /etc/cert/odoo.pigsty.cc.key  }

这里,修改签发证书的服务器定义项,添加 certbot: <domain-name> ,这里的 <domain-name> 指的是 certbot 签发的文件名。 通常与 domain 一样,但如果你同时申请多个域名证书,certbot 会将其合并为一个证书,比如这里合并为两个文件:

Certificate is saved at: /etc/letsencrypt/live/pigsty.cc/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/pigsty.cc/privkey.pem

因此将证书中间的 pigsty.cc 抽出来填入 certbot,然后重新运行:

./infra.yml -t nginx_config,nginx_launch

即可让 Pigsty 重新生成 Nginx 配置文件,回退 Certbot 对配置进行的其他修改,只保留申请的证书。 以后需要续期更新证书的时候就不需要重复这个过程了,直接使用 certbot renew 即可。

3 - 自建Supabase:创业出海的首选数据库

如何在本地/云端物理机/裸金属/虚拟机上使用 Pigsty 自建企业级 Supabase?

Supabase 非常棒,拥有你自己的 Supabase 那就是棒上加棒!本文介绍了如何在本地/云端物理机/裸金属/虚拟机上自建企业级 Supabase。


目录


简短版本

curl -fsSL https://repo.pigsty.io/get | bash; && cd ~/pigsty
./bootstrap               # 准备 Pigsty 依赖
./configure -c app/supa   # 使用 Supabase 应用模板
./install.yml             # 安装 Pigsty,以及各种数据库
./docker.yml              # 安装 Docker 与 Docker Compose
./app.yml                 # 使用 Docker 拉起 Supabase 无状态部分

Supabase是什么?

Supabase 是一个开源的 Firebase,是一个 BaaS (Backend as Service)。 Supabase 对 PostgreSQL 进行了封装,并提供了身份认证,消息传递,边缘函数,对象存储,以及基于 PG 数据库模式自动生成的 REST API 与 GraphQL API。

Supabase 旨在为开发者提供一站式的后端解决方案,减少开发和维护后端基础设施的复杂性,使开发者专注于前端开发和用户体验。 用大白话来说就是:让开发者告别绝大部分后端开发的工作,只需要懂数据库设计与前端即可快速出活!

目前,Supabase 是 PostgreSQL 生态 人气最高的开源项目,在 GitHub 上已经有高达 7万4千 的Star数。 并且和 Neon,Cloudflare 一起并称为赛博菩萨 —— 因为他们都提供了非常不错的云服务免费计划。 目前,Supabase 和 Neon 已经成为许多初创企业的首选数据库 —— 用起来非常方便,起步还是免费的。


为什么要自建Supabase?

小微规模(4c8g)内的 Supabase 云服务极富性价比,人称赛博菩萨。那么 Supabase 云服务这么香,为什么要自建呢?

最直观的原因是是我们在《云数据库是智商税吗?》中提到过的:当你的规模超出云计算适用光谱,成本很容易出现爆炸式增长。 而且在当下,足够可靠的本地企业级 NVMe SSD在性价比上与云端存储有着三到四个数量级的优势,而自建能更好地利用这一点。

另一个重要的原因是功能, Supabase 云服务的功能受限 —— 出于与RDS相同的逻辑, 很多 强力PG扩展 因为多租户安全挑战与许可证的原因无法作为云服务提供。 故而尽管PG扩展是 Supabase 的一个核心特色,在云服务上也依然只有 64 个可用扩展,而 Pigsty 提供了多达 404 个开箱即用的 PG 扩展。

此外,尽管 Supabase 虽然旨在提供一个无供应商锁定的 Google Firebase 开源替代,但实际上自建高标准企业级的 Supabase 门槛并不低: Supabase 内置了一系列由他们自己开发维护的 PG 扩展插件,而这些扩展在 PGDG 官方仓库中并没有提供。 这实际上是某种隐性的供应商锁定,阻止了用户使用除了 supabase/postgres Docker 镜像之外的方式自建。

Pigsty 解决了这些问题,我们将所有 Supabase 自研与用到的 10 个缺失的扩展打成开箱即用的 RPM/DEB 包,确保它们在所有主流Linux操作系统发行版上都可用:

  • pg_graphql:提供PG内的GraphQL支持 (RUST),Rust扩展,由PIGSTY提供
  • pg_jsonschema:提供JSON Schema校验能力,Rust扩展,由PIGSTY提供
  • wrappers:Supabase提供的外部数据源包装器捆绑包,,Rust扩展,由PIGSTY提供
  • index_advisor:查询索引建议器,SQL扩展,由PIGSTY提供
  • pg_net:用 SQL 进行异步非阻塞HTTP/HTTPS 请求的扩展 (supabase),C扩展,由PIGSTY提供
  • vault:在 Vault 中存储加密凭证的扩展 (supabase),C扩展,由PIGSTY提供
  • pgjwt:JSON Web Token API 的PG实现 (supabase),SQL扩展,由PIGSTY提供
  • pgsodium:表数据加密存储 TDE,扩展,由PIGSTY提供
  • supautils:用于在云环境中确保数据库集群的安全,C扩展,由PIGSTY提供
  • pg_plan_filter:使用执行计划代价过滤阻止特定查询语句,C扩展,由PIGSTY提供

我们还在 Supabase 中默认安装了以下扩展,您可以参考可用扩展列表启用更多。

- timescaledb postgis pg_graphql pg_jsonschema wrappers pg_search pg_analytics pg_parquet plv8 duckdb_fdw pg_cron pg_timetable pgqr
- supautils pg_plan_filter passwordcheck plpgsql_check pgaudit pgsodium pg_vault pgjwt pg_ecdsa pg_session_jwt index_advisor
- pgvector pgvectorscale pg_summarize pg_tiktoken pg_tle pg_stat_monitor hypopg pg_hint_plan pg_http pg_net pg_smtp_client pg_idkit

同时,Pigsty 还会负责好底层 高可用 PostgreSQL 数据库集群,高可用 MinIO 对象存储集群的自动搭建,甚至是 Docker 容器底座的部署与 Nginx 域名配置与证书签发。 最终,您可以使用 Docker Compose 拉起任意数量的无状态 Supabase 容器集群,并使用外部由 Pigsty 托管的企业级 PostgreSQL 数据库与 MinIO 对象存储,甚至连反向代理的 Nginx 等都已经为您配置准备完毕!

在这一自建部署架构中,您获得了使用不同内核的自由(PG 14-17),加装 404 个扩展的自由,扩容与伸缩 Supabase/Postgres/MinIO 的自由,免于数据库运维的自由,以及告别供应商锁定的自由。 而相比于使用 Supabase 云服务需要付出的代价,不过是准备一(几)台物理机/虚拟机 + 敲几行命令,等候十几分钟的区别。


单节点自建快速上手

让我们先从单节点 Supabase 部署开始,我们会在后面进一步介绍多节点高可用部署的方法。

首先,使用 Pigsty 标准安装流程 安装 Supabase 所需的 MinIO 与 PostgreSQL 实例; 然后额外运行 docker.ymlapp.yml 完成剩余的工作, 拉起无状态部分的 Supabase 容器,Supabase 就可以使用了(默认端口 8000/8433)。

curl -fsSL https://repo.pigsty.io/get | bash; && cd ~/pigsty
./bootstrap               # 准备 Pigsty 依赖
./configure -c app/supa   # 使用 Supabase 应用模板
./install.yml             # 安装 Pigsty,以及各种数据库
./docker.yml              # 安装 Docker 与 Docker Compose
./app.yml                 # 使用 Docker 拉起 Supabase 无状态部分

请在部署 Supabase 前,根据您的实际情况,修改自动生成的 pigsty.yml 配置文件中的参数(主要是密码!) 如果您只是将其用于本地开发测试,可以先不管这些,我们将在后面介绍如何通过修改配置文件来定制您的 Supabase。

如果您的配置没有问题,那么大约在 10 分钟后,您就可以在本地网络通过 http://<your_ip_address>:8000 访问到 Supabase Studio 管理界面了。 默认的用户名与密码分别是: supabasepigsty

asciicast


检查清单

  • 硬件/软件:准备所需的机器资源:Linux x86_64/arm64 服务器一台,全新安装主流 Linux 操作系统
  • 网络/权限:有 ssh 免密登陆权限,所用用户有免密 sudo 权限
  • 确保机器有内网静态IPv4网络地址,并可以访问互联网。中国地区 DockerHub 需要翻墙,需要有可用的代理或镜像站点
    • configure 过程中,请输入节点的内网首要 IP 地址,或直接通过 -i <primary_ip> 命令行参数指定
    • 如果您的网络环境无法访问 DockerHub,请通过 docker_registry_mirrors 使用镜像站 或 proxy_env 绕过防火墙。
  • 确保使用了 app/supa 配置模板,并按需修改了参数
    • 您是否修改了所有与密码有关的配置参数?【可选】
    • 您是否需要使用外部 SMTP 服务器?是否配置了 apps.<supabase>.conf 中的 SMTP 相关参数?【可选】

中国地区的用户请注意,如果您没有配置好 Docker 镜像站点或代理服务器,那么会有极大概率会翻车在 ./supabase 最后一步的镜像拉取上。我们建议您掌握科学上网技巧,参考 Docker 模块 FAQ 的说明配置镜像站代理服务器。 请注意,我们提供 Supabase 自建专门咨询服务,¥2000 / 例·半小时,购买附赠预制离线安装包,可以无需互联网(自然也无需翻墙)安装,将您的企业级自建 Supabase 安稳扶上马!

修改后的配置文件,应该如下所示:

对默认生成的配置文件进行修改
all:
  children:

    # supabase 无状态部分的定义 (默认用户名密码: supabase/pigsty)
    supa:
      hosts:
        10.10.10.10: {}
      vars:
        app: supabase # 指定默认安装等应用名称(supabase)
        apps:         # 定义应用列表,这里只有 supabase
          supabase:   # Supabase 应用定义
            conf:     # 下面的 Supabase 配置项会自动覆盖或追加到 /opt/supabase/.env 文件中(模板路径:app/supabase/.env ,内容详见:https://github.com/Vonng/pigsty/blob/main/app/supabase/.env)

              # 【非常重要】: 请修改下面的 JWT_SECRET 以及 ANON_KEY 与 SERVICE_ROLE_KEY : https://supabase.com/docs/guides/self-hosting/docker#securing-your-services
              JWT_SECRET: your-super-secret-jwt-token-with-at-least-32-characters-long
              ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
              SERVICE_ROLE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q
              DASHBOARD_USERNAME: supabase
              DASHBOARD_PASSWORD: pigsty

              # postgres connection string (use the correct ip and port)
              POSTGRES_HOST: 10.10.10.10
              POSTGRES_PORT: 5436             # access via the 'default' service, which always route to the primary postgres
              POSTGRES_DB: postgres
              POSTGRES_PASSWORD: DBUser.Supa  # password for supabase_admin and multiple supabase users

              # expose supabase via domain name
              SITE_URL: http://supa.pigsty                # <------- Change This to your external domain name
              API_EXTERNAL_URL: http://supa.pigsty        # <------- Otherwise the storage api may not work!
              SUPABASE_PUBLIC_URL: http://supa.pigsty     # <------- Do not forget!

              # if using s3/minio as file storage
              S3_BUCKET: supa
              S3_ENDPOINT: https://sss.pigsty:9000
              S3_ACCESS_KEY: supabase
              S3_SECRET_KEY: S3User.Supabase
              S3_FORCE_PATH_STYLE: true
              S3_PROTOCOL: https
              S3_REGION: stub
              MINIO_DOMAIN_IP: 10.10.10.10  # sss.pigsty domain name will resolve to this ip statically

              # if using SMTP (optional)
              #SMTP_ADMIN_EMAIL: admin@example.com
              #SMTP_HOST: supabase-mail
              #SMTP_PORT: 2500
              #SMTP_USER: fake_mail_user
              #SMTP_PASS: fake_mail_password
              #SMTP_SENDER_NAME: fake_sender
              #ENABLE_ANONYMOUS_USERS: false


    # infra 集群,包含 Prometheus & Grafana 监控基础设施
    infra: { hosts: { 10.10.10.10: { infra_seq: 1 } } }

    # etcd 集群,本例为单节点 Etcd,用于提供 PG 高可用
    etcd: { hosts: { 10.10.10.10: { etcd_seq: 1 } }, vars: { etcd_cluster: etcd } }

    # minio 集群,单节点 SNSD 的 S3 兼容对象存储
    minio: { hosts: { 10.10.10.10: { minio_seq: 1 } }, vars: { minio_cluster: minio } }

    # pg-meta, Supabase 底层实际的 PostgreSQL 数据库
    pg-meta:
      hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
      vars:
        pg_cluster: pg-meta
        pg_users:
          # supabase roles: anon, authenticated, dashboard_user
          - { name: anon           ,login: false }
          - { name: authenticated  ,login: false }
          - { name: dashboard_user ,login: false ,replication: true ,createdb: true ,createrole: true }
          - { name: service_role   ,login: false ,bypassrls: true }
          # 【注意】如果你要修改 Supabase 业务用户的密码,请在这里统一修改所有用户的密码
          - { name: supabase_admin             ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: true   ,roles: [ dbrole_admin ] ,superuser: true ,replication: true ,createdb: true ,createrole: true ,bypassrls: true }
          - { name: authenticator              ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin, authenticated ,anon ,service_role ] }
          - { name: supabase_auth_admin        ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin ] ,createrole: true }
          - { name: supabase_storage_admin     ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin, authenticated ,anon ,service_role ] ,createrole: true }
          - { name: supabase_functions_admin   ,password: 'DBUser.Supa' ,pgbouncer: true ,inherit: false  ,roles: [ dbrole_admin ] ,createrole: true }
          - { name: supabase_replication_admin ,password: 'DBUser.Supa' ,replication: true ,roles: [ dbrole_admin ]}
          - { name: supabase_read_only_user    ,password: 'DBUser.Supa' ,bypassrls: true ,roles: [ dbrole_readonly, pg_read_all_data ] }
        # 【注意】 这里定义了 Supabase 使用的底层 Postgres 业务数据库,
        pg_databases:
          - name: postgres
            baseline: supabase.sql  # 这里的 files/supabase.sql 文件包含了初始化 Supabase 所必需的模式迁移脚本,非常重要!
            owner: supabase_admin   # 这里的数据库所有者,必须是上面定义的 supabase_admin,我们建议使用此用户进行模式变更。
            comment: supabase postgres database
            schemas: [ extensions ,auth ,realtime ,storage ,graphql_public ,supabase_functions ,_analytics ,_realtime ]
            extensions:             # 定义在这里的扩展会默认在数据库中 “创建并启用”
              - { name: pgcrypto  ,schema: extensions } # cryptographic functions
              - { name: pg_net    ,schema: extensions } # async HTTP
              - { name: pgjwt     ,schema: extensions } # json web token API for postgres
              - { name: uuid-ossp ,schema: extensions } # generate universally unique identifiers (UUIDs)
              - { name: pgsodium        }               # pgsodium is a modern cryptography library for Postgres.
              - { name: supabase_vault  }               # Supabase Vault Extension
              - { name: pg_graphql      }               # pg_graphql: GraphQL support
              - { name: pg_jsonschema   }               # pg_jsonschema: Validate json schema
              - { name: wrappers        }               # wrappers: FDW collections
              - { name: http            }               # http: allows web page retrieval inside the database.
              - { name: pg_cron         }               # pg_cron: Job scheduler for PostgreSQL
              - { name: timescaledb     }               # timescaledb: Enables scalable inserts and complex queries for time-series data
              - { name: pg_tle          }               # pg_tle: Trusted Language Extensions for PostgreSQL
              - { name: vector          }               # pgvector: the vector similarity search
              - { name: pgmq            }               # pgmq: A lightweight message queue like AWS SQS and RSMQ
        # supabase required extensions
        pg_libs: 'timescaledb, plpgsql, plpgsql_check, pg_cron, pg_net, pg_stat_statements, auto_explain, pg_tle, plan_filter'
        pg_parameters:
          cron.database_name: postgres
          pgsodium.enable_event_trigger: off
        pg_hba_rules: # supabase hba rules, require access from docker network
          - { user: all ,db: postgres  ,addr: intra         ,auth: pwd ,title: 'allow supabase access from intranet'    }
          - { user: all ,db: postgres  ,addr: 172.17.0.0/16 ,auth: pwd ,title: 'allow access from local docker network' }
        node_crontab: [ '00 01 * * * postgres /pg/bin/pg-backup full' ] # make a full backup every 1am



  #==============================================================#
  # Global Parameters
  #==============================================================#
  vars:
    version: v3.3.0                   # pigsty version string
    admin_ip: 10.10.10.10             # admin node ip address
    region: default                   # upstream mirror region: default|china|europe
    node_tune: oltp                   # node tuning specs: oltp,olap,tiny,crit
    pg_conf: oltp.yml                 # pgsql tuning specs: {oltp,olap,tiny,crit}.yml
    docker_enabled: true              # enable docker on app group

    # WARNING: YOU MAY HAVE
    #docker_registry_mirrors: ["https://docker.m.daocloud.io"] # use dao cloud mirror in mainland china
    proxy_env:                        # global proxy env when downloading packages & pull docker images
      no_proxy: "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.tsinghua.edu.cn"
      #http_proxy:  127.0.0.1:12345 # add your proxy env here for downloading packages or pull images
      #https_proxy: 127.0.0.1:12345 # usually the proxy is format as http://user:pass@proxy.xxx.com
      #all_proxy:   127.0.0.1:12345

    infra_portal:                     # domain names and upstream servers
      home         : { domain: h.pigsty }
      grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
      prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
      alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
      minio        : { domain: m.pigsty ,endpoint: "10.10.10.10:9001", https: true, websocket: true }
      blackbox     : { endpoint: "${admin_ip}:9115" }
      loki         : { endpoint: "${admin_ip}:3100" }  # expose supa studio UI and API via nginx
      supa         : { domain: supa.pigsty ,endpoint: "10.10.10.10:8000", websocket: true }
      # certbot --nginx --agree-tos --email your@email.com -n -d supa.your.domain    # replace with your email & supa domain

    #----------------------------------#
    # Credential: CHANGE THESE PASSWORDS
    #----------------------------------#
    #grafana_admin_username: admin
    grafana_admin_password: pigsty
    #pg_admin_username: dbuser_dba
    pg_admin_password: DBUser.DBA
    #pg_monitor_username: dbuser_monitor
    pg_monitor_password: DBUser.Monitor
    #pg_replication_username: replicator
    pg_replication_password: DBUser.Replicator
    #patroni_username: postgres
    patroni_password: Patroni.API
    #haproxy_admin_username: admin
    haproxy_admin_password: pigsty
    #minio_access_key: minioadmin
    minio_secret_key: minioadmin      # minio root secret key, `minioadmin` by default

    # use minio as supabase file storage, single node single driver mode for demonstration purpose
    minio_buckets: [ { name: pgsql }, { name: supa } ]
    minio_users:
      - { access_key: dba , secret_key: S3User.DBA, policy: consoleAdmin }
      - { access_key: pgbackrest , secret_key: S3User.Backup,   policy: readwrite }
      - { access_key: supabase   , secret_key: S3User.Supabase, policy: readwrite }
    minio_endpoint: https://sss.pigsty:9000    # explicit overwrite minio endpoint with haproxy port
    node_etc_hosts: ["10.10.10.10 sss.pigsty"] # domain name to access minio from all nodes (required)

    # use minio as default backup repo for PostgreSQL
    pgbackrest_method: minio          # pgbackrest repo method: local,minio,[user-defined...]
    pgbackrest_repo:                  # pgbackrest repo: https://pgbackrest.org/configuration.html#section-repository
      local:                          # default pgbackrest repo with local posix fs
        path: /pg/backup              # local backup directory, `/pg/backup` by default
        retention_full_type: count    # retention full backups by count
        retention_full: 2             # keep 2, at most 3 full backup when using local fs repo
      minio:                          # optional minio repo for pgbackrest
        type: s3                      # minio is s3-compatible, so s3 is used
        s3_endpoint: sss.pigsty       # minio endpoint domain name, `sss.pigsty` by default
        s3_region: us-east-1          # minio region, us-east-1 by default, useless for minio
        s3_bucket: pgsql              # minio bucket name, `pgsql` by default
        s3_key: pgbackrest            # minio user access key for pgbackrest
        s3_key_secret: S3User.Backup  # minio user secret key for pgbackrest
        s3_uri_style: path            # use path style uri for minio rather than host style
        path: /pgbackrest             # minio backup path, default is `/pgbackrest`
        storage_port: 9000            # minio port, 9000 by default
        storage_ca_file: /pg/cert/ca.crt  # minio ca file path, `/pg/cert/ca.crt` by default
        bundle: y                     # bundle small files into a single file
        cipher_type: aes-256-cbc      # enable AES encryption for remote backup repo
        cipher_pass: pgBackRest       # AES encryption password, default is 'pgBackRest'
        retention_full_type: time     # retention full backup by time on minio repo
        retention_full: 14            # keep full backup for last 14 days

    # download docker and all available extensions
    pg_version: 17
    repo_modules: node,pgsql,infra,docker
    repo_packages: [node-bootstrap, infra-package, infra-addons, node-package1, node-package2, pgsql-utility, docker ]
    repo_extra_packages: [pg17-core ,pg17-time ,pg17-gis ,pg17-rag ,pg17-fts ,pg17-olap ,pg17-feat ,pg17-lang ,pg17-type ,pg17-util ,pg17-func ,pg17-admin ,pg17-stat ,pg17-sec ,pg17-fdw ,pg17-sim ,pg17-etl ]
    pg_extensions:                  [pg17-time ,pg17-gis ,pg17-rag ,pg17-fts ,pg17-feat ,pg17-lang ,pg17-type ,pg17-util ,pg17-func ,pg17-admin ,pg17-stat ,pg17-sec ,pg17-fdw ,pg17-sim ,pg17-etl ] #,pg17-olap]

自建关键技术决策

以下是一些自建 Supabase 会涉及到的关键技术决策,供您参考:

使用默认的单节点部署 Supabase 无法享受到 PostgreSQL / MinIO 的高可用能力。 尽管如此,单节点部署相比官方纯 Docker Compose 方案依然要有显著优势: 例如开箱即用的监控系统,自由安装扩展的能力,各个组件的扩缩容能力,以及数据库时间点恢复能力等。

如果您只有一台服务器,Pigsty 建议您直接使用外部的 S3 作为对象存储,存放 PostgreSQL 的备份,并承载 Supabase Storage 服务。 这样的部署在故障时可以提供一个最低标准的 RTO (小时级恢复时长)/ RPO (MB级数据损失)兜底容灾水平。 此外,如果您选择在云上自建,我们也建议您直接使用 S3,而非默认使用的本体 MinIO ,单纯在本地 EBS 上再套一层 MinIO 转发,除了便于开发测试外,对生产实用并没有意义。

在严肃的生产部署中,Pigsty 建议使用至少3~4个节点的部署策略,确保 MinIO 与 PostgreSQL 都使用满足企业级高可用要求的多节点部署。 在这种情况下,您需要相应准备更多节点与磁盘,并相应调整 pigsty.yml 配置清单中的集群配置,以及 supabase 集群配置中的接入信息。

部分 Supabase 的功能需要发送邮件,所以要用到 SMTP。除非单纯用于内网,否则对于严肃的生产部署,我们建议您考虑使用外部的 SMTP 服务。 自建的邮件服务器发送的邮件可能会被对方邮件服务器拒收,或者被标记为垃圾邮件。

如果您的服务直接向公网暴露,我们建议您使用 Nginx 进行反向代理,使用真正的域名与 HTTPS 证书,并通过不同的域名区分不同的多个实例。

接下来,我们会依次讨论这几个主题:

  • 进阶主题:安全加固
  • 高可用的 PostgreSQL 集群部署与接入
  • 高可用的 MinIO 集群部署与接入
  • 使用 S3 服务替代 MinIO
  • 使用外部 SMTP 服务发送邮件
  • 使用真实域名,证书,通过 Nginx 反向代理

进阶主题:安全加固

Pigsty基础组件

对于严肃的生产部署,我们强烈建议您修改 Pigsty 基础组件的密码。因为这些默认值是公开且众所周知的,不改密码上生产无异于裸奔:

以上密码为 Pigsty 组件模块的密码,强烈建议在安装部署前就设置完毕。

Supabase密钥

除了 Pigsty 组件的密码,你还需要 修改 Supabase 的密钥,包括

这里请您务必参照 Supabase教程:保护你的服务 里的说明:

  • 生成一个长度超过 40 个字符的 jwt_secret,并使用教程中的工具签发 anon_keyservice_role_key 两个 JWT。
  • 使用教程中提供的工具,根据 jwt_secret 以及过期时间等属性,生成一个 anon_key JWT,这是匿名用户的身份凭据。
  • 使用教程中提供的工具,根据 jwt_secret 以及过期时间等属性,生成一个 service_role_key,这是权限更高服务角色的身份凭据。
  • 如果您使用的 PostgreSQL 业务用户使用了不同于默认值的密码,请相应修改 `postgres_password`` 的值
  • 如果您的对象存储使用了不同于默认值的密码,请相应修改 s3_access_key``](https://github.com/Vonng/pigsty/blob/main/conf/app/supa.yml#136) 与 [s3_secret_key`` 的值

Supabase 部分的凭据修改后,您可以重启 Docker Compose 容器以应用新的配置:

cd /opt/supabase; docker compose up

进阶主题:域名接入

如果你在本机或局域网内使用 Supabase,那么可以选择 IP:Port 直连 Kong 对外暴露的 HTTP 8000 端口,当然这样并不好,我们建议您使用域名与 HTTPS 来访问。

使用默认的本地域名 supa.pigsty 时,您可以在浏览器本机的 /etc/hosts 或局域网 DNS 里来配置它的解析,将其指向安装节点的【对外】IP地址。 Pigsty 管理节点上的 Nginx 会为此域名申请自签名的证书(浏览器显示《不安全》),并将请求转发到 8000 端口的 Kong,由 Supabase 处理。

不过,更为实用与常见的用例是:Supabase 通过公网队外提供服务。在这种情况下,通常您需要进行以下准备:

  • 您的服务器应当有一个公网 IP 地址
  • 购买域名,使用 云/DNS/CDN 供应商提供的 DNS 解析服务,将其指向安装节点的公网 IP(下位替代:本地 /etc/hosts
  • 申请证书,使用 Let’s Encrypt 等证书颁发机构签发的免费 HTTPS 证书,用于加密通信(下位替代:默认自签名证书,手工信任)

您可以 参考certbot教程 ,申请免费的 HTTPS 证书,这里我们假设您使用的自定义域名是:supa.pigsty.cc

certbot --nginx --agree-tos --email your@email.com -n -d supa.pigsty.cc

申请证书后,请修改 pigsty.yml 配置文件中 Nginx 与 Supabase 的配置:

  • all.vars.infra_portal 部分的 supa 域名(domain,然后填入生成证书的 certkey 文件路径)
  • 修改 supabase 中的三个域名字段。
all:
  vars:     # 全局配置 
    #.....
    infra_portal:                     # domain names and upstream servers
      home         : { domain: h.pigsty }
      grafana      : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
      prometheus   : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
      alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
      minio        : { domain: m.pigsty ,endpoint: "10.10.10.10:9001", https: true, websocket: true }
      blackbox     : { endpoint: "${admin_ip}:9115" }
      loki         : { endpoint: "${admin_ip}:3100" }
      
      #supa        : { domain: supa.pigsty ,endpoint: "10.10.10.10:8000", websocket: true }  # 如果使用申请的 HTTPS 证书,请在这里指定证书的存放路径
      supa         : { domain: supa.pigsty.cc ,endpoint: "10.10.10.10:8000", websocket: true ,cert: /etc/cert/suap.pigsty.cc.crt ,key: /etc/cert/supa.pigsty.cc.key }

    # the supabase stateless (default username & password: supabase/pigsty)
    supa:
      hosts:
        10.10.10.10: {}
      vars:
        app: supabase # specify app name (supa) to be installed (in the apps)
        apps:         # define all applications
          supabase:   # the definition of supabase app
            conf:     # override /opt/supabase/.env
              
              # 请在这里更新 Supabase 使用的域名 (换成了真实 HTTPS 域名)
              site_url: https://supa.pigsty.cc
              api_external_url: https://supa.pigsty.cc
              supabase_public_url: https://supa.pigsty.cc

然后更新 Nginx 与 Supabase App 的配置后应用生效:

./infra.yml    -t nginx_config,nginx_launch
./app.yml      -t app_config,app_launch

使用以上命令重新加载 Nginx 和 Supabase 的配置。


进阶主题:外部对象存储

您可以使用 S3 或 S3 兼容的服务,来作为 PGSQL 备份与 Supabase 使用的对象存储。这里我们使用一个 阿里云 OSS 对象存储作为例子。

Pigsty 提供了一个 terraform/spec/aliyun-meta-s3.tf 模板,用于在阿里云上拉起一台服务器,以及一个 OSS 存储桶。

首先,我们修改 all.children.supa.vars.apps.[supabase].conf 中 S3 相关的配置,将其指向阿里云 OSS 存储桶:

# if using s3/minio as file storage
S3_BUCKET: supa
S3_ENDPOINT: https://sss.pigsty:9000
S3_ACCESS_KEY: supabase
S3_SECRET_KEY: S3User.Supabase
S3_FORCE_PATH_STYLE: true
S3_PROTOCOL: https
S3_REGION: stub
MINIO_DOMAIN_IP: 10.10.10.10  # sss.pigsty domain name will resolve to this ip statically

同样使用以下命令重载 Supabase 配置:

./app.yml -t app_config,app_launch

您同样可以使用 S3 作为 PostgreSQL 的备份仓库,在 all.vars.pgbackrest_repo 新增一个 aliyun 备份仓库的定义:

all:
  vars:
    pgbackrest_method: aliyun          # pgbackrest 备份方法:local,minio,[其他用户定义的仓库...],本例中将备份存储到 MinIO 上
    pgbackrest_repo:                   # pgbackrest 备份仓库: https://pgbackrest.org/configuration.html#section-repository
      aliyun:                          # 定义一个新的备份仓库 aliyun
        type: s3                       # 阿里云 oss 是 s3-兼容的对象存储
        s3_endpoint: oss-cn-beijing-internal.aliyuncs.com
        s3_region: oss-cn-beijing 
        s3_bucket: pigsty-oss
        s3_key: xxxxxxxxxxxxxx
        s3_key_secret: xxxxxxxx
        s3_uri_style: host
        
        path: /pgbackrest
        bundle: y
        cipher_type: aes-256-cbc
        cipher_pass: PG.${pg_cluster}   # 设置一个与集群名称绑定的加密密码
        retention_full_type: time 
        retention_full: 14

然后在 all.vars.pgbackrest_mehod 中指定使用 aliyun 备份仓库,重置 pgBackrest 备份:

./pgsql.yml -t pgbackrest

Pigsty 会将备份仓库切换到外部对象存储上。


进阶主题:备份策略

你可以使用操作系统的 Crontab 来设置定时备份策略,例如,向默认的 all.children.pg-meta.vars 中添加 node_crontab 参数:

all:
  children:
    pg-meta:
      hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
      vars:
        pg_cluster: pg-meta  # 每天凌晨一点做个全量备份
        node_crontab: [ '00 01 * * * postgres /pg/bin/pg-backup full' ]

然后执行以下命令,将 Crontab 配置应用到节点上:

./node.yml -t node_crontab

更多关于备份策略的主题,请参考 备份策略


进阶主题:使用SMTP

你可以使用 SMTP 来发送邮件,修改 supabase 配置,添加 SMTP 信息:

all:
  children:
    supabase:
      vars:
        supa_config:
          smtp_host: smtpdm.aliyun.com:80
          smtp_port: 80
          smtp_user: no_reply@mail.your.domain.com
          smtp_pass: your_email_user_password
          smtp_sender_name: MySupabase
          smtp_admin_email: adminxxx@mail.your.domain.com
          enable_anonymous_users: false

不要忘了使用 app.yml -t app_config,app_launch 来重载配置


进阶主题:真·高可用

经过上面的配置,您已经可以使用一个带有公网域名,HTTPS 证书,SMTP 邮件服务器,备份的 Supabase 了。

如果您的这个节点挂了,起码外部 S3 存储中保留了备份,您可以在新的节点上重新部署 Supabase,然后从备份中恢复。 这样的部署在故障时可以提供一个最低标准的 RTO (小时级恢复时长)/ RPO (MB级数据损失)兜底容灾水平 兜底。

但如果您想要达到 RTO < 30s ,零数据丢失,那么就需要用到多节点高可用集群了。多节点部署有三个维度:

  • ETCD: DCS 需要使用三个节点或以上,才能容忍一个节点的故障。
  • PGSQL: PGSQL 同步提交不丢数据模式,建议使用至少三个节点。
  • INFRA:监控基础设施故障影响稍小,但我们建议生产环境使用三副本
  • Supabase 本身也可以是多节点的副本,实现高可用

我们建议您参考 triosafe 中的集群配置,将您的集群配置升级到三节点或以上。

在这种情况下,您还需要修改 PostgreSQL 与 MinIO 的接入点,使用 DNS / L2 VIP / HAProxy 等 高可用接入点