containerdでcreator-eeのv0.5.0+のイメージがpullに失敗するという事象が発生した。(KubernetesのコンテナランタイムがDockerからcontainerdに代わってから、初めてruntimeの挙動の差にハマった気がした)
containerdのコード読むよい機会だったので、コンテナイメージのpull周りを読みつつ、この件についてまとめてみた。今回の問題は既にコミュニティにより containerd - #7094 で修正されている。
背景
ローカルのDocker for Desktopでは問題なく動いていたが、Kubernetes上で動いているDrone1でcreator-ee2のv0.6.0のイメージを使うと何のエラーも出ずに停止する問題があった。
> sudo docker pull quay.io/ansible/creator-ee:v0.5.0
...
ff3ce4e37203: Pull complete
be34f4f093fc: Pull complete
Digest: sha256:4d21770ba05e0373ef6506df50eaebcf1586c4dbfffb728f52dfba08eb0dc222
Status: Downloaded newer image for quay.io/ansible/creator-ee:v0.5.0
quay.io/ansible/creator-ee:v0.5.0
> echo $?
0
確認したこと
-
creator-eeのバージョンを二分探索したところ、v0.5.0+のイメージが動かないことが分かった
-
droneはKubernetes上で動いているので該当pipelineのPodを確認したが、該当コンテナのログはUI上と同じく何も出力されておらず、Exit(2)になっていた
-
droneを経由させずにKubernetes上で該当イメージを立ち上げたところ、イメージのpull時に権限エラーでレイヤーの展開に失敗していることが分かった
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 30m default-scheduler Successfully assigned default/ee50 to demo-ladicle-w-default-a9d89c28-htgqp Normal Pulling 27m (x4 over 30m) kubelet Pulling image "quay.io/ansible/creator-ee:v0.5.0" Warning Failed 27m (x4 over 29m) kubelet Error: ErrImagePull Warning Failed 27m (x6 over 29m) kubelet Error: ImagePullBackOff Normal BackOff 5m17s (x94 over 29m) kubelet Back-off pulling image "quay.io/ansible/creator-ee:v0.5.0" Warning Failed 19s (x10 over 29m) kubelet Failed to pull image "quay.io/ansible/creator-ee:v0.5.0": rpc error: code = Unknown desc = failed to pull and unpack image "quay.io/ansible/creator-ee:v0.5.0": failed to extract layer sha256:140b571c235813a8eb5ba7f5f474f8852eef0218d4e5efe99801351d4c76aa6b: operation not permitted: unknown
-
手元の環境でcontainerd(ctr)単体のイメージpullを試したが、同様のエラーで失敗するためkubernetes & 環境固有の問題ではないことが分かった
$ sudo ctr image pull quay.io/ansible/creator-ee:v0.5.0 ... layer-sha256:1cb646aa85f30788a51b53eda39160008325a7ae05b03050ba8b2726aee91549: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:764ac4e7ad9bfa3a618b07693be79a7fd44b6a2fd35d497a7256458925219d33: done |++++++++++++++++++++++++++++++++++++++| elapsed: 56.0s total: 540.1 (9.6 MiB/s) unpacking linux/amd64 sha256:4d21770ba05e0373ef6506df50eaebcf1586c4dbfffb728f52dfba08eb0dc222... INFO[0064] apply failure, attempting cleanup error="failed to extract layer sha256:140b571c235813a8eb5ba7f5f474f8852eef0218d4e5efe99801351d4c76aa6b: operation not permitted: unknown" key="extract-661548272-faQw sha256:f5544d59a150fa51ce83f2fce9214393ff91001ccaaac6405d892a6dbb3ac4c7" ctr: failed to extract layer sha256:140b571c235813a8eb5ba7f5f474f8852eef0218d4e5efe99801351d4c76aa6b: operation not permitted: unknown
-
containerdの特定バージョンの問題か確認するため、v1.6.4とv1.4.4で試したが同様のエラーが発生した
-
Dockerでは動作し、containerdでは複数バージョンで失敗するためcontainerdに報告を上げた
原因と解決方法
ref: https://github.com/containerd/containerd/pull/7094
原因は、tarからファイルを展開3する際に、拡張属性を設定できないシンボリックリンクに対してsetxattrを実行し、そのままエラーを返したことだった。
ユーザ拡張属性はレギュラーファイルとディレクトリのみに設定できる4。しかし、公式のcreator-eeのコンテナイメージでは、シンボリックリンクの /etc/krb5.conf.d/crypto-policies
tarヘッダ5にこの拡張属性情報が入っていた。containerdはこれを設定しようとして EPERM
で失敗していた6。
解決策として、上記PRではレギュラーファイルとディレクトリ以外に対しては EPERM
を無視するように修正された。
func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header, reader io.Reader) error {
...
if strings.HasPrefix(key, paxSchilyXattr) {
key = key[len(paxSchilyXattr):]
if err := setxattr(path, key, value); err != nil { // ここでsymlinkに対するsetxattrがEPERMで失敗
if errors.Is(err, syscall.ENOTSUP) {
log.G(ctx).WithError(err).Warnf("ignored xattr %s in archive", key)
continue
}
return err
}
}
NOTE: docker(moby)の場合
全てのストレージドライバが xattrs
をサポートしているわけではない。ということで、 EPERM
(& ENOTSUP
)エラーは全て無視する仕様になっている。
var errors []string
for key, value := range hdr.Xattrs {
if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
if err == syscall.ENOTSUP || err == syscall.EPERM {
// We ignore errors here because not all graphdrivers support
// xattrs *cough* old versions of AUFS *cough*. However only
// ENOTSUP should be emitted in that case, otherwise we still
// bail.
// EPERM occurs if modifying xattrs is not allowed. This can
// happen when running in userns with restrictions (ChromeOS).
errors = append(errors, err.Error())
continue
}
return err
}
-
ansible/creator-ee: Ansible Execution environment targeted for content creators. ↩
-
(単純にtarを展開しているのではなく) OCIの Applying ChangeSets の仕様に基づき、ファイルやwhiteoutを展開先のディレクトリに適用している ↩
-
xattr(7) - Linux manual page のUser extended attributes参照 (実装はlinux/fs/xattr.c#L129) ↩
-
https://docs.studygolang.com/src/archive/tar/common.go#L191 ↩
-
そもそも何故、拡張属性がシンボリックリンクに付いているのかはまだ分かっていない。creator-eeが提供しているpodman/dockerでそれぞれ自前ビルドしたイメージをpullしてみたが、その場合は問題なく展開できた。creator-eeのビルドオプションの関係なんだろうか。 ↩