<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>

          cgroup 掛載失敗是什么鬼???

          共 6508字,需瀏覽 14分鐘

           ·

          2021-03-22 13:51


          問題

          線上 k8s 集群在進行容器創(chuàng)建時報如下錯誤:

          Failed create pod sandbox: rpc error: code = Unknown desc = failed to start sandbox container for pod “xxx-sf-32c80-0”: Error response from  daemon: cgroups: cannot find cgroup mount destination: unknown

          之前遇到過 cgroup 相關(guān)問題,但是這個問題還是頭一次見,網(wǎng)上搜索了關(guān)鍵字,社區(qū)有類似報錯的 issue,如cgroups: cannot found cgroup mount destination: unknown[1],聯(lián)系最近做過的線上變更及問題,懷疑跟某自定義組件有關(guān),詳細背景參考這篇[2]。

          排查過程

          光看問題云里霧里的,只知道和 cgroup 有關(guān),登陸宿主查看此錯誤是 kubelet 請求 docker 時 docker 返回的,docker 18.06 版本,沒有更詳細的日志了,但是開源的一個好處在于查問題的時候有源碼,這大大降低了查問題的難度,直接去 docker 項目中搜索關(guān)鍵詞,最終發(fā)現(xiàn)是在 containerd 的源碼中,相關(guān)代碼如下

          // PidPath will return the correct cgroup paths for an existing process running inside a cgroup
          // This is commonly used for the Load function to restore an existing container
          func PidPath(pid int) Path {
           p := fmt.Sprintf("/proc/%d/cgroup", pid)
           paths, err := parseCgroupFile(p)
           if err != nil {
            return errorPath(errors.Wrapf(err, "parse cgroup file %s", p))
           }
           return existingPath(paths, "")
          }

          func existingPath(paths map[string]string, suffix string) Path {
           // localize the paths based on the root mount dest for nested cgroups
           for n, p := range paths {
            dest, err := getCgroupDestination(string(n))
            if err != nil {
             return errorPath(err)
            }
            rel, err := filepath.Rel(dest, p)
            if err != nil {
             return errorPath(err)
            }
            if rel == "." {
             rel = dest
            }
            paths[n] = filepath.Join("/", rel)
           }
           return func(name Name) (string, error) {
            root, ok := paths[string(name)]
            if !ok {
             if root, ok = paths[fmt.Sprintf("name=%s", name)]; !ok {
              return "", fmt.Errorf("unable to find %q in controller set", name)
             }
            }
            if suffix != "" {
             return filepath.Join(root, suffix), nil
            }
            return root, nil
           }
          }

          func getCgroupDestination(subsystem string) (string, error) {
           f, err := os.Open("/proc/self/mountinfo")
           if err != nil {
            return "", err
           }
           defer f.Close()
           s := bufio.NewScanner(f)
           for s.Scan() {
            fields := strings.Split(s.Text(), " ")
            if len(fields) < 10 {
             // broken mountinfo?
             continue
            }
            if fields[len(fields)-3] != "cgroup" {
             continue
            }
            for _, opt := range strings.Split(fields[len(fields)-1], ",") {
             if opt == subsystem {
              return fields[3], nil
             }
            }
           }
           if err := s.Err(); err != nil {
            return "", err
           }
           return "", ErrNoCgroupMountDestination
          }

          func parseCgroupFile(path string) (map[string]string, error) {
           f, err := os.Open(path)
           if err != nil {
            return nil, err
           }
           defer f.Close()
           return parseCgroupFromReader(f)
          }

          func parseCgroupFromReader(r io.Reader) (map[string]string, error) {
           var (
            cgroups = make(map[string]string)
            s       = bufio.NewScanner(r)
           )
           for s.Scan() {
            if err := s.Err(); err != nil {
             return nil, err
            }
            var (
             text  = s.Text()
             parts = strings.SplitN(text, ":"3)
            )
            if len(parts) < 3 {
             return nil, fmt.Errorf("invalid cgroup entry: %q", text)
            }
            for _, subs := range strings.Split(parts[1], ",") {
             if subs != "" {
              cgroups[subs] = parts[2]
             }
            }
           }
           return cgroups, nil
          }

          邏輯比較清晰,先從/proc/id/cgroup 中解析得到所有的 subsystem,對應(yīng)上面 parseCgroupFromReader 函數(shù),/proc/id/cgroup 內(nèi)容如下

          先按冒號分隔每行字符串,然后取第 2 列,再根據(jù)逗號分隔得到所有的子系統(tǒng),最終返回所有子系統(tǒng)。然后調(diào)用 existingPath 檢查是否所有子系統(tǒng)都存在,內(nèi)部又調(diào)用 getCgroupDestination,最終的報錯就是在這個函數(shù)里報出來的。

          getCgroupDestination 的邏輯是讀取/proc/id/mountinfo 信息,判斷是否傳入的子系統(tǒng)存在

          先根據(jù)空格分隔,找到所有 cgroup 類型的目錄,然后再根據(jù)逗號分隔遍歷所有的子系統(tǒng)是否是傳入的子系統(tǒng)。找不到的話就會報錯,但是不得不吐槽的就是這個報錯報的太沒有誠意了,要是直接把找不到的子系統(tǒng)報出來,問題會直觀很多。

          結(jié)論

          到此可以明白是 agent 隔離程序先 mount 了自定義目錄 cpu_mirror 到 cgroup 目錄下,然后影響到了 java 程序去獲取正確的核數(shù),為了修復(fù)特意執(zhí)行了 umount 的操作,但是 umount 之后/proc/id/cgroup 還是存在 cpu_mirror 相關(guān)信息而/proc/id/mountinfo 中已經(jīng)不存在了,在容器重新創(chuàng)建的時候進行檢查進而報錯。

          對比線上其他 docker 版本,比如 1.13.1 中就沒有此問題,因為 1.13.1 用的 containerd 中并沒有上面提到的檢驗邏輯

          通過這個問題也暴露出來我們在測試、灰度過程中的問題,由于線上環(huán)境復(fù)雜,系統(tǒng)版本眾多、組件版本也不統(tǒng)一,在上線一個功能或者執(zhí)行線上操作的時候,測試用例需要充分覆蓋所有場景,灰度時也需要所有類型的機器至少都覆蓋到了之后才可以放量繼續(xù)靠擴大灰度范圍,否則很容易出現(xiàn)類似的問題。

          參考資料

          [1]

          cgroups: cannot found cgroup mount destination: unknown: https://github.com/docker/for-linux/issues/219

          [2]

          這篇: https://www.likakuli.com/posts/docker-java-cpu


          原文鏈接:https://www.likakuli.com/posts/docker-cgroup-unknown/



          你可能還喜歡

          點擊下方圖片即可閱讀

          GitHub 又又又多了一個新主題 —— Dimmed Dark 主題!

          云原生是一種信仰 ??


          關(guān)注公眾號

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



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


          發(fā)現(xiàn)朋友圈變“安靜”了嗎?

          瀏覽 131
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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 国产在线播放福利 | 国产日批v久久 | 精品国产久久久久 | 国产黄色免费在线观看 | 欧美激情亚洲五码 |