补充一些遗漏的问题.

本章主要内容:

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

1. 指定service约束

在前几章中, 我们创建的 registry 服务,是由swarm自动调度定义执行在某个节点上的. 这样的话我们如果我们重启 service 以后, registry 服务可能会被启动再随机的节点.

造成我们上传的镜像都不见了. 如何解决这个问题呢?

在创建service的时候可以使用 --constraints 参数,后面跟表达式,限制service容器在每个节点的调度情况.比如你想指定service运行在某个节点上等.

例如指定service运行在node01上:

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 命令添加, 例如:

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

查看自定义labels

# 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配置, 例如:

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

2. volume 创建管理

有了service约束, 我们可以保证我们的 registry 服务, 一直在node01节点上了. 不过还有一个问题, 就是如果我们删除了 registry 服务. 那我们上传的容器镜像也就被删除了.

如何保证即使 registry 服务被删除, 镜像可以保留呢?

这里我们可以使用 docker volume 指定挂载一个数据卷用来保存镜像, 即使 registry 服务被删除了. 我们重新启动一个服务, 挂载这个数据卷. 我们上传的镜像还可以保存的.

在 swarm 集群中我们可以创建本地卷或者全局卷来挂载到容器, 用来保存数据.

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

  • 本地卷, 就只存在与某个节点本地的一个挂载卷.

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

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

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

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

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

# 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 来找回我们的镜像.

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币镜像

[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

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 内容

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

删除 registry 服务

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

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

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

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

[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"]}

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

上一章我们介绍了ELK日志平台, 用于管理收集swarm集群的应用日志. 当时我们使用的logspout容器, 自动将我们swarm节点上所有容器日志发送的日志平台.

可是如果我们关心特定容器的日志, 并不想收集其他容器的日志怎么办呢?

我们可以时候创建service的时候使用 --log-driver 参数, 使用GELF驱动将日志发送到ELK平台.

创建ELK平台

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

创建Logging网络
docker network create --driver overlay logging
创建Elasticsearch服务
docker service create --network logging --name elasticsearch \
--mount source=elasticsearch-vol,type=volume,target=/usr/share/elasticsearch \
--constraint 'node.hostname==node02' \
elasticsearch
创建Kibana服务
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 这个配置文件.

[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

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

发送容器日志到logstash

我们已经有了ELK平台, 下面我们启动 docker币 应用, 发送容器日志到logstash.

很简单, 只要在创建 service 的时候添加 --log-driver 和 --log-opt两个参数就行了.

#创建应用网络
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 应用:

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数据库:

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下应用, 多启动一些容器:

docker service scale worker=10 rng=10

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

##4. visualier

visualizer是一个小程序,用于可视化 swarm 集群容器分布情况.

在 swarm 的manager节点上运行, HOST 参数指定节点IP地址.

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/ 容器多了不是很好看 :-( 

最后

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

influxdb

docker service create --network logging \
-p 8083:8083 -p 8086:8086 \
--mount source=influxdb-vol,type=volume,target=/var/lib/influxdb \
--name=influxdb --constraint 'node.hostname==node03' \
influxdb:alpine

http://192.168.33.101:8083/

docker service create --network logging --name cadvisor --mode global \
--mount source=/var/run/,type=bind,target=/var/run,readonly=false \
--mount source=/,type=bind,target=/rootfs,readonly=true \
--mount source=/sys,type=bind,target=/sys,readonly=true \
--mount source=/var/lib/docker/,type=bind,target=/var/lib/docker,readonly=true \
google/cadvisor:latest -storage_driver=influxdb -storage_driver_host=influxdb:8086 \
-storage_driver_db=cadvisor
docker service create --network logging -p 3000:3000 -e INFLUXDB_HOST=influxdb -e INFLUXDB_PORT=8086 -e INFLUXDB_NAME=grafana -e INFLUXDB_USER=root -e INFLUXDB_PASS=root -e "GF_SECURITY_ADMIN_PASSWORD=admin" --constraint 'node.hostname==node05' --name grafana grafana/grafana

http://192.168.33.101:3000/

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐