解决国产系统 Docker 拉取大镜像卡顿之谜

今天解决了客户 arm64 机器上 docker pull 大镜像卡住的问题。

由来

同事让我帮忙解决客户现场 Docker 镜像无法拉取的问题,故障如下会一直卡住:

  1.  
    $ docker pull xxx:5000/xxxx
  2.  
    xxx: Pulling from xxx/xxxxxx
  3.  
    7c0b344a74c2: Extracting [>                                                  ]  294.9kB/26.66MB
  4.  
    7c0b344a74c2: Download complete
  5.  
    e53ed7fd3110: Download complete
  6.  
    d2cae797bc79: Download complete
  7.  
    ec3ddc176f08: Download complete
  8.  
    2969517e196e: Download complete
  9.  
    097fa64722e8: Download complete
  10.  
    1dde4ca01a5a: Download complete

离线文件 load -i 后,打上 tag 推送到镜像仓库,然后本地删除这个镜像,然后拉取还是像上面这样卡住,部分小镜像拉取没问题,所以不可能是 docker data-root 的挂载 option 影响。环境信息如下:

  1.  
    $ docker info
  2.  
    ...
  3.  
     Server Version: 19.03.15
  4.  
     Storage Driver: overlay2
  5.  
      Backing Filesystem: extfs
  6.  
      Supports d_type: true
  7.  
      Native Overlay Diff: true
  8.  
     Logging Driver: json-file
  9.  
     Cgroup Driver: cgroupfs
  10.  
     Plugins:
  11.  
      Volume: local
  12.  
      Network: bridge host ipvlan macvlan null overlay
  13.  
      Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
  14.  
     Swarm: inactive
  15.  
     Runtimes: runc
  16.  
     Default Runtime: runc
  17.  
     Init Binary: docker-init
  18.  
     containerd version: ea765aba0d05254012b0b9e595e995c09186427f
  19.  
     runc version: v1.0.0-0-g84113eef
  20.  
     init version: fec3683
  21.  
     Security Options:
  22.  
      seccomp
  23.  
       Profile: default
  24.  
     Kernel Version: 4.19.90-2211.5.0.0178.22.uel20.aarch64
  25.  
     Operating System: UnionTech OS Server 20
  26.  
     OSType: linux
  27.  
     Architecture: aarch64
  28.  
     CPUs: 24
  29.  
     Total Memory: 94.56GiB
  30.  
     Name: host-xxxx
  31.  
     ID: RTQS:5TXE:5T3S:YW7X:OHPK:FZ7D:7EHD:DH5Z:JNBV:FVXS:24FA:EIVS
  32.  
     Docker Root Dir: /data/kube/docker
  33.  
     Debug Mode: true
  34.  
      File Descriptors: 29
  35.  
      Goroutines: 46
  36.  
      System Time: 2023-04-12T16:10:25.33362426+08:00
  37.  
    $ uname -a
  38.  
    Linux host-x 4.19.90-2211.5.0.0178.22.uel20.aarch64 #1 SMP Thu Nov 24 10:33:07 CST 2022 aarch64 aarch64 aarch64 GNU/Linux
  39.  
    $ cat /etc/os-release
  40.  
    PRETTY_NAME="UnionTech OS Server 20"
  41.  
    NAME="UnionTech OS Server 20"
  42.  
    VERSION_ID="20"
  43.  
    VERSION="20"
  44.  
    ID=uos
  45.  
    HOME_URL="https://www.chinauos.com/"
  46.  
    BUG_REPORT_URL="https://bbs.chinauos.com/"
  47.  
    VERSION_CODENAME=fuyu
  48.  
    PLATFORM_ID="platform:uel20"

排查

卡住的过程中,另外开一个 ssh top 发现进程 unpigz 占用较高,利用其 pid 查看了一些信息:

  1.  
    $ pstree -sp 1170083
  2.  
    systemd(1)───dockerd(1169795)───unpigz(1170083)─┬─{unpigz}(1170084)
  3.  
                                                    ├─{unpigz}(1170086)
  4.  
                                                    └─{unpigz}(1170087)

发现这个进程是 Docker 调用的,strace 只能看到卡住,kill 了 unpigz 后,卡住的 pull 开始报错了:

failed to register layer: Error processing tar file(exit status 1): unexpected EOF
 

Docker 的镜像每层 layer 实际是 tar,pull 时会下载 tar 包然后解压,这个看着是解压相关的逻辑出现了问题。在 Docker 源码里搜索 Error processing tar file 后找到:

  1.  
    // https://github.com/moby/moby/blob/v19.03.15/pkg/chrootarchive/archive_unix.go#L90-L116
  2.  
     cmd := reexec.Command("docker-untar", dest, root)
  3.  
      ...
  4.  
     if err := cmd.Wait(); err != nil {
  5.  
      // when `xz -d -c -q | docker-untar ...` failed on docker-untar side,
  6.  
      // we need to exhaust `xz`'s output, otherwise the `xz` side will be
  7.  
      // pending on write pipe forever
  8.  
      io.Copy(ioutil.Discard, decompressedArchive)
  9.  
     
  10.  
      return fmt.Errorf("Error processing tar file(%v): %s", err, output)
  11.  
     }
  12.  
     return nil

看注释里的xz -d -c -q | docker-untar ...,与 unpigz 的 cmdline 和一个卡住的 docker-untar 进程相符:

  1.  
    $ xargs -0 < /proc/1170083/cmdline
  2.  
    /usr/bin/unpigz -d -c
  3.  
    $ ps aux | grep docker-unta[r]
  4.  
    root     1164788  0.0  0.0 1491008 39488 pts/2   Sl+  15:21   0:00 docker-untar / /data/kube/docker/overlay2/546b7b992b53b243450807b8150c4a1905e93afae604da69a21bbaaf443f178e/diff

看来是 exec 调用 unpigz 解压管道给 reexec 注册的 docker-untar,而上面的 unpigz 进程树显示是 docker 默认调用的而非 xz。搜索后发现,unpigz 是一个在 gz 格式处理上比 gzip 更快的实现。既然 Docker 是 exec 调用的 unpigz,那就在源码里搜索它看看:

  1.  
    // https://github.com/moby/moby/blob/v19.03.15/pkg/archive/archive.go#L32-L39
  2.  
    func init() {
  3.  
     if path, err := exec.LookPath("unpigz"); err != nil {
  4.  
      logrus.Debug("unpigz binary not found in PATH, falling back to go gzip library")
  5.  
     } else {
  6.  
      logrus.Debugf("Using unpigz binary found at path %s", path)
  7.  
      unpigzPath = path
  8.  
     }
  9.  
    }

往下翻看,发现 unpigzPath 的 exec 调用逻辑:

  1.  
    // https://github.com/moby/moby/blob/v19.03.15/pkg/archive/archive.go#L160-L174
  2.  
    func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
  3.  
     if unpigzPath == "" {
  4.  
      return gzip.NewReader(buf)
  5.  
     }
  6.  
     
  7.  
     disablePigzEnv := os.Getenv("MOBY_DISABLE_PIGZ")
  8.  
     if disablePigzEnv != "" {
  9.  
      if disablePigz, err := strconv.ParseBool(disablePigzEnv); err != nil {
  10.  
       return nil, err
  11.  
      } else if disablePigz {
  12.  
       return gzip.NewReader(buf)
  13.  
      }
  14.  
     }
  15.  
     
  16.  
     return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d""-c"), buf)
  17.  
    }

现场 unpigz 版本:

  1.  
    $ rpm -qf /bin/unpigz
  2.  
    pigz-2.4-7.uel20.01.aarch64
  3.  
    $ rpm -V pigz
  4.  
    # -V 查看包也没被修改

注意看其中有个 env 设置不使用 PIGZ 而是使用 gzip,然后启动 Docker Daemon 时设置这个 env 就可以拉取镜像了:

  1.  
    $ systemctl stop docker
  2.  
    # 临时命令行前台 debug 启动下看看是没问题的
  3.  
    $ MOBY_DISABLE_PIGZ=true dockerd --debug

后续

UOS 这个系统需要授权才能使用 yum 安装升级,访问 repo 里的 url 会报错 401,让客户联系 UOS 厂商升级 pigz 包发现是最新的版本,只能使用 MOBY_DISABLE_PIGZ 环境变量回退到 gz 了。

e92f2a06f0482bbd1e02d5b24db440ba.gif

ef12f9fd8f26d59fb7c57b73bf676766.png

你可能还喜欢

点击下方图片即可阅读

 

尝试用 ChatGPT 完整的实现一个 Serverless 后端工程

2023-04-19

0e41e3b9cafba1434221b2a413f06c36.jpeg

 

Laf Assistant:云开发从未如此爽快!

2023-04-12

607fb8eed8c787ca0af46e7a620f639f.jpeg

 

AI 绘画神还原王维的《鸟鸣涧》

2023-04-11

ff828caf4274a01a5a28ad56e738d6e4.jpeg

 

ChatGPT 与 Midjourney 强强联手,让先秦阿房宫重现辉煌!

2023-04-06

b110c197e83f2bf724e4d2206f9ebed0.jpeg

63d3d3266aee9184f1325f0dbf9d0fba.gif

云原生是一种信仰 🤘

关注公众号

后台回复◉sealos◉获取以 kubernetes 为内核的云操作系统发行版,在云桌面中管理 Kubernetes 集群生命周期,运行分布式应用程序!

d9544cf67caa71799207a29692536d6c.gif

9c791774227a70c2702b44da4a1233cb.gif

点击 "阅读原文" 获取更好的阅读体验!

发现朋友圈变“安静”了吗?

33a168819f725ce0a0353682d1a38797.gif

 
posted @ 2024-05-08 16:11  MaskerFan  阅读(232)  评论(0编辑  收藏  举报