当前位置 博文首页 > 博客园团队:【故障公告】K8s CofigMap 挂载问题引发网站故障

    博客园团队:【故障公告】K8s CofigMap 挂载问题引发网站故障

    作者:博客园团队 时间:2021-01-30 15:25

    这是我们自去年2月23日将生产环境切换到 k8s 之后第一次与这个 CofigMap 挂载问题相遇,到目前我们也不知道为什么会这样?但我们知道这不是百年修得同船渡的缘分,这是我们接下来面临的一个挑战——上船容易开船难。非常抱歉,这次故障给您带来了很大的麻烦,请您谅解!园子的高可用是我们今年重点解决的一个问题,请给我们一些时间。

    今天凌晨我们用阿里云服务器自建的 kubernetes 集群出现突发异常情况,博客站点(blog-web)与博客 web api(blog-api)的 pod 无法正常启动(CrashLoopBackOff)。

    kubectl get pods -l app=blog-web

    NAME                        READY   STATUS             RESTARTS   AGE 
    blog-web-79d579cd94-5t8w4   0/1     CrashLoopBackOff   10         34h 
    blog-web-79d579cd94-gjwct   0/1     CrashLoopBackOff   10         34h 
    blog-web-79d579cd94-hsgfv   1/1     Running            1          32m 
    blog-web-79d579cd94-jj4gt   1/1     Running            0          34h 
    blog-web-79d579cd94-k5rmv   1/1     Running            0          34h 
    blog-web-79d579cd94-mc8hs   1/1     Running            1          24h 
    blog-web-79d579cd94-td9pp   1/1     Running            1          32m 
    blog-web-79d579cd94-trpsn   0/1     CrashLoopBackOff   10         34h 
    blog-web-79d579cd94-w9w7v   1/1     Running            1          109m
    blog-web-79d579cd94-zgrq4   1/1     Running            1          109m
    blog-web-79d579cd94-zm4sh   0/1     CrashLoopBackOff   10         34h 
    blog-web-79d579cd94-zrqln   1/1     Running            0          34h
    

    kubectl get pods -l app=blog-api

    NAME                        READY   STATUS             RESTARTS   AGE   IP             
    blog-api-599bdd9787-9cpn7   0/1     CrashLoopBackOff   78         33h   192.168.139.55 
    blog-api-599bdd9787-zfbdh   0/1     CrashLoopBackOff   76         33h   192.168.132.239
    

    CrashLoopBackOff 的原因是将 CofigMap 挂载到容器 volume 失败。

    blog-web 的错误日志

    failed to start container "blog-web": Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused "rootfs_linux.go:58: mounting \"/var/lib/kubelet/pods/022d72c9-a85f-4c58-bc27-c8ba414c5d5a/volume-subpaths/appsettings/blog-web/0\" to rootfs \"/var/lib/docker/overlay2/f4c8e87344c54969e041f11ef73d1617970c64f05e5415c5d5456517e208a5a0/merged\" at \"/var/lib/docker/overlay2/f4c8e87344c54969e041f11ef73d1617970c64f05e5415c5d5456517e208a5a0/merged/app/appsettings.Production.json\" caused \"no such file or directory\""": unknown

    blog-api 的错误日志

    OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused "rootfs_linux.go:58: mounting \"/var/lib/kubelet/pods/81c1715d-7ac4-469f-afa8-980b87d604b1/volume-subpaths/appsettings/blog-api/0\" to rootfs \"/var/lib/docker/overlay2/9a5dc28604d305180bc9e026db21570b22ff685d0b4db3e3df863f3dfca0f515/merged\" at \"/var/lib/docker/overlay2/9a5dc28604d305180bc9e026db21570b22ff685d0b4db3e3df863f3dfca0f515/merged/app/appsettings.Production.json\" caused \"no such file or directory\""": unknown

    blog-web 部署的 pod replica 比较多,只有部分 pod 宕机,对博客站点的访问影响不大。而 blog-api 只部署了2个 pod replica,全部宕机,本来即使 blog-api 全部宕机也不会造成致命影响,但是。。。

    但是,在博客后台(i-web)的 pod 健康检查(readinessProbe与livenessProbe)中却强依赖了 blog-api(这个地方会改进),在健康检查时会请求 blog-api 进行检查,如果请求失败,i-web 的健康检查也失败,结果 blog-api pod 全部宕机最大的受害者是博客后台, i-web 的 pod 因健康检查失败全部宕机。

    NAME                     READY   STATUS             RESTARTS   AGE  
    i-web-7996f9679b-fk6hk   0/1     CrashLoopBackOff   98         5d10h
    i-web-7996f9679b-gsz2j   0/1     CrashLoopBackOff   107        5d13h
    i-web-7996f9679b-xfj5d   0/1     CrashLoopBackOff   101        5d10h
    

    从而造成从凌晨1:49左右故障发生开始,博客后台一直502,直到7:50左右才恢复。

    发现故障后,我们采取的处理方法是强制删除处于 CrashLoopBackOff 状态的 pod

    kubectl delete pod $1 --force --grace-period 0
    

    旧版 pod 删除后,新 pod 都能正常启动,于是故障恢复。

    这是我们自去年2月23日将生产环境切换到 k8s 之后第一次与这个 CofigMap 挂载问题相遇,到目前我们也不知道为什么会这样?但我们知道这不是百年修得同船渡的缘分,这是我们接下来面临的一个挑战——上船容易,开船难。而且,今年我们正在进行全员登船——将所有部署环境都迁移到k8s上,这个挑战将变得更大,但我们已经下定决心,2013年上云,2021年拥抱云原生。

    非常抱歉,这次故障给您带来了很大的麻烦,请您谅解!园子的高可用是我们今年重点解决的一个问题,请给我们一些时间。

    补充

    deployment 配置文件中对应的 ConfigMap 挂载配置

    volumes:
      - name: appsettings
        configMap:
          name: i-web-appsettings
    containers:
      - name: i-web
        workingDir: /app
        volumeMounts:
          - name: appsettings
            mountPath: /app/appsettings.Production.json
            subPath: appsettings.Production.json
            readOnly: true
    

    改进措施

    根据评论中 @疯狂的小企鹅 建议:

    最好还是不要用subpath的方式将configMap Volume挂载到容器里去

    我们将采用 projected volume 的方式挂载 ConfigMap,对应的配置如下

    volumes:
      - name: appsetttings
        projected:
          sources:
            - configMap:
                name: i-web-appsettings
                items:
                - key:  appsettings.Production.json
                  path: appsettings.Production.json
    containers:
      - name: i-web
        volumeMounts:
          - name: appsetttings
            mountPath: /app/setttings
            readOnly: true
        command: ["/bin/sh"]
        args: ["-c", "ln -s /app/setttings/appsettings.Production.json /app/appsettings.Production.json && sh run.sh"]