近期的工作中有这么一个场景:项目代码需要进行频繁的更新,并打包成镜像在客户现场进行部署。由于需要使用CUDA的镜像,因此每次打包完的tar包都有13G之多。为了解决每次更新都会出现的传输过慢问题,我们采用了大镜像包+小更新镜像包的方式。
思路很简单,就是先构建一个大的基础包,每次将需要更新的内容传进容器,再build一个新版本。
基础镜像
首先,我们有一个基础镜像的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| version: '3.1'
services: frontend: platform: linux/amd64 build: context: ../frontend dockerfile: Dockerfile image: behavior-detector-frontend:${TAG}
infers: platform: linux/amd64 build: context: ../infers dockerfile: Dockerfile args: - TAG=${TAG} image: behavior-detector-infers:${TAG}
backend: platform: linux/amd64 build: context: ../backend dockerfile: Dockerfile args: - TAG=${TAG} image: behavior-detector-backend:${TAG} depends_on: - frontend
|
通过构建以上的compose
文件,就能得到前后端和推理端的基础镜像。
更新包脚本
我们通常在项目的各个路径下进行开发。当我们把infers
或者backend
开发完毕后,就需要对这两个项目的文件夹进行打包。为此,我们定义一个save_patch.sh
来将内容进行打包。
1 2 3 4 5 6 7 8 9 10 11
| #! /usr/bin/env bash PROJECT_DIR=$(cd `dirname $0`/..; pwd)
. .env
tar -cvf infers_patch_${TAG}.${PATCH_VERSION}.tar -C ${PROJECT_DIR}/infers/ .
tar -cvf backend_patch_${TAG}.${PATCH_VERSION}.tar -C ${PROJECT_DIR}/backend/ .
|
上面的脚本做了两件事,读取配置文件;将推理和后端的文件夹infers
和backend
打成tar包。
加载包脚本
当我们拿到了更新包以后,就需要将包导入到已有的镜像,并发布一版新镜像。这里给出一个load_patch.sh
。
1 2 3 4 5 6 7 8 9 10 11
| #! /usr/bin/env bash
. .env
CURRENT_DIR=$(cd `dirname $0`; pwd)
\cp -rf ${CURRENT_DIR}/infers_patch_${TAG}.${PATCH_VERSION}.tar infers_patch.tar \cp -rf ${CURRENT_DIR}/backend_patch_${TAG}.${PATCH_VERSION}.tar backend_patch.tar docker compose -f docker-compose.patch.yaml build
|
这里做的几件事:将更新包复制成infers_path.tar
和backend_patch.tar
。这两个包会被之后的compose
文件使用来构建镜像。
Docker Compose Patch
构建更新后的镜像需要用到docker compose.patch.yaml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| version: '3.1'
services: infers: platform: linux/amd64 build: context: . dockerfile: Dockerfile.infers.patch args: - TAG=${TAG} - PATCH_VERSION=${PATCH_VERSION} image: behavior-detector-infers:${TAG}.${PATCH_VERSION}
backend: platform: linux/amd64 build: context: . dockerfile: Dockerfile.backend.patch args: - TAG=${TAG} - PATCH_VERSION=${PATCH_VERSION} image: behavior-detector-backend:${TAG}.${PATCH_VERSION}
|
当我们build的时候,就会生成两个镜像:behavior-detector-infers
和behavior-detector-backend
。
更新包Dockfile
项目中频繁更新的部分是infers
和backend
容器,因此为这两个部分的更新包单独写Dockerfile。
后端更新包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ARG TAG=1.0.0 ARG PATCH_VERSION=1 FROM behavior-detector-backend:${TAG}
WORKDIR /backend_app
RUN rm -rf /backend_app/*
COPY backend_patch.tar /backend_app/ RUN tar xvf /backend_app/backend_patch.tar -C /backend_app
EXPOSE 80
EXPOSE 7091 EXPOSE 7092 EXPOSE 7093
CMD ["bash", "start.sh"]
|
推理更新包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ARG TAG=1.0.0 ARG PATCH_VERSION=1 FROM behavior-detector-infers:${TAG}
WORKDIR /infer_app
RUN rm -rf /infer_app/*
COPY infers_patch.tar /infer_app/ RUN tar xvf /infer_app/infers_patch.tar -C /infer_app
EXPOSE 58090
CMD ["bash", "start.sh", "prod"]
|
Docker Compose Prod
最后还需要有一个实际部署的docker compose
文件,这个文件是实际真正部署的容器镜像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| version: '3.1' services: redis-service: image: redis:latest restart: always ports: - "6379:6379"
detector-service-api: image: behavior-detector-backend:${TAG}.${PATCH_VERSION} restart: always environment: - RUNTIME_ENV=prod - RUNTIME_SERVICE_TYPE=api
- POSTGRESQL_HOST=xxxx - POSTGRESQL_PORT=xxxx - POSTGRESQL_DBNAME=xxxx - POSTGRESQL_USER=xxxx - POSTGRESQL_PASSWD=xxxx
- REDIS_HOST=xxxx - REDIS_PORT=6379 - REDIS_PASSWD=
volumes: - /app/logs:/app/logs - /app/captures:/app/captures - /app/targets:/app/targets - /app/html:/app/html ports: - "7901:80" depends_on: - redis-service
detector-service-stream: image: behavior-detector-backend:${TAG}.${PATCH_VERSION} restart: always environment:
- RUNTIME_ENV=prod - RUNTIME_SERVICE_TYPE=stream
- POSTGRESQL_HOST=xxxx - POSTGRESQL_PORT=xxxx - POSTGRESQL_DBNAME=xxxx - POSTGRESQL_USER=xxxx - POSTGRESQL_PASSWD=xxxx
- REDIS_HOST=xxxx - REDIS_PORT=6379 - REDIS_PASSWD=
volumes: - /app/logs:/app/logs - /app/captures:/app/captures - /app/targets:/app/targets - /app/html:/app/html
depends_on: - redis-service staytime: image: behavior-detector-infers:${TAG}.${PATCH_VERSION} restart: always ports: - "58093:58090" volumes: - /app:/app environment: - ENV_MODEL_NAME=staytime - ENV_GUNICORN_WORKERS=1 - ENV_CUDA_DEVICE=0 deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [ gpu ]
|
实际使用中,使用docker compose -f docker-compose.prod.yaml up -d
即可进行部署。
2024/4/22 于苏州