# 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)

### 准备数据库
```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
```
## 项目运行测试
**准备数据库**

**准备Redis**

启动项目成功

浏览器测试
http://localhost:6001/user/find/88

访问swagger页面进行测试
http://localhost:6001/swagger-ui.html

测试成功
查询数据库插入成功

Redis插入成功

## 使用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
```

进入容器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;
```

**单独的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
```

**修改application.properties**

打包上传到Linux虚拟机

**编写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
```

**编译dockerFile文件并指定打包成镜像指定版本**
```shell
docker build -f docker_demo -t zk_docker:1.8 .
```

**查询镜像**
```shell
docker images
# 对外暴露端口
docker run -d -p 6001:6001 容器ID
docker ps
```

**运行成功!**
进入浏览器测试
http://ip:6001/user/find/88

http://ip:6001/swagger-ui.html

## 使用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**

新建一个文件夹mydocker
```shell
mkdir docker
cd docker
docker build -f docker_demo -t zk_docker:1.9 .
docker-compose up -d
```
**确保3个文件在同一个文件夹里**
修改dokcerfile文件


**现在我们有2个版本,一个是不使用DockerCompose部署,一个是使用DockerCompose部署的**

**一键部署3个项目起来**

进入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='用户表';
```
**测试**
成功查询数据

查看日志成功插入数据
```shell
docker-compose logs
```

**Redis 6399端口也连接成功**

**进入mysql容器查看数据**

**进入redis容器查看数据**

至此Docker部署SpringBoot的流程已经完成,以后只要编写一个`DockerFile`和`docker-compose.yml`就可以实现一键部署!