Docker Compose详解

前言

前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker builddocker run 等命令操作容器。

然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知

使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具

安装 Docker Compose

安装

1
2
apt install docker.io
apt install docker-compose

查看版本

1
2
docker --version
docker-compose --version

设置Docker镜像

针对Docker客户端版本大于 1.10.0 的用户

创建或修改 /etc/docker/daemon.json 文件

1
vi /etc/docker/daemon.json

添加或修改

1
2
3
{
"registry-mirrors": ["https://tiaudqrq.mirror.aliyuncs.com"]
}

重启Docker

1
2
systemctl daemon-reload
systemctl restart docker.service

查找可用的Java8镜像

1
docker search java8

拉取镜像

1
docker pull williamyeh/java8

部署Jar服务

假如我们的项目是helloworld-0.0.1-SNAPSHOT.jar,对应端口是8080

把服务的jar放到 /root/helloworld/目录下

在 jar 包所在路径创建 Dockerfile 文件,添加以下内容

1
2
3
4
5
6
7
8
FROM williamyeh/java8
# 将本地文件tmp挂载到容器
VOLUME /tmp
# 拷贝jar
ADD helloworld-0.0.1-SNAPSHOT.jar /app.jar
# 设置暴露的端口号
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

测试下 Dockerfile 文件是否可用

1
docker build -t psvmc/helloworld /root/helloworld/

在 jar 包所在路径创建文件 docker-compose.yml,添加以下内容

1
2
3
4
5
6
version: '2'
services:
hello-world:
build: /root/helloworld
ports:
- "9000:8080"

注意

hello-world 是指定服务名称

build 指定 Dockerfile 所在文件夹路径

docker-compose.yml 所在路径下执行该命令 Compose 就会自动构建镜像并使用镜像启动容器

1
docker-compose -f docker-compose.yml up -d hello-world

这样服务就可以访问了

http://www.psvmc.cn:9000/

网络

官方文档:https://docs.docker.com/compose/networking/

查看网络

查看网络列表

1
docker network ls

上面的示例中没有设置网络,就会产生

NETWORK ID NAME DRIVER SCOPE
a327e58d7a47 helloworld_default bridge local

也就是说它会根据文件所在文件夹的名称_default作为网络名

查看指定的网络设置

1
docker network inspect helloworld_default

注意这里的helloworld_default 可以传网络的ID或者是网络的名称

从返回的信息中我们可以看到容器内的网络

网络互通

上面的例子中我们的服务是单独的,但是实际上我们的服务之间需要互相能访问,比如我们的接口和数据库,这样该如何设置呢?

/root/nettest 下创建 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: '2'
services:
web1:
image: nginx
ports:
- "6061:80"
container_name: "web1"
networks:
- dev
web2:
image: nginx
ports:
- "6062:80"
container_name: "web2"
networks:
- dev
- pro
web3:
image: nginx
ports:
- "6063:80"
container_name: "web3"
networks:
- pro

networks:
dev:
driver: bridge
pro:
driver: bridge

启动

1
docker-compose -f docker-compose.yml up -d

这样就可以访问

http://www.psvmc.cn:6061
http://www.psvmc.cn:6062
http://www.psvmc.cn:6063

执行

1
docker network ls

我们可以看到

NETWORK ID NAME DRIVER SCOPE
423fc9d510c3 nettest_dev bridge local
a2c26563c23e nettest_pro bridge local

其实上面的本质上相当于创建了对应的网络

1
docker network create nettest_dev

或者

1
docker network create --driver=bridge --subnet=172.25.0.1/16 --gateway=172.25.0.1 mynet

查看指定的网络设置

1
2
docker network inspect nettest_dev
docker network inspect nettest_pro

可以看到

image-20210224171502834

image-20210224171743432

可以看出

web1和web2在一个内网中

web2和web3在一个内网中

复用之前的网络

/root/nettest2 下创建 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '2'
services:
web4:
image: nginx
ports:
- "6064:80"
container_name: "web4"
networks:
- nettest_dev
networks:
nettest_dev:
external:
name: nettest_dev

启动

1
2
cd /root/nettest2
docker-compose up -d

这时再查看网络

1
docker network inspect nettest_dev

如下图

image-20210224195015431

会发现web1web2web4已经在同一个网段下了。

示例

1
2
3
4
5
6
7
8
version: "3"
services:
web:
build: .
links:
- "db:database"
db:
image: mysql

这样我们就可以在web的服务中用dbdatabase就可以访问到数据库了

也就是说在web容器中可以用类似于mysql://db:3306或者 mysql://database:3306 来代替之前的mysql://{DOCKER_IP}:3306来访问数据库

depends_on

当使用 depends_on 来定义服务之间的依赖关系时会造成下面的影响2

  • docker-compose up 会依据依赖顺序启动服务
  • docker-compose up 启动时 SERVICE 会自动包括 SERVICE 的依赖

看这个例子:

1
2
3
4
5
6
7
8
9
10
11
version: '2'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: mysql

这个例子中 db ,redis 容器启动顺序要优先于 web 容器;当启动 web 容器时会自动创建 redis 和 db 容器。

不过需要注意的是, depends_on 不会等到 db 和 redis 容器 ready 再启动,web 容器仅仅等到 redis 和 db 容器启动就开始启动。

还是上面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '2'

services:
web:
build: .
depends_on:
- db
networks:
- dev
db:
image: mysql
networks:
dev:
aliases:
- database
pro:
aliases:
- mydb
networks:
dev:
driver: bridge
pro:
driver: bridge

web依赖于db这样我们就可以在web的服务中用database就可以访问到数据库了

也就是说在web容器中可以用类似于 mysql://database:3306 来代替之前的mysql://{DOCKER_IP}:3306来访问数据库.

在网络dev中可以用类似于 mysql://database:3306 来访问
在网络pro中可以用类似于 mysql://mydb:3306 来访问

必要条件

  1. 两者有依赖关系
  2. 在同一个网段下
  3. 被依赖的例如db设置网络的别名

常见命令

ps:列出所有运行容器

1
docker-compose ps

logs:查看服务日志输出

1
docker-compose logs

port:打印绑定的公共端口,下面命令可以输出 web1 服务 8761 端口所绑定的公共端口

1
docker-compose port web1 8761

build:构建或者重新构建服务

1
docker-compose build

start:启动指定服务已存在的容器

1
docker-compose start web1

stop:停止已运行的服务的容器

1
docker-compose stop web1

注意

这里的web1是服务名,不是容器名

rm:删除指定服务的容器

1
docker-compose rm web1

up:构建、启动容器

1
docker-compose up

更新容器

当服务的配置发生更改时,可使用 docker-compose up 命令更新配置

此时,Compose 会删除旧容器并创建新容器,新容器会以不同的 IP 地址加入网络,名称保持不变,任何指向旧容起的连接都会被关闭,重新找到新容器并连接上去

kill:通过发送 SIGKILL 信号来停止指定服务的容器

1
docker-compose kill web1

pull:下载服务镜像

scale:设置指定服务运气容器的个数,以 service=num 形式指定

/root/nettest2 下创建 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
version: '2'
services:
web4:
image: nginx
ports:
- "80"
networks:
- nettest_dev
networks:
nettest_dev:
external:
name: nettest_dev

然后执行

1
docker-compose up --scale web1=3 -d

和之前的配置不一样的地方有

  1. 端口设置只设置内部端口,外部端口自动分配
  2. 不能设置container_name

run:在一个服务上执行一个命令

1
docker-compose run web4 bash