9

前言

最近花了将近一个月的时间研究了 Docker 在生产环境中的使用,作为新手,期间走了无数的弯路,这里纪录一下,希望给别人带来微小的帮助。

前面几部分,介绍了在搭建集群之前需要做的一些工作,后面 <集群实践> 一块结合实际应用,介绍如何架构 docker 集群。

修改存储目录

生产环境中,镜像多了之后很容易把硬盘写满造成服务器宕机,所以需要挂载一块较大的硬盘,修改 docker 的默认存储路径,下面提供两种方案。

方案一:软连接

service docker stop
mv /var/lib/docker /mnt/sdc/docker
ln -s /mnt/sdc/docker /var/lib/docker

方案二:修改配置

配置 deamon 启动时 -g 参数,可以直接改变存储路径。

Ubuntu 系统需要先修改 /lib/systemd/system/docker.service 文件:

...
[Service]
ExecStart=/usr/bin/docker -d $DOCKER_OPTS
...
EnvironmentFile=-/etc/default/docker
...

其中 ExecStart 就是 deamon 的启动命令,可以直接在后面加参数,也可以选择上述 EnvironmentFile 配置 ,然后将启动参数写到 /etc/default/docker 文件中:

DOCKER_OPTS="-g /mnt/sdc/docker"

配置好之后,执行下面命令重新加载配置文件:

systemctl daemon-reload
systemctl restart docker

参考:How do I change the Docker image installation directory?

Insecure Registry

生产环境中部署 docker,需要在搭建一个私有 registry:

docker run -d -p 5000:5000 --restart=always --name registry registry:2

启动之后,没法直接进行镜像的 push/pull,因为默认要求配置 TLS。

为了图方便,可以暂时把私有的 registry 加入为 insecure-registry 进行测试。

insecure-registry 也是通过添加 deamon 的启动参数实现的,可在 /etc/default/docker 中配置:

DOCKER_OPTS="--insecure-registry 192.168.1.19:5000"

然后重启即可。

参考:Deploying a plain HTTP registry

集群方案

docker 集群部署目前有两种方案,一般称作 一代 swarm二代 swarm

一代 swarm

官方文档

一代 swarm 是以容器的方式进行集群管理的,需要在每个节点上运行一个 swarm 容器,便可进行集群管理,简单部署测试可参考: 在ubuntu上使用swarm搭建docker集群

需要注意的是,一代 swarm 还需要自己手动运行 k/v 服务容器,参考,运行起来之后可能会遇到报错:

Error response from daemon: datastore for scope "global" is not initialized

这其实是 deamon 没有配置 cluster-advertisecluster-store 所致,需要在/etc/default/docker 中配置这两项,具体可参考 Nodes discovery

二代 swarm

官方文档

二代 swarm 直接将 swarm 模式集成在 docker 里面,只需要简单的配置即可,参考:Create a swarm

界面方案

在之前的文章中,介绍过 ui-for-docker 作为管理的 web 界面,但是过于简陋,后来有找到一个基于 ui-for-docker 实现的 portainer,支持 swarm mode,使用起来非常方便,也可以根据自己的需求修改。

集群实践

实践过程中我分别尝试了一代 swarm 和二代 swarm。

版本 配置过程 管理 扩容
一代 1. 配置 k/v store。2. 宿主机 docker deamon 监听某个端口。 3.在每台宿主机上运行 swarm 容器。 在任何一个节点都可以进行集群的管理 手动扩容
二代 1. manager init swarm mode 2. worker join 只能在 manager 节点进行管理 自动扩容

目前实现了把 Segmentfault 的 web 服务迁移到容器中,简单的说就是一个 nginx + php 的环境,以下是具体步骤:

  1. 使用二代 swarm。

  2. 创建一个 overlay 网络。

  3. 创建 nginx 服务。

  4. 创建 php 及 web 代码服务。

需要明确的几点:

  1. 二代 swarm 在同一个网络下服务可以通过服务名发现其他服务。

  2. 二代 swarm 部署之后,将会监听每个节点上 publish 的端口,收到的请求会负载均衡到所有的 tasks 中。

nginx

nginx 服务只需选择官方提供的镜像,建议使用最轻量的 nginx:alpine 版本,自定义配置文件覆盖原生的即可。

php

镜像直接基于官方发布的php版本版本创建。

注意: ubuntu 宿主机尽量使用 Debian 版本的基础镜像,其他的可能会遇到各种坑。

我在开始为了追求镜像尽量小,使用了基于 alpine 的基础镜像,遇到了以下问题:

  1. 7.1.0RC5-fpm-alpine 版本,使用 session_set_save_handler 修改 session 的 save header 为 memached 之后没法写入 session,换成了 7.0.12-alpine 就好了。

  2. web 代码对 mount 到容器中的 www-data 用户所属的目录没有写入权限,是因为 alpine 系统中默认 www-datauid 是 82, 而宿主机 Ubuntu/Debian 的是 33,在 alpineuid 是 33 的用户是 xfs,所以 mount 之后容器内部看到的文件所属用户是 xfs,而 php-fpm 的执行用户是 www-data, 所以才无法写入。
    解决办法就是弃用 alpine,使用基于 Debian 的基础镜像,这样带来的代价就是镜像大小翻了 10 倍。

更改时区

RUN apk update && apk add ca-certificates && \
    apk add tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone

还有 php.ini 的时区配置。

DNS 服务

docker 容器的 /etc/hosts 文件默认是不允许修改的,所以要自定义域名的解析就需要配置自己的内网 DNS 服务,推荐使用 dnsmsq, 然后将 deamon 的启动参数修改为 --dns=192.168.x.x 自己的 DNS 服务器,就可以实现自定义域名解析的需求。

上述修改可解决服务器被墙导致谷歌、Facebook 等三方API无法使用的问题,也可以加速服务器访问外网。

代码更新

作为 web 应用,代码需要时常上线更新,又需要在集群中部署,如果使用目录挂载的办法将带来额外的工作量,所以选择把代码直接打包到 php 容器中,每次上线重新构建镜像,具体步骤:

  1. 代码 push 到 gitlab,触发 CI 或者 webhook,构建镜像。

  2. 镜像 push 到私有的 registry 仓库中。

  3. 二代 swarm 提供 rolling update 的机制,执行相关命令即可。

一些经验

  1. Docker 是进程容器,理论上一个容器只跑一个进程,杜绝当虚拟机使用。

  2. 要使用和宿主机一个体系的基础镜像。

  3. 国内使用建议搜索下 daocloud 镜像加速,会提升幸福感。

  4. DNS 服务器会默认使用 8.8.8.8,所以正式环境一定要配置 DNS 服务器,否则一些三方登录的接口将会变得异常缓慢。

  5. Docker 里面包含了很多新的思路,如果总是用老套路去思考,很可能就走入死胡同。

  6. 不要用百度搜索中文资料,一定要看最新的英文资料。

  7. 为了安全,确保 deamon 只监听 /var/run/docker.sock

感谢

  • 感谢能容忍我无数次搞挂生产环境的老板。

  • 感谢每次走入死胡同后一句话点醒我的同事。

  • 感谢群里每一位帮助我的朋友。

  • 感谢自己的坚持。

参考

  • 官方文档: 要想真的让 docker 为你工作,这份文档应当翻来覆去读无数遍。

  • 蜗牛问答录: 蜗牛大神的问答录,新手入门需要反复拜读。

  • QQ 群: 325486037 蜗牛大神所建 docker 中文交流群,基本配置过程中遇到的问题都能得到解决。


Tairy
3.1k 声望404 粉丝

一介书生