负责实现对 Docker 容器集群的快速编排,允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project),从而实现一键启动/关闭容器
例如在使用 docker 部署微服务项目A时,项目A需要按顺序依次启动 Nginx > Nacos > RabbtiMQ > Redis > MySQL … 等中间件,根据以往的 docker run 方式启动是非常繁琐的,还有可能误操作导致容器顺序启动不对,此时使用 Docker-compose 就能很好解决这一痛点
Docker-compose 适用于几十个内的项目模块,如果项目模块数量过多,docker-compose.yml 将不好维护,且只能用于单机环境,因此实际开发中如果是集群中间件,容器编排会使用 k8s
核心概念:
文件:docker-compose.yml
服务:docker 内的容器应用实例
工程:由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
使用步骤:
- 编写 Dockerfile 定义各个微服务应用并构建出对应的镜像文件
- 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务
- 执行 docker-compose up (V1版本)命令来启动并运行整个应用程序,完成一键部署上线
项目创建:
创建一个需要使用 Redis + MySQL 的 SpringBoot 项目来演示 Docker-compose 的使用案例
项目地址:Gitee
非 docker-compose 发布项目:
演示一个不使用 docker-compose 发布项目的步骤
编写 Dockerfile 生成项目镜像:
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER dmbjz
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为dmbjz_docker.jar
ADD dmbjz-0.0.1-SNAPSHOT.jar dmbjz_docker.jar
# 运行jar包
RUN bash -c 'touch /dmbjz_docker.jar'
ENTRYPOINT ["java","-jar","/dmbjz_docker.jar"]
#暴露8181端口作为微服务
EXPOSE 8181
![image.png](https://cdn.nlark.com/yuque/0/2022/png/21405095/1660379161302-fee436ca-a372-4775-99fa-1b23c23c2e6c.png#clientId=ubb681977-4f38-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=501&id=u7a3c7263&margin=%5Bobject%20Object%5D&name=image.png&originHeight=903&originWidth=917&originalType=binary&ratio=1&rotation=0&showTitle=true&size=78600&status=done&style=stroke&taskId=ue220920c-6a07-44d7-9c1a-098ccf13d76&title=%E9%A1%B9%E7%9B%AE%E9%95%9C%E5%83%8F%E7%94%9F%E6%88%90%E6%88%90%E5%8A%9F&width=508.3333740234375 "项目镜像生成成功")
创建 MySQL 容器:
docker run -p 3306:3306 --name mysql57 --privileged=true -v /dmbjz/mysql/conf:/etc/mysql/conf.d -v /dmbjz/mysql/logs:/logs -v /dmbjz/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
创建数据库:
使用 Navicat 等工具远程连接上 MySQL 容器,创建 docker 数据库 和 t_user 表
CREATE TABLE `t_user` (
`id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '密码',
`sex` int NOT NULL DEFAULT 0 COMMENT '性别 0=女 1=男 ',
`deleted` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '删除标志,默认0不删除,1删除',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
![image.png](https://cdn.nlark.com/yuque/0/2022/png/21405095/1660379668184-03a8de21-bd89-494b-87cd-c813b83226d8.png#clientId=u3349d78a-0a79-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=223&id=u2501eebc&margin=%5Bobject%20Object%5D&name=image.png&originHeight=236&originWidth=202&originalType=binary&ratio=1&rotation=0&showTitle=true&size=10362&status=done&style=stroke&taskId=u4bf71e43-4248-4d9f-b85e-fb2f70e26b1&title=%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E5%AF%B9%E5%BA%94%E8%A1%A8&width=190.6666717529297 "创建数据库与对应表")
创建 Redis 容器:
docker run -p 6379:6379 --name redis608 --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf
启动项目:
docker run -d -p 8181:8181 dmbjz:1.0
测试API:
Docker-Compose 操作:
执行命令时需要在 docker-compose.yml 文件所在路径下,否则可能提示 docker-compose.yml 找不到
下载安装:
使用 GitHub 下载 1.29.2 版本,然后改名 docker-compose 并放到 /usr/local/bin/ 路径下
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
授予读写权限
chmod +x /usr/local/bin/docker-compose
查看安装情况
docker-compose --version
卸载:
直接删除 docker-compose 文件即可
rm /usr/local/bin/docker-compose
常用命令:
在 docker-compose.yml 文件所在路径进行操作,如果文件名不为 docker-compose.yml,需要使用 -f 参数进行指定
当 docker-compose.yml 中使用了 build 指定 Dockerfile 文件时,如果后期 Dockerfile 文件有修改,需要使用 docker-compose build 或者 docker-compose up -d —build 进行更新镜像(原因:在首次 up 后 docker-compose 不会再关注 Dockerfile 文件是否被修改)
当 docker-compose.yml 中删除了不需要的服务,可以通过 docker-compose up -d —remove-orphans 在下次启动容器编排时移除被删除服务对应的容器
docker-compose -h # 查看帮助
docker-compose up # 启动所有docker-compose服务
docker-compose up -d # 启动所有docker-compose服务并后台运行
docker-compose down # 停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps # 展示当前docker-compose编排过的运行的所有容器
docker-compose top # 展示当前docker-compose编排过的容器进程
docker-compose rm # 删除由docker-compose创建且已经停止的容器
docker-compose build # 重新构建镜像(例如修改docker-compose.yml文件中dockerFile的参数)
docker-compose logs yml里面的服务id # 查看容器输出日志
docker-compose config # 检查配置
docker-compose config -q # 检查配置,有问题才有输出
docker-compose restart # 重启 docker-compose 服务
docker-compose start # 启动 docker-compose 服务
docker-compose stop # 停止 docker-compose 服务
使用 Docker-compose 发布:
对之前演示的案例使用 Docker-compose 进行发布
docker-compose.yml 语法模板:
docker-compose 与 docker 引擎之间的版本兼容关系参考官方文档,这里以 V3 版为例:
version: "3.8"
services: # 容器
servicename: # 服务名字,不同的服务之间可以通过该名称进行ping通
image: # 镜像的名字与版本
build: #相当于 docker build。如果有build,image相当于指定生成后的镜像的 镜像名:版本号
context: #dockerfile 文件路径
dockerfile: #dockerfile 文件
command: # 可选,如果设置,则会覆盖默认镜像里的 CMD命令
environment: # 可选,相当于 docker run里的 --env,可以从文件中读取值
volumes: # 可选,相当于docker run里的 -v
networks: # 可选,相当于 docker run里的 --network
ports: # 可选,相当于 docker run里的 -p
servicename2:
servicename3:
volumes: # 可选,相当于 docker volume create
networks: # 可选,相当于 docker network create,如果没有该参数 docker-compose 会自动创建一个叫 compose_defaulf 的自定义网络
name1: #自定义网络1
name2: #自定义网络1
编写 docker-compose.yml:
使用 docker-compose.yml 创建 docker 网络会自动在名称之前加上当前 docker-compose.yml 所在文件夹的前缀,虽然当前案例中创建的网络名为: dmbjz_net,然而实际网络名称为:当前docker-compose.yml所在文件夹名_dmbjz_net
如果依赖使用的镜像没有指定容器名(containername),其创建出的容器名称的命名规则与网络名类似,为:**当前docker-compose.yml所在文件夹名镜像名_数字(从1开始逐渐递增)**
容器名问题:
在实际项目部署时,如果不指定 depends_on 中镜像生成后的容器名( container_name ),项目容器会优先使用通过 docker-compose.yml 中创建的镜像容器,因此当多个服务使用同一个文件夹进行容器编排,可能会导致调用错误,所以实际编写 docker-compose.yml 需要指定容器名
version: "3" #docker-compose文件编写版本
services: #使用的服务,固定
microService: #具体的service名称,类似 spring.application.name
image: dmbjz:2.0 #需要使用的项目镜像:版本号
container_name: springboot_docker #生成的容器名称
ports:
- "8181:8181" #对外暴露端口(与项目server.port一致)
volumes:
- /app/microService:/data #数据卷挂载,可用于查看日志
networks:
- dmbjz_net #指定网络,必须为自定义网络
depends_on: #定义容器启动顺序,按照数组顺序依次启动,有该参数的服务最后启动
- redis
- mysql
redis:
image: redis:6.0.8
container_name: dmbjz_redis #生成的容器名称
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- dmbjz_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
container_name: dmbjz_mysql #生成的容器名称
environment:
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'docker'
MYSQL_USER: 'dmbjz'
MYSQL_PASSWORD: 'dmbjz'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- dmbjz_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
networks:
dmbjz_net:
修改项目:
将项目中的 MySQL 与 Redis 地址换成容器名称后重新生成 jar 文件并生成镜像
spring.datasource.url=jdbc:mysql://dmbjz_mysql:3306/docker?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8
spring.redis.host=dmbjz_redis
校验 docker-compose.yml 文件,查看其格式是否编写错误
docker-compose config -q
启动项目:
使用 docker-compose 的启动命令,根据 docker-compose.yml 进行一键启动
docker-compose up -d
创建数据表:
使用 Navicat 等工具远程连接上 MySQL 容器,创建 t_user 表
API测试:
使用 Controller 内的 API 方法
一键关停:
一键关闭 docker-compose.yml 内所有镜像容器
docker-compose stop
水平拓展:
动态增加或减少某个服务的数量并自动创建负载均衡
docker-compose up -d --scale 需要拓展的servicename名称=数量
从文件读取环境变量:
如果希望 docker-compose.yml 文件中的变量可以其他文件中读取,可以通过同级的 .env 文件进行指定
如果文件不为 .env,可以通过 —env-file 文件名 进行指定,具体命令如下:
docker-compose --env-file 文件名 config
案例演示:
docker-compose.yml 文件:
默认会自动寻找 .env 文件来查询变量值
version: "3.8"
services:
redis-server:
image: redis:latest
command: redis-server --requirepass ${REDIS_PASSWORD}
.env文件:
REDIS_PASSWORD=ABC123
![image.png](https://cdn.nlark.com/yuque/0/2022/png/21405095/1661600581920-46734ce6-0133-475f-a402-bf80b068652f.png#clientId=u9be4a23e-93ca-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=281&id=u7794d2e2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=461&originWidth=788&originalType=binary&ratio=1&rotation=0&showTitle=true&size=61447&status=done&style=stroke&taskId=u1cf7d3f5-db93-4869-b2ec-d322d5057c3&title=%E4%BB%8E%20.env%20%E6%96%87%E4%BB%B6%E8%8E%B7%E5%8F%96%E5%88%B0%E5%8F%98%E9%87%8F&width=480 "从 .env 文件获取到变量")<br /> ![image.png](https://cdn.nlark.com/yuque/0/2022/png/21405095/1661600521424-6e5b0330-f46d-456d-b344-0ad55ce3b214.png#clientId=u9be4a23e-93ca-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=259&id=u8fa7fafe&margin=%5Bobject%20Object%5D&name=image.png&originHeight=434&originWidth=804&originalType=binary&ratio=1&rotation=0&showTitle=true&size=62270&status=done&style=stroke&taskId=uf1032d33-2cdc-42e5-95c6-fab395c2167&title=%E6%96%87%E4%BB%B6%E4%B8%8D%E4%B8%BA%20.env%20%E7%9A%84%E6%8C%87%E5%AE%9A%E6%96%B9%E5%BC%8F&width=480 "文件不为 .env 的指定方式")