使用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: .
ports:
- "8761:8761"
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端口所绑定的公共端口。
docker-compose ps
也可列出指定服务的容器
docker-compose ps eureka
- pull:下载服务镜像。
- rm:删除指定服务的容器。
docker-compose rm eureka
docker-compose run web bash
- scale:设置指定服务运行容器的个数,以service=num的形式指定。
docker-compose scale user=3 movie=3
docker-compose start eureka
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
|