我正在参加「掘金·启航计划」
一、Docker Compose 概述
Compose 是Docker公司推出的一个软件,可以管理多个Docker容器组成一个应用。我们只需要定义一个YAML
格式的配置文件 docker-compose.yaml
配置好多个容器之间的调用关系,最后只需要一个命令,就可以同时控制这些容器进行启动 / 关闭。Compose 允许用户通过一个单独的 docker-compose.yaml
模板文件定义一组容器为一个项目。
举个例子:
假设我们一个项目中使用到了Redis、Mysql、nginx等等很多组件技术,那么对应的Docker容器的实例也会变得非常杂乱。如果其中有一个订单的微服务需要运行,那么就需要前面Redis、Mysql….所有容器都必须启动之后,订单服务才可以正常运行。 至此,就面临到了两个问题:
① 容器实例非常繁多,复杂
② 容器的启停以及他们之间的启动顺序需要合理的管理
这时,我们就需要一个介质去管理容器实例之间的协作方式,Docker Compose就为我们合理的解决掉了所面临的问题。当我们要进行启动容器时,只需要执行一条命令,就可以启动全部实例,不需要每个容器都再去一遍一遍 的docker run…..
PS:最新版本的Docker本身已经自带了Compose 这个工具,直接使用即可。
二、使用 Docker Compose
Docker Compose使用的步骤如下:
① 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
② 使用docker-compose.yaml 定义一个完整的业务单元,安排好整体应用中的各个容器服务
③ 执行docker-compose up命令来启动并运行整个程序,完成一键部署
三、常用命令
- 创建并启动docker-compose服务
docker-compose up# 后台运行docker-compose up -ddocker-compose up # 后台运行 docker-compose up -ddocker-compose up # 后台运行 docker-compose up -d
- 停止并删除容器、网络、卷、镜像
docker-compose downdocker-compose downdocker-compose down
- 进入容器实例内部
docker-compose exec <yml里面的服务id> /bin/bashdocker-compose exec <yml里面的服务id> /bin/bashdocker-compose exec <yml里面的服务id> /bin/bash
- 展示当前docker-compose编排过的运行的所有容器
docker-compose psdocker-compose psdocker-compose ps
- 展示当前docker-compose编排过的容器进程
docker-compose topdocker-compose topdocker-compose top
- 查看容器输出日志
docker-compose log <yml里面的服务id>docker-compose log <yml里面的服务id>docker-compose log <yml里面的服务id>
- 检查配置
docker-compose config# 有问题才输出docker-compose config -qdocker-compose config # 有问题才输出 docker-compose config -qdocker-compose config # 有问题才输出 docker-compose config -q
- 重启服务
docker-compose restartdocker-compose restartdocker-compose restart
- 启动服务:(类似 docker start)
docker-compose startdocker-compose startdocker-compose start
- 停止服务
docker-compose stopdocker-compose stopdocker-compose stop
四、编排微服务
接下来通过一个案例学习使用Compose编排微服务,具体操作如下:
Ⅰ、搭建微服务
① 创建测试数据库表:t_user
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='用户表'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='用户表'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='用户表'
② 搭建SpringBoot测试工程
编写配置文件
server.port=6001# ========================alibaba.druid相关配置=====================spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.username=rootspring.datasource.password=123456spring.datasource.druid.test-while-idle=false# ========================redis相关配置=====================spring.redis.database=0spring.redis.host=47.109.24.39spring.redis.port=6379spring.redis.password=spring.redis.lettuce.pool.max-active=8spring.redis.lettuce.pool.max-wait=-1msspring.redis.lettuce.pool.max-idle=8spring.redis.lettuce.pool.min-idle=0# ========================mybatis相关配置===================mybatis.mapper-locations=classpath:mapper/*.xmlmybatis.type-aliases-package=com.zhao.docker.entities# ========================swagger=====================spring.swagger2.enabled=trueserver.port=6001 # ========================alibaba.druid相关配置===================== spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.druid.test-while-idle=false # ========================redis相关配置===================== spring.redis.database=0 spring.redis.host=47.109.24.39 spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 # ========================mybatis相关配置=================== mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.zhao.docker.entities # ========================swagger===================== spring.swagger2.enabled=trueserver.port=6001 # ========================alibaba.druid相关配置===================== spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.druid.test-while-idle=false # ========================redis相关配置===================== spring.redis.database=0 spring.redis.host=47.109.24.39 spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 # ========================mybatis相关配置=================== mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.zhao.docker.entities # ========================swagger===================== spring.swagger2.enabled=true
搭建好服务后,我们编写两个测试Controller(这里主要介绍Compose 相关使用,具体Java代码不过多纠结):
@Api(description = "用户User接口")@RestController@Slf4jpublic class UserController {@Resourceprivate UserService userService;@ApiOperation("新增用户 新增3条")@RequestMapping(value = "/user/add",method = RequestMethod.POST)public void addUser(){for (int i = 0; i <=3 ; i++) {User user = new User();user.setDeleted((byte)0);user.setUpdateTime(new Date());user.setCreateTime(new Date());user.setUsername("zp"+i);user.setPassword(IdUtil.simpleUUID().substring(0,6));user.setSex((byte) new Random().nextInt(2));userService.addUser(user);}}@ApiOperation("查询用户")@RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET)public User findUser(@PathVariable Integer id){return userService.findUserById(id);}}@Api(description = "用户User接口") @RestController @Slf4j public class UserController { @Resource private UserService userService; @ApiOperation("新增用户 新增3条") @RequestMapping(value = "/user/add",method = RequestMethod.POST) public void addUser(){ for (int i = 0; i <=3 ; i++) { User user = new User(); user.setDeleted((byte)0); user.setUpdateTime(new Date()); user.setCreateTime(new Date()); user.setUsername("zp"+i); user.setPassword(IdUtil.simpleUUID().substring(0,6)); user.setSex((byte) new Random().nextInt(2)); userService.addUser(user); } } @ApiOperation("查询用户") @RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET) public User findUser(@PathVariable Integer id){ return userService.findUserById(id); } }@Api(description = "用户User接口") @RestController @Slf4j public class UserController { @Resource private UserService userService; @ApiOperation("新增用户 新增3条") @RequestMapping(value = "/user/add",method = RequestMethod.POST) public void addUser(){ for (int i = 0; i <=3 ; i++) { User user = new User(); user.setDeleted((byte)0); user.setUpdateTime(new Date()); user.setCreateTime(new Date()); user.setUsername("zp"+i); user.setPassword(IdUtil.simpleUUID().substring(0,6)); user.setSex((byte) new Random().nextInt(2)); userService.addUser(user); } } @ApiOperation("查询用户") @RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET) public User findUser(@PathVariable Integer id){ return userService.findUserById(id); } }
③ 使用maven将编好的SpringBoot工程进行打包,放至linux服务器中
Ⅱ、编写Dockerfile构建镜像
编写Dockerfile将SpringBoot工程构建为镜像,命令如下:
# 基础镜像 javaFROM java:8# 作者 xiaozhaoMAINTAINER xiaozhao# 指定临时文件位 /tempVOLUME /temp# 将jar包添加到容器 更名为zp_dockerADD docker_boot-0.0.1-SNAPSHOT.jar zp_docker.jar# 运行 jarRUN bash -c 'touch /zp_docker.jar'ENTRYPOINT ["java","-jar","/zp_docker.jar"]# 暴露6001端口EXPOSE 6001# 基础镜像 java FROM java:8 # 作者 xiaozhao MAINTAINER xiaozhao # 指定临时文件位 /temp VOLUME /temp # 将jar包添加到容器 更名为zp_docker ADD docker_boot-0.0.1-SNAPSHOT.jar zp_docker.jar # 运行 jar RUN bash -c 'touch /zp_docker.jar' ENTRYPOINT ["java","-jar","/zp_docker.jar"] # 暴露6001端口 EXPOSE 6001# 基础镜像 java FROM java:8 # 作者 xiaozhao MAINTAINER xiaozhao # 指定临时文件位 /temp VOLUME /temp # 将jar包添加到容器 更名为zp_docker ADD docker_boot-0.0.1-SNAPSHOT.jar zp_docker.jar # 运行 jar RUN bash -c 'touch /zp_docker.jar' ENTRYPOINT ["java","-jar","/zp_docker.jar"] # 暴露6001端口 EXPOSE 6001
将编写好的Dockerfile文件放至SpringBoot工程同一目录下
执行命令构建镜像
Ⅲ、启动容器,测试服务
启动容器,使用Swagger进行测试
访问Swagger成功,搭建微服务完成。
Ⅳ、使用Compose编排容器
① 在Dockerfile同级目录下编写docker-compose.yml文件
# 版本version: "3"#服务容器实例services:# 服务名microService:# 镜像名 + 版本号image: zp_docker:1.0# 容器名称container_name: my01# 端口配置ports:- "6001:6001"# 容器数据卷volumes:- /app/microService:/data# 网络(一下容器运行在同一个网段内)networks:- xiaozhao_net# 依赖的容器,服务的启动依赖depends_on:- redis- mysqlredis:image: redis:6.0.8ports:- "6379:6379"volumes:- /app/redis/redis.conf:/etc/redis/redis.conf- /app/redis/data:/datanetworks:- xiaozhao_netcommand: redis-server /etc/redis/redis.confmysql:image: mysql:5.7environment:MYSQL_ROOT_PASSWORD: '123456'MYSQL_ALLOW_EMPTY_PASSWORD: 'no'MYSQL_DATABASE: 'docker_boot'ports:- "3306:3306"volumes:- /app/mysql/db:/var/lib/mysql- /app/mysql/conf/my.cnf:/etc/my.cnf- /app/mysql/init:/docker-entrypoint-initdb.dnetworks:- xiaozhao_netcommand: --default-authentication-plugin=mysql_native_password #解决外部无法访问networks:xiaozhao_net :# 版本 version: "3" #服务容器实例 services: # 服务名 microService: # 镜像名 + 版本号 image: zp_docker:1.0 # 容器名称 container_name: my01 # 端口配置 ports: - "6001:6001" # 容器数据卷 volumes: - /app/microService:/data # 网络(一下容器运行在同一个网段内) networks: - xiaozhao_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: - xiaozhao_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: 'docker_boot' 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: - xiaozhao_net command: --default-authentication-plugin=mysql_native_password #解决外部无法访问 networks: xiaozhao_net :# 版本 version: "3" #服务容器实例 services: # 服务名 microService: # 镜像名 + 版本号 image: zp_docker:1.0 # 容器名称 container_name: my01 # 端口配置 ports: - "6001:6001" # 容器数据卷 volumes: - /app/microService:/data # 网络(一下容器运行在同一个网段内) networks: - xiaozhao_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: - xiaozhao_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: 'docker_boot' 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: - xiaozhao_net command: --default-authentication-plugin=mysql_native_password #解决外部无法访问 networks: xiaozhao_net :
② 修改微服务中的配置,将mysql和redis的IP地址换为docker-compose.yml文件中的服务名。避免宕机重启IP变更引发问题。
server.port=6001# ========================alibaba.druid相关配置=====================spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name=com.mysql.jdbc.Driver#spring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.url=jdbc:mysql://mysql:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.username=rootspring.datasource.password=123456spring.datasource.druid.test-while-idle=false# ========================redis相关配置=====================spring.redis.database=0#spring.redis.host=47.109.24.39spring.redis.host=redisspring.redis.port=6379spring.redis.password=spring.redis.lettuce.pool.max-active=8spring.redis.lettuce.pool.max-wait=-1msspring.redis.lettuce.pool.max-idle=8spring.redis.lettuce.pool.min-idle=0# ========================mybatis相关配置===================mybatis.mapper-locations=classpath:mapper/*.xmlmybatis.type-aliases-package=com.zhao.docker.entities# ========================swagger=====================spring.swagger2.enabled=trueserver.port=6001 # ========================alibaba.druid相关配置===================== spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver #spring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.url=jdbc:mysql://mysql:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.druid.test-while-idle=false # ========================redis相关配置===================== spring.redis.database=0 #spring.redis.host=47.109.24.39 spring.redis.host=redis spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 # ========================mybatis相关配置=================== mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.zhao.docker.entities # ========================swagger===================== spring.swagger2.enabled=trueserver.port=6001 # ========================alibaba.druid相关配置===================== spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver #spring.datasource.url=jdbc:mysql://47.109.24.39:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.url=jdbc:mysql://mysql:3306/docker_boot?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.druid.test-while-idle=false # ========================redis相关配置===================== spring.redis.database=0 #spring.redis.host=47.109.24.39 spring.redis.host=redis spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 # ========================mybatis相关配置=================== mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.zhao.docker.entities # ========================swagger===================== spring.swagger2.enabled=true
③ 重新将微服务打包放至linux服务器中
④ 重新构建镜像
⑤ 执行命令,检查docker-compose.yml配置
docker-compose config -qdocker-compose config -qdocker-compose config -q
没有出现报错,配置正常。
⑥ 执行启动命令,一键启动所有容器
# 后台启动所有容器docker-compose up -d# 后台启动所有容器 docker-compose up -d# 后台启动所有容器 docker-compose up -d
当在docker-compose.yml文件中不设置容器名称时,默认为:当前文件所处的目录名称_镜像名_容器数量
⑦ 查看容器启动情况,可以看到所有容器启动成功
⑧ 使用Swagger测试服务
测试添加用户功能
测试查询功能
测试成功,功能接口正常,Mysql,redis服务正常!!
五、总结
在以上的测试项目中,我们用到了redis、Mysql组件,再加上搭建的SpringBoot工程镜像,一共三个对应容器。当我们不使用Compose 去编排服务时,我们总会遇到如下的问题:
- 启动容器的先后顺序要求固定,必须先启动Mysql、Redis才能微服务访问成功。
- 每次启动都要执行多个run命令。
- 容器间的启停或宕机,有可能导致IP地址对应的容器实例变化,映射出错。
在有了Compose 后容器间的管理就非常丝滑了,本次分享的文章到这里就结束了,希望对大家有所帮助。兄弟们,快快用起来吧!