Docker Compose 笔记 ongoing
文档用途
说明如何编写 compose.yaml 以及如何使用 docker compose 命令.
Nvidia GPU
需要先安装 Nvidia Container Toolkit.
若在 WSL 中运行 Docker, 则 不需要 单独安装.
使用 apt 安装 Nvidia Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update && sudo apt install -y nvidia-container-toolkit然后需要配置 nvidia container runtime 并重启 docker engine.
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker在 compose.yaml 中使用 Nvidia GPU 的示例:
services:
test:
image: nvidia/cuda:10.2-base
command: nvidia-smi
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]端口映射
services:
demo:
ports:
# 宿主系统的端口:容器的端口
- "80:80"
# 网络驱动为默认的 bridge 时,
# 如果不指定宿主系统的端口, docker 就会随机分配一个可用端口.
# 例如这里会随机分配宿主系统的端口, 并映射到容器的 5389 端口.
- "5389"当网络驱动为 host, ipvlan, macvlan 时, 不应配置端口.
对于 host 网络驱动, 容器会直接使用宿主系统的端口 (存在端口冲突的可能).
对于 ipvlan 和 macvlan , 容器则会使用 Docker 虚拟出来的 IP 地址的端口.
环境变量
在 compose.yaml 文件所在的同级路径中, 创建名为 .env 的文件.
当 compose.yaml 被解析时, docker 会读取 .env 中的环境变量.
# .env
ROOT_USER=xxx
ROOT_PASSWORD=xxxservices:
demo:
image: minio/minio:latest
environment:
- "ROOT_USER=${ROOT_USER}"
- "ROOT_PASSWORD=${ROOT_PASSWORD}"TIP
可以通过这种方式来避免在代码仓库中记录密码等敏感信息.
例如可以在 .gitignore 中忽略 .env 文件, 并仅在生产环境中创建 .env.
.env 中的环境变量只会给 compose.yaml 用, 不会 被容器实例获取.
而 compose.yaml 中还存在另一个 "看起来有关, 实则很容易产生歧义" 的地方:env_file 字段所引用文件, 不会 被 compose.yaml 获取, 只会被容器实例获取 (详见 issue).
services:
demo:
env_file:
- ./file.env默认网络
如果不使用 network 字段创建网络实例, 那么 docker 就会自动创建.
例如某个 compose.yaml 所在的文件夹名称为 demo,
那么自动创建的 network 就会叫做 demo_default.
构建并运行
假设所使用的 Dockerfile 如下:
# 注意这里的 final 名称, 在后面要用到
FROM alpine AS final
CMD ["echo", "yahaha"]那么就可以这样配置 compose.yaml:
# compose.yaml
services:
server:
build:
context: .
target: final之后就可以通过以下命令构建并运行:
docker compose up --build如果本地测试结束, 建议使用此命令移除处于状态的容器实例:
docker compose down持续监测改动
Docker 可以持续监测目标路径中的文件变动, 并按照配置做出应对.
例如某些路径中的配置文件发生变动时, 就将它们同步到正在运行的容器内;
或是某个路径下的文件发生变动时, 就重新构建并运行镜像.
services:
server:
build: .
develop:
watch:
# 当 path 中指定的文件发生变动时,
# 重新构建镜像并运行.
- action: rebuild
# 通过 path 指定工程目录中需要被监测的文件.
path: package.json
# 当 path 中指定的文件发生变动时,
# 将改动同步到正在运行的容器中.
- action: sync
path: ./web
# 通过 target 指定文件被映射到的容器内的路径.
target: /src/web
# 通过 ignore 字段指定更多需要被忽略的路径.
# (写在 .dockerignore 中的依然起效).
ignore:
- node_modules/与以上配置配合使用的命令如下:
# 通过此命令启动
docker compose watch
# 开发结束时, 记得释放资源
docker compose down -v在实际使用中, 可能会遇到以下问题:
cannot take exclusive lock for project "xxx": process with PID xxx is still running此时的解决方案是: 将记录 docker pid 的文件删除.
在以下路径中找到 [PROJECT_NAME].pid 文件并删除即可:
~/AppData/Local/docker-compose/~/Library/Application Support/com.docker.compose/触发式命令
开发过程中, 我们可能需要用到 "需要额外安装的命令行工具".
这样通常会出现 "有人装了有人没装" 或 "每个人安装的版本不一致" 的问题.
此时我们就可以借助 docker compose run 命令, 解决以上问题:
services:
# 配置名为 terraform 的 service.
# 只用于执行命令, 不持续运行在宿主系统中.
terraform:
image: hashicorp/terraform:1.7.4
# 通过 volume mount 机制,
# 可以让那些 "最好位于工程根目录的文件" 能被放在子文件夹中.
volumes:
- ./terraform:/infra
working_dir: /infra# 单独运行某个定义在 compose.yaml 中的某个 service.
docker compose run --rm [SERVICE] [COMMAND]
docker compose run --rm terraform init这个方案还可以满足 "single source of truth" 原则.