阅读本文前,请确保您至少有三台电物理机或者三台虚拟机,并且每个机器都部署了最新的Docker Engine。如若没有,请参考给物理机或者虚拟机部署Docker Engine。
参考官方文档。
节点的特性
每个容器化的机器(即安装了Docker Engine)可以变为一个swarm节点。
swarm节点分为两种,管理节点(manager node)和工作节点(worker node)。
管理节点的主要功能是:
- 维护集群状态
- 安排部署服务
- 提供集群API服务
工作节点的主要功能是:
- 运行容器
一个swarm可以是管理节点,也可以是工作节点,也可以两者都是。默认情况下,一个管理节点既有管理节点的职能,也有工作节点的职能,也就是两者都是。可以通过docker node update --availability drain NODE把节点置为Drain状态,这样管理节点就不会部署容器了。
一个swarm集群最少有一个管理节点,可以没有工作节点。(swarm单节点模式)
一个swarm集群最好有大于1个的奇数个管理节点。为什么要大于一个呢?主要是为了容错,当其中一个管理节点挂掉之后,其他的管理节点能够正常运行提供服务(管理节点的职能,创建任务,维护状态等)。管理节点间通过Raft算法保证状态的一致性,工作节点的状态不参与Raft算法的计算过程。
三个管理节点可以容许一个管理节点挂掉,五个管理节点可以容许两个管理节点挂掉,n个管理节点可以容许(N-1)/2个管理节点挂掉。添加更多的管理节点不一定增加集群的可伸缩性和性能,但是减少管理节点却会降低可伸缩性和性能。
首先选择三个容器化机器的任何一个,把它变为管理节点:
//变为一个管理节点
docker swarm init --advertise-addr <MANAGER-IP>
获取以管理节点加入的token,因为需要三个管理节点,所以获取的是加入管理节点的token。
//获取以管理节点加入的token
docker swarm join-token manager
//也可以获取以工作节点加入的token,这里都是管理节点,暂时不用
docker swarm join-token worker
把其他两个机器以管理节点身份加入进来。
//加入swarm集群
docker swarm join --token *** --advertise-addr ***
至此,三个容器化机器都以管理节点身份加入到集群中,后续可以部署服务了。
swarm集群通过管理节点部署服务,只要配置好yml文件即可,语法可以参考docker-compose文件语法。定义服务时,可以指令下面的参数:
- 服务的端口
- 服务的网络
- cpu和内存的限制和预留
- 回滚策略
- 同一服务的个数
服务、任务和容器的关系
一个服务可以有多个,分别由不同的任务去创建容器,每个容器运行相同服务的一个副本。
任务及其调度
首先客户端(一般指命令行)下发一个创建服务的指令给管理节点(通过Api),管理节点创建一个任务,并把这个任务分发给工作节点。工作节点确认任务后,执行任务并创建容器,之后工作节点不断给管理节点上报数据,告知管理节点服务的状态。
如果集群没有可用的节点部署服务,服务会一直处于pending状态直到有节点可用。可以给服务指定预留的内存,当预留的内存足够时,才执行部署任务。如果给服务预留太大的内存,那么服务会一直处于pending状态。某些情况下,不想yml文件的服务被部署,可以把服务的数量指定为0。
对于系统管理员来说,只需要指定服务最终的状态(比如数量,cpu和内存限制等)即可,无需直接操作任务本身。管理节点和工作节点配合会让服务达到最终给定的状态。
复制和全局服务
复制服务的个数由replicated参数决定,而全局服务是每个节点都会运行的服务,一般是日志或者杀毒服务。
假设现在已经在一台window10上安装了Docker Engine,并且使用docker-machine配合hyperv创建了三台虚拟机xxl、zzz、lll,并且都已管理节点加入集群中。
为了避免网速缓慢,选择一个管理节点安装Registry,把测试镜像放到里面
网速快可以直接上传到Docker Hub
//运行注册服务
docker service create --name registry --publish published=5000,target=5000 registry:2
编写测试服务
创建文件夹demo,并在其中加入文件:
- app.py
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
count = redis.incr('hits')
return 'Hello World! I have been seen {} times.\n'.format(count)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)
- requirements.txt
flask
redis
- Dockerfile
FROM python:alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
- docker-compose.yml
version: '3'
services:
web:
image: 127.0.0.1:5000/demo //试过用localhost和路由器ip地址都报错,只有127.0.0.1可以上传成功
build: .
ports:
- "8000:8000"
redis:
image: redis:alpine
使用docker-compose构建服务并上传到本地的registry服务
//构建
docker-compose build --no-cache
//上传
docker-compose push
//查看上传的仓库
curl http://127.0.0.1:5000/v2/_catalog
部署集群的服务
集群的配置文件swarm.yml内容如下:
version: "3"
services:
portainer: //查看集群服务
image: portainer/portainer
ports:
- "9000:9000"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
web:
image: 127.0.0.1:5000/demo
ports:
- "8000:8000"
deploy:
replicas: 3
redis:
image: redis:alpine
deploy:
replicas: 3
把swarm.yml文件复制到管理节点并部署:
//运行服务栈
docker stack deploy -c swarm.yml xxl
其他常用命令
//查看服务状态
docker service ps --no-trunc {serviceName}