一文入门 Docker Compose

本文同步发布在我的博客之中:一文入门 Docker Compose - Lynn的小站

前言

借助 Docker,您可将容器当做轻巧、模块化的虚拟机来使用。同时,您还将获得高度灵活性,实现对容器的高效创建、部署及复制,并在环境之间迁移它们,从而有助于您针对云来优化应用。

——《什么是 Docker?》

Docker Compose is a tool for running multi-container applications on Docker defined using the Compose file format. A Compose file is used to define how one or more containers that make up your application are configured. Once you have a Compose file, you can create and start your application with a single command: docker compose up

——《Docker Compose 官方介绍》

简单来说,Docker可以让我们把一个项目的所有依赖环境配置好,我们可以快速的运行起来,而无需处理环境的依赖问题;

而某个项目需要用到诸如数据库、Redis等其他项目的时候,使用Docker Compose可以将所有的项目和项目依赖通过一个yml完全配置好,我们只需要通过一行命令就可以快速启动这个项目。

Compose 使用的三个步骤:

  • 使用 Dockerfile 定义应用程序的环境。
  • 使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
  • 最后,执行 docker-compose up 命令来启动并运行整个应用程序。

——《Docker Compose - 菜鸟教程》

安装 Docker Compose

这部分,我们在这里仅介绍Linux的安装步骤,Windows和MacOS已经在Docker Desktop之中内置了Docker Compose的功能,如果你有其他特殊的需求,也可以参考官方文档。

基本上来说,只需要这两条命令即可:

sudo curl -L "https://github.com/docker/compose/releases/download/<版本号>/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

如果你使用的是Ubuntu系统,那么安装的步骤则会变得更为简单:

sudo apt install docker-compose

在安装完成后,我们可以通过下面的命令检查是否安装成功

docker compose --version

配置文件

接下来,我们聊聊配置文件,也就是Docker Compose的核心部分,在这里我们设计了两个服务,一种为源文件构造,另一种是拉取现有的镜像部署。

# compose.yml 
services:        # 定义服务
  web:           # Web 应用
    build: ./app   # 使用 ./app 目录下的 Dockerfile 构建镜像
    command: flask run --host=0.0.0.0 --port=5000   # 启动 Flask 应用
    ports:
      - "8000:5000"   # 本机8000端口映射到容器5000端口
    environment:
      # 数据库连接URL,这里通过服务名 db 连接 Postgres
      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      db:
        condition: service_healthy   # 等待数据库健康检查通过再启动

  db:            # 数据库服务(PostgreSQL)
    image: postgres:16   # 使用官方 Postgres 16 镜像
    restart: unless-stopped   # 意外退出时自动重启
    environment:
      POSTGRES_USER: postgres          # 数据库用户名
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}  # 从 .env 文件读取密码
      POSTGRES_DB: ${POSTGRES_DB}      # 默认数据库名
    volumes:
      - db_data:/var/lib/postgresql/data   # 数据持久化,保存到数据卷 db_data 之中
    healthcheck:   # 健康检查,保证数据库可用
      test: ["CMD-SHELL", "pg_isready -U postgres -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 10

volumes:        # 定义数据卷
  db_data:      # PostgreSQL 的数据卷

Service - Web服务(从源文件构造)

我们以Web服务为例,这里我们设计了一个Python Flask项目作为示例:

  web:           # Web 应用服务(Flask 示例)
    build: ./app   # 使用 ./app 目录下的 Dockerfile 构建镜像
    command: flask run --host=0.0.0.0 --port=5000   # 启动 Flask 应用
    ports:
      - "8000:5000"   # 本机8000端口映射到容器5000端口
    environment:
      # 数据库连接URL,这里通过服务名 db 连接 Postgres
      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      db:
        condition: service_healthy   # 等待数据库健康检查通过再启动
  • build:指定构建镜像的目录(./app 里需要有 Dockerfile)。
  • command:覆盖容器启动命令,这里用flask启动了一个允许所有IP访问的,端口为5000的服务端。

[!warning]
这里应该允许所有IP访问(0.0.0.0),而不是只允许本地访问(127.0.0.1),否则会导致无法从外部连接到容器。

  • ports:端口映射,本机 8000 → 容器 5000。
  • env_file:从 .env 文件读取环境变量。
  • environment:额外定义的环境变量,在这里我们设置了数据库地址。
  • depends_on:表示依赖关系,这里web服务要等到db服务通过了健康检查,才会启动。

Services - db服务(拉取现有的镜像)

  db:            # 数据库服务(PostgreSQL)
    image: postgres:16   # 使用官方 Postgres 16 镜像
    restart: unless-stopped   # 意外退出时自动重启
    environment:
      POSTGRES_USER: postgres          # 数据库用户名
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}  # 从 .env 文件读取密码
      POSTGRES_DB: ${POSTGRES_DB}      # 默认数据库名
    volumes:
      - db_data:/var/lib/postgresql/data   # 数据持久化,保存到卷 db_data
    healthcheck:   # 健康检查,保证数据库可用
      test: ["CMD-SHELL", "pg_isready -U postgres -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 10
  • image:使用构建好的镜像。这里我们固定了版本为16,如果使用latest可能会导致新旧版本的兼容性导致问题。
  • restart:容器异常退出后会自动重启。
  • environment:POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB 仅在第一次初始化数据目录时生效。之后数据由卷持久化,再改这些变量不会重置已有库。
  • volumes:用 命名卷(db_data)作为文件的存储,后面说映射到容器内的存储目录。
  • healthcheck
    • CMD-SHELL:表示这条命令会在容器的 shell 里执行(相当于 bash -c)。
    • pg_isready:这是 PostgreSQL 自带的一个小工具,用来检测数据库是否可以连接。
    • -U postgres:指定用 postgres 这个用户去检测。
    • -d ${POSTGRES_DB}:指定要检测的数据库名字(这里我们使用的是变量,会从 .env 文件读取同名的环境变量值)。

数据卷

在 Docker Compose 中,数据卷的定义通常放在最底部的 volumes 字段中。我们可以为每个服务定义一个或多个数据卷,以便在容器之间共享数据或持久化数据。
数据卷无需我们关心文件在机器的存放位置,Docker 会自动处理,而且数据卷也可以方便的在不同的容器之间共享。

volumes:
  db_data:   # PostgreSQL 的数据卷

而实际上大多数情况是,一些项目需要大家git clone下来,在仓库的路径进行操作,如果我们希望直接把这个目录挂载到容器中,可以在 volumes 中进行配置。

  web:
    volumes:
      - ./app:/app

上面的配置将宿主机的 ./app 目录挂载到容器的 /app 目录中,这样我们就可以在宿主机上直接修改代码,而容器内的应用会直接使用这个路径里的内容,同步改变的内容。

常用的命令

命令 作用 备注
docker compose up -d 后台启动所有服务 -d 表示 detached 模式,不占用当前终端
docker compose down 停止并清理容器、网络 数据卷默认保留,如果需要一起清理,可以使用docker compose down -v
docker compose ps 查看当前服务运行状态 类似 docker ps,但只显示 Compose 管理的容器
docker compose logs -f 查看日志(实时刷新) -f 类似 tail -f,适合调试
docker compose exec <服务名> bash 进入容器内部 比如:docker compose exec web bash 进入 web 容器

[!tip]

如果只想启动单个服务,可以用docker compose up -d web来启动。

迁移Docker项目到Docker Compose

接下来我们来尝试一下把现有的Docker项目迁移到Docker Compose,

说白了,其实就是把我们使用的各种参数转换成配置文件中的每一个字段,并且填写进去。

这里附上一份简单的转换表:

docker run Compose 字段 示例
–name app container_name container_name: app
-p 8080:80 ports ports: [“8080:80”]
-v host:ctr[:ro] volumes volumes: [“app_data:/var/lib/app”] 或 [“./cfg:/etc/app:ro”]
-e KEY=VAL environment / .env environment: [“KEY=${KEY}”]
–env-file .env env_file env_file: .env
–restart unless-stopped restart restart: unless-stopped
–network mynet networks networks: [“mynet”]
–health-cmd … healthcheck.test `test: [“CMD-SHELL”,“curl -f http://localhost/health
–cpus/–memory deploy.resources(本地用 deploy 限制有限) deploy: { resources: { limits: { cpus: “1.0”, memory: “512M”} } }
–dns/–add-host dns / extra_hosts extra_hosts: [“db.local:10.0.0.5”]
–log-driver logging.driver logging: { driver: “json-file” }

我们来看一个示例:

docker run -d --name db \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=devpassword \
  -e POSTGRES_DB=mydb \
  --restart unless-stopped \
  postgres:16

让我们转换成docker compose的配置文件,便得到了如下结果:

services:
  db:
    image: postgres:16
    container_name: db
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL","pg_isready -U postgres -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 10
    ports:
      - "5432:5432"
    restart: unless-stopped

volumes:
  pgdata:

上面我们使用了一些环境变量,在这里我们可以有两种方式使用他们,第一种便是直接写入到配置文件之中,也就是:

    environment:
      POSTGRES_PASSWORD: devpassword
      POSTGRES_DB: mydb

但是这样的话,我们如果把这个配置文件分享给其他人用,或者是上传到Github仓库的时候,那就不是很安全了,所以我们还有第二种方式,也就是上面我使用的方式,我们新建一个.env文件来存储这些变量的值。

.env(与 yml 同目录):

POSTGRES_PASSWORD=devpassword
POSTGRES_DB=mydb

个人技巧

更新镜像以始终保持最新

在使用LobeChat的时候,我希望保持镜像的最新以体验最新的功能,所以在官方脚本的参考下设计了一个更完善的脚本:

此处脚本因为使用了AI进行优化,所以不放出原文,大家可以点击链接查看。

如果需要适配其他服务,只需要修改WORK_DIRIMAGE_REPO的内容即可,同时IMAGE_TAG可以匹配不同TAG,方便使用。

后记

这篇文章只是简单聊了聊Docker Compose的安装、配置文件和常用命令,并没有涉及到一些更深层的内容和配置,以及容器内部连接等内容,如果想要了解更多,你可以参考官方文档和现有的教程,也可以在下面留言,如果有时间也会写一下文章介绍给大家。

参考内容

162 个赞

挠头,怎么开目录功能来着()

5 个赞

很有用,最近一直在部署一些项目正好能用到,谢谢佬。

6 个赞

感谢佬分享

4 个赞

最近在部署 一些本地自用的sever,刚好能用上,感谢佬

6 个赞

很详细,感谢佬友分享 :folded_hands:

4 个赞

感谢分享:tieba_003:

4 个赞

感谢大佬!

2 个赞

感谢分享,教程很详细学习了

5 个赞

这是老版命令,新安装的docker会报错。

Command 'docker-compose' not found, 
but can be installed with:sudo apt install docker-compose

新版命令是

docker compose version

佬的引用的是v2写法,后面给的示例又回到了V1,建议统一下。

(记住是不可能记住的,docker命令都是现场问AI)

7 个赞

建议楼主更新下, 现在新版都是 docker compose 这个命令了

4 个赞

还是建议看官方文档啊,而且最新的现在docker compose文件直接命名compose.yml就行,感觉OP的内容很多都过时了。How Compose works | Docker Docs

7 个赞

没错,是这样的。
我记得docker-compose命令开代理拉镜像,有的镜像拉不下来或容器跑不起来一直重启 :smiling_face_with_tear:

5 个赞

命令不对应该是直接报错了吧,拉不下来应该是网络的问题。

4 个赞

很有用,我认真学习。

3 个赞

感谢分享

4 个赞

感谢佬,我入门就够了

4 个赞

好嘞 谢谢佬友提醒 等下吃完饭就回来改一下~

2 个赞

感谢分享,收藏!

3 个赞

还有首行的version弃用。帖子不错,楼主好好维护下,很有价值。

3 个赞