zl程序教程

您现在的位置是:首页 >  工具

当前栏目

【重识云原生】第六章容器6.1.11节——docker-compose容器编排

Docker容器 11 原生 Compose 第六章 编排 重识云
2023-06-13 09:13:28 时间

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命令的格式:

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指令

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指令

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指令

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指令

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命令用于查看服务中的端口被映射到了宿主机的哪个端口上,使用这条命令时必须通知指定服务名称和内部端口号,完整命令示例:

$ 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指令

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指令

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指令

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)标识。例如:

$ 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标识。

$ 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],例如:

$ docker-compose scale web=2 worker=3

        这条命令可以使某项服务启动多个容器,但当容器有到主机的端口映射时,因为所有容器都指向一个宿主机的端口,所以只能启动一个容器,其他的会失败。

1.6.13 start & stop指令

  • docker-compose start命令启动运行某个服务的所有容器;
  • docker-compose stop命令停止运行一个服务的所有容器;

1.6.14 up指令

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

  1. 安装 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

# 运行此命令下载当前的 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:

@Slf4j 
@RestController 
public class HelloController { 
    @GetMapping("/getHello") 
    public String getHello(){ 
        log.info("myapp works!"); 
        return "myapp is running ok!!!"; 
    } 
}

请务必保证程序能正常运行,再进行如下操作。并进行package,打成jar包。

编写Dockerfile:

FROM openjdk:8 
EXPOSE 8080 
ADD target/myapp-0.0.1-SNAPSHOT.jar /demo.jar 
ENTRYPOINT ["java", "-jar", "demo.jar"]

        编写docker-compose.yml文件:

# 使用的yml版本 
version: "3.9" 
services: 
  # 服务名称,可以自定义 
  myapp: 
    # 容器名称,可以自定义 
    container_name: myapp 
    # 指定Dockerfile所在的目录 
    build: . 
    ports: 
      - "8080:8080"

        然后执行docker-compose up即可,主要完成以下的两步操作:

  • 镜像构建
docker build;
  • 启动yml中的所有容器
docker run;

        执行过程如下:

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的支持依赖:

<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。

server: 
  port: 8080 
spring: 
  redis: 
    host: 127.0.0.1 
    port: 6379 
    lettuce: 
      pool: 
        max-active: 8 
        max-idle: 8 
        min-idle: 0

        再增加redis的序列化和反序列化的配置:

@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:

@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:

version: "3.9" 
services: 
  myapp: 
    container_name: myapp 
    build: . 
    ports: 
      - "8080:8080" 
    depends_on: 
      - myredis 
  myredis: 
    image: "redis:latest"

        其它内容不变,如此配置就全部完成了,注意在执行如下操作之前,先确保程序能够正常运行,可以先自行运行一个redis容器做下实验。

        docker-compose up启动工程,过程如下:

...... 
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。

@Slf4j 
@RestController 
public class HelloRest { 
    @GetMapping("/service1/getHello") 
    public String getHello(){ 
      return "hello from service1"; 
    } 
}
@Slf4j 
@RestController 
public class HelloRest { 
    @GetMapping("/service2/getHello") 
    public String getHello(){ 
      return "hello from service2"; 
    } 
}

        确保两个子项目都能正常运行后再进行下面的步骤。

        执行maven的package命令,确保两个服务都生成了各自的jar,然后在各自的目录内新建Dockerfile:

FROM openjdk:8 
EXPOSE 8080 
ADD target/service1-0.0.1-SNAPSHOT.jar /demo.jar 
ENTRYPOINT ["java", "-jar", "demo.jar"]
FROM openjdk:8 
EXPOSE 8081 
ADD target/service2-0.0.1-SNAPSHOT.jar /demo.jar 
ENTRYPOINT ["java", "-jar", "demo.jar"]

        然后在父工程目录下新建docker-compose.yml

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,同时创建两个容器并启动。

Creating service1 ... done 
Creating service2 ... done 
Attaching to service1, service2

2.5 单服务多容器使用

        我们在一开始讲解docker-compose概念的时候,有提到过服务和容器之间的关系,即一个服务可以有多个容器,但是在上面的例子中,我们都是一个服务一个容器的,那么想要实现一个服务启动多个容器该怎么操作呢?

        我们还是拿2.2节的例子作为演示,只要修改docker-compose.yml文件的内容:

version: "3.9" 
services: 
  myapp: 
    build: . 
    ports: 
      - "8080"

        我们把container_name: myapp去掉了,因为容器的名称要求是唯一的,如果指定了名字,那么哪个容器叫这个名字呢?就不好区分了,去掉后,多个容器会使用工程名+服务名+数字进行自动命名。

        还有,需要把端口也改造为只指定容器的端口,不要指定host的端口,这样会自动绑定host上未使用的随机端口。其实如果Dockerfile中指定了暴露的端口,此处也可以不需要ports设置了。

        到此,设置完毕,执行启动命令myapp>docker-compose up --scale myapp=2,就会启动一个服务的两个容器实例。

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 
...
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博客