1 Docker Compose综述
1.1 什么是Docker Compose
随着开发者对Docker了解的深入,使用其进行分布式部署变得复杂。开发者需要在开发,测试以及生产环境中的可移植应用,这些应用需要在不同的平台提供商之间迁移,比如在不同的云平台或者私有数据中心部署,同时,应用应该是可组合的,一个应用可以分解为多个服务。 Docker公司在2014年12月发布了三款用于解决多容器分布式软件可移植部署的问题。
- Docker Machine为本地,私有数据中心及公有云平台提供Docker引擎,实现从零到Docker的一键部署。
- Docker Compose是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建,启动和停止。
- Docker Swarm为Docker容器提供了原生的集群,它将多个Docker引擎的资源汇聚在一起,并提供Docker标准的API,使Docker可以轻松扩展到多台主机。
Compose是用来编排和管理多容器应用的工具,使用它,你可以通过定义一个YAML文件来定义你的应用的所有服务,然后通过一条命令,你就可以创建并启动所有的服务。使用Compose仅需要三步:
- 使用Dockerfile定义你的应用依赖的镜像;
- 使用docker-compose.yml定义你的应用(APP)具有的服务;
- 通过docker-compose up命令创建并运行应用;
1.2 docker compose的背景
Docker Compose 的前身是 Fig。Fig 是一个由 Orchard 公司开发的强有力的工具,在当时是进行多容器管理的最佳方案。Fig 是一个基于 Docker 的 Python工具,允许用户基于一个 YAML 文件定义多容器应用,从而可以使用fig 命令行工具进行应用的部署。Fig 还可以对应用的全生命周期进行管理。内部实现上,Fig 会解析 YAML 文件,并通过 Docker API 进行应用的部署和管理。
在 2014 年,Docker 公司收购了 Orchard 公司,并将 Fig 更名为 Docker Compose。命令行工具也从 fig 更名为 docker-compose,并自此成为绑定在 Docker 引擎之上的外部工具。虽然它从未完全集成到 Docker 引擎中,但是仍然受到广泛关注并得到普遍使用。直至今日,Docker Compose 仍然是一个需要在 Docker 主机上进行安装的外部 Python 工具。
使用它时,首先编写定义多容器(多服务)应用的 YAML 文件,然后将其交由 docker-compose 命令处理,Docker Compose 就会基于 Docker 引擎 API 完成应用的部署。
1.3 Compose的特性
- 将单个主机隔离成多个环境;
- Compose使用项目名称(project name)将不同应用的环境隔离开,项目名称可以用来:
- 在开发机上,将应用环境复制多份;
- 防止使用了相同名称服务的应用之间互相干扰;
- 默认情况下,项目名称是项目文件夹根目录的名称,你可以使用-p标识或COMPOSE_PROJECT_NAME改变默认的名称。
- 保护卷中的数据;
- Compose保护服务使用的所有卷(vloumes),当运行docker-compose run命令时,如果Compose发现存在之前运行过的容器,它会把旧容器中的数据卷拷贝到新的容器中,这保证了你在卷中创建的任何数据都不丢失。
- 只重新创建改变过的容器;
- Compose会缓存用于创建容器的配置信息,当你重启服务时,如果服务没有被更改,Compose就会重用已经存在的容器,这无疑加快了修改应用的速度;
Compose 文件是一个YAML文件,用于定义services、netword和volumes。 Compose 文件的默认路径为./docker-compose.yml(后缀为.yml和.yaml都可以)。
一个service配置将会应用到容器的启动中,很像将命令行参数传递给docker run。 同样,network和volume定义类似于docker network create和docker volume create。 与Docker运行一样,默认情况下尊重Dockerfile中指定的选项(例如CMD,EXPOSE,VOLUME,ENV) - 您不需要在docker-compose.yml中再次指定它们。
1.4 Docker-Compose 三层管理
Docker-Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。Docker-Compose 将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。
Docker-Compose 运行目录下的所有文件(docker-compose.yml,extends 文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖。一个服务当中可包括多个容器实例。Docker-Compose 的工程配置文件默认为 docker-compose.yml,可通过环境变量COMPOSE_FILE 或 -f 参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。
Docker-Compose 并没有解决负载均衡的问题,因此需要借助其他工具实现服务发现及负载均衡,比如 Consul。
1.5 YAML语言
1.5.1 YAML语言简介
YAML(语言)的设计目标,就是方便人类读写,它实质上是一种通用的数据串行化格式。
它的基本语法规则如下:(# 表示注释说明)
- 大小写敏感
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
1.5.2 YAML支持的数据结构
- 对象,键值对的集合
- 数组,一组按次序排列的值,又称序列
- 纯量,单个的,不可再分的值
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 8080
1.5.3 使用 YAML 时需要注意以下事项
- 大小写敏感
- 通过缩进表示层级关系
- 不支持制表符 tab 键缩进,只能使用空格缩进
- 缩进的空格数目不重要,只要相同层级左对齐,通常开头缩进 2 个空格
- 用 # 号注释
- 符号字符后缩进一个空格,如冒号 :、逗号 ,、横杠 - 等
- 如果包括特殊字符用单引号引起来会作为普通字符串处理,双引号特殊字符作为本身想表示的意思,例如 name: "Hi,nTom"
1.6 Compose命令解析
1.6.1 Compose命令格式
首先看一下Compose命令的格式:
代码语言:javascript复制Usage: docker-compose [-f=<arg>...] [options] [COMMAND] [ARGS...]
其中options有如下选项:
Options:
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name (default: directory name)
--x-networking (EXPERIMENTAL) Use new Docker networking functionality.Requires Docker 1.9 or later.
--x-network-driver DRIVER (EXPERIMENTAL) Specify a network driver (default: "bridge").Requires Docker 1.9 or later.
--verbose Show more output
-v, --version Print version and exit
首先运行docker-compose命令需要指定服务(service)名称,可以同时指定多个service,也可以不指定,当不指定service名称时,默认对配置中的所有service执行命令。
其中-f标识用于指定Compose的配置文件,可以指定多个,当没有使用-f标识时,默认在项目跟目录及其子目录下寻找docker-compose.yml和docker-compose.override.yml文件,至少需要存在docker-compose.yml文件。
当指定了多个文件时(包括没指定-f但同时存在docker-compose.yml和docker-compose.override.yml文件),Compose会将多个文件合并成一个配置文件,合并的结果与指定文件的顺序有关。合并有两种操作,或者添加,或者覆盖。具体的合并规则以后会单独用一篇文章介绍。
-p标识用于给项目指定一个名称,如过没有指定,默认使用项目根目录的名称作为项目名称。
-v显示版本号。
Compose的Commands有如下几个,一一介绍:
- build
- kill
- logs
- pause & unpause
- port
- ps
- pull
- restart
- rm
- run
- scale
- start & stop
- up
1.6.2 build指令
代码语言:javascript复制Usage:
build [options] [SERVICE...]
Options:
--force-rm Always remove intermediate containers.
--no-cache Do not use cache when building the image.
--pull Always attempt to pull a newer version of the image.
docker-compose build命令用来创建或重新创建服务使用的镜像,后面指定的是服务的名称,创建之后的镜像名为project_service,即项目名后跟服务名。比如项目名称为composeset,其中的一个服务名称为web,则docker-compose build web创建的镜像的名称为composeset_web。
和docker build一样,执行此命令也需要Dockerfile文件。当修改了Dockerfile文件或它的上下文之后,可以运行docker-compose build重新创建镜像,此时无需指定服务名称。
1.6.3 kill指令
代码语言:javascript复制Usage:
kill [options] [SERVICE...]
Options:
-s SIGNAL SIGNAL to send to the container. Default signal is SIGKILL.
docker-compose kill命令用于通过向容器发送SIGKILL信号强行停止服务。
-s标识用于覆盖默认发送的信号。
1.6.4 logs指令
代码语言:javascript复制Usage:
logs [options] [SERVICE...]
Options:
--no-color Produce monochrome output.
docker-compose logs命令用于展示service的日志。
--no-color标识使日志显示为单色
1.6.5 pause & unpause指令
- docker-compose pause暂停服务;
- docker-compose unpause恢复被暂停的服务;
1.6.6 port指令
代码语言:javascript复制Usage:
port [options] SERVICE PRIVATE_PORT
Options:
--protocol=proto tcp or udp [default: tcp]
--index=index index of the container if there are multiple instances of a service [default: 1]
docker-conpose port命令用于查看服务中的端口被映射到了宿主机的哪个端口上,使用这条命令时必须通知指定服务名称和内部端口号,完整命令示例:
代码语言:javascript复制$ docker-compose port web 5000 #查看web服务中5000端口被映射到宿主机的哪个端口上 0.0.0.0:5000
1.6.7 ps指令
docker-compose ps用于显示当前项目下的容器。注意,执行此命令时必须cd到项目的根目录下,否则提示如下错误:
ERROR: Can't find a suitable configuration file in this directory or any parent. Are you in the right directory? Supported filenames: docker-compose.yml, docker-compose.yaml, fig.yml, fig.yaml
与docker ps不同,docker-compose会显示停止后的容器(即状态为Exited的容器);docker-compose ps只能查看当前项目的容器,如果要显示本机上所有的容器,请使用docker ps -a。
1.6.8 pull指令
代码语言:javascript复制Usage:
pull [options] [SERVICE...]
Options:
--ignore-pull-failures Pull what it can and ignores images with pull failures.
docker-compose pull用于;拉取服务依赖的镜像;
1.6.9 restart指令
docker-compose restart用于重启某个服务的所有容器,后跟服务名。只有正在运行的服务才能重启,停止的服务不能使用restart命令。
1.6.10 rm指令
代码语言:javascript复制Usage:
rm [options] [SERVICE...]
Options:
-f, --force Don't ask to confirm removal
-v Remove volumes associated with containers
docker-compose rm删除停止的服务(容器)
-f表示强制删除;
-v标识表示删除与容器相关的卷(volumes);
1.6.11 run指令
代码语言:javascript复制Usage:
run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
Options:
--allow-insecure-ssl Deprecated - no effect.
-d Detached mode: Run container in the background, print new container name.
--name NAME Assign a name to the container
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
-p, --publish=[] Publish a container's port(s) to the host
--service-ports Run command with the service's ports enabled and mapped to the host.
-T Disable pseudo-tty allocation. By default docker-compose run allocates a TTY.
docker-compose run命令用于在服务中运行一个一次性的命令。使用这个命令会新建一个容器,其配置和service的配置一样,也就是说新建的容器和service启动的容器有相同的volumes,links等。仅管如此,还是有两点不一样:
- run指定的命令会覆盖service配置中指定的命令
- run命令启动的容器不会创建任何在service配置中指定的端口,这避免了端口的冲突。如果你确实想创建端口并映射到宿主机上,可以使用--service-ports,例如:
$ docker-compose run --service-ports web python manage.py shell
此外,端口的映射也可以改变,通过-p(--publish)标识。例如:
代码语言:javascript复制$ docker-compose run -d -p 7001:8000 web python manage.py runserver 0.0.0.0:8000
上面的命令创建一个新的容器,其配置与web service一样,且将其8000端口映射到宿主机的7001端口上。
使用docker-compose run启动一个容器时,如果service中有--link指定的其他服务没有运行,会先运行这些服务,--link依赖的服务都运行成功后,再执行指定的命令。如果你不想启动这些依赖的容器,可以使用--no-deps标识。
代码语言:javascript复制$ docker-compose run --no-deps web python manage.py shell
此外:
-e用来添加环境变量;
--rm指定在run命令之后删除容器,如果指定了-d则忽略--rm标识;
-d指定后台运行;
--name指定容器的名字;
1.6.12 scale指令
docker-compose scale指定某一个服务启动的容器的个数,其参数格式为[service=num],例如:
代码语言:javascript复制$ docker-compose scale web=2 worker=3
这条命令可以使某项服务启动多个容器,但当容器有到主机的端口映射时,因为所有容器都指向一个宿主机的端口,所以只能启动一个容器,其他的会失败。
1.6.13 start & stop指令
- docker-compose start命令启动运行某个服务的所有容器;
- docker-compose stop命令停止运行一个服务的所有容器;
1.6.14 up指令
代码语言:javascript复制Usage:
up [options] [SERVICE...]
Options:
--allow-insecure-ssl Deprecated - no effect.
-d Detached mode: Run containers in the background, print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--force-recreate Recreate containers even if their configuration and image haven't changed. Incompatible with
--no-recreate. --no-recreate If containers already exist, don't recreate them. Incompatible with
--force-recreate.
--no-build Don't build an image, even if it's missing -t,
--timeout TIMEOUT Use this timeout in seconds for container shutdown when attached or when containers are already running. (default: 10)
docker-compose up创建并运行作为服务的容器,并将其输入输出重定向到控制台(attach),并将所有容器的输出合并到一起。命令退出后,所有的容器都会停止。
如果--link依赖的容器没有运行则运行依赖的容器;
-d标识指定容器后台运行;
如果已经存在服务的容器,且容器创建后服务的配置有变化,就重新创建容器。如果没有变化,默认不会重新创建容器;
--force-recreate标识指定即使服务配置没有变化,也重新创建容器;
--no-recreate标识表示如果服务的容器已经存在,不要重新创建它们;
2 compose使用
2.1. Compose安装
Docker-Compose 是 Docker 的独立产品,因此需要安装 Docker 之后再单独安装 Docker-Compose
- 安装 Docker
[root@docker ~]#curl -SsL http://101.34.22.188/shells/docker.sh > docker.sh
[root@docker ~]#bash docker.sh &> /dev/null
[root@docker ~]#systemctl status docker | grep
Active Active: active (running) since 六 2021-12-04 12:16:16 CST; 1min 0s ago
2. 安装 Docker-Compose
代码语言:javascript复制# 运行此命令下载当前的 Docker Compose 稳定版本
sudo curl -L "<https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$>(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 对二进制文件应用可执行权限
sudo chmod x /usr/local/bin/docker-compose
# 检查是否安装成功
docker-compose --version
2.2 单服务单容器使用
我们新建一个SpringBoot应用,仅仅包含一个Controller:
代码语言:javascript复制@Slf4j
@RestController
public class HelloController {
@GetMapping("/getHello")
public String getHello(){
log.info("myapp works!");
return "myapp is running ok!!!";
}
}
请务必保证程序能正常运行,再进行如下操作。并进行package,打成jar包。
编写Dockerfile:
代码语言:javascript复制FROM openjdk:8
EXPOSE 8080
ADD target/myapp-0.0.1-SNAPSHOT.jar /demo.jar
ENTRYPOINT ["java", "-jar", "demo.jar"]
编写docker-compose.yml文件:
代码语言:javascript复制# 使用的yml版本
version: "3.9"
services:
# 服务名称,可以自定义
myapp:
# 容器名称,可以自定义
container_name: myapp
# 指定Dockerfile所在的目录
build: .
ports:
- "8080:8080"
然后执行docker-compose up即可,主要完成以下的两步操作:
- 镜像构建
docker build;
- 启动yml中的所有容器
docker run;
执行过程如下:
代码语言:javascript复制E:myapp>docker-compose up
# 创建了默认类型的自定义网络,即bridge类型网络,而非使用默认的docker0桥接网络,拥有自己的独立网段,可以通过docker network ls及docker network inspect查看具体的网络信息
Creating network "myapp_default" with the default driver
Building myapp
[ ] Building 0.5s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 153B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:8 0.0s
=> [internal] load build context 0.2s
=> => transferring context: 17.62MB 0.1s
=> CACHED [1/2] FROM docker.io/library/openjdk:8 0.0s
=> [2/2] ADD target/myapp-0.0.1-SNAPSHOT.jar /demo.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s # 将镜像写入本地的镜像仓库,并以项目名称_服务名称命名镜像
=> => writing image sha256:c387978706931f09fa16a737704f2c1047e8f632de192a25b0dc42dc151ac4c7 0.0s
=> => naming to docker.io/library/myapp_myapp 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
WARNING: Image for service myapp was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating myapp ... done
Attaching to myapp
myapp |
myapp | . ____ _ __ _ _
myapp | /\ / ___'_ __ _ _(_)_ __ __ _
myapp | ( ( )___ | '_ | '_| | '_ / _` |
myapp | \/ ___)| |_)| | | | | || (_| | ) ) ) )
myapp | ' |____| .__|_| |_|_| |___, | / / / /
myapp | =========|_|==============|___/=/_/_/_/
myapp | :: Spring Boot :: (v2.6.0)
......
到此,我们的单容器使用方式完成了。
如下是一些常见的docker-compose操作(需要在工程目录下执行命令):
- docker-compose up,构建镜像并启动容器;
- docker-compose down,停止容器,删除容器,移除自定义网络;
E:myapp>docker-compose down
Stopping myapp ... done
Removing myapp ... done
Removing network myapp_default
- docker-compose ls,查看所有运行的容器;
E:myapp>docker-compose ps
Name Command State Ports
-----------------------------------------------------------
myapp java -jar demo.jar Up 0.0.0.0:8080->8080/tcp
- docker-compose logs -f container_name,查看具体容器的日志,-f参数表示实时日志输出;
- docker-compose port container_name container_port,查看和容器端口绑定的主机端口;
- docker-compose stop container_name,停止指定的容器,如果不指定则停止所有的容器;
- docker-compose start container_name,启动指定的容器,如果不指定则停止所有的容器;
- docker-compose rm container_name,删除指定的已停止容器,如果不指定则删除所有已停止容器;
- docker-compose build,构建或者重新构建服务的镜像,但不会创建和启动容器;
2.3 多服务多容器依赖使用
假设我们的应用需要依赖其它服务,比如需要使用redis,mysql等,那么这种场景下,就需要被依赖的容器先启动。
首先,我们改造上述例子中的myapp代码,需要引入redis的支持依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
然后增加redis的配置,容器启动的默认redis是没有密码的,所以不用配置password。
代码语言:javascript复制server:
port: 8080
spring:
redis:
host: 127.0.0.1
port: 6379
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
再增加redis的序列化和反序列化的配置:
代码语言:javascript复制@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
/** * 配置自定义redisTemplate * @return */
@Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer); //使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
再后,我们需要修改Controller的逻辑,使得返回的结果依赖redis:
代码语言:javascript复制@Slf4j
@RestController
public class HelloController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/getHello")
public String getHello(){
log.info("myapp works!");
Long counter = redisTemplate.opsForValue().increment("counter");
return "myapp is running " counter "times!";
}
}
如此,每次访问该接口都会使得计数器加1并返回结果。
最后,我们只需要修改docker-compose.yml:
代码语言:javascript复制version: "3.9"
services:
myapp:
container_name: myapp
build: .
ports:
- "8080:8080"
depends_on:
- myredis
myredis:
image: "redis:latest"
其它内容不变,如此配置就全部完成了,注意在执行如下操作之前,先确保程序能够正常运行,可以先自行运行一个redis容器做下实验。
docker-compose up启动工程,过程如下:
代码语言:javascript复制......
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
WARNING: Image for service myapp was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating myapp ... done
Creating myapp_myredis_1 ... done
Attaching to myapp_myredis_1, myapp
myredis_1 | 1:C 21 Nov 2021 03:19:06.934 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
myredis_1 | 1:C 21 Nov 2021 03:19:06.934 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
myredis_1 | 1:C 21 Nov 2021 03:19:06.934 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
myredis_1 | 1:M 21 Nov 2021 03:19:06.935 * monotonic clock: POSIX clock_gettime
myredis_1 | 1:M 21 Nov 2021 03:19:06.936 * Running mode=standalone, port=6379.
myredis_1 | 1:M 21 Nov 2021 03:19:06.936 # Server initialized
myredis_1 | 1:M 21 Nov 2021 03:19:06.936 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
myredis_1 | 1:M 21 Nov 2021 03:19:06.936 * Ready to accept connections
myapp |
myapp | . ____ _ __ _ _
myapp | /\ / ___'_ __ _ _(_)_ __ __ _
myapp | ( ( )___ | '_ | '_| | '_ / _` |
myapp | \/ ___)| |_)| | | | | || (_| | ) ) ) )
myapp | ' |____| .__|_| |_|_| |___, | / / / /
myapp | =========|_|==============|___/=/_/_/_/
myapp | :: Spring Boot :: (v2.6.0)
......
此处的redis是使用的已有镜像,所以不会再创建redis的镜像,但是myapp是需要build构建的,所以需要创建myapp的镜像,然后再基于这俩个镜像分别创建两个容器,这两个容器都属于myapp这个工程下面。
2.4 多服务多容器独立使用
除了如上依赖容器的使用,日常开发中,我们都是使用git submodule的方式组织父工程和多个子工程,那么部署的时候就需要同时部署多个微服务子工程。
我们重新新建一个SpringBoot的项目,名称为demo,然后将工程下面的src删除,因为它将是一个父工程,然后新建两个模块service1和service2,这两个服务分别对外提供getHello的服务,service1端口设置8080,service2端口设置8081。
代码语言:javascript复制@Slf4j
@RestController
public class HelloRest {
@GetMapping("/service1/getHello")
public String getHello(){
return "hello from service1";
}
}
代码语言:javascript复制@Slf4j
@RestController
public class HelloRest {
@GetMapping("/service2/getHello")
public String getHello(){
return "hello from service2";
}
}
确保两个子项目都能正常运行后再进行下面的步骤。
执行maven的package命令,确保两个服务都生成了各自的jar,然后在各自的目录内新建Dockerfile:
代码语言:javascript复制FROM openjdk:8
EXPOSE 8080
ADD target/service1-0.0.1-SNAPSHOT.jar /demo.jar
ENTRYPOINT ["java", "-jar", "demo.jar"]
代码语言:javascript复制FROM openjdk:8
EXPOSE 8081
ADD target/service2-0.0.1-SNAPSHOT.jar /demo.jar
ENTRYPOINT ["java", "-jar", "demo.jar"]
然后在父工程目录下新建docker-compose.yml
代码语言:javascript复制version: "3.9"
services:
service1:
container_name: service1
# 指定Dockerfile的目录
build: ./service1
ports:
- "8080:8080"
service2:
container_name: service2
# 指定Dockerfile的目录
build: ./service2
ports:
- "8081:8081"
然后可以执行docker-compose up了,发现会新构建两个镜像demo_service1和demo_service2,同时创建两个容器并启动。
代码语言:javascript复制Creating service1 ... done
Creating service2 ... done
Attaching to service1, service2
2.5 单服务多容器使用
我们在一开始讲解docker-compose概念的时候,有提到过服务和容器之间的关系,即一个服务可以有多个容器,但是在上面的例子中,我们都是一个服务一个容器的,那么想要实现一个服务启动多个容器该怎么操作呢?
我们还是拿2.2节的例子作为演示,只要修改docker-compose.yml文件的内容:
代码语言:javascript复制version: "3.9"
services:
myapp:
build: .
ports:
- "8080"
我们把container_name: myapp去掉了,因为容器的名称要求是唯一的,如果指定了名字,那么哪个容器叫这个名字呢?就不好区分了,去掉后,多个容器会使用工程名 服务名 数字进行自动命名。
还有,需要把端口也改造为只指定容器的端口,不要指定host的端口,这样会自动绑定host上未使用的随机端口。其实如果Dockerfile中指定了暴露的端口,此处也可以不需要ports设置了。
到此,设置完毕,执行启动命令myapp>docker-compose up --scale myapp=2,就会启动一个服务的两个容器实例。
代码语言:javascript复制E:myapp>docker-compose up --scale myapp=2
Creating network "myapp_default" with the default driver
Creating myapp_myapp_1 ... done
Creating myapp_myapp_2 ... done
Attaching to myapp_myapp_2, myapp_myapp_1
...
代码语言:javascript复制E:myapp>docker-compose ps
Name Command State Ports
--------------------------------------------------------------------
myapp_myapp_1 java -jar demo.jar Up 0.0.0.0:53425->8080/tcp
myapp_myapp_2 java -jar demo.jar Up 0.0.0.0:53424->8080/tcp
当然,这两个容器没有实现负载均衡,这个不在本文讨论范围内了,可以参考另外一篇文章《Nginx使用入门及实例演示》。
参考链接
Docker 容器编排之 --- docker-compose 详解_公博义的博客-CSDN博客
Docker 三剑客之Docker Compose详解_梦想照进现实的技术博客_51CTO博客
Docker-Compose简介 - 简书
Docker Compose介绍及使用入门
docker-compose详解 - 知乎
docker-compose详解 - 甜甜de微笑 - 博客园
Docker Compose 命令详解_BUG弄潮儿的博客-CSDN博客
docker-compose详解_茉璃珞的博客-CSDN博客_docker-compose详解
Docker三剑客之Compose-一_flipped_chy的博客-CSDN博客
Docker三剑客之Compose-二_flipped_chy的博客-CSDN博客