Docker-compose:管理多个容器
170.Docker-compose容器编排
Docker-Compose 是 Docker 公司推出的一个开源工具软件,可以管理多个 Docker 容器组成一个应用。用户需要定义一个 YAML 格式的配置文件 docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器(或者说能实现对 Docker 容器集群的快速管理,编排)
为什么要用 compose
Docker 建议我们每一个容器中只运行一个服务,因为 Docker 容器本身占用资源极少,所以最好是将每个服务单独的分割开来。但是这样我们又面临了一个问题:
如果需要同时部署好多个服务,难道要每个服务单独写 Dockerfile 然后在构建镜像,构建容器?非常麻烦。
所以 Docker 官方给我们提供了 Docker-compose 多服务部署的工具。
例如要实现一个 Web 微服务项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库 MySQL 容器,Redis 服务器,注册中心 eureka,甚至还包括负载均衡容器等等...
Compose 允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
和 Spring 类似,在 Spring 之前,我们得自己管理各个类之间的关系
- 有时候一个类的创建,需要其他类的支持,此时就涉及到对象的启动顺序和加载条件
- 当类越来越多,就涉及到各种模式,除了用 new 之外,还可以用工厂模式,装饰器模式等来创建对象
其实 Spring 也是一个容器,可以用配置文件来管理各个组件的关系
安装
根据文档,安装 Compose 的方式有:
- 安装 Docker Desktop
- 安装 Compose plugin
- 单独安装 Compose(不推荐)
在我们安装 Docker 的时候,已经安装了 Compose plugin,也就是方式 2,因此不用再次安装。
随着 Docker 版本的更新,安装方法可能会有不同,具体以官网为准。
此外,旧版本的 docker-compose 命令中,是带有短线的,例如 docker-compose up;新版的没有,直接就是 docker compose up
指令格式:docker compose [OPTIONS] COMMAND
。例如查看版本:
$ docker compose version
Docker Compose version v2.20.2
更多用法参考:docker run --help
Compose 核心概念
一文件:docker-compose.yml
两要素:
- 服务(service): 一个个应用容器实例,比如订单微服务、库存微服务、MySQL 容器、Nginx 容器或 Redis 容器
- 工程(project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
Compose 使用的三个步骤
- 编写 Dockerfile 定义各个微服务应用并构建出对应的镜像文件
- 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
- 最后,执行 docker-compose up 命令,来启动并运行整个应用程序,完成一键部署上线
Compose 常用命令
docker compose -h :查看帮助
docker compose up:启动所有 docker-compose 服务
docker compose up -d :启动所有 docker-compose 服务并后台运行
docker compose down:停止并删除容器、网络、卷、镜像。
docker compose exec yml 里面的服务 id :进入容器实例内部中的 /bin/bash
docker compose ps :展示当前 docker-compose 编排过的运行的所有容器
docker compose top:展示当前 docker-compose 编排过的容器进程
docker compose logs yml 里面的服务 id : 查看容器输出日志
docker compose config :检查配置
docker compose config -q :检查配置,有问题才有输出
docker compose restart :重启服务
docker compose start :启动服务
docker compose stop :停止服务
compose 实战
接下来就是实战了,我们改造下之前用的 Java 工程(之前“Docker 微服务实战”一文中用到的),并使用 compose 的方式来管理,一键上线。
现在我们的需求:引入 Redis 作为缓存,MySQL 作为数据库。然后写一个 user 类,先从 Redis 中查询有误命中缓存,没有则从数据库中查。
建库建表
这里使用的是本地的 MySQL(读者也可以自行用 Docker 新建 MySQL,或用云数据库等):
CREATE DATABASE db2021;
use db20201;
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
`sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'
业务代码
接下来就是写 bean 类、mappe,接口等。在课件中,老师已经贴出了代码;也可以拷贝一份我的 GitHub 或 Gitee 项目代码(分支 demo2),或用代码生成工具(在课程中,老师使用的是 Maven 的插件 mybatis-generator)。
说明:
-
关于 Java 方面的知识,这里不是重点,就不逐个说明了。在项目的 readme.md 文档中有说明
-
微服务不是单独指某个语言,而是一种设计思想,其他编程语言也可以用微服务
-
拷贝项目后需要修改的地方:MySQL 连接信息,Redis 连接信息
-
本项目主要参考老师的源码 + B 站评论区-我自己 1213812138 的源码
docker 微服务第二个实例 今天刚做的 里面 zip 和 jar 包都有 一起学习
链接:https://pan.baidu.com/s/1f2aMCKGToDp6DwPLKMJUXw
提取码:cbwn
最好先本地启动测试一下,没问题再打包。测试的时候可以用本机的 MySQL 和 Redis。启动后先用 postman 发送 get 请求,然后查找用户 1:
访问 Swagger:
用 mvn package
打包,上传到/mydocker 目录下,并重命名为 docker_boot-0.0.1-SNAPSHOT.jar
编写 Dockerfile
在/mydocke 编写 Dockerfile:
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作为微服务
EXPOSE 6001
构建镜像:
docker build -t zzyy_docker:1.6 .
不用 Compose 的情况下
这里先演示下,不使用 compose 的情况下,有什么问题出现。按照如下顺序启动容器:
- 单独的 MySQL 容器实例(先停止本机的 MySQL,然后新建一个实例,并新建库和表)
- 单独的 Redis 实例
- 微服务工程
先停止本机的 MySQL 实例 systemctl stop mysqld
,新建 MySQL 容器(这里用的是 MySQL8):
docker run -p 3306:3306 --name mysql57 --privileged=true -v /zzyyuse/mysql/conf:/etc/mysql/conf.d -v /zzyyuse/mysql/logs:/logs -v /zzyyuse/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
进入 mysql 容器实例并新建库 db2021+ 新建表 t_user:
docker exec -it mysql57 /bin/bash
mysql -uroot -p
CREATE DATABASE db2021;
use db20201;
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
`sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别 0=女 1=男 ',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'
新建 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 6001:6001 zzyy_docker:1.6
接下来可以用 postman 发送 POST 请求:http://192.168.2.242:6001/user/add
然后访问 http://192.168.2.242:6001/user/find/1
,可以看到是正常运行的:
此外 Swagger 也能正常运行,这里就不贴图了。
上面成功了,有哪些问题?
- 先后顺序要求固定,先 MySQL + Redis 才能微服务访问成功
- 每次启停都要运行多个 run 命令...
- 容器间的启停或宕机,有可能导致 IP 地址对应的容器实例变化,映射出错, 要么生产 IP 写死(不推荐),要么通过服务调用
使用 Compose
编写.mydocker/docker-compose.yml 文件,内容如下:
version: "3"
services:
microService:
image: zzyy_docker:1.6
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- atguigu_net
depends_on:
- redis
- mysql
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- atguigu_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2021'
MYSQL_USER: 'zzyy'
MYSQL_PASSWORD: 'zzyy123'
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:
- atguigu_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
networks:
atguigu_net:
内容解读:
-
version: "3",是当前 Docker compose 的版本,目前是 3
-
services: 固定写法,里面的内容是各个容器实例的配置
- microService:微服务,我们自定义的服务,然后 container_name,ports,networks,volumes,都是 Docker 的选项。depends_on 则定义了容器的启停顺序。
- redis:没用定义容器名字,然后配置了容器卷和网络
- mysql:同理,配置了端口,容器卷以及网络,密码等。
-
networks:会自动创建网络,网络名字为 atguigu_net
接下来我们修改下微服务项目的配置文件,使其通过服务名来访问 MySQL 和 Redis:
......
# spring.datasource.url=jdbc:mysql://192.168.2.242:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.url=jdbc:mysql://mysql:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false
.....
# ======================== redis =====================
spring.redis.database=0
# spring.redis.host=192.168.2.242
spring.redis.host=redis
......
然后重新打包,上传到 Linux 里并重命名,删除之前的镜像,然后重新构建镜像:
docker rmi 镜像ID
docker build -t zzyy_docker:1.6 .
将之前的 Redis 和 MySQL 实例也删掉:
docker rm -f mysql57 redis608
之前讲过 compose 常用命令,接下来就是用的时候了。在一键启动之前,先检查下配置文件有没问题:
docker compose config -q
如果没有任何输出,说明是正常的。
现在我们来一键启动,在/mydocker 目录下执行:
docker compose up -d
输出:
[+] Running 4/4
✔ Network mydocker_atguigu_net Created 0.2s
✔ Container mydocker-mysql-1 Started 2.8s
✔ Container mydocker-redis-1 Started 2.8s
✔ Container ms01 Started 3.7d
并且网络也自动创建好了:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
f1c9ebe5d961 bridge bridge local
553d2c47bcaa host host local
fd3878f8116a mydocker_atguigu_net bridge local
1ddfe19eb462 none null local
daf309d234de zzyy_network bridge local
根据 compose 的规则,会在网络名字加个目录名作为前缀。同理,如果不指定容器名,则容器名也会加个前缀(例如 mydocker-mysql-1,mydocker-redis-1)
接下来我们去 MySQL 容器中新建库和表,然后测试下微服务是否正常运行即可
接下来我们测试下一键关停:
docker compose stop
输出:
docker compose stop
[+] Stopping 3/3
✔ Container ms01 Stopped 0.8s
✔ Container mydocker-redis-1 Stopped 0.4s
✔ Container mydocker-mysql-1 Stopped 4.1s
可以看到正常关闭了,至此测试完毕
(完)