使用Docker Compose编排微服务

Posted shi_zi_183

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Docker Compose编排微服务相关的知识,希望对你有一定的参考价值。

使用Docker Compose编排微服务

经过前文讲解,可使用Dockerfile(或Maven)构建镜像,然后使用docker命令操作容器,例如docker run、docker kiil等。然而,使用微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署国歌示例。如果每个微服务都有手动启停,那么效率之低、维护量之大可想而知。

Docker Compose简介

Compose是一个用于定义和运行多容器Docker应用程序的工具,前身是Fig。它非常适合用在开发、测试、构建CI工作流等场景。这里使用的Compose版本是1.10.0。
1)通过以下命令自动下载并安装适应系统版本的Compose

curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

2)为安装脚本添加执行权限

chmod +x /usr/local/bin/docker-compose

这样,Compose就按照完成了。
可使用以下命令测试安装结果:

docker-compose --version

Docker Compose快速入门

基本步骤

Compose大致有3个步骤:

  • 使用Dockerfile(或其他方式)定义应用程序环境,以便在任何地方重现该环境
  • 在docker-compose.yml中定义组成应用程序的服务,以便各个服务在一个隔离的环境中一起运行。
  • 运行docker-compose up命令,启动并运行整个应用程序

入门示例

1)使用mvn clean package命令打包项目,获得jar包。
2)在jar所在路径创建DOckerfile文件,并在其中添加如下内容。

#基于哪个镜像
FROM java:8

#将本地文件夹挂载到当前容器
VOLUME /tmp

#复制文件到容器
ADD microservice-discovery-eureka-1.0-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'

#声明需要暴露的端口
EXPOSE 8761

#配置容器启动后执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

3)在jar所在路径创建文件docker-compose.yml,在其中添加

version: '3.8'
services:
  eureka:         #指定服务名称
    build: .      #指定Dockerfile所在路径
    ports:		  
      - "8761:8761"  #指定端口映射,类似docker run -p选项,注意使用字符串形式

4)在docker-compose.yml所在路径执行以下命令:

docker-compose up


工程、服务、容器

Docker Compose将管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。Docker Compose运行目录下的所有文件(docker-compose.yml、extends文件或环境变量文件等)组成一个工程(默认为docker-compose.yml所在目录的目录名称)。一个工程可包含多个服务,每个服务中定义了容器运行的镜像、参数和依赖,一个服务可包括多个容器示例。
对应上文,工程名称是docker-compose.yml所在的目录名。该工程包含了1个服务,服务名称是eureka。执行docker-compose up时,启动类eureka 服务的一个容器实例。

docker-compose.yml常用命令

docker-compose.yml是Compose的默认模板文件。该文件有多种格式,例如Version 1 file format、Version 2 file format、Version 2.1 file format、Version 3 file format等。其中,Version 1 file format、将逐步被弃用,Version 2.x及Version3.x基本兼容。

这里只讨论Version 3 file format下的常用命令

  • build:配置构建时的选项,Compose会利用它自动构建镜像。build的值可以是一个路径,例如:
build: ./dir

也可以是一个对象,用于指定Dockerfile和参数,例如:

build:
  context: ./dir
  dockerfile: Dockerfile-alternate
  args:
    buildno:1
  • command:覆盖容器启动后默认执行的命令,示例:
command: bundle exec thin -p 3000

也可以是一个list,类似Dockerfile中的CMD指令,格式如下:

command: [bundle, exec, thin, -p, 3000]
  • dns:配置dns服务器。可以是一个值,也可以是一个列表。
dns: 8.8.8.8
dns: 
  - 8.8.8.8
  - 9.9.9.9
  • dns_search:配置DNS的搜索域,可以是一个值,也可以是一个列表。
dns_search: example.com
dns_search:
  - dc1.example.com
  - dc2.example.com
  • environment:环境变量设置,可使用数组或字典两种方式。
environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:
environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET
  • env_file:从文件中获取环境变量,可指定一个文件路径或路径列表。如果通过docker-compose -f FILE指定了Compose文件,那么env_file中的路径是Compose文件所在目录的相对路径。使用environment指定的环境变量会覆盖env_file指定的环境变量。
env_file: .env
env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env
  • expose:暴露端口,只将端口暴露给连接的服务,而不暴露给宿主机。
expose:
  - "3000"
  - "8000"
  • external_links:连接到docker-compose.yml外部的容器,甚至并非Compose管理的容器,特别是提供共享或公共服务的容器。格式跟links类似。
external_links:
  - redis_1
  - project_db_1:mysql
  - project_db_1:postgersql
  • image:指定镜像名称或镜像ID,如果本地不存在该镜像,Compose会尝试下载该镜像。
image: java
  • links:连接到其他服务的容器。可以指定服务名称和服务别名(SERVICE:ALIAS),也可只指定服务名称。
web:
  links:
    - db
    - db:database
    - redis
  • networks:详见后面
  • network_mode:设置网络模式。
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
  • ports:暴露端口信息,可使用HOST:CONTAINER的格式,也可只指定容器端口(此时宿主机将随机选择端口),类似于docker run -p。
    需要注意的是,当使用HOST:CONTAINER格式映射端口时,容器端口小于60将得到错误的接口,因为yaml会把xx:yy的数字解析为60进制。因此,建议使用字符串的形式。
ports:
 - "3000"
 - "3000-3005"
 - "8000:8000"
 - "9090-9091:8080-8081"
 - "49100:22"
 - "127.0.0.1:8001:8001"
 - "127.0.0.1:5000-5010:5000-5010"
  • volumes:卷挂路径设置。可以设置宿主主机路径(HOST:CONTAINER),也可指定访问模式(HOST:CONTAINER:ro)。
volumes:
  - /var/lib/mysql
  - /opt/data:/var/lib/mysql
  - ./cache:/tmp/cache
  - ~/configs:/etc/configs/:ro
  - datavolume:/var/lib/mysql

注:docker-compose.yml还有很多其他命令,比如depends_on、pid、devices等略。

docker-compose常用命令

和docker命令一样,docker-compose命令也有很多选项

  • build:构建或重新构建服务。服务被构建后将以project_service的形式标记,例如composetest_db。
  • help:查看指定命令的帮助文档,该命令非常实用。docker-compose所有命令的帮助文档都可通过该命令查看。
docker-compose help COMMAND
  • kill:通过发送SIGKILL信号停止指定服务的容器。
docker-compose kill eureka

该命令也支持通过参数来指定发送的信号。

docker-compose kill -s SIGINT
  • logs:查看服务的日志输出。
  • port:打印绑定的公共端口。
docker-compose port eureka 8761

这样就可输出eureka服务8761端口所绑定的公共端口。

  • ps:列出所有容器。
docker-compose ps

也可列出指定服务的容器

docker-compose ps eureka
  • pull:下载服务镜像。
  • rm:删除指定服务的容器。
docker-compose rm eureka
  • run:在一个服务上执行一个命令
docker-compose run web bash
  • scale:设置指定服务运行容器的个数,以service=num的形式指定。
docker-compose scale user=3 movie=3
  • start:启动指定服务已存在的容器。
docker-compose start eureka
  • stop:停止已运行的容器。
docker-compose stop eureka

停止后,可使用docker-compose start再次启动这些容器。

  • up:构建、创建、重新创建、启动,连接服务的相关容器。所有连接的服务都会启动,除非它们已经运行。
    docker-compose up命令会聚合所有容器的输出,当命令退出时。所有容器都会停止。使用docker-compose up -d可在后台运行所有容器。

Docker Compose网络设置

基本概念

默认情况下,Compose会为应用创建一个网络,服务的每个容器都会加入该网络中。这样,容器就可被网络中的其他容器访问,不仅如此,该容器还能以服务名称作为hostname被其他容器访问。
默认情况下,应用程序的网络名称基于Compose的工程名称,而项目名称基于docker-compose.yml所在目录的名称。如需修改工程名称,可使用-project-name标识或COMPOSE_PORJECT_NAME环境变量。
例子:
一个应用程序在名为myapp的目录中,并且docker-compose.yml

version: '3'
service:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres

当运行docker-compose up时,将执行以下几步:
1)创建一个名为myapp_default的网络。
2)使用web服务的配置创建容器,它以"web"这个名称加入网络myapp_default。
3)使用db服务的配置创建容器,它以"db"这个名称加入网络myapp_default。
容器间可使用服务名称(web或db)作为hostname相互访问。例如,web这个服务可使用postgres://db:5432访问db容器。

更新容器

当服务的配置发生更改时,可使用docker-compose up命令更新配置。
此时,Compose会删除就容器并创建新容器。新容器会以不同的IP地址加入网络,名称保持不变。任何指向旧容器的连接都会被关闭,容器会重新找到新容器并链接上去。

links

前文讲过,默认情况下,服务之间可使用服务名称相互访问。links允许定义一个别名从而使用该别名访问其他服务。

version: '3'
services:
  web:
    build: .
    links:
      - "db:database"
  db:
    image: postgres

这样Web服务就可使用db或database作为hostname访问db服务了。

指定自定义网络

一些场景下,默认的网络配置满足不了我们的需求,此时可使用networks命令自定义网络。networks命令允许创建更加复杂的网络拓扑并指定自定义网络驱动和选项。不仅如此,还可使用networks将服务连接到不是由Compose管理的、外部创建的网络。
如下,在其中定义了两个自定义网络。

version: '3'
services:
  proxy:
    build: ./proxy
    networks:
      - front
  app:
    build: ./app
    netoworks:
      - front
      - back
  db:
    image: postgres
    networks:
      - back

networks:
  front:
    driver: custom-driver-1
  back:
    driver: custom-driver-2
    driver_opts:
      foo: "1"
      bar: "2"

其中,proxy服务与db服务隔离,两者分别使用自己的网络,app服务可与两者通信。使用networks命令即可方便实现服务间的网络隔离与连接。

配置默认网络

version: '3'
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
networks:
  default:
    driver: custom-driver-1

使用已存在的网络

一些场景下,并不需要创建新的网络,只需加入已存在的网络,此时可使用external选项。

networks:
  default:
    external:
      name: my-pre-existing-network

综合实战:使用Docker Comose编排Spring Cloud微服务

编排Spring Cloud微服务

微服务项目名称项目微服务中的角色
microservice-discovery-eureka服务发现组件
microservice-provider-user服务提供者
microservice-consumer-movie-ribbon-hystrix服务消费者

编写代码

1)使用Maven插件构建Docker镜像,在各个项目的pom.xml中添加以下内容

            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.0.0</version>
                <executions>
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <imageName>example/${project.artifactId}:${project.version}</imageName>
                    <forceTags>true</forceTags>
                    <dockerHost>http://192.168.2.120:2375</dockerHost>
                    <baseImage>192.168.2.120:5000/java</baseImage>
                    <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]</entryPoint>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>

2)前文中为各个项目配置的eureka.client.serviceUrl.defaultZone的值是http://localhost:8761/eureka/。由于Docker默认的网络模式是bridge,各个容器的IP都不相同,因此使用http://localhost:8761/eureka/满足不了需求。可为Eureka Server所在容器配置一个主机名,并让各个微服务使用主机名访问Eureka Server。
将所有微服务eureka.client.serviceUrl.defaultZone修改为

eureka:
  client:
    service-url:
      defaultZone: http://discovery:8761/eureka/

3)在每个项目的根目录执行以下命令,构建Docker镜像

mvn clean package docker:build

4)编写docker-compose.yml

version: '3'
services:
  microservice-discovery-eureka:
    image: example/microservice-discovery-eureka:1.0-SNAPSHOT
    ports:
      - "8761:8761"
  microservice-provider-user:
    image: example/microservice-provider-user:1.0-SNAPSHOT
    links:
      - microservice-provider-user:discovery
  microservice-comsumer-movie-ribbon-hystrix:
    image: example/microservice-comsumer-movie-ribbon-hystrix:1.0-SNAPSHOT
      links:
        - microservice-comsumer-movie-ribbon-hystrix:discovery

测试
1)执行以下命令启动项目

docker-compose up

2)测试


编排高可用的Eureka Server

1)执行以下命令构建Docker镜像

mvn clean package docker:build

2)编写docker-compose.yml

version: '3'
services:
  microservice-discovery-eureka-ha1:
    hostname: peer1
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    links:
      - microservice-discovery-eureka-ha2
    ports:
      - "8761:8761"
    environment:
      - spring.profiles.active=peer1
  microservice-discovery-eureka-ha2:
    hostname: peer2
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    links:
      - microservice-discovery-eureka-ha1
    ports:
      - "8762:8762"
    environment:
      - spring.profiles.active=peer2


从异常可知,该写法存在循环依赖,也就是说,links无法实现双向连接。

version: '3'
services:
  peer1:
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    ports:
      - "8761:8761"
    environment:
      - spring.profiles.active=peer1
  peer2:
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    hostname: peer2
    ports:
      - "8762:8762"
    environment:
      - spring.profiles.active=peer2

编排高可用Spring Cloud微服务集群及动态伸缩

1)由于使用了microservice-discovery-eureka-ha,需要将所有微服务的eureka.client.serviceUrl.defaultZone属性修改为

eureka:
  client:
    service-url:
      defaultZone: http://pee1:8761/eureka/,http://pee2:8762/eureka/

2)在每个项目的根目录,执行以下命令构建Docker镜像。

mvn clean package docker:build

3)编写docker-compose.yml

version: '2'
services:
  peer1:
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    ports:
      - "8761:8761"
    environment:
      - spring.profiles.active=peer1
  peer2:
    image: example/microservice-discovery-eureka-ha:1.0-SNAPSHOT
    hostname: peer2
    ports:
      - "8762:8762"
    environment:
      - spring.profiles.active=peer2
  microservice-provider-user:
    image: example/microservice-provider-user:1.0-SNAPSHOT
  microservice-comsumer-movie-ribbon-hystrix:
    image: example/microservice-comsumer-movie-ribbon-hystrix:1.0-SNAPSHOT

测试
1)执行以下命令启动项目

docker-compose up

2)测试

3)执行以下命令,为各个微服务动态扩容。让除Eureka Server以外的所有服务启动3个示例

docker-compose scale microservice-provider-user=3 microservice-comsumer-movie-ribbon-hystrix=3

以上是关于使用Docker Compose编排微服务的主要内容,如果未能解决你的问题,请参考以下文章

11:docker-compose(单机版的容器编排工具)

docker-compose编排springcloud微服务

[云原生专题-14]:容器 - 批量定义构建运行多个微服务- docker编排工具Compose

微服务架构 - 解决Docker-Compose服务编排启动顺序问题

容器编排工具Docker compose

Docker之Compose服务编排