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 了。


最后修改 2025-02-27: update release note (bf999b0)