本文来自作者 大白菜 在 GitChat 上分享「Docker 落地踩过的坑」,「阅读原文」查看交流实录
「文末高能」
编辑 | 嘉仔
接触 Docker 是在大四毕业设计中,当时选的课题是:基于 Docker 的自动化运维研究。也很幸运,毕业从事的工作也是 Docker 相关的工作。第一份工作是在杭州铜板街,杭州的朋友可能听过,在那里工作了近 2 年时间。主要的工作是将 Docker 落地于公司,提高整个交付流程的效率。第二份工作是涂鸦智能,主要工作是,实现公司的 DevOps 。在整个 Docker 落地过程中,踩过无数的坑,这当中,包括技术的选型,如何将现有的开发习惯都 Docker 化,以及最大的困难和挑战就是让大家相信 Docker 能在 DevOps 中带来更好的帮助较于传统的自动化运维。本文,主要介绍几个在Docker 落地过程中的一些建议和常见的几个坑。
包括:
Registry 使用 Harbor 对镜像仓库管理
Registry 使用 oss 或者 s3 作为存储
Registry 批量清理镜像
Docker dubbo 服务注册
Docker 删除上一次部署的容器的镜像
RocketMQ broker 注册 IP 问题
….
正文
Registry 使用 Harbor 对镜像仓库管理
目前主流的 Registry 有 Portus(https://github.com/SUSE/Portus )、Harbor(https://github.com/vmware/harbor )、docker-registry-web 等。本文建议使用 VMware 的开源 Harbor 作为 Docker 镜像管理仓库。Harbor 相关文档较全面,社区相对活跃,而且功能强大。
Harbor 主要的功能有:
基于角色的访问控制
镜像同步复制
图形界面管理镜像
漏洞扫描
支持 LDAP/AD
镜像删除和垃圾回收
对所有操作进行审计
提供 RESTful API
详细介绍请参考官方文档 Harbor(https://github.com/vmware/harbor)。
需要注意的一点是,尽量使用 https,安装文档:https 访问安装文档(https://github.com/vmware/harbor/blob/master/docs/configure_https.md)
使用 oss 或者 s3 作为存储
Docker 提供了丰富的镜像仓库存储方式,默认我们是使用本地的文件系统。这种方式有很多弊端,一个就是本地磁盘有限,因为镜像仓库是很耗存储的。如果咱们用的是 aws 或者 aliyun 机器,那么使用 s3 或 oss 作为镜像仓库的存储是超级便利的,也就再也不用担心磁盘不够,磁盘损坏等问题。这里介绍一下使用 aliyun 的 oss 最为镜像仓库的存储。
先看看使用 oss 最为存储的配置项:
storage:
oss:
accesskeyid: accesskeyid
accesskeysecret: accesskeysecret
region: OSS region name
endpoint: optional endpoints
internal: optional internal endpoint
bucket: OSS bucket
encrypt: optional data encryption setting
secure: optional ssl setting
chunksize: optional size valye
rootdirectory: optional root directory
注:这里是 registry 的配置文件 config.yml 中 storage 配置
具体 配置项((https://docs.docker.com/registry/storage-drivers/oss/) 含义:
下面是一个具体例子:
storage:
cache:
layerinfo: inmemory
oss:
accesskeyid: xxxx
accesskeysecret: xxxx
region: oss-cn-hongkong
endpoint: xxx.oss-cn-hongkong.aliyuncs.com
bucket: xxx
maintenance:
uploadpurging:
enabled: false
delete:
enabled: true
这里有点比较有用的提示:
internal 这个配置默认是 false 的,意味着 endpoint 则是外网 endpoint 。从阿里云的 OSS中:
我们可以看到,外网流出的费用是需要收费的,所以如果 oss 和 开的 ECS 都是在同一个 Region ,建议配置 endpoint : true ,然后 endpoint 配置内网的地址。可以节省成本。
如果公司使用的是 aliyun ECS ,而且机器涉及到跨 Region 。那么建议搭建多个镜像仓库。比如开发环境或者日常环境搭使用一个仓库,线上使用一个仓库。线上的仓库只存储预发环境或者是线上环境的镜像。具体仓库部署可以后面一起讨论。
Registry 批量清理镜像
在长期频繁使用镜像仓库后,由于仓库清理镜像比较费劲,业内也没有一个比较好的清理方案,官方提供的仓库清理也比较费劲,导致 Docker 镜像仓库越积越大,严重消耗磁盘空间。基于该现状,并结合上面提到的使用 Harbor 作为仓库管理,完美解决 Registry 清理镜像的问题。
镜像命名规则
如果想方便快捷的清理镜像,镜像的名字和 tag 的命名都极为重要。尽量做到分层,分类。这样有助于批量删除清理镜像。比如下面的命名规则:
这样做的好处是能够方便的做到批量删除镜像。比如想删除日常环境下的镜像,只需要匹配 xxx/deploy/app/daily 就行。
借助 Harbor Restful API 删除镜像或 Tag
主要有下面几个步骤:
如果想删除 xxx/deploy/app/daily 下的所有镜像。
1. 使用 GET /api/repositories ,该 API 有个 filter 参数,设置 filter=xxx/deploy/app/daily ,则可以匹配查找出xxx/deploy/app/daily 下的所有 repositories。
2. 遍历上一步拿到的 repositories, 使用 DELETE /api/repositories/${repoName}
如果想删除某个 repo 的 tag,同理,也可以调用 API 删除
上述删除动作 ,实际上并没有删除,只是删除了 Registry 的索引。实际文件并没有删除。
最后还需要执行镜像的垃圾回收:
docker run -it --name gc --rm --volumes-from registry vmware/registry:2.6.2-photon garbage-collect /etc/registry/config.yml
⚠️ 注意:最好停止 registry 服务,防止在回收的时候有 push 操作,从而造成镜像分层的混乱和误删。
通过使用 Harbor API ,可以方便快捷的操作 Registry,开源社区还提供了 Harbor 的 Java API,本质上我们自己也可以封装。Github 地址:Harbor Java API(https://github.com/johnnywong233/harbor-java-client)。
Docker dubbo 服务注册
背景
在容器部署应用和常规部署应用混合使用一套 zk 或者是在 Docker 集群下容器部署应用,将出现 dubbo 服务调用失败的问题。
原因
在容器中部署应用时,应用中的 dubbo 服务,获取容器 hostname 的 IP 地址和配置文件中的端口号向注册中心中注册。由于不同网段不能互相通信访问,这就导致了,容器部署应用和常规部署应用混合共用一套注册中心或是在 Docker 集群下容器部署应用时,将出现 dubbo 消费者不能访问服务提供者。
分析
由于 dubbo 服务注册,是去拿 hostname 的 IP 向,通过这点,hostname 必须为宿主机的 IP 地址。为了解决上述问题,首要解决的问题是网络通信问题。
解决方法
方法一:启用容器时,指定容器的网络模式为 host 模式,因为 host 模式容器共享主机的网络。
docker run --net=host
优点:简单,容易配置。
缺点:一个机器相同应用只能起一份。
方法二:想办法让主机 hostname 的 IP 为宿主机的 IP,这样通过宿主机的 IP 和映射的 dubbo 端口号,就能避免这个问题。这里推荐一个比较挫的方法。
启动容器时,指定 hostname ,并且将宿主机的 /etc/hosts 挂载至容器中,如:/tmp/hosts,容器启动的时候,读取/tmp/hosts 中宿主机的 IP,写入到容器的 /etc/hosts 中。
具体步骤如下:
宿主机的 /etc/hosts,追加宿主机的 IP,如下:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.216 hostname
启动容器时将宿主机的 /etc/hosts 挂载至容器中的/tmp 中,并指定 hostname 为 dubbo-service-provider
docker run -v /etc/hosts:/tmp/hosts --hostname dubbo-service-provider
容器启动时执行脚本,步骤如下:
1.首先从 /tmp/hosts 中获取宿主机的 IP
hostIP=
grep -F hostname /home/tomcat/hosts |awk '{print $1}'
2.注释掉容器自身的host 配置(由于容器中的 /etc/hosts 权限问题,需采用如下曲折方法)
cp /etc/hosts /etc/hosts.temp
sed -i 's/^[^#].*dubbo-service-provider/#&/' /etc/hosts.temp
cat /etc/hosts.temp >/etc/hosts
3.将宿主机IP追加到容器/etc/hosts
echo "${hostIP} dubbo-service-provider" >> /etc/hosts
以上两种方法可以解决 dubbo docker 化中网络通信问题。如果是在线上,建议使用第一种。不仅仅是 dubbo 服务注册会遇到这种网络通信问题,很多中间件都有同样的问题,比如 rocketMq、kafka 等。下面介绍一下 RocketMQ broker 注册 IP 问题。
RocketMQ broker 注册 IP 问题
问题描述
RocketMQ 装入容器中时,Broker 注册地址将使用容器自身的 IP ,导致 consumer 端不能从 broker 中拿到消费消息。
分析
RocketMQ broker 启动时,使用了默认的配置文件,其中 brokerip1 的值默认是本机IP地址,默认系统自动识别,但是某些多网卡机器会存在识别错误的情况,在 Docker 环境中,都是多网卡。所以该值需要手动配置。
解决办法
生成broker默认配置模版:sh mqbroker -m > broker.p
修改配置文件broker.p
brokerIp1=192.168.1.23
加载修改过的配置文件:nohup sh mqbroker -c broker.p
总结
由于篇幅和时间有限,以上整理的几个建议和踩过的坑,分享给在 Docker 化落地的朋友,希望能够在这条路上少踩点坑。未来不久,我们会计划开源我们的 DevOps 平台,和大家一起探索和进步。
近期热文
《Java 多线程编程核心技术有哪些》
《Python 的 C 扩展开发惯例》
《如何成为一名程序员面霸》
《轻松入门 | 用 WordPress 和主题模板做网站》
《Java 8 Stream API 实用指南》
福利
「阅读原文」看交流实录,你想知道的都在这里