最近遇到了一个奇妙的问题,在Kubernetes中的Gitlab runner网络不稳定。
pipeline(docker:bind
)在stage执行docker build
时,从阿里云镜像获取依赖巨慢无比,一次都没有完成过。
从构建中的runner pod 内 ping阿里云镜像的地址mirrors.aliyun.com是Bad address。这种奇怪的现象让我头大了2天。最后发现一切与/etc/resolv.conf
里的ndots
有关……小小的参数,杀伤力怎么那么大呢?
#Dockerfile
FROM daocloud.io/php:7.2-fpm-alpine
# 阿里镜像
# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
apk update ...
起初我怀疑是集群内kube-dns运作出现了问题,准备了一个busybox试验。
# 安装一个旧版busybox,注意新版有bug
kubectl run busybox --image=busybox:1.28.3
# 让其检查各种不通的域名解析如何
kubectl get pod | grep busybox |awk '{system("kubectl exec "$1" nslookup mirrors.aliyun.com")}'
kubectl get pod | grep busybox |awk '{system("kubectl exec "$1" nslookup blog.crazyphper.com")}'
执行结果对比,busybox OK,说明集群本身没有问题。
我在宿主机的/etc/resolv.conf
里增加2枚阿里云内网DNSIP,无效。
我在宿主机的/etc/docker/daemon.json
里增加了DNS,无效。
vim /etc/docker/daemon.json
#增加dnspod的dns IP
"dns": ["119.29.29.29"]
vim /etc/resolv.conf
# 增加了这2行阿里云内网DNS
nameserver 100.100.2.138
nameserver 100.100.2.136
# 更新配置
systemctl daemon-reload
CoreDNS默认会将pod所在宿主机上的/etc/resolv.conf
拿去使用(kube-dns同理),这次怎么没起作用呢?
使用docker exec -ti [runner容器id] --user root sh
以root权限进入容器排查:
ping 自己的博客(成功)
ping 阿里镜像(失败)
我惊了。
我将阿里云内网2枚DNS IP加入到/etc/resolv.conf
中,再次ping 阿里镜像,发现可以了。
问题进入白热化……Gitlab本身应该没有对runner起什么作用,因为Gitlab是用docker-compose安装在服务器上,runner使用helm安装在集群内部,由集群进行编排。
在Gitlab docs中搜索dns、nameserver关键字没有任何结果或者配置项,这也是为何我在Gitlab这个方向上毫无进展的原因。
经过大量的搜索,在下边这些链接中,我找到了原因:
- https://gitlab.com/gitlab-org/charts/gitlab/-/issues/1494
- https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47283
- https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/618
- https://rsmitty.github.io/KubeDNS-Tweaks/
发现了“罪魁祸首”——ndots。runner我设置为hostNetwork:true
将优先请求集群所在域查询。
#runner-value.yaml
hostNetwork: true
在Kubernetes中,可以针对每个Pod设置DNS的策略,dnsPolicy
字段可以指定相应的策略,目前支持的策略如下:
Default
: Pod继承所在宿主机的设置,也就是直接将宿主机的/etc/resolv.conf
内容挂载到容器中。ClusterFirst
: 默认的配置,所有请求会优先在集群所在域查询,如果没有才会转发到上游DNS。ClusterFirstWithHostNet
: 和ClusterFirst
一样,不过是Pod运行在hostNetwork:true
的情况下强制指定的。None
: 1.9版本引入的一个新值,这个配置忽略所有配置,以Pod的dnsConfig
字段为准。
kube-dns默认会给pod内的/etc/resolv.conf
设置ndots:5
。这意味着任何包含少于5个.
(点)的解析请求都将在所有搜索域中循环并试图解析。整个ndots就像一个神秘花园,扰乱了请求。
想要知道一个域名有几个.
使用host domain
命令。
这也是为什么在runner构建的容器中可以ping通我的博客,却ping不到阿里镜像的原因,之一。
这与我安装的runner镜像版本有关image:gitlab/gitlab-runner:alpine-v12.5.0
。alpine轻便,但是它对于遵循FQDN标准要求更加严格。可以使用尾随点.
来绕过/etc/resolv.conf 的ndots
。
我对这个解决办法并不满意,一个Dockerfile会从很多地方获取依赖,有些地方还是隐式的,无法修改。
可以通过安装非alpine版runner来解决,v12.1.0
这个版本亲测可行,安装最新版会出现各种权限问题。
# 编辑runner的chart
vim values.yaml
#image: gitlab/gitlab-runner:alpine-v12.5.0
#使用这一镜像
image: gitlab/gitlab-runner:v12.1.0
#如果仍然不行,可以加上这个配置,让pod直接挂载宿主机的/etc/resolv.conf
#dnsPolicy: "Default"
#保存退出后,更新部署
helm upgrade --namespace kube-system -f gitlab-runner/values.yaml gitlab-runner gitlab/gitlab-runner
问题终于解决。
但是效率仍然不够友好,我希望在10分钟内完成一次CICD。
归根结底,应该是Kubernetes+Gitlab(Docker)+Gitlab Runner(Helm)这套组合对于PHP这类依赖编译项多的程序不够友好。这套组合对于JAVA却非常适合,因为依赖极少,在我之前的JAVA程序为首的集群中运行良好。
之后,我又对GitLab Runner进行了调试与优化,将它改成docker run
的形式运作,每次CICD的时间平均稳定在8-10分钟。
发表回复