简介
-
对于现代应用来说,大多都是通过很多的微服务互相协同组成的一个完整应用。例如,订单管理、用户管理、品类管理、缓存服务、数据库服务等,它们构成了一个电商平台的应用。而部署和管理大量的服务容器是一件非常繁琐的事情。而 Docker Compose 就是解决这类问题的。
-
Docker Compose 是一个需要在 Docker 主机上进行安装的 Docker 容器编排外部工具。其并不是通过脚本或各种冗长的 Docker 命令来将应用组件组织起来,而是通过一个声明式的配置文件描述整个应用,然后通过一条命令完成应用部署。部署成功后,还可通过一系列简单命令实现对其完整生命周期的管理。
-
Docker Compose 的前身是由 Orchard Laboratories 公司开发的 Fig 项目。Orchard Laboratories 是一家在 2011 年成立的英国创业公司,专注于提供 Docker 托管服务。在 2014 年,Docker 公司收购了 Orchard Laboratories,并将 Fig 项目纳入自己的项目群,进一步发展成为了我们现在所熟知的 Docker Compose。
compose 文件
一、 文件简介
Docker Compose 使用 YAML 文件来定义服务。官方推荐的默认文件名为 compose.yml ,但同时也支持 docker-compose.yml。由于一个 compose 文件中定义的为一个项目的所有服务,所以一般为在创建 compose 文件之前先新建一个目录,目录名称一般为项目名称,然后再将项目所需的所有镜像、微服务的 Dockerfile 放入该目录,并在该目录中新建 compose 文件。
compose 文件中包含 6 个顶级属性:version、services、networks、volumes、configs 与 secrets,及很多的它们下面所包含的属性。下面简单介绍一下常用的属性。
二、 version
version是一个顶级属性,从 Docker Compose 1.27.0 开始,version 属性已经被标记为过时。这是因为 Docker Compose 现在可以根据 Compose 文件中使用的特性自动确定版本,所以不再需要手动指定。虽然 version 属性已经过时,但在现有的 Docker Compose 文件中仍然可以使用,不会报错。只是在新的 Compose 文件中,我们不再推荐使用它。
三、 services
services 是一个顶级属性,用于定义一个应用中所包含的服务。Docker Compose 会将每个服务部署在各自的容器中。其下包含的第一级的属性即为服务名称,这个名称可以根据服务内容随意命名。而在服务名称下还可包含很多的属性,常用属性如下:
-
build
用于指定一个 Dockerfile 的路径。而该 Dockerfile 则是用于创建当前服务镜像的。这个路径可以是以斜杠(/)开头的绝对路径,也可以是相对于当前 compose 文件的、以点(.)号开头的相对路径。如果 Dockerfile 文件名不是默认名称,则需要通过 build 下的 context 属性指定路径,dockerfile 属性指定文件名。yaml# 层级关系用两个空格键表示,而不是Tab键# 每个:后面一定要有一个空格services: mysqlDB: build: context: ./ dockerfile: myDockerfile -
image
用户指定当前服务所需要使用的镜像,这个镜像可以是本地镜像,也可以是远程镜像仓库中的镜像(会自动 pull)。如果设置了 build,此时再设置的 image 属性即为构建出的镜像的名称与 Tag。yamlservices: mysqlDB: image: mysql:8.0 -
container_name
该属性用于设置容器名称,但并不是必须的。如果没有设置该属性,容器名称则会采用“合成方式”。而合成时需要用到 services 下的第一级属性。在 services 下存在一级属性,称为服务名称。该级属性是作为 services 下的第一级属性出现的。服务名称将来会作为容器名称的一部分出现。容器的名称格式为:当前 compose 文件所在目录名_ 服务名称。yamlservices: mysqlDB: image: mysql:8.0 container_name: mysql_container -
ports
一个列表。
前面为暴露出的端口号,后面为容器中应用的端口号。如果仅设置了一个端口号,那么这个端口号是容器中应用的端口号,其暴露到宿主机的端口号会被随机分配。yamlservices: mysqlDB: image: mysql:8.0 ports: - 80:80 # 绑定容器的 80 端口到主机的 80 端口 - 9000:80 # 绑定容器的 80 端口到主机的 9000 端口 - 443 # 绑定容器的 443 端口到主机的任意端口 -
command
用于覆盖 Dockerfile 中的 CMD 指令内容,即启动该服务容器后立即运行的命令。如果直接按照 Dockerfile 中的 CMD 指令内容执行即可,则 compose 文件中无需该 command 属性。command 属性的语法和 Dockerfile 中的语法一样,有 shell 和 exec 两种语法。yamlservices: mysqlDB: image: mysql:8.0 command: ["/bin/bash","-c" "pwd"] -
depends_on
一个列表。用于指定当前服务的启动所依赖的应用名称。即列表中指定的服务会先于当前服务启动。
yamlservices: mysqlDB: image: mysql:8.0 depends_on: - nginx - initConf -
deploy
用于指定当前服务容器的部署设置,它只在 Swarm 模式下有效。其下有一个常用属性 replicas,用于指定该服务启动的容器的数量。即实现一个服务多个容器。一旦指定了 deploy: replicas,就不能再指定 container_name 属性了。因为各个启动的容器名称不能相同,而只能由系统自动生成。yamlservices: mysqlDB: image: mysql:8.0 deploy: mode: replicated replicas: 6 -
networks
用于指定当前服务容器要连接到的网络。该网络必须是已经存在的,或通过顶级属性 networks 创建的网络。yaml# 将mysqlDB服务连接到frontend和backend两个网络:services: mysqlDB: image: mysql:8.0 networks: - frontend - backend需要注意以下几点:
- 如果你没有指定任何网络,那么服务将会连接到一个默认的网络,这个网络的名称是
项目名_default。 - 如果你指定了多个网络,那么服务的容器将会有多个网络接口,每个网络接口连接到一个网络。
- 你可以使用
docker network create命令或者 Docker Compose 的networks选项来创建网络。
- 如果你没有指定任何网络,那么服务将会连接到一个默认的网络,这个网络的名称是
-
volumes
用于指定当前服务容器所使用到的所有 volume。这些 volume 可以使用路径与卷标两种方式。例如,下面是路径方式,非常直观,易于查看,但需要管理本地路径。volumes 属性值的格式是<宿主机路径>:<容器路径>yamlservices: mysqlDB: image: mysql:8.0 volumes: - /etc/mysql:/var/lib/mysql再如,下面是卷标方式。volumes 属性值的格式是
<数据卷名>:<容器路径>backend 与 backup 两个服务共享了 db-data 的卷,逻辑简洁明了,且无需管理本地路径。但具体卷标所代表的是 Docker 主机的哪个路径,并不能直观的看到。需要通过 docker volume inspect [卷标]来查看。yamlservices: backend: image: awesome/database volumes: - db-data:/var/lib/backend/data backup: image: backup-service volumes: - db-data:/var/lib/backup/datavolumes: db-data:
四、 networks
networks 作为一个顶级属性,用于定义和创建应用中所使用到的所有网络。其下包含的第一级属性即为网络名称,这个网络名称可以随意命名。而在网络名称下还可包含很多的属性,常用属性如下:
-
name
name属性允许你为网络指定一个自定义的名称。默认情况下,也就是不使用 name 属性的时候,创建的网络名称将为<项目名>_<网络名>。如果你为网络指定了 name 属性,那么网络将直接使用这个名称,而不是默认的名称。yamlnetworks: mynetwork: name: my_custom_network在这个例子中,由于设置了 name 属性,所以创建的网络的名称将为 my_custom_network,而不是默认的<项目名>_mynetwork。
-
driver
用于指定网络驱动,缺省驱动为 bridge。 -
attachable
attachable属性允许单独的服务或者容器连接到指定的网络。默认情况下,由 Docker Compose 创建的网络只能被在同一个 Compose 文件中定义的服务所使用。但是如果你将网络设为 attachable,那么其他的服务或者容器也可以连接到这个网络。yamlnetworks: mynetwork: driver: overlay attachable: true在这个例子中,mynetwork 是一个 attachable 网络,所以你可以在 docker run 命令中使用 --network=mynetwork 选项,将单独的容器连接到这个网络。需要注意的是,attachable 选项只对 overlay 和 macvlan 网络驱动有效。
五、volumes
volumes 作为一个顶级属性,用于定义和创建应用中所使用到的所有 volume。其下包含的第一级属性即为 volume 的卷标,这个卷标可以随意命名。这个卷标所代表的是当前 Docker 主机中的目录,至于该目录的具体位置,是由系统自动分配的。由于前面的 services 属性下已经用过 volumes 的用法了,所以这里不再过多展开。
常用命令
Docker Compose 通过 docker-compose 系列命令查看和控制 compose 中的所有服务容器。
-
docker-compose pull
拉取 compose 中服务依赖的全部镜像或指定镜像。通过在命令后添加服务名称来指定。
-
docker-compose config
检查 compose 文件是否正确。可添加选项-q,表示只有存在问题时才有输出。
-
docker-compose up
启动 compose 中的所有容器。-d 选项表示后台启动。
-
docker-compose logs
查看 comopse 中所有服务或指定服务的运行日志。通过在命令后添加服务名称来指定。默认情况下,将对不同的服务日志使用不同的颜色来区分。
-
docker-compose ps
列出 compose 中所有服务或指定服务。通过在命令后添加服务名称来指定。
-
docker-compose top
列出 compose 中当前正在运行的所有服务或指定服务。通过在命令后添加服务名称来指定。
-
docker-compose images
列出 compose 中所有服务或指定服务对应的镜像。通过在命令后添加服务名称来指定。
-
docker-compose port
列出指定服务容器的指定端口所映射的宿主机端口。
-
docker-compose run
在指定服务上执行一条命令。
-
docker-compose exec
进入指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose pause
暂停 compose 中所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose unpause
恢复 compose 中处于暂停状态的所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose stop
停止 compose 中所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose restart
重启 compose 中所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose start
启动 compose 中所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose kill
通过发送 SIGKILL 信号停止指定服务的容器。
-
docker-compose rm
删除 compose 中的、处于停止状态的所有服务容器或指定服务容器。通过在命令后添加服务名称来指定。
-
docker-compose down
停止并删除 compose 中的所有服务容器、网络、镜像、数据卷。
项目搭建
现在搭建一个使用 go 语言编写的后端网站,后端架构采用 gin 框架、mysql 和 redis 数据库,有一个用户表和一个文章表。由于我是在 windows 系统下面编写的 go 代码,所以需要使用交叉编译的方式生成一个项目的可执行文件。操作截图如下:

这样就会生成一个文件名为 main 的可以在 linux 系统上运行的可执行文件。接下来就需要在 centos 系统下创建一个目录,目录名就是当前的项目名:docker_compose_blog。将刚刚生成的 main 文件上传到该目录下,不过需要注意这个 main 文件是否有可执行权限,如果没有需要使用 chmod 命令赋予可执行权限。并且还需要把 go 项目当中的 config.yml 配置文件上传到该目录下。然后就需要编写 Dockerfile 文件和 compose.yml 文件。其中 Dockerfile 文件的内容如下:
FROM golang:1.19LABEL auth="bing" version="1.0" description="This is a blog service"WORKDIR /appCOPY ./main /appCOPY ./config.yml /app/config/config.ymlEXPOSE 8080Dockerfile 文件写完后就需要把这个 Dockerfile 构建为一个镜像。如下所示:

注意:由于当前的 web 服务依赖于 mysql 和 redis 的启动,所以就不能在 Dockerfile 文件中定义这个容器的启动命令,因此就需要我们在 compose 文件中定义容器的启动命令。
构建镜像的时间比较长,差不多用了 5 分钟。接下来就需要编写 compose.yml 文件了。compose.yml 文件的内容如下所示:
services: mysql: image: mysql:8.1 container_name: mysql environment: MYSQL_ROOT_PASSWORD: 111 ports: - 3306:3306 volumes: - /root/mysql/data:/var/lib/mysql - /root/mysql/log:/var/log/mysql - /root/mysql/conf:/etc/mysql/conf.d redis: image: redis:7.0 container_name: redis ports: - 6379:6379 volumes: - /root/redis/redis.conf:/etc/redis/redis.conf - /root/redis/data:/data gin: build: ./ container_name: blog ports: - 8080:8080 volumes: - ./logs:/var/applogs - ./config.yml:/app/config/config.yml - ./wait-for-it.sh:/app/wait-for-it.sh depends_on: - mysql - redis command: ["./wait-for-it.sh", "mysql:3306", "--", "./wait-for-it.sh", "redis:6379","--","/app/main"]刚开始我认为只要添加了 depends_on 就能让 mysql 和 redis 启动后再启动 gin 服务。但是后来报错了,原因是由于 mysql 还未完全启动,导致 gin 服务连接不了 mysql。所以还需要一个等待其他服务启动完成的一个脚本。这个脚本就是 wait-for-it.sh。它在 github 上就能下载到。地址:https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh。所以,我们要将这个脚本上传到docker_compose_blog目录下,并挂载到gin服务这个容器中(- ./wait-for-it.sh:/app/wait-for-it.sh)。下面是 docker_compose_blog 目录下的所有需要用到的文件:

Compose 编排启动项目
前面就完成了所有的准备工作,现在只需要敲一些命令就行。使用 docker-compose config 命令可以查看我们的 compose.yml 文件是否有语法错误,并且加上-q 选项就会只输出错误信息,没有任何消息输出就表示没有语法错误。使用 docker-compose up 命令就能启动所有容器,使用-d 选项表示后台启动。

接口测试


由于接口较多,这里就不逐个展示。
查看网络信息
使用 docker network ls 命令就能查看当前 docker 的所有网络。可以看到使用 docker compose 它能创建一个网络(docker_compose_blog_default),模式为 bridge。

使用 docker network inspect docker_compose_blog_default 命令就能查看 docker_compose_blog_default 网络的详细信息。主要的内容如下:

可以看到 docker compose 给每个服务都上传相应的容器并分配相应的 IP 地址,这也是为什么 go 项目中的 config.yml 配置文件中的 host 不需要写具体 IP 地址的原因。当然关于 docker_compose_blog_default 这个网络的名字也是可以自定义,这需要通过 compose.yml 文件来控制。关于 docker compose 的配置和命令还有很多,以后用到了在慢慢探索。