如果线上 Dify
是通过 docker-compose.yaml
文件部署的,那么当 Dify
版本升级时该如何操作呢?官方已经给出了 Docker compose
和 Source Code
两种方式。相对而言,前者更简单些,至少不需要安装依赖包和迁移数据库文件。为了更加具体化理解,假设把 Dify v0.9.1
升级为 Dify v0.9.1-fix1
版本[1]。
本文主要介绍当 docker-compose.yaml
文件部署时,Dify
线上版本升级过程。主要用到的内容就是 docker
目录中的这些文件。对这些文件的详细介绍,参考文献[2]。当docker-compose.yaml文件部署时,Dify线上版本升级过程:https://z0yrmerhgi8.feishu.cn/wiki/O0q9wPKL9istdgkldDzcArV9nMe
一.重点文件介绍
1.yaml 和.env 文件关系
(1).env
文件作用
yaml
文件和 .env
文件什么关系呢?比如 .env
和 docker-compose.yaml
,middleware.env
和 docker-compose.middleware.yaml
。简单理解,docker-compose.yaml
文件可引用 .env
文件中的环境变量。docker-compose
支持从 .env
文件中加载环境变量,并在 docker-compose.yaml
文件中使用它们。这通常用于配置敏感信息或根据不同环境(开发、生产等)动态设置参数。
(2)默认加载.env 文件
需要注意的是默认情况下,无论是 docker-compose.yaml
还是 docker-compose.middleware.yaml
,它们都只会读取当前目录下的 .env
文件,不会自动读取其他命名的 .env
文件(如 middleware.env
)。
因此,docker-compose.middleware.yaml
不会默认读取 middleware.env
文件中的环境变量,除非显式指定它。
(3)指定加载 middleware.env
文件
如何让 docker-compose.middleware.yaml
读取 middleware.env
呢?需要通过 --env-file
选项来显式指定 middleware.env
文件,这样它才能被正确加载。比如,docker-compose --env-file middleware.env -f docker-compose.middleware.yaml up
。具体到 docker-compose.middleware.yaml
文件中指定 middleware.env
如下所示:
(4)docker-compose.yaml
文件部署
因为线上使用 docker-compose.yaml
文件部署,所以用到了 .env
和 docker-compose.yaml
,没有用到 middleware.env
和 docker-compose.middleware.yaml
。
二.线上版本更新 [1]
1.需要备份的文件
包括 docker-compose.yaml
文件,.env
文件,volumes
目录。如下所示:
cd docker
cp docker-compose.yaml docker-compose.yaml.$(date +%s).bak
cp .env .env.$(date +%s).bak
tar -cvf volumes-$(date +%s).tgz volumes
2.需要更新的文件
包括 docker-compose.yaml
文件,.env
文件。操作方式是使用目标版本(比如 Dify v0.9.1-fix1
版本)的对应文件,同时更新相关参数,比如 URL、数据库密码、开放端口等。
3.线上升级操作过程
备份和更新文件后,首先停止服务,然后更新服务,如下所示:
sudo docker compose -f docker-compose.yaml -p "dify0901" up -d
sudo docker compose -f docker-compose.yaml -p "dify0901-fix1" down
三.线上更新原理
1.镜像是何时更新的
如果使用官方镜像,系统会自动从 Docker Hub
拉取。如果自行打包镜像,那么需要上传服务器后进行加载。
2.数据库是合适迁移的
langgenius/dify-api:0.9.1-fix1
镜像就是通过如下 Dockerfile
打包的,容器启动时会执行命令 ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
,如下所示:
在 ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
命令中就有数据库迁移的脚本,如下所示:
根据什么迁移呢?就是 alembic_version
表中的 version_num
。迁移什么内容呢?就是 dify\api\migrations\versions
目录下自动生成的文件。如下所示:
具体到 Dify v0.9.1
升级为 Dify v0.9.1-fix1
版本,对应的文件为 2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py
。如下所示:
3.解释迁移文件
以 2024_10_09_1329-d8e744d88ed6_fix_wrong_service_api_history.py
为例,如下所示:
"""fix wrong service-api history
Revision ID: d8e744d88ed6
Revises: 33f5fac87f29
Create Date: 2024-10-09 13:29:23.548498
"""
from alembic import op
from constants import UUID_NIL
import models as models
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd8e744d88ed6'
down_revision = '33f5fac87f29'
branch_labels = None
depends_on = None
# (UTC) release date of v0.9.0
v0_9_0_release_date= '2024-09-29 12:00:00'
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = f"""UPDATE
public.messages
SET
parent_message_id = '{UUID_NIL}'
WHERE
invoke_from = 'service-api'
AND parent_message_id IS NULL
AND created_at >= '{v0_9_0_release_date}';"""
op.execute(sql)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = f"""UPDATE
public.messages
SET
parent_message_id = NULL
WHERE
invoke_from = 'service-api'
AND parent_message_id = '{UUID_NIL}'
AND created_at >= '{v0_9_0_release_date}';"""
op.execute(sql)
# ### end Alembic commands ###
这段 Alembic
[3]迁移脚本用于修正 service-api
相关的数据问题,具体是更新 messages
表中的 parent_message_id
字段。
(1)头部注释
"""fix wrong service-api history
Revision ID: d8e744d88ed6
Revises: 33f5fac87f29
Create Date: 2024-10-09 13:29:23.548498
"""
- 功能描述:这段注释说明了本次迁移的主要功能是修复
service-api
的历史记录数据。 - Revision ID:
d8e744d88ed6
是此次迁移的唯一标识符。 - Revises: 依赖于之前的迁移版本
33f5fac87f29
。 - Create Date: 创建日期为 2024-10-09。
(2)导入相关模块
from alembic import op
from constants import UUID_NIL
import models as models
import sqlalchemy as sa
alembic.op
:用于执行数据库操作,如op.execute()
,这是 Alembic 提供的操作接口。UUID_NIL
:假设UUID_NIL
是一个常量,表示空的 UUID。UUID_NIL
通常代表一个零值 UUID (00000000-0000-0000-0000-000000000000
)。models
和sqlalchemy
:虽然导入了models
和sqlalchemy
,但在此迁移脚本中并未直接使用这些模块。可能这些导入是模板代码的产物,或者为将来使用做准备。
(3)版本信息
revision = 'd8e744d88ed6'
down_revision = '33f5fac87f29'
branch_labels = None
depends_on = None
revision
:本次迁移的唯一标识符。down_revision
:指向上一个迁移的 ID,即33f5fac87f29
,用于定义迁移顺序。branch_labels
和depends_on
:没有定义分支标签或依赖。
(4)定义版本发布日期
v0_9_0_release_date = '2024-09-29 12:00:00'
v0_9_0_release_date
:这是版本 v0.9.0
的发布日期。用作 SQL 查询中的条件,用来确保只影响在此日期及之后创建的数据。
(5)upgrade
函数
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = f"""UPDATE
public.messages
SET
parent_message_id = '{UUID_NIL}'
WHERE
invoke_from = 'service-api'
AND parent_message_id IS NULL
AND created_at >= '{v0_9_0_release_date}';"""
op.execute(sql)
# ### end Alembic commands ###
-
功能:当运行
upgrade
时,执行一段 SQL 来修复parent_message_id
为NULL
的记录。 -
SQL 逻辑
- UPDATE public.messages:更新
messages
表。 - SET parent_message_id = ‘{UUID_NIL}’:将
parent_message_id
字段设置为UUID_NIL
。 - WHERE 子句
- invoke_from = ‘service-api’:只影响由
service-api
触发的记录。 - parent_message_id IS NULL:筛选出
parent_message_id
为NULL
的记录。 - AND created_at >= ‘{v0_9_0_release_date}’:确保只影响在
v0_9_0
版本发布后的数据(即2024-09-29 12:00:00
之后创建的记录)。
- invoke_from = ‘service-api’:只影响由
- UPDATE public.messages:更新
(6)downgrade
函数
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = f"""UPDATE
public.messages
SET
parent_message_id = NULL
WHERE
invoke_from = 'service-api'
AND parent_message_id = '{UUID_NIL}'
AND created_at >= '{v0_9_0_release_date}';"""
op.execute(sql)
# ### end Alembic commands ###
-
功能:当需要回滚迁移时,执行
downgrade
函数,恢复之前的状态。 -
SQL 逻辑
- UPDATE public.messages:更新
messages
表。 - SET parent_message_id = NULL:将
parent_message_id
字段恢复为NULL
。 - WHERE 子句
- invoke_from = ‘service-api’:只影响由
service-api
触发的记录。 - parent_message_id = ‘{UUID_NIL}’:筛选出
parent_message_id
为UUID_NIL
的记录。 - AND created_at >= ‘{v0_9_0_release_date}’:确保只影响在
v0_9_0
版本发布后的数据。
- invoke_from = ‘service-api’:只影响由
- UPDATE public.messages:更新
小结:这个迁移脚本的作用是修正 service-api
触发的消息记录,其中 parent_message_id
是 NULL
的记录被更新为 UUID_NIL
。upgrade
函数负责修改数据,downgrade
函数则负责回滚修改。
参考文献
[1] https://github.com/langgenius/dify/releases/tag/0.9.1-fix1
[2] Dify 的 Docker 部署指南 (中文翻译):https://z0yrmerhgi8.feishu.cn/wiki/GDscwWIeiir986kTkyNcIZZAnpe
[3] SQLAlchemy(alembic)和 Flask-SQLAlchemy(Flask-Migrate)入门教程:https://z0yrmerhgi8.feishu.cn/wiki/IbMsw5xbLintiGkCc2bcFDStnMc
[4] 当docker-compose.yaml文件部署时,Dify线上版本升级过程:https://z0yrmerhgi8.feishu.cn/wiki/O0q9wPKL9istdgkldDzcArV9nMe