一、文章核心内容
容器化时代,看山不是山,看天全是云~
文章核心内容,可快速完成SpringBoot服务+依赖服务一起编排,完成服务容器化
-
docker-compose编排Redis,附有原生docker命令对比和docker编排指令的注释说明 -
docker-compose编排MySQL,注明核心配置 -
docker-compose编排SpringBoot微服务(这里以一个简单的SpringBoot集成Redis&MySQL服务代替) -
SpringBoot调用Redis、MySQL容器提供的服务,编排时的依赖关系处理
文章注意前提事项说明
-
需要掌握Docker的基本使用,文章不会对基础命令进行过多说明 -
此处的服务使用SpringBoot,其他可以连接Redis和MySQL的服务也可以 -
容器间相互连通有两大方式
- 通过IP地址:这种方式明确写死了ip地址,在生产中不推荐,当Docker容器重启时IP可能会发生变化
- 通过容器名称:这种方式屏蔽了ip地址,只要服务名称不变,服务不受到影响,核心就是docker network
-
docker的基础和docker-compose前置知识可以参考本专栏的前面的文章
二、Redis编排
tip:通过容器名进行相互连通的容器间必须要加入到同一个网络
2.1 编写配置文件
编写redis的docker-compose.yml配置文件,参数含义参考注释
不同的编排文件建议放到单独的文件夹中,比如单独新建一个redis目录,mysql的就创建mysql目录,依次类推
version: "3"
services:
redis:
container_name: redis
image: redis:latest
ports:
- "6379:6379"
volumes:
- /root/docker/compose/redis/redis.conf:/etc/redis/redis.conf
- /root/docker/compose/redis/data:/data
networks:
- my_network
command: redis-server /etc/redis/redis.conf
networks:
my_network:
2.2 运行配置文件
- 在docker-compose.yml所在文件夹通过docker-compose up运行,也可以添加-d参数在后台运行
- 此处docker-compose.yml所在文件夹:/root/docker/compose
Creating network "compose_my_network" with the default driver
Creating redis ... done
Attaching to redis
redis | 1:C 24 Mar 2022 00:13:38.331
redis | 1:C 24 Mar 2022 00:13:38.331
redis | 1:C 24 Mar 2022 00:13:38.332
redis | 1:M 24 Mar 2022 00:13:38.332 * monotonic clock: POSIX clock_gettime
- 查看docker实例
- 进入容器内部,可以执行redis的客户端工具
root@f5865bc42306:/data
- 查看网络,发现compose会自动创建在编排文件中的配置文件
NETWORK ID NAME DRIVER SCOPE
012d4d17c012 bridge bridge local
88c54133c87a compose_my_network bridge local
1524da0505ee host host local
f4cbc4779eaf none null local
- 查看网络细节
- docker network inspect compose_my_network
[
{
"Name": "compose_my_network",
"Id": "88c54133c87a04cc8c86a55c240d2ccafb9bfb456b8e7d185a9a246b5d274025",
"Created": "2022-03-24T00:13:37.623603357Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.20.0.0/16",
"Gateway": "172.20.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"f5865bc42306380b1bd72690d6f67d8abdd46a240a96ea5f9bcfee412fe76c17": {
"Name": "redis",
"EndpointID": "8638b4c9850e533778d331bfd916b120f8a1ac59a9f547763c5e352df93ed897",
"MacAddress": "02:42:ac:14:00:02",
"IPv4Address": "172.20.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "my_network",
"com.docker.compose.project": "compose",
"com.docker.compose.version": "1.29.2"
}
}
]
- 停止容器
- docker-compose stop:停止但不删除
- docker-compose down:停止并且删除网络等一些列配置
Removing redis ... done
Removing network compose_my_network
三、MySQL编排
3.1 编写配置文件
注意这里只贴出了compose的构建配置,等同的docker命令参考redis部分,只是内容变了,但是等级等价的命令并没有太大差异
version: "3"
services:
mysql:
container_name: mysql
image: mysql:5.7
ports:
- "33306:3306"
environment:
MYSQL_ROOT_PASSWORD: 'tianxin'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'docker'
MYSQL_USER: 'docker'
MYSQL_PASSWORD: '123456'
volumes:
- /root/docker/compose/mysql/data:/var/lib/mysql
- /root/docker/compose/mysql/conf/my.cnf:/etc/my.cnf
- /root/docker/compose/mysql/init:/docker-entrypoint-initdb.d
networks:
- my_network
command: --default-authentication-plugin=mysql_native_password
networks:
my_network:
3.2 运行配置文件
- 在mysql docker-compose.yml所在文件夹通过docker-compose up运行,也可以添加-d参数在后台运行
- 运行之后养成随手就是 docker ps 查看容器是否启动的好习惯
- 进入容器内容,通过指定的密码登录
# docker-compose exec mysql bash
root@6cb40ff45da5:/# mysql -uroot -ptianxin
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.36 MySQL Community Server (GPL)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 这里可以看到,自动创建了docker数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| docker |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> use docker;
Database changed
mysql> show tables;
Empty set (0.00 sec)
四、微服务编排
tip:MySQL和Redis相对于独立,但是对于服务而言,依赖于Redis和MySQL,所以在服务启动前需要先启动Redis和MySQL,如果依赖服务较多,人工管理是一件比较麻烦的事情。通过docker-compose的编排可以指定依赖关系等等
4.1 编写服务
- 创建SpringBoot服务,采用SpringBoot+MyBatis+MySQL+Redis完成简单的SpringBoot应用
- maven的pom.xml依赖如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<groupId>com.codecoord</groupId>
<artifactId>docker</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 应用配置文件
特别说明:服务依赖的容器服务,使用容器服务名称,比如 jdbc:mysql://mysql:3306/docker,不要通过IP。端口号是容器内部端口,不是外部端口号!比如 -p 13306:3306,13306是对外端口,3306才是容器内部服务端口,服务依赖时不要指定为外部端口,生产中也不建议除了服务外的容器直接对外提供访问
server:
port: 80
spring:
redis:
port: 6379
host: redis
datasource:
url: jdbc:mysql://mysql:3306/docker?userSSL=false
username: root
password: tianxin
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
- 控制器
import com.codecoord.docker.domain.User;
import com.codecoord.docker.service.UserService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
@RestController
@RequestMapping("/docker")
public class DockerController {
@Resource
private UserService userService;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@RequestMapping("/list")
public List<User> list() {
return userService.list();
}
@RequestMapping("/add")
public boolean add() {
return userService.save(new User() {{
setName(UUID.randomUUID().toString());
}});
}
@RequestMapping("/get/{id}")
public Object get(@PathVariable("id") Long id) {
Map<String, Object> returnMap = new HashMap<>();
Object cache = redisTemplate.opsForValue().get(id);
if (Objects.nonNull(cache)) {
returnMap.put("data", cache);
returnMap.put("cache", "缓存命中");
return returnMap;
}
User user = userService.getById(id);
returnMap.put("data", user);
returnMap.put("cache", "缓存未命中");
redisTemplate.opsForValue().set(id.toString(), user);
return returnMap;
}
}
- 其他DAO层的按照MP格式搭建接口,注意实体类需要序列化
4.2 服务构建
先安装java:8镜像,参考Docker-高级篇(1)-Dockerfile(核心&构建Redis&构建JDK8) - 掘金 (juejin.cn)
- 编写Dockerfile,将服务项目打成jar包(普通的springboot使用maven package打包后的jar包)之后和Dockerfile放于同一个文件文件夹下
# 基础镜像使用java8,需要先构建
FROM java:8
# 作者
MAINTAINER tianxincoord@163.com
# 容器卷,指定临时文件的目录为/tmp
VOLUME /tmp
# 指定工作目录
WORKDIR /tmp
# 重命名可选操作,方便docker ps查看
add docker-1.0.jar core_service.jar
# 运行jar包
ENTRYPOINT ["java", "-jar", "core_service.jar"]
# 暴露30001端口作为服务端口
EXPOSE 30001
- 构建镜像
Sending build context to Docker daemon 245.5MB
Step 1/7 : FROM java:8
---> 95064f52c03b
Step 2/7 : MAINTAINER tianxincoord@163.com
---> Running in 4e2fe1672499
Removing intermediate container 4e2fe1672499
---> e17943871098
Step 3/7 : VOLUME /tmp
---> Running in b7f7d56bc0cf
Removing intermediate container b7f7d56bc0cf
---> 7b67adbb7378
Step 4/7 : WORKDIR /tmp
---> Running in 973e07d965cb
Removing intermediate container 973e07d965cb
---> 70f5211073eb
Step 5/7 : add docker-1.0.jar core_service.jar
---> 5015d0feafeb
Step 6/7 : ENTRYPOINT ["java", "-jar", "core_service.jar"]
---> Running in a8b45a171493
Removing intermediate container a8b45a171493
---> 0e9ea7fb43c7
Step 7/7 : EXPOSE 30001
---> Running in 01e5efc042a6
Removing intermediate container 01e5efc042a6
---> af170edd9100
Successfully built af170edd9100
Successfully tagged core_service:1.0
- 查看构建完成后的镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
core_service 1.0 af170edd9100 58 seconds ago 648MB
java 8 95064f52c03b 8 minutes ago 610MB
centos 7 eeb6ee3f44bd 6 months ago 204MB
4.3 服务编排(redis&mysql&服务)
- 编写compose配置文件
version: "3"
services:
macro_server:
image: core_service:1.0
container_name: core_service
ports:
- "80:80"
volumes:
- /root/docker/compose/app:/data
networks:
- my_network
depends_on:
- redis
- mysql
redis:
container_name: redis
image: redis:latest
ports:
- "6379:6379"
volumes:
- /root/docker/compose/redis/redis.conf:/etc/redis/redis.conf
- /root/docker/compose/redis/data:/data
networks:
- my_network
command: redis-server /etc/redis/redis.conf
mysql:
container_name: mysql
image: mysql:5.7
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 'tianxin'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'docker'
MYSQL_USER: 'docker'
MYSQL_PASSWORD: '123456'
volumes:
- /root/docker/compose/mysql/data:/var/lib/mysql
- /root/docker/compose/mysql/conf/my.cnf:/etc/my.cnf
- /root/docker/compose/mysql/init:/docker-entrypoint-initdb.d
networks:
- my_network
command: --default-authentication-plugin=mysql_native_password
networks:
my_network:
- 启动compose
Creating network "compose_my_network" with the default driver
Creating redis ... done
Creating mysql ... done
Creating core_service ... done
...
- 进入服务内部,ping一下服务名,看一下是否网络连接成功,只有这里ping得通,服务才会调用得通
4.5 服务测试
- 进入MySQL服务中,创建对应的库和表
docker exec -it mysql bash
use docker;
create table user
(
id int primary key auto_increment,
name varchar(50),
create_time datetime not null default current_timestamp,
modify_time datetime not null default current_timestamp on update current_timestamp
);
- 访问接口是否正常
- http://192.168.33.10:80/docker:未配置的路径,正常抛出404
- http://192.168.33.10:80/docker/list:获取列表
- 需要先执行:http://192.168.33.10:80/docker/add 添加数据
- 可以正常测试缓存:http://192.168.33.10:80/docker/get/262193154
五、结尾说明
5.1 自愈性
- 服务容器化之后可以比普通jar部署方式拥有更高自愈性,比如原来普通jar部署当服务挂了之后,必须要重新处理启动服务,此时也可以通过一个看门狗程序当发现进程挂了之后自动重启,但是当服务多了,管理起来是一个比较麻烦的时候
- 而对于容器来说,就是docker kill/stop和docker run就可以快速淘汰老的服务启动新服务
5.2 调度性
- 传统jar部署方式最大的问题在于环境的一致性,比如现在A服务器上的服务能力不够了,需要在B服务器上部署一个新服务,那么安装环境是一个很麻烦的事情,特别是对于配置项目较多的服务来说进行大规模的快速调度基本上是一个很麻烦且费力的操作
- 对于容器化服务来说,所有的依赖服务在一个镜像完成后就可以以此进行为模板快速docker run很多很多的相同配置环境的服务
- 那么在弹性扩缩容上,通过容器可以很方便达到这个目标,服务容器话是一个服务用户到了一定规模之后不得不考虑的问题
|