本篇主要介绍如何运维 DevOps 流水线,怎么解决一些常见的问题。问题主要分为两大类,一类是 Kubernetes 相关的,具有一定通用性;另一类是与业务相关,需要对领域有所了解,解决问题时才能事半功倍。文档内容,会不断滚动更新。
1. Kubernetes 问题排查
1.1 基本的创建流程
如上图所示,是用户创建一个 Deployment 的简单流程。主要分为以下步骤:
了解基本的创建流程,有利于排查各种可能的故障。故障可以理解为集群生命周期中的一个状态,而创建是整个生命周期的起点。同时,重置、重启是非常快速地解决问题的方法,都涉及创建。
1.2 解决集群故障的思路
如上图所示,是我的集群故障修复思路。主要分为以下步骤:
kubectl get
检查 node、pod 等资源是否符合预期kubectl describe
查看 Kubernetes 中的事件信息,包括 kube-sheduler 的调度、拉取镜像、启动是否成功等。通常能解决大部分的问题。kubectl logs
查看负载的日志。当 pod 处于 running,但是又无法正常提供服务时,logs 信息能够给出有用的提示。有时无法查看 pod 中容器的日志,那么需要去 pod 所在的节点查看 docker 的日志。journal
通过 -u
参数指定服务,通过 -f
查看滚动的最新日志,也十分有用。1.3 必要的检查项
这里列举一些必要的检查项,可以辅助排查问题。
- node 负载
node 负载过高时,有可能导致节点 NotReady。
- node 磁盘
磁盘包括两部分,大小和 inode。
- swap、firewall
安装集群时的配置,在重启机器之后,可能会失效。
- 跨 node 的 pod 通信
跨节点通信异常时,会导致服务无法访问。
- node 节点上 kubelet、docker 的状态
systemctl status kubelet
、systemctl status docker
查看组件的状态。
- 检查 kubelet 、docker 的配置
ps aux|grep kubelet
、docker info
查看相关配置是否符合预期。
- 查看存储 CSI 存储插件日志
通常 StorageClass 是通过 CSI 插件提供存储服务的,可以查看相关日志,检查是否有异常输出。
- 检查 ip 转发功能
执行命令 cat /proc/sys/net/ipv4/ip_forward
,如果输出 0 则表示未开启。ip 转发允许将一个端口的包转发到另外一个端口。未开启时,会导致网络访问失败,tcpdump 中发送了大量 syn 数据包,但是没有 ack 。
- 检查 Pod CIDR 与主机网段是否冲突
可以先查看 Pod 的 ip,检查其与主机、VPC 的 ip 是否有重叠。如果发生冲突,将会导致服务不可访问,提示 No route to host
类似的错误,需要修改 CIDR 。
2. 安装问题
如上图所示,是 installer 在执行安装 DevOps 时的依赖关系。DevOps 主要有两个核心组件 S2I 和 Jenkins 。S2I 依赖于 minio 存储文件,Jenkins 依赖于 uc 提供插件下载、依赖 openldap 打通账户体系。安装时,比较常见的是 minio 安装失败,导致 DevOps 无法继续安装。而 minio 安装失败通常又是存储和网络导致。
3. Jenkins 运维问题
3.1 修改默认配置导致异常
DevOps 允许用户自定义配置,但 Jenkins /configureSecurity/
页面中鉴权和 CSRF Protection 不要修改。修改上图,红色框中的内容,会导致流水线无法使用。
3.2 大量多分支流水线占满磁盘空间
多分支流水线扫描时,会将仓库拉取到 /var/jenkins_home/cache
中,查找 jenkinsfile 文件。如果大量实践多分支流水线扫描,请将 Jenkins 的 pv 设置大一些,50 GB 以上。
3.3 Lightweight 问题导致并发流水线冲突
具体可以参考文档: Jenkins 中 Lightweight 拉取代码问题分析
3.4 多节点集群,无法创建流水线
节点之间通信问题,可以将 ks-controller-manager 和 ks-jenkins 调度到一个节点进行验证。如果能够创建成功,那么 Pod 的跨节点通信有问题。
3.5 流水线并发数量少
调整 ks-jenkins 的 cpu 和 memory 限制,还有 Xms、Xmx 值。具体调整可以参考文档: Kubernetes 动态创建 Jenkins Agent 压力测试
3.6 流水线部署报错
Jenkins 的 Kubernetes Deploy 插件提示可读性不是十分友好,可能的原因有:
- apiVersion 版本不支持
- namespace 没有提前创建
- yaml 本身有问题
- 凭证有问题
- 创建的凭证和使用的凭证 ID 不一致
可以在节点上直接执行 kubectl apply
进行验证。怀疑凭证有问题时,可以直接使用 admin 的凭证,进行确认。如果是 EKS 集群,请参考 https://github.com/kubesphere/community/issues/165 单独生成凭证。EKS 上使用 DevOps 进行部署,需要的是 token 类型的凭证,证书类型的凭证没有足够的权限。简单点,可以直接使用 kubectl -n kubesphere-system get secret kubesphere-token-xxx -o jsonpath={.data.token} | base64 -d
拿到 token,替换至 kubeconfig 。
3.7 流水线一直 Pending,Kubernetes 无法创建 Pod
如果流水线无法执行,并且 kubectl -n kubesphere-devops-system get pod
下没有发现为运行流水线新创建的 Pod,那么很有可能是受限于准入控制。kube-apiserver 提供了在创建负载时,修改负载相关信息的能力。比如,Istio 通过 webhook 在创建 Pod 时,向其中注入 Sidecar 进行微服务治理。但如果 Istio 组件发生了异常,那么也会导致 Pod 一直等待注入,从而无法创建 Pod 的现象。解决办法是,给运行的命名空间加上豁免注入的标签,比如,kubectl label namespace kubesphere-devops-system istio-injection=disabled
。
3.8 变量引用不生效或者流水线引用变量报错
${env.<ParameterName>}
、${params.<ParameterName>}
、${<ParameterName>}
都可以引用变量,也可以用于 Stage 之间传值。在引用变量时,常见的错误是没区分单双引号。单引号并不替换变量,双引号才会替换变量,这与 Shell 一致。另外需要注意,作用域。如果提示 groovy.lang.MissingPropertyException: No such property:
,而你又十分确认定义了这个变量,那么很有可能是作用域的问题。只需要带上 env
、params
再试一次就行。
3.9 图形化编辑时,报错 至少需要一个嵌套步骤
API 提示报错 instance failed to match at least one schema
。流水线部分,前端页面图形化与后端数据模型是通过 pipeline-model-api 插件相互转换的。具体接口是 /tojson
和 /tojenkinsfile
,前端编辑的是 json ,后端需要的是 jenkinsfile 。例如,下面这个示例:
|
|
将被转换为如下内容:
|
|
json 格式的流水线对前端十分友好,scheme 非常简单,极大降低实现图形化编辑的难度。转换失败时,页面会展示错误提示。如果返回错误提示为空,那么页面会展示 当前 Jenkinsfile 不是标准的声明式 Jenkinsfile,无法进行图形化显示
。前端只覆盖了常见的几种情况,提示不是十分准确,例如,当 defaultValue 为空字符串时,提示 至少需要一个嵌套步骤
,不知所云。最好能够直接参考 /tojson
接口的返回进行处理。
3.10 构建镜像时,无法访问外部服务,报错 timeout
在 Jenkinsfile 中执行 docker build
命令时,由于是 Docker in Docker 网络,使用的是 Node 节点上的 Docker Daemon 进行构建。看着是动态创建的 Pod 无法访问外网,其实是 Node 节点无法访问指定外部服务。这很有可能是,对网络要求严格的运行环境,禁止了主机访问外网所致。使用 iptables 命令,添加白名单即可。
3.11 升级时,PVC 报错
常见的两种报错提示是 The PersistentVolumeClaim "pvc1" is invalid: spec: Forbidden: is immutable after creation except resources.requests for bound claims
和 Error: UPGRADE FAILED: cannot patch "mysql" with kind PersistentVolumeClaim: persistentvolumeclaims "mysql" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize
。报错原因是 StorageClass 不支持动态扩容,需要保持升级前后 PVC 容量一致。在升级之前,可以通过 describe 命令检查 StorageClass 是否支持动态扩容。如果 AllowVolumeExpansion 被设置,则代表支持;否则不支持。
3.12 安装不兼容的插件之后,Jenkins 无法启动
处理办法是进入 Jenkins 运行的 Pod,删掉不兼容的插件。首先 exec 进入 Pod
|
|
然后进入 /var/jenkins_home/plugins
目录,删掉 Jenkins Pod 日志中提到的引发错误的插件。如果你安装了大量的插件,这里有一个技巧是按照日期删除。有时候,插件之间具有复杂依赖,还有一个救急的方式是直接清空 /var/jenkins_home/plugins
,Jenkins 会根据 /var/jenkins_home/plugins.txt
从 UC 中下载插件,恢复至初始状态。
3.13 修改鉴权策略之后,无法登陆
处理办法是进入 Jenkins 运行的 Pod,将 useSecurity 改为 false,登陆之后改回原来的值。useSecurity 在 /var/jenkins_home/config.xml
中进行配置。
3.14 Jenkins 找不到用户,LDAP 报错(重置密码)
提示信息类似:
|
|
Jenkins 用户来源于 OpenLdap 组件,报错信息的含义是在 OpenLdap 组件中找不到 admin 用户。解决这个问题,需要检查两个地方:OpenLdap 组件是否异常、admin 用户是否在 OpenLdap 中。然后,修复组件,重新创建 admin 用户。重建方法从代码 https://github.com/kubesphere/kubesphere/blob/release-3.0/pkg/controller/user/user_controller.go#L537 中可以发现,需要编辑 admin 用户的加密注解。执行命令 kubectl patch users admin --type merge --patch '{"spec":{"password":"[email protected]"}, "metadata":{"annotations":{"iam.kubesphere.io/password-encrypted":"false"}}}'
将 admin 用户登陆密码重置为默认值,触发同步逻辑。此条命令也可以用于重置密码。
3.15 流水线创建之后,在页面查看不到
在 3.0.0 中,采用 CRD 对 DevOps 工程和 Pipeline 进行管理,最终同步到 Jenkins 中。流水线创建之后,在页面无法查看到,是没有完成同步。没有完成同步的可能性很多,ks-controller-manager 中的日志将会帮到你。可能是节点之间的网络通信问题,也有可能是 Jenkins 服务发生了异常,需要根据错误日志排查。
3.16 找不到 Docker 命令
首先得了解 Jenkins 创建 Agent 运行流水线的过程:
如果执行流水线时,提示找不到 Docker 命令,那么可能是使用了 jnlp 容器。请检查步骤 4, 选中一个包含 docker 命令,并挂载了 docker.socket 的容器。