<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Pod Terminating 續(xù)集:最終章

          共 16428字,需瀏覽 33分鐘

           ·

          2020-11-29 19:18

          f7feb14e1cc6915474541d5afb60aa40.webp

          更多奇技淫巧歡迎訂閱博客:https://fuckcloudnative.io

          前言

          承接?上文,近期我們排查彈性云線上幾起故障時,故障由多個因素共同引起,列舉如下:

          • 彈性云在逐步灰度升級 docker 版本至 18.06.3-ce
          • 由于歷史原因,彈性云啟用了 docker 服務的 systemd 配置選項 MountFlags=slave
          • 為了避免 dockerd 重啟引起業(yè)務容器重建,彈性云啟用了 live-restore=true 配置,docker 服務發(fā)生重啟,dockerd 與 shim 進程 mnt ns 不一致

          在以上三個因素合力作用下,線上容器在重建與漂移場景下,出現刪除失敗的事件。

          同樣,文章最后也給出了兩種解決方案:

          • 長痛:修改代碼,忽略錯誤
          • 短痛:修改配置,一勞永逸

          作為優(yōu)秀的社會主義接班人,我們當然選擇短痛了!依據官方提示 MountFlags=slavelive-restore=true 不能協同工作,那么我們只需關閉二者之一就能解決問題。

          與我們而言,docker 提供的 live-restore 能力是一個很關鍵的特性。docker 重啟的原因多種多樣,既可能是人為調試因素,也可能是機器的非預期行為,當 docker 重啟后,我們并不希望用戶的容器也發(fā)生重建。似乎關閉 MountFlags=slave 成了我們唯一的選擇。

          等等,回想一下docker device busy 問題解決方案[1],別人正是為了避免 docker 掛載泄漏而引起刪除容器失敗才開啟的這個特性。

          但是,這個 17 年的結論真的還具有普適性嗎?是與不是,我們親自驗證即可。

          1. 對比實驗

          為了驗證在關閉 MountFlags=slave 選項后,docker 是否存在掛載點泄漏的問題,我們分別挑選了一臺 1.13.118.06.3-ce 的宿主進行實驗。實驗步驟正如docker device busy 問題解決方案[2]所提示,在驗證之前,環(huán)境準備如下:

          • 刪除 docker 服務的 systemd 配置項 MountFlags=slave
          • 挑選啟用 systemd 配置項 PrivateTmp=true 的任意服務,本文以 httpd 為例

          下面開始驗證:

          ////// docker 1.13.1 驗證步驟及結果
          // 1. 重新加載配置
          [stupig@hostname2 ~]$ sudo systemctl daemon-reload


          // 2. 重啟docker
          [stupig@hostname2 ~]$ sudo systemctl restart docker


          // 3. 創(chuàng)建容器
          [stupig@hostname2 ~]$ sudo docker run -d nginx
          c89c2aeff6e3e6414dfc7f448b4a560b4aac96d69a82ba021b78ee576bf6771c


          // 4. 重啟httpd
          [stupig@hostname2 ~]$ sudo systemctl restart httpd


          // 5. 停止容器
          [stupig@hostname2 ~]$ sudo docker stop c89c2aeff6e3e6414dfc7f448b4a560b4aac96d69a82ba021b78ee576bf6771c
          c89c2aeff6e3e6414dfc7f448b4a560b4aac96d69a82ba021b78ee576bf6771c


          // 6. 清理容器
          [stupig@hostname2 ~]$ sudo docker rm c89c2aeff6e3e6414dfc7f448b4a560b4aac96d69a82ba021b78ee576bf6771c
          Error response from daemon: Driver overlay2 failed to remove root filesystem c89c2aeff6e3e6414dfc7f448b4a560b4aac96d69a82ba021b78ee576bf6771c: remove /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged: device or resource busy


          // 7. 定位掛載點
          [stupig@hostname2 ~]$ grep -rwn /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged /proc/*/mountinfo
          /proc/19973/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          /proc/19974/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          /proc/19975/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          /proc/19976/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          /proc/19977/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          /proc/19978/mountinfo:40:231 227 0:40 / /home/docker_rt/overlay2/6c77cfb6c0c4b1e809c47af3c5ff6a4732a783cc14ff53270a7709c837c96346/merged rw,relatime shared:119 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX


          // 8. 定位目標進程
          [stupig@hostname2 ~]$ ps -ef|egrep '19973|19974|19975|19976|19977|19978'
          root     19973     1  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
          apache   19974 19973  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
          apache   19975 19973  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
          apache   19976 19973  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
          apache   19977 19973  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
          apache   19978 19973  0 15:13 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

          docker 1.13.1 版本的實驗結果正如網文所料,容器讀寫層掛載點出現了泄漏,并且 docker rm 無法清理該容器(注意 docker rm -f 仍然可以清理,原因參考上文)。

          彈性云啟用 docker 配置 MountFlags=slave 也是為了避免該問題發(fā)生。

          那么現在壓力轉移到 docker 18.06.3-ce 這邊來了,新版本是否仍然存在這個問題呢?

          ////// docker 18.06.3-ce 驗證步驟及結果
          [stupig@hostname ~]$ sudo systemctl daemon-reload


          [stupig@hostname ~]$ sudo systemctl restart docker


          [stupig@hostname ~]$ sudo docker run -d nginx
          718114321d67a817c1498e530b943c2514ed4200f2d0d138880f8c345df7048f


          [stupig@hostname ~]$ sudo systemctl restart httpd


          [stupig@hostname ~]$ sudo docker stop 718114321d67a817c1498e530b943c2514ed4200f2d0d138880f8c345df7048f
          718114321d67a817c1498e530b943c2514ed4200f2d0d138880f8c345df7048f


          [stupig@hostname ~]$ sudo docker rm 718114321d67a817c1498e530b943c2514ed4200f2d0d138880f8c345df7048f
          718114321d67a817c1498e530b943c2514ed4200f2d0d138880f8c345df7048f

          針對 docker 18.06.3-ce 的實驗非常絲滑順暢,不存在任何問題?;仡櫳衔闹R點,當容器讀寫層掛載點出現泄漏后,docker 18.06.3-ce 清理容器必定失敗,而現在的結果卻成功了,說明容器讀寫層掛載點沒有泄漏。

          這簡直就是黎明的曙光。

          2. 蛛絲馬跡

          上一節(jié)對比實驗的結果給了我們莫大的鼓勵,本節(jié)我們探索兩個版本的 docker 的表現差異,以期定位癥結所在。

          既然核心問題在于掛載點是否被泄漏,那么我們就以掛載點為切入點,深入分析兩個版本 docker 的差異性。我們對比在兩個環(huán)境下執(zhí)行完 步驟4 后,不同進程內的掛載詳情,結果如下:

          // docker 1.13.1
          [stupig@hostname2 ~]$ sudo docker run -d nginx
          0fe8d412f99a53229ea0df3ec44c93496e150a39f724ea304adb7f924910d61b

          [stupig@hostname2 ~]$ sudo docker inspect -f {{.GraphDriver.Data.MergedDir}} 0fe8d412f99a53229ea0df3ec44c93496e150a39f724ea304adb7f924910d61b
          /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged

          // 共享命名空間
          [stupig@hostname2 ~]$ grep -rw /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged /proc/$$/mountinfo
          223 1143 0:40 / /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged rw,relatime - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX


          [stupig@hostname2 ~]$ sudo systemctl restart httpd

          [stupig@hostname2 ps -ef|grep httpd|head -n 1
          root     16715     1  2 16:09 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

          // httpd進程命名空間
          [stupig@hostname2 ~]$ grep -rw /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged /proc/16715/mountinfo
          257 235 0:40 / /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged rw,relatime shared:123 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX
          // docker 18.06.3-ce
          [stupig@hostname ~]$ sudo docker run -d nginx
          ce75d4fdb6df6d13a7bf4270f71b3752ee2d3849df1f64d5d5d19a478ac7db8d

          [stupig@hostname ~]$ sudo docker inspect -f {{.GraphDriver.Data.MergedDir}} ce75d4fdb6df6d13a7bf4270f71b3752ee2d3849df1f64d5d5d19a478ac7db8d
          /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged

          // 共享命名空間
          [stupig@hostname ~]$ grep -rw /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged /proc/$$/mountinfo
          218 43 0:105 / /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged rw,relatime shared:109 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX

          [stupig@hostname ~]$ sudo systemctl restart httpd

          [stupig@hostname ~]$ ps -ef|grep httpd|head -n 1
          root      63694      1  0 16:14 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND

          // httpd進程命名空間
          [stupig@hostname ~]$ grep -rw /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged /proc/63694/mountinfo
          435 376 0:105 / /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged rw,relatime shared:122 master:109 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX

          咋一看,好像沒啥區(qū)別啊!睜大你們的火眼金睛,是否發(fā)現差異所在了?

          如果細心對比,還是很容易分辨出差異所在的:

          • 共享命名空間中
            • docker 18.06.3-ce 版本創(chuàng)建的掛載點是 shared 的
            • 而 docker 1.13.1 版本創(chuàng)建的掛載點是 private 的
          • httpd 進程命名空間中
            • docker 18.06.3-ce 創(chuàng)建的掛載點仍然是共享的,并且接收共享組 109 傳遞的掛載與卸載事件,注意:共享組 109 正好就是共享命名空間中對應的掛載點
            • 而 docker 1.13.1 版本創(chuàng)建的掛載點雖然也是共享的,但是卻與共享命名空間中對應的掛載點沒有關聯關系

          可能會有用戶不禁要問:怎么分辨掛載點是什么類型?以及不同類型掛載點的傳遞屬性呢?請參閱:mount 命名空間說明文檔[3]。

          問題已然明了,由于兩個版本 docker 所創(chuàng)建的容器讀寫層掛載點具備不同的屬性,導致它們之間的行為差異。

          3. 刨根問底

          相信大家如果理解了上一節(jié)的內容,就已經了解了問題的本質。本節(jié)我們繼續(xù)探索問題的根因。

          為什么兩個版本的 docker 行為表現不一致?不外乎兩個主要原因:

          1. docker 處理邏輯發(fā)生變動
          2. 宿主環(huán)境不一致,主要指內核

          第二個因素很好排除,我們對比了兩個測試環(huán)境的宿主內核版本,結果是一致的。所以,基本還是因 docker 代碼升級而產生的行為不一致。理論上,我們只需逐個分析 docker 1.13.1 與 docker 18.06.3-ce 兩個版本間的所有提交記錄,就一定能夠定位到關鍵提交信息,大力總是會出現奇跡。

          但是,我們還是希望能夠從現場中發(fā)現有用信息,縮小檢索范圍。

          仍然從掛載點切入,既然兩個版本的 docker 所創(chuàng)建的掛載點在共享命名空間中就已經出現差異,我們順藤摸瓜,找找容器讀寫層掛載點鏈路上是否存在差異:

          // docker 1.13.1
          // 本掛載點
          [stupig@hostname2 ~]$ grep -rw /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged /proc/$$/mountinfo
          223 1143 0:40 / /home/docker_rt/overlay2/4e09fa6803feab9d96fe72a44fb83d757c1788812ff60071ac2e62a5cf14cd97/merged rw,relatime - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX


          // 定位本掛載點的父掛載點
          [stupig@hostname2 ~]$ grep -rw 1143 /proc/$$/mountinfo
          1143 44 8:4 /docker_rt/overlay2 /home/docker_rt/overlay2 rw,relatime - xfs /dev/sda4 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,prjquota


          // 繼續(xù)定位祖父掛載點
          [stupig@hostname2 ~]$ grep -rw 44 /proc/$$/mountinfo
          44 39 8:4 / /home rw,relatime shared:28 - xfs /dev/sda4 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,prjquota


          // 繼續(xù)往上
          [stupig@hostname2 ~]$ grep -rw 39 /proc/$$/mountinfo
          39 1 8:3 / / rw,relatime shared:1 - ext4 /dev/sda3 rw,stripe=64,data=ordered


          // docker 18.06.3-ce
          // 本掛載點
          [stupig@hostname ~]$ grep -rw /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged /proc/$$/mountinfo
          218 43 0:105 / /home/docker_rt/overlay2/a9823ed6b3c5a752eaa92072ff9d91dbe1467ceece3eedf613bf6ffaa5183b76/merged rw,relatime shared:109 - overlay overlay rw,lowerdir=XXX,upperdir=XXX,workdir=XXX


          // 定位本掛在點的父掛載點
          [stupig@hostname ~]$ grep -rw 43 /proc/$$/mountinfo
          43 61 8:17 / /home rw,noatime shared:29 - xfs /dev/sdb1 rw,attr2,nobarrier,inode64,prjquota


          // 繼續(xù)定位祖父掛載點
          [stupig@hostname ~]$ grep -rw 61 /proc/$$/mountinfo
          61 1 8:3 / / rw,relatime shared:1 - ext4 /dev/sda3 rw,data=ordered

          兩個版本的 docker 所創(chuàng)建的容器讀寫層掛載點鏈路上差異還是非常明顯的:

          • 容器讀寫層掛載點的父級掛載點不同
            • docker 18.06.3-ce 創(chuàng)建的容器讀寫層掛載點的父級掛載點是 /home/ ,并且是共享的
            • docker 1.13.1 創(chuàng)建的容器讀寫層掛載點的父級掛載點是 /home/docker_rt/overlay2 ,并且是私有的

          這里補充一個背景,彈性云機器在初始化階段,會將 /home 初始化為 xfs 文件系統(tǒng)類型,因此所有宿主上 /home 掛載點都具備相同屬性。

          那么,問題基本就是由 docker 1.13.1 中多出的一層掛載層 /home/docker_rt/overlay2 引起。

          如何驗證這個猜想呢?現在,其實我們已經具備了檢索代碼的關鍵目標,docker 1.13.1 會設置容器鏡像層根目錄的傳遞屬性。拿著這個先驗知識,我們直接查代碼,檢索過程基本沒費什么功夫,直接展示相關代碼:

          // filepath: daemon/graphdriver/overlay2/overlay.go
          func init() {
             graphdriver.Register(driverName, Init)
          }

          func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
             if err := mount.MakePrivate(home); err != nil {
                return nil, err
             }

             supportsDType, err := fsutils.SupportsDType(home)
             if err != nil {
                return nil, err
             }
             if !supportsDType {
                // not a fatal error until v1.16 (#27443)
                logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs))
             }

             d := &Driver{
                home:          home,
                uidMaps:       uidMaps,
                gidMaps:       gidMaps,
                ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
                supportsDType: supportsDType,
             }

             d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)

             if backingFs == "xfs" {
                // Try to enable project quota support over xfs.
                if d.quotaCtl, err = quota.NewControl(home); err == nil {
                   projectQuotaSupported = true
                }
             }

             return d, nil
          }

          很明顯,問題就出在 mount.MakePrivate 函數調用上。

          官方將 GraphDriver 根目錄設置為 Private,本意是為了避免容器讀寫層掛載點泄漏。那為什么在高版本中去掉了這個邏輯呢?顯然官方也意識到這么做并不能實現期望的目的,官方也在修復[4]中給出了詳細說明。

          實際上,不設置 GraphDriver 根目錄的傳播屬性,反而能避免絕大多數掛載點泄漏的問題。。。

          4. 結語

          現在,我們已經了解了問題的來龍去脈,我們總結下問題的解決方案:

          • 針對 1.13.1 版本 docker,存量宿主較多,我們可以忽略 device or resource busy 問題,基本也不會給線上服務帶來什么影響

          • 針對 18.06.3-ce 版本 docker,存量宿主較少,我們刪除 docker 服務的 systemd 配置項 MountFlags,通過故障自愈解決 docker 卡在問題

            • 在容器創(chuàng)建后,卸載容器讀寫層掛載,如果不影響容器內文件訪問。那么可以直接卸載所有掛載點,修改 docker 配置,并重啟 docker 服務【本方案尚未驗證】
          • 針對增量宿主,全部刪除 docker 服務的 systemd 配置項 MountFlags

          參考資料

          [1]

          docker device busy 問題解決方案: https://blog.terminus.io/docker-device-is-busy/

          [2]

          docker device busy 問題解決方案: https://blog.terminus.io/docker-device-is-busy/

          [3]

          mount 命名空間說明文檔: https://man7.org/linux/man-pages/man7/mount_namespaces.7.html

          [4]

          修復: https://github.com/moby/moby/pull/36047


          原文鏈接:https://www.likakuli.com/posts/docker-pod-terminating2/


          24925c6085755af8b26bdf782c601636.webp

          27fd732efec26cb8f752fcf6b6007021.webp

          你可能還喜歡

          點擊下方圖片即可閱讀

          24ccf5f244abce3230639321bbac81ad.webp

          Docker 限速不用怕,因為我有 Grafana

          786b6c3e3065e69d7de29fbd2b14cd67.webp

          云原生是一種信仰 ?


          f2e622abf4b923d07a8a6e7a6ea34b90.webp


          碼關注公眾號

          后臺回復?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!



          fc8a8390d8e8af2029a5223869dd136f.webp

          7efdfa46ff981cce609bc345eb8bdf82.webp

          點擊 "閱讀原文" 獲取更好的閱讀體驗!

          ??給個「在看」,是對我最大的支持??
          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩精品三区 | AV性淫| 成人一区二区三区 | 在线观看视频免费无码免费视频 | 久爱精品视频 |