提要

补充一些遗漏的问题.
本章主要内容:

  1. 使用constraints指定service约束.
  2. service挂载使用本地volume.
  3. ELK日志平台使用GELF日志驱动收集日志.

指定service约束

在前几章中, 我们创建的registry服务,是由swarm自动调度定义执行在某个节点上的. 这样的话我们如果我们重启service以后, registry服务可能会被启动再随机的节点.造成我们上传的镜像都不见了. 如何解决这个问题呢?
在创建service的时候可以使用--constraints参数,后面跟表达式,限制service容器在每个节点的调度情况.比如你想指定service运行在某个节点上等.
例如指定service运行在node01上:

1
2
docker service create --name registry --publish 5000:5000 \
--constraint 'node.hostname==node01' registry

除了hostname也可以使用其他节点属性来创建约束表达式写法参见下表:

节点属性 匹配 示例
node.id 节点 ID node.id == 2ivku8v2gvtg4
node.hostname 节点 hostname node.hostname != node02
node.role 节点 role: manager node.role == manager
node.labels 用户自定义 node labels node.labels.security == high
engine.labels Docker Engine labels engine.labels.operatingsystem == ubuntu 14.04

用户自定义labels可以使用docker node update命令添加, 例如:

1
docker node update --label-add security=high node01

查看自定义labels

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker node inspect node01
[
{
"ID": "0nhjsflo3tbd0b7hv2cyrjpin",
...
"Spec": {
"Labels": {
"security": "high"
},
"Role": "manager",
"Availability": "active"
},
"Description": {
"Hostname": "node01",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
...
}
]

对于已有service, 可以通过docker service update,添加constraint配置, 例如

1
2
docker service update registry \
--constraint-add 'node.labels.security==high'

volume 创建管理

有了service约束, 我们可以保证我们的registry服务, 一直在node01节点上了. 不过还有一个问题, 就是如果我们删除了registry服务. 那我们上传的容器镜像也就被删除了.
如何保证即使registry服务被删除, 镜像可以保留呢?
这里我们可以使用 docker volume指定挂载一个数据卷用来保存镜像, 即使registry服务被删除了. 我们重新启动一个服务, 挂载这个数据卷. 我们上传的镜像还可以保存的.
swarm集群中我们可以创建本地卷或者全局卷来挂载到容器, 用来保存数据.

  • 全局卷可以被挂载在swarm集群的任意节点, 所以不管你的服务容器启动在哪个节点, 都可以访问到数据. 不过docker目前还没有默认的全局卷驱动支持, 你可以安装一些插件驱动来实现全局卷例如Flocker, Portworx等.
  • 本地卷就只存在与某个节点本地的一个挂载卷.

Storage is Hard 这里我们还是使用简单的本地卷吧,^_^.

为我们刚刚新建的registry服务, 挂载一个本地卷,可以使用如下命令:

1
2
docker service update registry \
--mount-add type=volume,source=registry-vol,target=/var/lib/registry

source=registry-volregistry-vol为卷名字, 执行上述命令以后,docker会自动为我们创建一个registry-vol本地卷.

可以使用docker volume ls命令查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker volume ls
DRIVER VOLUME NAME
local registry-vol

docker volume inspect registry-vol
[
{
"Name": "registry-vol",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/registry-vol/_data",
"Labels": null,
"Scope": "local"
}
]

上面命令, 可以看到本机卷挂载到节点的目录.
这样即使我们现在删除registry服务. 也可以只用如下命令重新创建一个registry服务, 挂载registry-vol来找回我们的镜像.

1
2
3
4
docker service create --name registry --publish 5000:5000 \
--mount source=registry-vol,type=volume,target=/var/lib/registry \
-e SEARCH_BACKEND=sqlalchemy \
--constraint 'node.hostname==node01' registry

测试

使用docker-compose 编译上传我们的docker币镜像

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
31
32
[root@node01 ~]# git clone https://github.com/jpetazzo/orchestration-workshop.git
[root@node01 ~]# orchestration-workshop/dockercoins
[root@node01 dockercoins]# cat docker-compose.yml-images
version: "2"

services:
rng:
build: rng
image: ${REGISTRY_SLASH}rng${COLON_TAG}
ports:
- "8001:80"

hasher:
build: hasher
image: ${REGISTRY_SLASH}hasher${COLON_TAG}
ports:
- "8002:80"

webui:
build: webui
image: ${REGISTRY_SLASH}webui${COLON_TAG}
ports:
- "8000:80"
volumes:
- "./webui/files/:/files/"

redis:
image: redis

worker:
build: worker
image: ${REGISTRY_SLASH}worker${COLON_TAG}

上传我们的应用镜像到registry

1
2
3
4
export REGISTRY_SLASH=localhost:5000/
export COLON_TAG=:v0.01
docker-compose -f docker-compose.yml-images build
docker-compose -f docker-compose.yml-images push

测试registry内容

1
2
[root@node01 dockercoins]# curl localhost:5000/v2/_catalog
{"repositories":["hasher","rng","webui","worker"]}

删除registry服务

1
2
3
4
5
[root@node01 dockercoins]# docker service ls
ID NAME REPLICAS IMAGE COMMAND
5qvu78ths5wb registry 1/1 registry
[root@node01 dockercoins]# docker service rm registry
registry

服务删除以后我们可以看到本地卷还在.

1
2
3
[root@node01 dockercoins]# docker volume ls
DRIVER VOLUME NAME
local registry-vol

重建registry服务, 挂载registry-vol卷.

1
2
3
[root@node01 dockercoins]#  docker service create --name registry --publish 5000:5000  --mount source=registry-vol,type=volume,target=/var/lib/registry  -e SEARCH_BACKEND=sqlalchemy  --constraint 'node.hostname==node01' registry
[root@node01 dockercoins]# curl localhost:5000/v2/_catalog
{"repositories":["hasher","rng","webui","worker"]}

ELK日志平台使用GELF驱动收集日志

上一章我们介绍了ELK日志平台, 用于管理收集swarm集群的应用日志. 当时我们使用的logspout容器, 自动将我们swarm节点上所有容器日志发送的日志平台.
可是如果我们关心特定容器的日志, 并不想收集其他容器的日志怎么办呢?
我们可以时候创建service的时候使用--log-driver参数, 使用GELF驱动将日志发送到ELK平台.

创建ELK平台

上一章已经有详细说明了, 下面这里简单过一下, 凑字数.哈哈…

创建Logging网络

1
docker network create --driver overlay logging

创建Elasticsearch服务

1
2
3
4
docker service create --network logging --name elasticsearch \
--mount source=elasticsearch-vol,type=volume,target=/usr/share/elasticsearch \
--constraint 'node.hostname==node02' \
elasticsearch

创建Kibana服务

1
2
docker service create --network logging --name kibana --publish 5601:5601 \
-e LOGSPOUT=ignore -e ELASTICSEARCH_URL=http://elasticsearch:9200 kibana

http://192.168.33.101:5601/

创建Logstash服务

我们重新build一个logstash镜像, 使用~/orchestration-workshop/elk/logstash.conf这个配置文件.

1
2
3
4
5
6
7
[root@node01 ~]cd ~/orchestration-workshop/elk/
[root@node01 elk]# cat Dockerfile
FROM logstash
COPY logstash.conf /etc/logstash/
CMD ["-f", "/etc/logstash/logstash.conf"]
[root@node01 elk]# docker build -t localhost:5000/logstash .
[root@node01 elk]# docker push localhost:5000/logstash

启动logstash

1
2
docker service create --network logging --name logstash -p 12201:12201/udp \
localhost:5000/logstash

发送容器日志到logstash

我们已经有了ELK平台, 下面我们启动docker币应用, 发送容器日志到logstash.
很简单, 只要在创建service的时候添加--log-driver--log-opt两个参数就行了.

1
2
3
4
5
6
7
8
#创建应用网络
docker network create --driver overlay dockercoins
#启动我们的docker币 webui应用
DOCKER_REGISTRY=localhost:5000
TAG=v0.01
docker service create --network dockercoins --name webui \
--log-driver gelf --log-opt gelf-address=udp://127.0.0.1:12201 \
-p 8000:80 $DOCKER_REGISTRY/webui:$TAG

使用下面这段脚本启动hasher rng worker 应用:

1
2
3
4
5
6
7
DOCKER_REGISTRY=localhost:5000
TAG=v0.01
for SERVICE in hasher rng worker; do
docker service create --network dockercoins --name $SERVICE \
--log-driver gelf --log-opt gelf-address=udp://127.0.0.1:12201 \
$DOCKER_REGISTRY/$SERVICE:$TAG
done

启动redis数据库:

1
2
3
4
5
docker service create --network dockercoins \
--limit-memory 100M --name redis \
--mount source=redis-vol,type=volume,target=/data \
--constraint 'node.hostname==node03' \
redis

为了方便观看日志, 我们sacle up下应用, 多启动一些容器:

1
docker service scale worker=10 rng=10

好了现在用浏览器, 打开Kibana(http://192.168.33.101:5601/), 我们就可以看到容器日志了.
kibana

visualier

visualizer是一个小程序,用于可视化swarm集群容器分布情况.
swarm的manager节点上运行, HOST参数指定节点IP地址.

1
2
3
4
5
6
docker run --name visualizer -d \
-p 8089:8089 \
-e HOST=192.168.33.101 \
-e PORT=8089 \
-v /var/run/docker.sock:/var/run/docker.sock \
manomarks/visualizer

浏览器访问 http://192.168.33.101:8089/ 容器多了不是很好看:-(
v01

最后

挖坑, 有了日志收集平台, 我们还需要性能监控平台, 所以下一章介绍下如何使用cadvisor, influxdb, grafana搭建简单的swarm性能监控平台.