Docker容器编排
一、实验目标
- 了解Docker容器主流的编排技术
- 掌握Docker Swarm 的部署和基本使用
- 掌握Docker Compose的部署和基本使用
二、实验准备
规划节点:三台节点分别命名为master(Swarm集群mange节点)
、node(Swarm集群node节点)
、compose(compose节点)
,三台节点网络配置同一网段,提前关闭防火墙(关闭后重启docker),所有节点都已安装好Docker-ce
。
三、实验步骤
3.1、Docker Swarm的部署和基本使用
3.1.1、配置主机映射
vi /etc/hosts
#添加三台主机的IP映射-三台同步
192.168.200.10 master
192.168.200.20 node
192.168.200.30 compose
3.1.2、配置Docker API
master节点
和node节点
均需开启Docker API
# vi /lib/systemd/system/docker.service
将ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
修改为ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
# 重新加载配置文件并重启docker
# systemctl daemon-reload
# systemctl restart docker
3.1.3、拉取Swarm镜像
所有节点拉取Swarm镜像
# master和node同步拉取Swarm镜像
docker pull swarm
# 查看镜像
docker images
3.1.4、初始化集群
在master节点创建Swarm集群,注意记录报文,稍后用到
# docker swarm init --advertise-addr 192.168.200.10
Swarm initialized: current node (yqhx1ox536skwql7o9wb860ls) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-2e3dba9j8fbu6tx4gjv1d4gqk9j7cu70s0retx9db20owajn1u-4w8r9l6uogjxlbqukxkkhzw82 192.168.200.10:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
初始化命令中--advertise-addr
参数表示管理节点公布它的IP是多少。输出结果中包含了Swarm命令,通过--token
参数来判断加入集群时,是作为管理节点还是工作节点。
3.1.5、node节点加入集群
在node节点
执行加入Swarm集群的命令
(上个步骤的报文结果),如果加入失败,尝试清空iptables和关闭防火墙。
docker swarm join --token SWMTKN-1-2e3dba9j8fbu6tx4gjv1d4gqk9j7cu70s0retx9db20owajn1u-4w8r9l6uogjxlbqukxkkhzw82 192.168.200.10:2377
3.1.6、验证集群
在master节点
查看各节点状态
# docker node ls #状态为Active
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yqhx1ox536skwql7o9wb860ls * master Ready Active Leader 18.09.6
6d5uqxz97gavemwoz9onnqipm node Ready Active 18.09.6
3.1.7、安装Portainer(Docker的图形化管理工具)
master节点
安装Portainer
# docker volume create portainer_data
portainer_data
# docker service create --name portainer --publish 9000:9000 --replicas=1 --constraint 'node.role == manager' --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock --mount type=volume,src=portainer_data,dst=/data portainer/portainer -H unix:///var/run/docker.sock
#正确报文
vyljj48etqotxhd6oa082ona9
overall progress: 1 out of 1 tasks
1/1: running
verify: Service converged
3.1.8、浏览器登录Portainer
访问地址:http://192.168.200.10:9000/
首次登录时需设置用户名和密码,输入设置的用户名和密码(必须12位)例如:admin、admin0000000
ps:(设置账号密码后如果提示超时-如下图)
则切换到master节点重启Portainer
,步骤如下:
# 1、查询Portainer的容器名称
docker ps -a
# 2、拷贝Portainer的name字段
portainer.1.xxxxxxxxxxxxxxx
# 3、拼接重启命令
docker restart portainer.1.xxxxxxxxxxxxxxxxxxxxx
然后刷新浏览器配置账户登录,如下图所示:
3.1.9、运行service
docker service create --name web --replicas 2 nginx
# --name为service命名,nginx为镜像的名字,--replicas参数指定此服务在工作节点上的运行的任务数
#正确报文
02urum8zwtp4k9e0uko1b9q1q
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
# 查看当前Swarm中的service
docker service ls
# 报文
ID NAME MODE REPLICAS IMAGE PORTS
vyljj48etqot portainer replicated 1/1 portainer/portainer:latest *:9000->9000/tcp
02urum8zwtp4 web replicated 2/2 nginx:latest
REPLICAS
参数为当前副本信息,2/2的含义是service期望的容器副本数量为2,目前已经启动的副本数量为2,也即当前的service已经部署完成。命令docker service ps
可以查看service每个副本的状态。
# 查看web服务
docker service ps web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w3kw6jrx69s5 web.1 nginx:latest node Running Running 26 minutes ago
c276mupso514 web.2 nginx:latest master Running Running 26 minutes ago
查看到两个副本分别被分派到master和node节点,当前状态都为Runing
3.1.10、service的弹性伸缩
ps:web服务通常会运行多个实例,用以达到负载均衡
以及高可用
。
命令docker service scale 服务名=副本数量
可以增加service的副本数。在master上执行:
# docker service scale web=5
web scaled to 5
overall progress: 5 out of 5 tasks
1/5: running
2/5: running
3/5: running
4/5: running
5/5: running
verify: Service converged
#副本数增加到5 再次查看副本详细信息
[root@master ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
vyljj48etqot portainer replicated 1/1 portainer/portainer:latest *:9000->9000/tcp
02urum8zwtp4 web replicated 5/5 nginx:latest
[root@master ~]# docker service ps web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w3kw6jrx69s5 web.1 nginx:latest node Running Running 45 minutes ago
c276mupso514 web.2 nginx:latest master Running Running 45 minutes ago
kmfkx4iean3o web.3 nginx:latest node Running Running 10 minutes ago
pd5v047r27jd web.4 nginx:latest master Running Running 10 minutes ago
2ryv2lsx751a web.5 nginx:latest node Running Running 10 minutes ago
#可以看到5个副本都已分布到Swarm的各个节点上
3.1.11、调度节点
ps:默认配置下master节点
也是worker node
,所以master上也运行了副本,如果不希望在master上运行service,可以进行如下配置:(目前该操作会导致Portainer页面无法访问
)
[root@master ~]# docker node update --availability drain master
master
# 再次查看各个节点状态
# Drain代表不负责运行service
[root@master ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
yqhx1ox536skwql7o9wb860ls * master Ready Drain Leader 18.09.6
6d5uqxz97gavemwoz9onnqipm node Ready Active 18.09.6
# 查看之前由master负责运行的service会如何处理
[root@master ~]# docker service ps web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w3kw6jrx69s5 web.1 nginx:latest node Running Running about an hour ago
e3522ykvk23r web.2 nginx:latest node Running Running 7 minutes ago
c276mupso514 \_ web.2 nginx:latest master Shutdown Shutdown 7 minutes ago
kmfkx4iean3o web.3 nginx:latest node Running Running 26 minutes ago
mzi3jh9s01sx web.4 nginx:latest node Running Running 7 minutes ago
pd5v047r27jd \_ web.4 nginx:latest master Shutdown Shutdown 7 minutes ago
2ryv2lsx751a web.5 nginx:latest node Running Running 26 minutes ago
# 可以看到master负责的两个副本(2、4)已经关闭,为了确保5个副本,在node节点上又新增了2个相同的副本
3.1.12、外部网络访问service
ps:要访问service,首先要保证网络联通,其次服务的IP需要知道,每个副本都是一个容器,可以先查看容器的网络配置:(node节点)
[root@node ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
17b48fb5a31d nginx:latest "/docker-entrypoint.…" 19 minutes ago Up 19 minutes 80/tcp web.2.e3522ykvk23r5mm63cyayejpn
144ebf56bb17 nginx:latest "/docker-entrypoint.…" 19 minutes ago Up 19 minutes 80/tcp web.4.mzi3jh9s01sx7uke37kt0xjhr
45a54804e013 nginx:latest "/docker-entrypoint.…" 37 minutes ago Up 37 minutes 80/tcp web.3.kmfkx4iean3oi758mkukll737
dab2aa327f57 nginx:latest "/docker-entrypoint.…" 37 minutes ago Up 37 minutes 80/tcp web.5.2ryv2lsx751av8h6uz3sxv2vf
ed31fd0f0b0c nginx:latest "/docker-entrypoint.…" About an hour ago Up About an hour 80/tcp web.1.w3kw6jrx69s5xxc3ktknmi22h
# 在node节点上运行了5个容器,是web的副本,容器监听了80端口,但是并没有映射到DockerHost。
# 服务并没有暴露给外部网络,只能在Docker主机上访问。
# 将service暴露到外部,执行以下步骤:
[root@master ~]# docker service update --publish-add 8080:80 web
web
overall progress: 5 out of 5 tasks
1/5: running [==================================================>]
2/5: running [==================================================>]
3/5: running [==================================================>]
4/5: running [==================================================>]
5/5: running [==================================================>]
verify: Service converged
浏览器访问两个节点的8080端口:(会发现所有节点的IP都能正常访问,这是Swarm的优势之一,被称为routing mash,routing mash会将外部请求路由到不同主机的容器,从而实现外部网络对service的访问
)
ps:如果是新建的service,可以直接使用--publish
参数,例如:
docker service create --name web_server --publish 8080:80 --replicas=2 httpd
容器在80端口上监听http请求,--publish-add 8080:80
将容器的80映射到主机的8080端口,这样外部就能访问到service了。
3.2、Docker-Compose的部署和基本使用
3.2.1、安装Docker-Compose(compose节点)
# 拷贝指定版本docker-compose文件(docker引擎安装实验中解压的文件之一)到/usr/local/bin/目录下
[root@client ~]# cp -rvf compose/docker-compose /usr/local/bin/
# 添加可执行权限
chmod +x /usr/local/bin/docker-compose
# 查看软件版本信息
docker-compose --version
# 报文
docker-compose version 1.25.0-rc2, build 661ac20e
3.2.2、创建项目目录
#root目录下创建测试项目的文件夹并进入
mkdir composetest
cd composetest/
3.2.3、定义app.py文件
(由于python语言严苛的缩进问题,请务必选择纯手写或本地使用专业软件编辑完成后直接上传文件,直接拷贝粘贴可能会导致缩进异常
)
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis',port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return "Hello World! I have been seen {} times.\n".format(count)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
3.2.4、定义requirements.txt
# 创建并编辑requirements.txt文件
[root@compose composetest]# vi requirements.txt
# 编写以下内容
flask
redis
3.2.5、定义Dockerfile文件
# 创建并编辑Dockerfile文件
[root@compose composetest]# vi Dockerfile
# 编写以下内容
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python","app.py"]
#参数解释:
#1、构建一个基于Python3.4的镜像
#2、把当前目录添加到镜像中的/code路径下
#3、把工作路径设置成/code
#4、安装Python依赖
#5、设置容器的默认命令为python app.py
3.2.6、定义服务
# 编写docker-compose.yml文件
[root@compose composetest]# vi docker-compose.yml
version: "3"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
# 该文件定义两个服务web和redis:
# web服务使用当前目录Dockerfile构建出来的镜像,并且将容器上暴露的5000端口转发到主机的5000端口,使用Flask Web服务器的默认端口5000;
# redis服务使用从Docker Hub注册表中拉取的镜像。
3.2.7、运行服务
在项目路径下通过docker-compose up
命令启动应用
[root@client composetest]# docker-compose up
#省略...
...
Attaching to composetest_redis_1, composetest_web_1
redis_1 | 1:C 01 Nov 2023 06:06:11.610 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1 | 1:C 01 Nov 2023 06:06:11.610 # Redis version=6.2.6, bits=64, commit=00000000,
redis_1 | 1:C 01 Nov 2023 06:06:11.610 # Warning: no config file specified, using the derver /path/to/redis.conf
redis_1 | 1:M 01 Nov 2023 06:06:11.610 * monotonic clock: POSIX clock_gettime
redis_1 | 1:M 01 Nov 2023 06:06:11.610 * Running mode=standalone, port=6379.
redis_1 | 1:M 01 Nov 2023 06:06:11.610 # WARNING: The TCP backlog setting of 511 cannothe lower value of 128.
redis_1 | 1:M 01 Nov 2023 06:06:11.610 # Server initialized
redis_1 | 1:M 01 Nov 2023 06:06:11.610 # WARNING overcommit_memory is set to 0! Backgrouue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command
redis_1 | 1:M 01 Nov 2023 06:06:11.611 * Ready to accept connections
web_1 | * Serving Flask app "app" (lazy loading)
web_1 | * Environment: production
web_1 | WARNING: This is a development server. Do not use it in a production deploy
web_1 | Use a production WSGI server instead.
web_1 | * Debug mode: on
web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web_1 | * Restarting with stat
web_1 | * Debugger is active!
web_1 | * Debugger PIN: 977-468-128
# 该命令是前台运行所以不会主动退回交互窗口,如需继续操作需要另起一个窗口进行交互
另外打开一个新窗口进行查看:
[root@client ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0f7f0a887e15 redis:alpine "docker-entrypoint.s…" About an hour ago Up About an hour 6379/tcp composetest_redis_1
d4311cad5856 composetest_web "python app.py" About an hour ago Up About an hour 0.0.0.0:5000->5000/tcp composetest_web_1
# redis和app.py都是up状态
# redis是compose拉取的镜像,然后以代码构建一个本地镜像,并启动定义的服务。
# 构建本地镜像时,代码被静态拷贝到镜像中。查看所有镜像:
[root@client ~]# docker images ls
REPOSITORY TAG IMAGE ID CREATED SIZE
composetest_web latest 6f94e71d96e0 About an hour ago 49.9MB
nginx latest 605c77e624dd 22 months ago 141MB
192.168.200.10/library/nginx latest 605c77e624dd 22 months ago 141MB
redis alpine 3900abf41552 23 months ago 32.4MB
python 3.5-alpine 6d034ccc54a2 3 years ago 38MB
3.2.8、验证服务
[root@client ~]# curl 127.0.0.1:5000
Hello World! I have been seen 1 times.
[root@client ~]# curl 127.0.0.1:5000
Hello World! I have been seen 2 times.
# 也可以直接浏览器打开上述地址,如下:
3.2.9、停止服务
方式1:项目目录下执行docker-compose down
命令停止服务
方式2:在之前的运行窗口按Ctrl+C
组合键停止当前启动着的应用
# 停止服务(测试项目目录下)
[root@client composetest]# docker-compose down
3.2.10、更新服务文件
在项目路径下编辑docker-compose.yml
文件为Web服务添加一个绑定挂载路径。
[root@client composetest]# vi docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: "redis:alpine"
新的volumes键
将当前路径(项目路径)与容器中的/code路径挂载到一起,允许用户及时修改代码而不用重新构建镜像。
3.2.11、重建服务
在项目路径下,使用docker-compose up
命令启动服务,使用更新后的Compose文件构建应用并启动。
# 后台启动
[root@client composetest]# docker-compose up -d
# 再次访问测试
[root@client composetest]# curl 127.0.0.1:5000
Hello World! I have been seen 4 times.
3.2.12、免重构更新服务内容
ps:因为应用程序的代码通过volumes挂载到容器中,用户可以更改其代码并立即查看更改,而不必重新生成镜像。
# 修改app.py中的欢迎语并保存。例如修改为Hello from Docker!
[root@client composetest]# vi app.py
...省略
@app.route('/')
def hello():
count = get_hit_count()
return "Hello from Docker! I have been seen {} times.\n".format(count)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
# 再次测试
[root@client composetest]# curl 127.0.0.1:5000
Hello from Docker! I have been seen 5 times.
# 报文已经成功更新为Hello from Docker!......