Add Dify stack to ru-docker-infra
7 сервисов (api, worker, web, plugin-daemon, sandbox, ssrf-proxy, weaviate) на общих postgres (базы dify/dify_plugin) и redis (db 2/3). Vector store — weaviate. Внешний вход через traefik path-роутингом на dify.mxti.ru. Добавлены: ssrf_proxy/squid.conf, postgres init для баз, DNS-запись, сервис dify в inventory, deploy-ключ в ssh/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,11 @@ domains:
|
||||
server: ru-docker-infra
|
||||
purpose: "portainer UI"
|
||||
|
||||
- host: dify
|
||||
type: A
|
||||
server: ru-docker-infra
|
||||
purpose: "dify через traefik"
|
||||
|
||||
- host: n8n
|
||||
type: A
|
||||
server: nl-reverse-proxy
|
||||
|
||||
@@ -12,7 +12,7 @@ servers:
|
||||
role: core # основной узел: все ключевые сервисы
|
||||
env: prod
|
||||
location: yandex-cloud
|
||||
tags: [traefik, n8n, gitea, postgres, redis, portainer, xray, mtg]
|
||||
tags: [traefik, n8n, gitea, postgres, redis, portainer, xray, mtg, dify]
|
||||
services:
|
||||
- traefik
|
||||
- postgres
|
||||
@@ -23,6 +23,7 @@ servers:
|
||||
- gitea
|
||||
- portainer
|
||||
- mtg # Telegram MTProto-прокси
|
||||
- dify # LLM-app платформа (api/worker/web/plugin-daemon/sandbox/ssrf-proxy/weaviate)
|
||||
managed: true
|
||||
|
||||
- name: ru-vpn # входная нода VPN
|
||||
|
||||
@@ -22,6 +22,21 @@ REDIS_PASSWORD=31hjsbj34rjs
|
||||
# Сгенерируй: openssl rand -hex 32
|
||||
N8N_ENCRYPTION_KEY=fbac490fbf63b779fb5f3d90a2fe5445164aaacef1506b02f3f4331c23c2ba59
|
||||
|
||||
# --- Dify ---
|
||||
DIFY_HOST=dify.mxti.ru
|
||||
# Версии образов (api и web всегда одинаковые)
|
||||
DIFY_VERSION=1.4.3
|
||||
DIFY_PLUGIN_DAEMON_VERSION=0.1.2-local
|
||||
DIFY_SANDBOX_VERSION=0.2.12
|
||||
# Секреты (сгенерированы: openssl rand -base64 42 / -hex 24)
|
||||
DIFY_SECRET_KEY=UzWUvFZzVB0Ap0MhhEfQ4r4YiRUBOjJ0O3ra1KUVwwXHoeqmF6OxgqW9
|
||||
DIFY_PLUGIN_DAEMON_KEY=dAX7IwPuO4ggh1MBASC25G8zfm6DVUZ5pCqKJ5vgQBguYgsSs2OVToDR
|
||||
DIFY_INNER_API_KEY=7QZ0ijEkf+IAGI/38pQlopq3vhZMGPF682JBS3hSJCyjzGTRuoYZ9A5w
|
||||
DIFY_WEAVIATE_API_KEY=0fd018d333fae8fb59399a9ddc6051812e06365f64c46740
|
||||
DIFY_SANDBOX_API_KEY=fd3cd878e0cefe3410a81ecaa30facdd78e1d8357c10b927
|
||||
# Dify использует общий postgres (базы dify / dify_plugin под POSTGRES_USER)
|
||||
# и общий redis (db 2 для кэша, db 3 для celery-брокера).
|
||||
|
||||
# --- MTProto (mtg) ---
|
||||
# Фейк-домен для Fake-TLS — ОБЯЗАТЕЛЬНО тот же, под который сгенерирован секрет
|
||||
# в mtg/config.toml. Traefik матчит TLS-handshake по SNI этого домена (TCP router
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Dify ssrf_proxy (squid) — отрендеренный конфиг из upstream docker/ssrf_proxy/squid.conf.template.
|
||||
# Значения подставлены дефолтные: HTTP_PORT=3128, REVERSE_PROXY_PORT=8194,
|
||||
# SANDBOX_HOST=sandbox, SANDBOX_PORT=8194, COREDUMP_DIR=/var/spool/squid.
|
||||
# Forward-proxy для api/worker/sandbox (SSRF-защита) + accel reverse-proxy на sandbox.
|
||||
|
||||
acl localnet src 0.0.0.1-0.255.255.255
|
||||
acl localnet src 10.0.0.0/8
|
||||
acl localnet src 100.64.0.0/10
|
||||
acl localnet src 169.254.0.0/16
|
||||
acl localnet src 172.16.0.0/12
|
||||
acl localnet src 192.168.0.0/16
|
||||
acl localnet src fc00::/7
|
||||
acl localnet src fe80::/10
|
||||
acl SSL_ports port 443
|
||||
acl Safe_ports port 80
|
||||
acl Safe_ports port 21
|
||||
acl Safe_ports port 443
|
||||
acl Safe_ports port 70
|
||||
acl Safe_ports port 210
|
||||
acl Safe_ports port 1025-65535
|
||||
acl Safe_ports port 280
|
||||
acl Safe_ports port 488
|
||||
acl Safe_ports port 591
|
||||
acl Safe_ports port 777
|
||||
acl CONNECT method CONNECT
|
||||
acl allowed_domains dstdomain .marketplace.dify.ai
|
||||
http_access allow allowed_domains
|
||||
http_access deny !Safe_ports
|
||||
http_access deny CONNECT !SSL_ports
|
||||
http_access allow localhost manager
|
||||
http_access deny manager
|
||||
http_access allow localhost
|
||||
include /etc/squid/conf.d/*.conf
|
||||
http_access deny all
|
||||
|
||||
http_port 3128
|
||||
coredump_dir /var/spool/squid
|
||||
refresh_pattern ^ftp: 1440 20% 10080
|
||||
refresh_pattern ^gopher: 1440 0% 1440
|
||||
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
|
||||
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern . 0 20% 4320
|
||||
|
||||
http_port 8194 accel vhost
|
||||
cache_peer sandbox parent 8194 0 no-query originserver
|
||||
acl src_all src all
|
||||
http_access allow src_all
|
||||
|
||||
client_request_buffer_max_size 100 MB
|
||||
@@ -11,6 +11,10 @@ volumes:
|
||||
redis_data:
|
||||
portainer_data:
|
||||
gitea_data:
|
||||
dify_api_storage:
|
||||
dify_plugin_storage:
|
||||
dify_weaviate_data:
|
||||
dify_sandbox_deps:
|
||||
|
||||
services:
|
||||
traefik:
|
||||
@@ -50,6 +54,7 @@ services:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./postgres/init:/docker-entrypoint-initdb.d:ro
|
||||
networks:
|
||||
- internal
|
||||
healthcheck:
|
||||
@@ -228,3 +233,217 @@ services:
|
||||
- traefik.tcp.routers.mtg.entrypoints=websecure
|
||||
- traefik.tcp.routers.mtg.tls.passthrough=true
|
||||
- traefik.tcp.services.mtg.loadbalancer.server.port=3128
|
||||
|
||||
# --- Dify -----------------------------------------------------------------
|
||||
# LLM-app платформа. БД и redis — общие (postgres: база dify/dify_plugin,
|
||||
# redis: db 2/3). Vector store — отдельный weaviate. Внешний вход — traefik с
|
||||
# path-роутингом (вместо штатного dify-nginx): /console/api,/api,/v1,/files → api,
|
||||
# /e/ → plugin_daemon, остальное → web.
|
||||
|
||||
dify-api:
|
||||
image: langgenius/dify-api:${DIFY_VERSION}
|
||||
container_name: dify-api
|
||||
restart: unless-stopped
|
||||
environment: &dify-api-env
|
||||
MODE: api
|
||||
LOG_LEVEL: INFO
|
||||
SECRET_KEY: ${DIFY_SECRET_KEY}
|
||||
DEPLOY_ENV: PRODUCTION
|
||||
MIGRATION_ENABLED: "true"
|
||||
CONSOLE_API_URL: https://${DIFY_HOST}
|
||||
CONSOLE_WEB_URL: https://${DIFY_HOST}
|
||||
SERVICE_API_URL: https://${DIFY_HOST}
|
||||
APP_API_URL: https://${DIFY_HOST}
|
||||
APP_WEB_URL: https://${DIFY_HOST}
|
||||
FILES_URL: https://${DIFY_HOST}
|
||||
# --- Postgres (общий) ---
|
||||
DB_USERNAME: ${POSTGRES_USER}
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: dify
|
||||
# --- Redis (общий) ---
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
REDIS_DB: "2"
|
||||
CELERY_BROKER_URL: redis://:${REDIS_PASSWORD}@redis:6379/3
|
||||
# --- Vector store ---
|
||||
VECTOR_STORE: weaviate
|
||||
WEAVIATE_ENDPOINT: http://dify-weaviate:8080
|
||||
WEAVIATE_API_KEY: ${DIFY_WEAVIATE_API_KEY}
|
||||
# --- Storage (локальный том) ---
|
||||
STORAGE_TYPE: opendal
|
||||
OPENDAL_SCHEME: fs
|
||||
OPENDAL_FS_ROOT: storage
|
||||
# --- Code execution sandbox ---
|
||||
CODE_EXECUTION_ENDPOINT: http://dify-sandbox:8194
|
||||
CODE_EXECUTION_API_KEY: ${DIFY_SANDBOX_API_KEY}
|
||||
# --- SSRF proxy ---
|
||||
SSRF_PROXY_HTTP_URL: http://dify-ssrf-proxy:3128
|
||||
SSRF_PROXY_HTTPS_URL: http://dify-ssrf-proxy:3128
|
||||
# --- Plugin daemon ---
|
||||
PLUGIN_DAEMON_URL: http://dify-plugin-daemon:5002
|
||||
PLUGIN_DAEMON_KEY: ${DIFY_PLUGIN_DAEMON_KEY}
|
||||
INNER_API_KEY_FOR_PLUGIN: ${DIFY_INNER_API_KEY}
|
||||
MARKETPLACE_ENABLED: "true"
|
||||
MARKETPLACE_API_URL: https://marketplace.dify.ai
|
||||
volumes:
|
||||
- dify_api_storage:/app/api/storage
|
||||
networks:
|
||||
- proxy
|
||||
- internal
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
dify-weaviate:
|
||||
condition: service_started
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=proxy
|
||||
- traefik.http.routers.dify-api.rule=Host(`${DIFY_HOST}`) && (PathPrefix(`/console/api`) || PathPrefix(`/api`) || PathPrefix(`/v1`) || PathPrefix(`/files`))
|
||||
- traefik.http.routers.dify-api.priority=10
|
||||
- traefik.http.routers.dify-api.entrypoints=websecure
|
||||
- traefik.http.routers.dify-api.tls=true
|
||||
- traefik.http.routers.dify-api.tls.certresolver=le
|
||||
- traefik.http.routers.dify-api.service=dify-api
|
||||
- traefik.http.services.dify-api.loadbalancer.server.port=5001
|
||||
|
||||
dify-worker:
|
||||
image: langgenius/dify-api:${DIFY_VERSION}
|
||||
container_name: dify-worker
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
<<: *dify-api-env
|
||||
MODE: worker
|
||||
volumes:
|
||||
- dify_api_storage:/app/api/storage
|
||||
networks:
|
||||
- internal
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
dify-web:
|
||||
image: langgenius/dify-web:${DIFY_VERSION}
|
||||
container_name: dify-web
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
CONSOLE_API_URL: https://${DIFY_HOST}
|
||||
APP_API_URL: https://${DIFY_HOST}
|
||||
MARKETPLACE_API_URL: https://marketplace.dify.ai
|
||||
MARKETPLACE_URL: https://marketplace.dify.ai
|
||||
networks:
|
||||
- proxy
|
||||
- internal
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=proxy
|
||||
- traefik.http.routers.dify-web.rule=Host(`${DIFY_HOST}`)
|
||||
- traefik.http.routers.dify-web.priority=1
|
||||
- traefik.http.routers.dify-web.entrypoints=websecure
|
||||
- traefik.http.routers.dify-web.tls=true
|
||||
- traefik.http.routers.dify-web.tls.certresolver=le
|
||||
- traefik.http.routers.dify-web.service=dify-web
|
||||
- traefik.http.services.dify-web.loadbalancer.server.port=3000
|
||||
|
||||
dify-plugin-daemon:
|
||||
image: langgenius/dify-plugin-daemon:${DIFY_PLUGIN_DAEMON_VERSION}
|
||||
container_name: dify-plugin-daemon
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DB_USERNAME: ${POSTGRES_USER}
|
||||
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: dify_plugin
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
REDIS_DB: "2"
|
||||
SERVER_PORT: "5002"
|
||||
SERVER_KEY: ${DIFY_PLUGIN_DAEMON_KEY}
|
||||
DIFY_INNER_API_URL: http://dify-api:5001
|
||||
DIFY_INNER_API_KEY: ${DIFY_INNER_API_KEY}
|
||||
PLUGIN_REMOTE_INSTALLING_HOST: 0.0.0.0
|
||||
PLUGIN_REMOTE_INSTALLING_PORT: "5003"
|
||||
PLUGIN_WORKING_PATH: /app/storage/cwd
|
||||
PYTHON_ENV_INIT_TIMEOUT: "120"
|
||||
PLUGIN_MAX_EXECUTION_TIMEOUT: "600"
|
||||
PLUGIN_STORAGE_TYPE: local
|
||||
PLUGIN_STORAGE_LOCAL_ROOT: /app/storage
|
||||
FORCE_VERIFYING_SIGNATURE: "true"
|
||||
MARKETPLACE_ENABLED: "true"
|
||||
MARKETPLACE_API_URL: https://marketplace.dify.ai
|
||||
volumes:
|
||||
- dify_plugin_storage:/app/storage
|
||||
networks:
|
||||
- proxy
|
||||
- internal
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=proxy
|
||||
- traefik.http.routers.dify-plugin.rule=Host(`${DIFY_HOST}`) && PathPrefix(`/e/`)
|
||||
- traefik.http.routers.dify-plugin.priority=10
|
||||
- traefik.http.routers.dify-plugin.entrypoints=websecure
|
||||
- traefik.http.routers.dify-plugin.tls=true
|
||||
- traefik.http.routers.dify-plugin.tls.certresolver=le
|
||||
- traefik.http.routers.dify-plugin.service=dify-plugin
|
||||
- traefik.http.services.dify-plugin.loadbalancer.server.port=5002
|
||||
|
||||
dify-sandbox:
|
||||
image: langgenius/dify-sandbox:${DIFY_SANDBOX_VERSION}
|
||||
container_name: dify-sandbox
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
API_KEY: ${DIFY_SANDBOX_API_KEY}
|
||||
GIN_MODE: release
|
||||
WORKER_TIMEOUT: "15"
|
||||
ENABLE_NETWORK: "true"
|
||||
HTTP_PROXY: http://dify-ssrf-proxy:3128
|
||||
HTTPS_PROXY: http://dify-ssrf-proxy:3128
|
||||
SANDBOX_PORT: "8194"
|
||||
volumes:
|
||||
- dify_sandbox_deps:/dependencies
|
||||
networks:
|
||||
- internal
|
||||
depends_on:
|
||||
- dify-ssrf-proxy
|
||||
|
||||
dify-ssrf-proxy:
|
||||
image: ubuntu/squid:latest
|
||||
container_name: dify-ssrf-proxy
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./dify/ssrf_proxy/squid.conf:/etc/squid/squid.conf:ro
|
||||
networks:
|
||||
- internal
|
||||
|
||||
dify-weaviate:
|
||||
image: semitechnologies/weaviate:1.19.0
|
||||
container_name: dify-weaviate
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PERSISTENCE_DATA_PATH: /var/lib/weaviate
|
||||
QUERY_DEFAULTS_LIMIT: "25"
|
||||
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "false"
|
||||
DEFAULT_VECTORIZER_MODULE: none
|
||||
CLUSTER_HOSTNAME: node1
|
||||
AUTHENTICATION_APIKEY_ENABLED: "true"
|
||||
AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${DIFY_WEAVIATE_API_KEY}
|
||||
AUTHENTICATION_APIKEY_USERS: dify@local
|
||||
AUTHORIZATION_ADMINLIST_ENABLED: "true"
|
||||
AUTHORIZATION_ADMINLIST_USERS: dify@local
|
||||
volumes:
|
||||
- dify_weaviate_data:/var/lib/weaviate
|
||||
networks:
|
||||
- internal
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Создание баз для Dify в общем postgres.
|
||||
-- ВАЖНО: скрипты из /docker-entrypoint-initdb.d/ выполняются ТОЛЬКО при первой
|
||||
-- инициализации (пустой data-каталог). На уже работающем postgres базы нужно
|
||||
-- создать вручную один раз (см. servers/ru-docker-infra/README или CLAUDE.md):
|
||||
-- docker exec n8n-postgres psql -U n8n -c "CREATE DATABASE dify;"
|
||||
-- docker exec n8n-postgres psql -U n8n -c "CREATE DATABASE dify_plugin;"
|
||||
-- Dify подключается под существующим суперпользователем postgres (POSTGRES_USER).
|
||||
|
||||
SELECT 'CREATE DATABASE dify' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'dify')\gexec
|
||||
SELECT 'CREATE DATABASE dify_plugin' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'dify_plugin')\gexec
|
||||
@@ -0,0 +1,25 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAqCl4Bg7SRNO/t28BlAcuO+wJaiBYRfN+fGg3h1ykuVujNKteXwnVVZze
|
||||
hIc6aPOXRvDa0ssHNARSlE0SCQ7Q6rGXnoKlq7z791r6YZiJ7PQ5oCKA+J53WdB8abtO59i2
|
||||
15I1vUQffmBfuzkA3MQxhC8NJOoOVBzk7ICbEKsJGMePeieyI/iL3wi6Bwukulix9shqRysZ
|
||||
i1hTN31y5Do83pIUoTnE2sk8O2mRSqpyWPyWEmvCSNZrmPxYlqUCwj61OMZhAp8Y1wVW0arj
|
||||
Es3p8EY7494QYIZbENS9iWIUflmne0ms5ZHGmD8XJ83bucxnOaYu7llzrXvB46DHIHd3HwID
|
||||
AQABAoIBABBE5tBLOcuf4uH30uLD6RSyCt0iIPgvjRKzMIAfle467+jGFZzDQkkEosCWpq+F
|
||||
taKO5dTUAK/nJSgk5rRKoyq5oM4cnLOnVodBXYz4qYtru3GDNRAYoLpnQcom7YTfwhzMD/fu
|
||||
Mc/Tuh9Obgdt0c6UkMR8AoPfwcQh0x7DNV+Ysoj9lguvUF6Y6owajoYWxOz0PlpQ6PG6QNvW
|
||||
A39LV+O6mIas/Wc0uA6R4ztbsgmzxm0HVhVfesb67x68Q9iGSrrkZNadOFcMXIRJ1aofuXlX
|
||||
Kbltt+tiegZ9P6Mn04F20b6WFi2arZaPl+ZWsiBAZZmsxTA9EteU1DwgXKfYgQ0CgYEA7MJm
|
||||
yyhTIOwmdbhz2ITwMmOU2qPtGnKIUbFZipPTRPjzdxClc79KU3DeeAnIg+zgKM9vvWwwq6WJ
|
||||
wYhMMtryaoWVPW7EfHlxAciFXze1KlJIwtsKX6WP47LFWmUPwp5+X6TUgVIjsjrgWLtMS5Ae
|
||||
kmoVnOvT5UnR6k800jUqrL0CgYEAtdPzaQ+9E1Z6dLdc4ofDGZ5B5vJthygGq7PZryFPkDr0
|
||||
GZtrZKlPLM/gCUZqui5DdvCB1q8pfRIf1jI+yXOyohCYaOwC3eyfB22ga2kFQ3Cpys+jNKcV
|
||||
A41UHwhSnbpgBsGmqOSw9B2HgR3Z3ZCDn/TgdWIj4oul+TbZD0vKZwsCgYEA1pmC6QRdDQtf
|
||||
9PZC8u/UdCf1BZTuVjmYPEv/eG9zYSIyNCr/fQHXkfhNfgqIGKQ9rmAPzWdiWkMKsUQkpTf1
|
||||
4P3B/1QGfzjoaJBj66Gn8Xtx/kTiiz2bRe2FmuDdvy9AJttVzv4v4t4g+eyTRC6Wt2m2u2y6
|
||||
vSsDPVmb7ClKzBUCgYEAnHEfNd7PvEqkk7WMGm/awjjy1MTyzd33V1YnzlGnxh2jFkEttm8h
|
||||
6xlz1ica/tN9QmGOJOuSk8S7Vaznbqa4JE6JA4VEtjK3ZUnCQ4gZejv+KrxZ3JyBLQw1O3oK
|
||||
syC/LDEDtr8FLRQXGVVYtRR423ByiyyEWY/znqe9PDbmj58CgYB/wOFyktT3HcV89ZVCLGQp
|
||||
HfQ6yreocCDGiUucIvfwGH8cUjclfffi6TBA1MqFTSRWjcE+tq/LjEdHWO7yG4s8Gx072U66
|
||||
KjPCoUb1QsZ/o2dJEG2yOVQbviP3KrfdZfp76SYCVx4qz0jS9vIMusBvdad6uLunMMbbBdew
|
||||
+x4Bvg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user