はじめに
自然言語処理とか画像認識の機械学習向けのKubernetes環境にPython(Anacondaから構築した環境)のコンテナを使用する場合があると思います。処理のためのモデルやデータを含めてコンテナ化することがあると思いますが、7GぐらいのコンテナになってしまうとKubernetesのPull時に必ずエラーになるので、AKS(Azure Kubernetes Service)を使って対処法を検証してみました。(エラーメッセージは出ますが最終的にはPull完了しますします)
エラーメッセージの対処法を考える
例えば以下の様なエラーメッセージ。
kubernetesのgithubやstackoverflowを探しても色々な人が悩みを持っているようですね。それらをまとめると以下のような案がありました。
- Pullのタイムアウトを長くすることができるか?
- Pullの速度(ダウンロードと展開)を速くすることができるか?
- ネットワークは速いので展開時の時間を短くするために圧縮アルゴリズムを変更したい。
Pullのタイムアウトを長くすることができるか?
Pull時のタイムアウトは以下のURLにも書かれていますが、デフォルト設定で2分になっています。 https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
--runtime-request-timeout duration
Timeout of all runtime requests except long running request - pull, logs, exec and attach. When timeout exceeded, kubelet will cancel the request, throw out an error and retry later. (default 2m0s)
AKSの実際のタイムアウトは何分に設定されているか不明ですが、Azure Container Service EngineのGithubでは以下のように書かれていました。
https://github.com/Azure/acs-engine/blob/master/parts/k8s/kubernetesconfigs.sh
setKubeletOpts " --container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
どちらにしても、提供されているものを使っている限りはこの設定は変更できないので別の方法を探します。
Pullの速度(ダウンロードと展開)を早くすることができるか?
Pullの速度は、コンテナイメージのダウンロードと、tar.gzされたコンテナイメージのレイヤーを展開する測度が関係してきますが、ACR(Azure Container Registry)からのダウンロードはほぼ気にならないぐらい速いので、展開する方を速くできないか考えてみます。Githubでも色々な案が出ていましたが、現段階での実装でなんとかならないかと思い、gzipの測度を圧縮レベルを変えて測定してみます。
試しに/usrをtarしたファイルを2つ用意します。
$ ls -la-rw-rw-r-- 1 kenta kenta 1114408960 Jan 5 01:50 usr1.tar-rw-rw-r-- 1 kenta kenta 1114408960 Jan 5 01:52 usr9.tar
gzipする時の時間は遅くても問題ないのですが、とりあえず計測してみます。 -1は圧縮率が低いけど高速。-9は圧縮率が高いけど低速です。
$ time gzip -1 usr1.tarreal 0m19.791suser 0m19.091ssys 0m0.696s$ time gzip -9 usr9.tarreal 3m47.467suser 3m46.716ssys 0m0.747s
圧縮したファイルをtarで展開してみますが、そんなに変りませんね。。
$ time tar xfz usr1.tar.gzreal 0m21.931suser 0m10.419ssys 0m4.389s$ time tar xfz usr9.tar.gzreal 0m20.804suser 0m9.375ssys 0m4.292s
別の方法を考えて見ます。
コンテナイメージのレイヤーを複数に分割する
コンテナイメージを作る際はイメージサイズを小さくするためにレイヤーを纏めた方が良いとされていますが、レイヤーを纏めると1つのレイヤーの展開に時間がかかります。また、並列ダウンロードもされません。また、今回はRPCからエラーがでている事もあり、もっとレスポンスの良いコンテナイメージにしてあげる必要があるかもしれません。 そこで、レイヤーを纏めたコンテナイメージと、複数のレイヤーに分けたコンテナイメージを作成して、Pull時の動作を確認することにより、KubernetesのPullに適したコンテナ構造を検証します。
コンテナイメージを用意
レイヤーを纏めたDockerfile(1つのRUNで纏める)を用意しました。色々インストールしています。
FROM continuumio/anaconda3RUN buildDeps='make libc6-dev gcc g++' \&& set -x \&& echo 'Installing Mecab' \&& apt-get update && apt-get install -y $buildDeps --no-install-recommends \&& apt-get -y install mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 \&& mkdir -p `mecab-config --dicdir` \&& git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \&& /mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -y \&& export VAL=`mecab-config --dicdir`/mecab-ipadic-neologd; sed -i 's#^\(dicdir\s*=\s*\).*$#\1'$VAL'#' /etc/mecabrc \&& echo 'Installing Tika' \&& apt-get -y install openjdk-8-jre-headless tesseract-ocr tesseract-ocr-eng tesseract-ocr-jpn \&& curl --output /tmp/tika-server.jar http://search.maven.org/remotecontent?filepath=org/apache/tika/tika-server/1.16/tika-server-1.16.jar \&& curl --output /tmp/tika-server.jar.md5 http://search.maven.org/remotecontent?filepath=org/apache/tika/tika-server/1.16/tika-server-1.16.jar.md5 \&& echo 'Installing Python' \&& apt-get install -y swig \&& conda update -y -n base conda \&& conda install -y -c anaconda gensim requests nltk \&& conda install -y -c conda-forge tika \&& pip install tinysegmenter sumy mecab-python3 langdetect \&& rm -rf /mecab-ipadic-neologdclone \&& rm -rf /var/lib/apt/lists/*
複数のレイヤーに分割したDockerfile(複数のRUNで分割する)は以下の通り。
FROM continuumio/anaconda3RUN buildDeps='make libc6-dev gcc g++' \&& set -x \&& echo 'Installing Mecab' \&& apt-get update && apt-get install -y $buildDeps --no-install-recommendsRUN apt-get -y install mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8 \&& mkdir -p `mecab-config --dicdir` \&& git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.gitRUN /mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -y \&& export VAL=`mecab-config --dicdir`/mecab-ipadic-neologd; sed -i 's#^\(dicdir\s*=\s*\).*$#\1'$VAL'#' /etc/mecabrcRUN echo 'Installing Tika' \&& apt-get -y install openjdk-8-jre-headless tesseract-ocr tesseract-ocr-eng tesseract-ocr-jpn \&& curl --output /tmp/tika-server.jar http://search.maven.org/remotecontent?filepath=org/apache/tika/tika-server/1.16/tika-server-1.16.jar \&& curl --output /tmp/tika-server.jar.md5 http://search.maven.org/remotecontent?filepath=org/apache/tika/tika-server/1.16/tika-server-1.16.jar.md5RUN echo 'Installing Python' \&& apt-get install -y swig \&& conda update -y -n base conda \&& conda install -y -c anaconda gensim requests nltk \&& conda install -y -c conda-forge tikaRUN pip install tinysegmenter sumy mecab-python3 langdetect \&& rm -rf /mecab-ipadic-neologdclone \&& rm -rf /var/lib/apt/lists/*
ビルドします。
$ docker build -t kekekekenta.azurecr.io/nlp-tools nlp-tools$ docker build -t kekekekenta.azurecr.io/layered-nlp-tools layered-nlp-tools
ビルド後のコンテナイメージサイズは以下の通り。どちらも同じぐらいのサイズ。
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEkekekekenta.azurecr.io/nlp-tools latest 370bb5c6837e 4 hours ago 7.17GBkekekekenta.azurecr.io/layered-nlp-tools latest cff610a7e062 4 hours ago 7.17GBcontinuumio/anaconda3 latest 69acfdf1121f 41 hours ago 3.72GB
便利なdiveでレイヤーを確認。Anacondaのレイヤーは3.4G。大丈夫かな。。。 https://github.com/wagoodman/dive
$ dive kekekekenta.azurecr.io/nlp-tools[● Layers]────────────────────────────────────────────────────────────────────── [Current Layer Contents]────────────────────────────────────────────────────────Cmp Image ID Size Command Permission UID:GID Size Filetreesha256:b28ef0b6fef80faa25 101 MB #(nop) ADD file:58d5c21fcabcf1eec94e8676a drwxr-xr-x 0:0 5.2 MB ├── binsha256:60b398413f86c0f8ac 231 MB apt-get update --fix-missing && apt-get i -rwxr-xr-x 0:0 1.1 MB │ ├── bashsha256:b1f721b57648924150 3.4 GB wget --quiet https://repo.anaconda.com/ar -rwxr-xr-x 0:0 36 kB │ ├── catsha256:abd2c1804296f74fae 20 MB apt-get update --fix-missing && apt-get -rwxr-xr-x 0:0 64 kB │ ├── chgrpsha256:aac6d3b7d1450a4434 3.4 GB FROM sha256:aac6d3b7d1450a4434 -rwxr-xr-x 0:0 60 kB │ ├── chmod....$ dive kekekekenta.azurecr.io/layered-nlp-tools[● Layers]────────────────────────────────────────────────────────────────────── [Current Layer Contents]────────────────────────────────────────────────────────Cmp Image ID Size Command Permission UID:GID Size Filetreesha256:b28ef0b6fef80faa25 101 MB #(nop) ADD file:58d5c21fcabcf1eec94e8676a drwxr-xr-x 0:0 5.2 MB ├── binsha256:60b398413f86c0f8ac 231 MB apt-get update --fix-missing && apt-get i -rwxr-xr-x 0:0 1.1 MB │ ├── bashsha256:b1f721b57648924150 3.4 GB wget --quiet https://repo.anaconda.com/ar -rwxr-xr-x 0:0 36 kB │ ├── catsha256:abd2c1804296f74fae 20 MB apt-get update --fix-missing && apt-get -rwxr-xr-x 0:0 64 kB │ ├── chgrpsha256:546ef8f4ba85d10b69 154 MB buildDeps='make libc6-dev gcc g++' && -rwxr-xr-x 0:0 60 kB │ ├── chmodsha256:0cabb03f966e749806 282 MB apt-get -y install mecab libmecab-dev mec -rwxr-xr-x 0:0 64 kB │ ├── chownsha256:7abb783748c90e7e13 2.5 GB /mecab-ipadic-neologd/bin/install-mecab-i -rwxr-xr-x 0:0 130 kB │ ├── cpsha256:30242b13e105ca7ba4 293 MB echo 'Installing Tika' && apt-get -y -rwxr-xr-x 0:0 117 kB │ ├── dashsha256:752f8655a9f8c5b758 245 MB echo 'Installing Python' && apt-get i -rwxr-xr-x 0:0 105 kB │ ├── datesha256:89e71c7f05b61effb0 5.9 MB FROM sha256:89e71c7f05b61effb0 -rwxr-xr-x 0:0 77 kB │ ├── dd....
ACRに登録します。
$ az acr login -n kekekekenta$ docker push kekekekenta.azurecr.io/nlp-tools$ docker push kekekekenta.azurecr.io/layered-nlp-tools
初回のapply動作
AKSにapplyしてみます。(yamlファイルは省略しますがKubernetesにはJobとして展開しています。また、それぞれのコンテナで同じレイヤーを使っている箇所があるので、キャッシュされたレイヤーが使われないように異なるノードに展開します。)
レイヤーを纏めたコンテナイメージ
nlp-tools(レイヤーを纏めたコンテナイメージ)の初回Apply後の経過は以下の通り。エラーは出ますが最終的に約19分でJob動作完了。
$ kubectl apply -f nlp.yamljob.batch/nlp created$ kubectl get pods -o wide -wNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnlp-9thhn 0/1 ContainerCreating 0 14s <none> aks-agentpool-35064155-0 <none> <none>nlp-9thhn 0/1 ErrImagePull 0 2m6s 10.244.1.12 aks-agentpool-35064155-0 <none> <none>nlp-9thhn 0/1 ImagePullBackOff 0 2m22s 10.244.1.12 aks-agentpool-35064155-0 <none> <none>nlp-9thhn 0/1 ContainerCreating 0 10m 10.244.1.12 aks-agentpool-35064155-0 <none> <none>nlp-9thhn 0/1 Completed 0 19m 10.244.1.12 aks-agentpool-35064155-0 <none> <none>$ kubectl describe pods nlp-9thhn....Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 33m default-scheduler Successfully assigned default/nlp-9thhn to aks-agentpool-35064155-0Warning Failed 31m kubelet, aks-agentpool-35064155-0 Failed to pull image "kekekekenta.azurecr.io/nlp-tools": rpc error: code = FailedPrecondition desc = unexpected EOFWarning Failed 31m kubelet, aks-agentpool-35064155-0 Error: ErrImagePullNormal BackOff 31m kubelet, aks-agentpool-35064155-0 Back-off pulling image "kekekekenta.azurecr.io/nlp-tools"Warning Failed 31m kubelet, aks-agentpool-35064155-0 Error: ImagePullBackOffNormal Pulling 31m (x2 over 33m) kubelet, aks-agentpool-35064155-0 pulling image "kekekekenta.azurecr.io/nlp-tools"Normal Pulling 22m kubelet, aks-agentpool-35064155-0 pulling image "kekekekenta.azurecr.io/nlp-tools"Normal Pulled 14m kubelet, aks-agentpool-35064155-0 Successfully pulled image "kekekekenta.azurecr.io/nlp-tools"Normal Created 14m kubelet, aks-agentpool-35064155-0 Created containerNormal Started 14m kubelet, aks-agentpool-35064155-0 Started container
複数レイヤーに分割したコンテナイメージ
layered-nlp-tools(複数レイヤーに分割したコンテナイメージ)の初回apply後の経過は以下の通り。エラーは出ずに約8分でJob動作完了。
$ kubectl apply -f layerednlp.yamljob.batch/layerednlp created$ kubectl get pods -o wide -wNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESlayerednlp-ngkng 0/1 ContainerCreating 0 13s <none> aks-agentpool-35064155-2 <none> <none>layerednlp-ngkng 0/1 Completed 0 8m17s 10.244.0.12 aks-agentpool-35064155-2 <none> <none>$ kubectl describe pods layerednlp-ngkng....Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 11m default-scheduler Successfully assigned default/layerednlp-ngkng to aks-agentpool-35064155-2Normal Pulling 11m kubelet, aks-agentpool-35064155-2 pulling image "kekekekenta.azurecr.io/layered-nlp-tools"Normal Pulled 3m47s kubelet, aks-agentpool-35064155-2 Successfully pulled image "kekekekenta.azurecr.io/layered-nlp-tools"Normal Created 3m18s kubelet, aks-agentpool-35064155-2 Created containerNormal Started 3m18s kubelet, aks-agentpool-35064155-2 Started container
複数レイヤーに分割したコンテナイメージの方は、レイヤーを纏めたコンテナイメージと比べて、エラーも出ず、素早くPull完了していますね。
2回目以降のapplyの動作
2回目以降のapplyはキャッシュされているコンテナイメージを使うので、それぞれ約2秒で完了しています。
レイヤーを纏めたコンテナイメージ
$ kubectl apply -f nlp.yamljob.batch/nlp created$ kubectl get pods -o wide -wNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnlp-zzl4s 0/1 Completed 0 28s 10.244.1.16 aks-agentpool-35064155-0 <none> <none>$ kubectl describe pods nlp-zzl4s....Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 53s default-scheduler Successfully assigned default/nlp-zzl4s to aks-agentpool-35064155-0Normal Pulling 52s kubelet, aks-agentpool-35064155-0 pulling image "kekekekenta.azurecr.io/nlp-tools"Normal Pulled 52s kubelet, aks-agentpool-35064155-0 Successfully pulled image "kekekekenta.azurecr.io/nlp-tools"Normal Created 51s kubelet, aks-agentpool-35064155-0 Created containerNormal Started 51s kubelet, aks-agentpool-35064155-0 Started container
複数レイヤーに分割したコンテナイメージ
$ kubectl apply -f layerednlp.yamljob.batch/layerednlp created$ kubectl get pods -o wide -wNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESlayerednlp-9p478 0/1 Completed 0 13s 10.244.0.13 aks-agentpool-35064155-2 <none> <none>$ kubectl describe pods layerednlp-9p478....Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 2m23s default-scheduler Successfully assigned default/layerednlp-9p478 to aks-agentpool-35064155-2Normal Pulling 2m22s kubelet, aks-agentpool-35064155-2 pulling image "kekekekenta.azurecr.io/layerd-nlp-tools"Normal Pulled 2m21s kubelet, aks-agentpool-35064155-2 Successfully pulled image "kekekekenta.azurecr.io/layerd-nlp-tools"Normal Created 2m21s kubelet, aks-agentpool-35064155-2 Created containerNormal Started 2m21s kubelet, aks-agentpool-35064155-2 Started container
まとめ
機械学習系のコンテナイメージをKubernetesに展開する際は、なるべく小さいサイズのレイヤーに分けた方が良さそうですね。
もしくは、もっと細かくコンテナを分けた方が良いかも知れません。Pullされた後にセットアップするようなコンテナでも良いですね。
では。メモでしたー。
参照先
https://github.com/kubernetes/kubernetes/blob/39529006f0f47a105f6c08371df11be00726378a/pkg/kubelet/kubelet.go#L315 https://github.com/moby/moby/issues/1266 https://github.com/moby/moby/pull/34610