社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  docker

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

云原生实验室 • 2 年前 • 274 次点击  

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

由来

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

$ docker pull xxx:5000/xxxx
xxx: Pulling from xxx/xxxxxx
7c0b344a74c2: Extracting [>                                                  ]  294.9kB/26.66MB
7c0b344a74c2: Download complete
e53ed7fd3110: Download complete
d2cae797bc79: Download complete
ec3ddc176f08: Download complete
2969517e196e: Download complete
097fa64722e8: Download complete
1dde4ca01a5a: Download complete

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

$ docker info
...
 Server Version: 19.03.15
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: ea765aba0d05254012b0b9e595e995c09186427f
 runc version: v1.0.0-0-g84113eef
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.19.90-2211.5.0.0178.22.uel20.aarch64
 Operating System: UnionTech OS Server 20
 OSType: linux
 Architecture: aarch64
 CPUs: 24
 Total Memory: 94.56GiB
 Name: host-xxxx
 ID: RTQS:5TXE:5T3S:YW7X:OHPK:FZ7D:7EHD:DH5Z:JNBV:FVXS:24FA:EIVS
 Docker Root Dir: /data/kube/docker
 Debug Mode: true
  File Descriptors: 29
  Goroutines: 46
  System Time: 2023-04-12T16:10:25.33362426+08:00
$ uname -a
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
$ cat /etc/os-release
PRETTY_NAME="UnionTech OS Server 20"
NAME="UnionTech OS Server 20"
VERSION_ID="20"
VERSION="20"
ID=uos
HOME_URL="https://www.chinauos.com/"
BUG_REPORT_URL="https://bbs.chinauos.com/"
VERSION_CODENAME=fuyu
PLATFORM_ID="platform:uel20"

排查

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

$ pstree -sp 1170083
systemd(1)───dockerd(1169795)───unpigz(1170083)─┬─{unpigz}(1170084)
                                                ├─{unpigz}(1170086)
                                                └─{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 后找到:




    
// https://github.com/moby/moby/blob/v19.03.15/pkg/chrootarchive/archive_unix.go#L90-L116
 cmd := reexec.Command("docker-untar", dest, root)
  ...
 if err := cmd.Wait(); err != nil {
  // when `xz -d -c -q | docker-untar ...` failed on docker-untar side,
  // we need to exhaust `xz`'s output, otherwise the `xz` side will be
  // pending on write pipe forever
  io.Copy(ioutil.Discard, decompressedArchive)

  return fmt.Errorf("Error processing tar file(%v): %s", err, output)
 }
 return nil

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

$ xargs -0 
/usr/bin/unpigz -d -c
$ ps aux | grep docker-unta[r]
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,那就在源码里搜索它看看:

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

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

// https://github.com/moby/moby/blob/v19.03.15/pkg/archive/archive.go#L160-L174
func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
 if unpigzPath == "" {
  return gzip.NewReader(buf)
 }

 disablePigzEnv := os.Getenv("MOBY_DISABLE_PIGZ")
 if disablePigzEnv != "" {
  if disablePigz, err := strconv.ParseBool(disablePigzEnv); err != nil {
   return nil, err
  } else if disablePigz {
   return gzip.NewReader(buf)
  }
 }

 return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d""-c"), buf)
}

现场 unpigz 版本:

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

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

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

后续

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




你可能还喜欢

点击下方图片即可阅读

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

2023-04-19

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

2023-04-12

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

2023-04-11

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

2023-04-06


云原生是一种信仰 🤘

关注公众号

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



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


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

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/153843
 
274 次点击