Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。 负责实现对Dokcer容器集群的快速编排。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?
如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具
例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。。。。。。
Compose允许用户通过一个单独的docker-compose.yml
模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
下载安装 Compose file version 3 reference | Docker Documentation
Install Docker Compose | Docker Documentation
1 2 3 4 5 6 7 8 9 10 sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s) -$(uname -m) " -o /usr/local /bin/docker-compose sudo chmod +x /usr/local /bin/docker-compose docker-compose --version sudo rm /usr/local /bin/docker-compose
常用命令
命令
解释
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 logs yml里面的服务id
查看容器输出日志
dokcer-compose config
检查配置
dokcer-compose config -q
检查配置,有问题才有输出
docker-compose restart
重启服务
docker-compose start
启动服务
docker-compose stop
停止服务
使用步骤
编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
使用docker-compose.yml定义一个完整业务单元,安排好整体应用中的各个容器服务
执行docker-compose up
命令来启动并运行整个应用程序,完成一键部署上线。
改造微服务项目 properties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 server.port =6001 spring.datasource.type =com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.datasource.url =jdbc:mysql://ip:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username =root spring.datasource.password =123456 spring.datasource.druid.test-while-idle =false spring.redis.database =0 spring.redis.host =ip 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.mapper-locations =classpath:mapper/*.xml mybatis.type-aliases-package =com.kylin.docker_boot.entity spring.swagger2.enabled =true
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.8</maven.compiler.source > <maven.compiler.target > 1.8</maven.compiler.target > <junit.version > 4.12</junit.version > <log4j.version > 1.2.17</log4j.version > <lombok.version > 1.16.18</lombok.version > <mysql.version > 5.1.47</mysql.version > <druid.version > 1.1.16</druid.version > <mapper.version > 4.1.5</mapper.version > <mybatis.spring.boot.version > 1.3.0</mybatis.spring.boot.version > </properties > <dependencies > <dependency > <groupId > com.google.guava</groupId > <artifactId > guava</artifactId > <version > 23.0</version > </dependency > <dependency > <groupId > org.redisson</groupId > <artifactId > redisson</artifactId > <version > 3.13.4</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <version > 2.9.2</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <version > 2.9.2</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-cache</artifactId > </dependency > <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-pool2</artifactId > </dependency > <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > <version > 3.1.0</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > ${druid.version}</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > ${mybatis.spring.boot.version}</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-amqp</artifactId > </dependency > <dependency > <groupId > commons-codec</groupId > <artifactId > commons-codec</artifactId > <version > 1.10</version > </dependency > <dependency > <groupId > cn.hutool</groupId > <artifactId > hutool-all</artifactId > <version > 5.2.3</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > ${junit.version}</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <scope > runtime</scope > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > ${log4j.version}</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > ${lombok.version}</version > <optional > true</optional > </dependency > <dependency > <groupId > javax.persistence</groupId > <artifactId > persistence-api</artifactId > <version > 1.0.2</version > </dependency > <dependency > <groupId > tk.mybatis</groupId > <artifactId > mapper</artifactId > <version > ${mapper.version}</version > </dependency > </dependencies >
config RedisConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Configuration @Slf4j public class RedisConfig { @Bean public RedisTemplate<String, Serializable> redisTemplate (LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String,Serializable> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
SwaggerConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Configuration @EnableSwagger2 public class SwaggerConfig { @Value("${spring.swagger2.enabled}") private Boolean enabled; @Bean public Docket createRestApi () { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(enabled) .select() .apis(RequestHandlerSelectors.basePackage("com.kylin.docker_boot" )) .paths(PathSelectors.any()) .build(); } public ApiInfo apiInfo () { return new ApiInfoBuilder() .title("尚硅谷Java大厂技术" +"\t" +new SimpleDateFormat("yyyy-MM-dd" ).format(new Date())) .description("docker-compose" ) .version("1.0" ) .termsOfServiceUrl("https://www.atguigu.com/" ) .build(); } }
entitiy User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 @Table(name = "t_user") public class User { @Id @GeneratedValue(generator = "JDBC") private Integer id; private String username; private String password; private Byte sex; private Byte deleted; @Column(name = "update_time") private Date updateTime; @Column(name = "create_time") private Date createTime; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } public Byte getSex () { return sex; } public void setSex (Byte sex) { this .sex = sex; } public Byte getDeleted () { return deleted; } public void setDeleted (Byte deleted) { this .deleted = deleted; } public Date getUpdateTime () { return updateTime; } public void setUpdateTime (Date updateTime) { this .updateTime = updateTime; } public Date getCreateTime () { return createTime; } public void setCreateTime (Date createTime) { this .createTime = createTime; } }
UserDTO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 @NoArgsConstructor @AllArgsConstructor @Data @ApiModel(value = "用户信息") public class UserDTO implements Serializable { @ApiModelProperty(value = "用户ID") private Integer id; @ApiModelProperty(value = "用户名") private String username; @ApiModelProperty(value = "密码") private String password; @ApiModelProperty(value = "性别 0=女 1=男 ") private Byte sex; @ApiModelProperty(value = "删除标志,默认0不删除,1删除") private Byte deleted; @ApiModelProperty(value = "更新时间") private Date updateTime; @ApiModelProperty(value = "创建时间") private Date createTime; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } public Byte getSex () { return sex; } public void setSex (Byte sex) { this .sex = sex; } public Byte getDeleted () { return deleted; } public void setDeleted (Byte deleted) { this .deleted = deleted; } public Date getUpdateTime () { return updateTime; } public void setUpdateTime (Date updateTime) { this .updateTime = updateTime; } public Date getCreateTime () { return createTime; } public void setCreateTime (Date createTime) { this .createTime = createTime; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", sex=" + sex + '}' ; } }
mapper UserMapper
1 2 public interface UserMapper extends Mapper <User > {}
UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.atguigu.docker.mapper.UserMapper" > <resultMap id ="BaseResultMap" type ="com.atguigu.docker.entities.User" > <id column ="id" jdbcType ="INTEGER" property ="id" /> <result column ="username" jdbcType ="VARCHAR" property ="username" /> <result column ="password" jdbcType ="VARCHAR" property ="password" /> <result column ="sex" jdbcType ="TINYINT" property ="sex" /> <result column ="deleted" jdbcType ="TINYINT" property ="deleted" /> <result column ="update_time" jdbcType ="TIMESTAMP" property ="updateTime" /> <result column ="create_time" jdbcType ="TIMESTAMP" property ="createTime" /> </resultMap > </mapper >
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @Service @Slf4j public class UserService { public static final String CACHE_KEY_USER = "user:" ; @Resource private UserMapper userMapper; @Resource private RedisTemplate redisTemplate; public void addUser (User user) { int i = userMapper.insertSelective(user); if (i > 0 ) { user = userMapper.selectByPrimaryKey(user.getId()); String key = CACHE_KEY_USER+user.getId(); redisTemplate.opsForValue().set(key,user); } } public User findUserById (Integer id) { User user = null ; String key = CACHE_KEY_USER+id; user = (User) redisTemplate.opsForValue().get(key); if (user == null ) { user = userMapper.selectByPrimaryKey(id); if (user == null ) { return user; }else { redisTemplate.opsForValue().set(key,user); } } return user; } }
controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @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 = 1 ; i <=3 ; i++) { User user = new User(); user.setUsername("zzyy" +i); user.setPassword(IdUtil.simpleUUID().substring(0 ,6 )); user.setSex((byte ) new Random().nextInt(2 )); userService.addUser(user); } } @ApiOperation("删除1条记录") @RequestMapping(value = "/user/delete/{id}",method = RequestMethod.POST) public void deleteUser (@PathVariable Integer id) { userService.deleteUser(id); } @ApiOperation("修改1条记录") @RequestMapping(value = "/user/update",method = RequestMethod.POST) public void updateUser (@RequestBody UserDTO userDTO) { User user = new User(); BeanUtils.copyProperties(userDTO,user); userService.updateUser(user); } @ApiOperation("查询1条记录") @RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET) public User findUserById (@PathVariable Integer id) { return userService.findUserById2(id); } }
同样将其打成jar包,传输到服务器的/mydocker
目录中
构建镜像 在/mydocker
中运行docker build -t kylin_docker:1.6 .
不使用compose 创建MySQL容器 首先我们要在Docker中运行MySQL容器
1 docker run -p 3306:3306 --name mysql57 --privileged=true -v /kylinuse/mysql/conf:/etc/mysql/conf.d -v /kylinuse/mysql/logs:/logs -v /kylinuse/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
接着使用docker exce -it mysql57 /bin/bash
进入容器中
接着使用mysql -u root -p
输入密码进入mysql
create database db2021;
创建库
创建表
1 2 3 4 5 6 7 8 9 10 11 12 use db2021; 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= utf8mb4 COMMENT= '用户表' ;
创建Redis容器 1 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 exec -it redis608 /bin/bash
进入容器
使用redis-cli
连接redis服务
使用keys *
查看数据
运行微服务镜像 docker run -d -p 6001:6001 kylin_docker:1.6
访问ip:6001/swagger-ui.html
接着我们可以看数据库,Redis是否插入成功
成功访问,但是还是存在一些问题
先后顺序要求固定,先mysql+redis才能微服务访问成功
要运行多个run命令
容器间的启停或宕机,有可能导致ip地址对应的容器实例变化,映射出错,要么生产IP写死(不推荐),要么通过服务调用。
使用compose 编写docker-compose.yml
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 version: "3" services: microService: image: kylin_docker:1.6 container_name: ms01 ports: - "6001:6001" volumes: - /app/microService:/data networks: - kylin_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: - kylin_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: 'kylin' MYSQL_PASSWORD: 'kylin123' 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: - kylin_net command: --default-authentication-plugin=mysql_native_password networks: kylin_net:
docker-compose config -q
检查是否有语法错误
修改项目的配置文件,采用服务名访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 server.port =6001 spring.datasource.type =com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.datasource.url =jdbc:mysql://mysql:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username =root spring.datasource.password =123456 spring.datasource.druid.test-while-idle =false spring.redis.database =0 spring.redis.host =redis spring.redis.port =6379 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.mapper-locations =classpath:mapper/*.xml mybatis.type-aliases-package =com.kylin.docker_boot.entity spring.swagger2.enabled =true
接着上传到服务器/mydocker
目录下,docker build -t kylin_docker:1.6 .
构建镜像
使用docker-compose up -d
可以看见启动了三个容器,同时可以看到mysql,redis我们没有给容器命名,采用的是所在目录_服务名_序列号
的命名方式
mydocker_redis_1
mydocker_mysql_1
docker network ls
网桥命名则会加上所在目录_
为前缀
接着同样进入msyql
容器创建库,表。进入swagger发送请求,运行成功!