# DockerDemo **Repository Path**: xinxin2fangfang/docker-demo ## Basic Information - **Project Name**: DockerDemo - **Description**: Docker部署SpringBoot项目的Demo - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2024-07-03 - **Last Updated**: 2024-07-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DockerCompose部署SpringBoot项目 ## 准备SpringBoot项目 项目目录一览 [项目源码Gitee地址](https://gitee.com/zhuang-kang/docker-demo) ![在这里插入图片描述](https://img-blog.csdnimg.cn/bd0c2cf1960b44fda49766f5845d49fd.png) ### 准备数据库 ```sql 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='用户表'; ``` ### 添加pom依赖 ```xml org.springframework.boot spring-boot-starter-parent 2.5.6 UTF-8 8 8 4.12 1.2.17 1.16.18 5.1.47 1.1.16 4.1.5 1.3.0 com.google.guava guava 23.0 org.redisson redisson 3.13.4 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator io.springfox springfox-swagger2 2.9.2 io.springfox springfox-swagger-ui 2.9.2 org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-cache org.apache.commons commons-pool2 redis.clients jedis 3.1.0 mysql mysql-connector-java 5.1.47 com.alibaba druid-spring-boot-starter 1.1.10 com.alibaba druid ${druid.version} org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.spring.boot.version} commons-codec commons-codec 1.10 cn.hutool hutool-all 5.2.3 junit junit ${junit.version} org.springframework.boot spring-boot-devtools runtime true org.springframework.boot spring-boot-starter-test test log4j log4j ${log4j.version} org.projectlombok lombok ${lombok.version} true javax.persistence persistence-api 1.0.2 tk.mybatis mapper ${mapper.version} ``` #### **主启动类** ```java @SpringBootApplication @MapperScan("com.zhuang.docker.mapper") public class DockerBootApplication { public static void main(String[] args) { SpringApplication.run(DockerBootApplication.class, args); } } ``` #### **Config层** **RedisConfig** ```java @Configuration @Slf4j public class RedisConfig { /** * @param lettuceConnectionFactory * @return redis序列化的工具配置类,下面这个请一定开启配置 * 127.0.0.1:6379> keys * * 1) "ord:102" 序列化过 * 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过 */ @Bean public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); //设置key序列化方式string redisTemplate.setKeySerializer(new StringRedisSerializer()); //设置value的序列化方式json redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } } ``` **SwaggerConfig** ```java @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.zhuang.docker")) //你自己的package .paths(PathSelectors.any()) .build(); } public ApiInfo apiInfo() { return new ApiInfoBuilder() .title("康小庄的Docker项目" + "\t" + new SimpleDateFormat("yyyy-MM-dd").format(new Date())) .description("docker-compose") .version("1.0") .termsOfServiceUrl("https://itkxz.cn/") .build(); } } ``` #### Entity层 ```java @Table(name = "t_user") public class User { @Id @GeneratedValue(generator = "JDBC") private Integer id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 性别 0=女 1=男 */ private Byte sex; /** * 删除标志,默认0不删除,1删除 */ private Byte deleted; /** * 更新时间 */ @Column(name = "update_time") private Date updateTime; /** * 创建时间 */ @Column(name = "create_time") private Date createTime; /** * @return id */ public Integer getId() { return id; } /** * @param id */ public void setId(Integer id) { this.id = id; } /** * 获取用户名 * * @return username - 用户名 */ public String getUsername() { return username; } /** * 设置用户名 * * @param username 用户名 */ public void setUsername(String username) { this.username = username; } /** * 获取密码 * * @return password - 密码 */ public String getPassword() { return password; } /** * 设置密码 * * @param password 密码 */ public void setPassword(String password) { this.password = password; } /** * 获取性别 0=女 1=男 * * @return sex - 性别 0=女 1=男 */ public Byte getSex() { return sex; } /** * 设置性别 0=女 1=男 * * @param sex 性别 0=女 1=男 */ public void setSex(Byte sex) { this.sex = sex; } /** * 获取删除标志,默认0不删除,1删除 * * @return deleted - 删除标志,默认0不删除,1删除 */ public Byte getDeleted() { return deleted; } /** * 设置删除标志,默认0不删除,1删除 * * @param deleted 删除标志,默认0不删除,1删除 */ public void setDeleted(Byte deleted) { this.deleted = deleted; } /** * 获取更新时间 * * @return update_time - 更新时间 */ public Date getUpdateTime() { return updateTime; } /** * 设置更新时间 * * @param updateTime 更新时间 */ public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } /** * 获取创建时间 * * @return create_time - 创建时间 */ public Date getCreateTime() { return createTime; } /** * 设置创建时间 * * @param createTime 创建时间 */ public void setCreateTime(Date createTime) { this.createTime = createTime; } } ``` **UserDTO** ```java @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; /** * @return id */ public Integer getId() { return id; } /** * @param id */ public void setId(Integer id) { this.id = id; } /** * 获取用户名 * * @return username - 用户名 */ public String getUsername() { return username; } /** * 设置用户名 * * @param username 用户名 */ public void setUsername(String username) { this.username = username; } /** * 获取密码 * * @return password - 密码 */ public String getPassword() { return password; } /** * 设置密码 * * @param password 密码 */ public void setPassword(String password) { this.password = password; } /** * 获取性别 0=女 1=男 * * @return sex - 性别 0=女 1=男 */ public Byte getSex() { return sex; } /** * 设置性别 0=女 1=男 * * @param sex 性别 0=女 1=男 */ public void setSex(Byte sex) { this.sex = sex; } /** * 获取删除标志,默认0不删除,1删除 * * @return deleted - 删除标志,默认0不删除,1删除 */ public Byte getDeleted() { return deleted; } /** * 设置删除标志,默认0不删除,1删除 * * @param deleted 删除标志,默认0不删除,1删除 */ public void setDeleted(Byte deleted) { this.deleted = deleted; } /** * 获取更新时间 * * @return update_time - 更新时间 */ public Date getUpdateTime() { return updateTime; } /** * 设置更新时间 * * @param updateTime 更新时间 */ public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } /** * 获取创建时间 * * @return create_time - 创建时间 */ public Date getCreateTime() { return createTime; } /** * 设置创建时间 * * @param createTime 创建时间 */ public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", sex=" + sex + '}'; } } ``` #### Mapper层 ```java public interface UserMapper extends Mapper { } ``` #### Service层 ```java @Service @Slf4j public class UserService { public static final String CACHE_KEY_USER = "user:"; @Resource private UserMapper userMapper; @Resource private RedisTemplate redisTemplate; /** * addUser * * @param user User */ public void addUser(User user) { //1 先插入mysql并成功 int i = userMapper.insertSelective(user); if (i > 0) { //2 需要再次查询一下mysql将数据捞回来并ok user = userMapper.selectByPrimaryKey(user.getId()); //3 将捞出来的user存进redis,完成新增功能的数据一致性。 String key = CACHE_KEY_USER + user.getId(); redisTemplate.opsForValue().set(key, user); } } /** * findUserById * * @param id Integer * @return User */ public User findUserById(Integer id) { User user = null; String key = CACHE_KEY_USER + id; //1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql user = (User) redisTemplate.opsForValue().get(key); if (user == null) { //2 redis里面无,继续查询mysql user = userMapper.selectByPrimaryKey(id); if (user == null) { //3.1 redis+mysql 都无数据 //你具体细化,防止多次穿透,我们规定,记录下导致穿透的这个key回写redis return user; } else { //3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率 redisTemplate.opsForValue().set(key, user); } } return user; } /** * @param id */ public void deleteUser(Integer id) { userMapper.deleteByPrimaryKey(id); } /** * @param user User */ public void updateUser(User user) { userMapper.updateByPrimaryKey(user); } } ``` #### Controller层 ```java @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("zk" + 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.findUserById(id); } } ``` #### 配置文件 ```properties server.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://ip:3306/docker?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.druid.test-while-idle=false # ========================redis相关配置===================== 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相关配置=================== mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.zhuang.docker.entity # ========================swagger===================== spring.swagger2.enabled=true ``` ## 项目运行测试 **准备数据库** ![在这里插入图片描述](https://img-blog.csdnimg.cn/1cc2d65ef2fb4911a16fd4216144cfed.png) **准备Redis** ![在这里插入图片描述](https://img-blog.csdnimg.cn/d9eab3ffb0cb4bb885c851a3b37ed23f.png) 启动项目成功 ![](https://img-blog.csdnimg.cn/f608dbc37c2c47ff99ae06f6ed3fc1e7.png) 浏览器测试 http://localhost:6001/user/find/88 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6d3b71ab69f14d2ba752b71b115e782d.png) 访问swagger页面进行测试 http://localhost:6001/swagger-ui.html ![在这里插入图片描述](https://img-blog.csdnimg.cn/f6d708263f0e4983a6160aed6effaf03.png) 测试成功 查询数据库插入成功 ![在这里插入图片描述](https://img-blog.csdnimg.cn/da99b0b8eb3b4474b9f31886bec1280c.png) Redis插入成功 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b2255633658a43728eb3d0c53646e9b1.png) ## 使用Docker部署 ```shell docker run -p 3307:3306 --name mysql57 --privileged=true -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7 ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/a531a277654b4f9399bc8c157868d9d1.png) 进入容器bash ```shell docker exec -it mysql57 /bin/bash ``` 创建数据库 ```sql mysql -uroot -p create database docker; 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='用户表'; use docker; ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/d0188c1f9b3341e2ab2940fcae1bdc3f.png) **单独的redis容器实例** ```shell docker run -p 6399: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 ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/93c1d7630247486d96787ae8716c22e2.png) **修改application.properties** ![在这里插入图片描述](https://img-blog.csdnimg.cn/56c947bd59b44efebf4c5c6d3aa5966b.png) 打包上传到Linux虚拟机 ![在这里插入图片描述](https://img-blog.csdnimg.cn/c9ae86dca31845df82b36bac333180eb.png) **编写DockerFile文件** ```dockerfile # 基础镜像使用java FROM java:8 # 作者 MAINTAINER zk # VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp VOLUME /tmp # 将jar包添加到容器中并更名为zk_docker.jar ADD DockerDemo-1.0-SNAPSHOT.jar zk_docker.jar # 运行jar包 RUN bash -c 'touch /zk_docker.jar' ENTRYPOINT ["java","-jar","/zk_docker.jar"] #暴露6001端口作为微服务 EXPOSE 6001 ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/b6d679e8c2124a0eb7b6c042b36fdfea.png) **编译dockerFile文件并指定打包成镜像指定版本** ```shell docker build -f docker_demo -t zk_docker:1.8 . ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/1d7dd8c55be14226ba30a13866795b4c.png) **查询镜像** ```shell docker images # 对外暴露端口 docker run -d -p 6001:6001 容器ID docker ps ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/02e6a3033e2d4744bf49354094783449.png) **运行成功!** 进入浏览器测试 http://ip:6001/user/find/88 ![在这里插入图片描述](https://img-blog.csdnimg.cn/850218dceaae4b808b63f654df671ff6.png) http://ip:6001/swagger-ui.html ![在这里插入图片描述](https://img-blog.csdnimg.cn/0d08abb61d4347939ee19d01bcb01ee0.png) ## 使用Compose部署 编写docker-compose.yml文件 ```yml version: "3" services: microService: image: zk_docker:1.9 container_name: ms01 ports: - "6001:6001" volumes: - /app/microService:/data networks: - zk_net depends_on: - redis - mysql redis: image: redis:6.0.8 ports: - "6399:6379" volumes: - /app/redis/redis.conf:/etc/redis/redis.conf - /app/redis/data:/data networks: - zk_net command: redis-server /etc/redis/redis.conf mysql: image: mysql:5.7 container_name: mysql environment: MYSQL_ROOT_PASSWORD: 'root' MYSQL_ALLOW_EMPTY_PASSWORD: 'no' MYSQL_DATABASE: 'docker' MYSQL_USER: 'zzkk' MYSQL_PASSWORD: 'zzkk123' ports: - "3307:3306" volumes: - /app/mysql/db:/var/lib/mysql - /app/mysql/conf/my.cnf:/etc/my.cnf - /app/mysql/init:/docker-entrypoint-initdb.d networks: - zk_net command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci networks: zk_net: ``` **修改application.properties** ![在这里插入图片描述](https://img-blog.csdnimg.cn/dd88d0a13afe4bc2ae96bed081a0839d.png) 新建一个文件夹mydocker ```shell mkdir docker cd docker docker build -f docker_demo -t zk_docker:1.9 . docker-compose up -d ``` **确保3个文件在同一个文件夹里** 修改dokcerfile文件 ![在这里插入图片描述](https://img-blog.csdnimg.cn/789604bec87d45aeb7d618ca7ee6291f.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/d9f6bb5a119c4fc89b974999d9a9ad98.png) **现在我们有2个版本,一个是不使用DockerCompose部署,一个是使用DockerCompose部署的** ![在这里插入图片描述](https://img-blog.csdnimg.cn/bf98ef2b1806436d9633ab1953bd2221.png) **一键部署3个项目起来** ![在这里插入图片描述](https://img-blog.csdnimg.cn/3bda264fab404c5a98310c13056192f4.png) 进入mysql容器实例并新建库docker+新建表t_user ```shell docker exec -it 容器实例id /bin/bash ``` ```sql mysql -uroot -p create database docker; use docker; 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='用户表'; ``` **测试** 成功查询数据 ![在这里插入图片描述](https://img-blog.csdnimg.cn/30db6f44466c477cb02974833ac05ea7.png) 查看日志成功插入数据 ```shell docker-compose logs ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/b8f5fe9d3fe94c0381b61f5060d83e10.png) **Redis 6399端口也连接成功** ![在这里插入图片描述](https://img-blog.csdnimg.cn/c225feba058042c2ae887d3cce0f1678.png) **进入mysql容器查看数据** ![在这里插入图片描述](https://img-blog.csdnimg.cn/ea633929c5b24fdf8eb15a66257335f7.png) **进入redis容器查看数据** ![在这里插入图片描述](https://img-blog.csdnimg.cn/abe44367cef9401c98d259c091c73a5f.png) 至此Docker部署SpringBoot的流程已经完成,以后只要编写一个`DockerFile`和`docker-compose.yml`就可以实现一键部署!