こんにちは もしくは こんばんわ!
ANDPADボード プロダクトテックリードの原田 土屋(tomtwinkle)です
最近めでたく戸籍が代わり名字がリネームされました
この記事はDebian12 bookwormが正式リリースされ、Debian11 Bullseyeが今までの流れでいうと来年辺りEOLになりそうな雰囲気なので今のうちに切り替えておこうと奮闘した記録とAlpine Linuxからdistrolessに変更したらKubernetesのpreStopが上手く動かなくなった件の対応をした記録の合せ技です。
TL;DR
- DockerのBuild base imageを
Debian11 Bullseye
からDebian12 bookworm
にしただけで docker build がコケるようになったならdocker/buildx
のversionを上げてみよう circleci/aws-ecr@9.0.1
ではecr_login
の前にcircleci/aws-cli
のsetup
が必要- Kubernetesで
distroless image
を使う時sleep command
の移植は忘れるな
Debian11 Bullseye からDebian12 bookworm へアップデートするまでの戦い
アップデート作業の前にアップデートしたい環境がどんな構成かを軽く説明しておくと
- EKS上でApp(golang)は動作している
- CircleCIでDocker imageをbuildしてECRにpushし、ECRのimageを使ってhelm upgradeでKubernetesの更新をかけている
というよくある構成です。 今回、CircleCIで利用していたorbのバージョンが重要なので記載しておきます
- aws-cli: circleci/aws-cli@1.3.1
- aws-ecr: circleci/aws-ecr@8.2.1
aws-cliの最新がv4.1.2なのでめちゃくちゃ古いですね。今回そっちは関係ないんですけれども……
1. まずはDockerfileを更新してみる
Debian11 Bullseye
でbuildするのに使用していたDockerfileはだいたい以下のような感じでした
# Build用image FROM golang:1.21.4-bullseye as builder SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV GOPATH /go ENV PATH $GOPATH/bin:$PATH ENV CGO_ENABLED 0 ENV GOPRIVATE github.com/xxxxx # go moduleで参照しているprivateのgithub repository ARG GITHUB_TOKEN # privateのgithub repositoryを参照可能なPAT RUN : \ && apt-get update \ && apt-get install --no-install-recommends -y curl git make \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && git config --global url."https://${GITHUB_TOKEN}:x-oauth-basic@github.com/".insteadOf "https://github.com/" \ && : WORKDIR /app COPY go.mod go.sum ./ RUN go mod download # 以下go buildのための諸々省略 # Deploy用 image FROM gcr.io/distroless/static-debian11 as runner COPY --from=builder /main ./ # 以下Security対策のための諸々省略
これを素直に Debian12 bookworm
に更新してみます、変更するのはimageだけなので差分だけならこうです
# Build用image -FROM golang:1.21.4-bullseye as builder +FROM golang:1.21.4-bookworm as builder # Deploy用 image -FROM gcr.io/distroless/static-debian11 as runner +FROM gcr.io/distroless/static-debian12 as runner
良さそうですね。特に問題無さそうに見えます。 しかし、CircleCIでbuildしてみると以下のエラーが出ました。
#9 [base 3/6] RUN : && apt update && apt-get install --no-install-recommends -y curl git make && apt-get clean && rm -rf /var/lib/apt/lists/* && git config --global url."https://****************************************:x-oauth-basic@github.com/".insteadOf "https://github.com/" && : #9 sha256:0e532e7716c1cbffe6c9f5faa2a948d98525829391e968a28bfec62b79aa8e08 #9 0.391 Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB] #9 0.398 Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB] #9 0.398 Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB] #9 0.450 Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8780 kB] #9 0.521 Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [6668 B] #9 0.552 Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [105 kB] #9 1.413 Fetched 9143 kB in 1s (8886 kB/s) #9 1.413 Reading package lists... #9 1.891 E: Problem executing scripts APT::Update::Post-Invoke 'rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true' #9 1.891 E: Sub-process returned an error code
2. エラー原因探索
どうも apt-get update
のscriptでコケてるらしいので問題の切り分けのためひとまず apt-get update
を外してみると
当該処理は通り、apt-get update
さえ何とかすればいけるか?と思ったのもつかの間
#13 [base 6/6] RUN go mod download #13 sha256:xxxxxxxxxx #13 1.599 go: github.com/xxxxx/xxxxxxx@vx.x.x: reading github.com/xxxxx/xxxxxxx/go.mod at revision vx.x.x: git ls-remote -q origin in /go/pkg/mod/cache/vcs/xxxxxxxxxx: exit status 128: #13 1.599 fatal: unable to access 'https://github.com/xxxxx/xxxxxxx/': getaddrinfo() thread failed to start #13 ERROR: executor failed running [/bin/bash -o pipefail -c go mod download]: exit code: 1 ------ > [base 6/6] RUN go mod download: ------ error: failed to solve: rpc error: code = Unknown desc = executor failed running [/bin/bash -o pipefail -c go mod download]: exit code: 1
今度は go mod download
がコケました。むむむ……
PrivateのGitHub Repositoryを参照するPAT自体は有効であることは確認し
GOPRIVATE
の環境変数の渡し方を変更してみたり色々試行錯誤してもどうも通らない。
3. エラー原因解明! 新たなトラップ発動
そこで最初に出た APT::Update::Post-Invoke
の errorに立ち戻ってBingると以下のStack Overflowの投稿を見つけました。
There was a Debian release a few days ago. python:3.9-slim derives from this new Debian version and there are some issues with running images based on this Debian version with older versions of docker (certificates and/or keys, updated version of glibc perhaps).
Docker更新しないと証明書や鍵、glibcのバージョンの差異で問題起きるよとのこと 天才!それだ!ありがとう僕らのstackoverflow!! と感謝を述べつつaws-ecrとaws-cliのorbを更新します。
orbs: - aws-cli: circleci/aws-cli@1.3.1 + aws-cli: circleci/aws-cli@4.1.2 - aws-ecr: circleci/aws-ecr@8.2.1 + aws-ecr: circleci/aws-ecr@9.0.1
circleci/aws-ecr@8.2.1
から circleci/aws-ecr@9.0.1
への更新は何故かcommandやargumentの名称がハイフンから全部アンダースコアになっていたりして変換がちょい面倒でしたがそれ以外大きな変更はなさそうに見えました。
これで無事docker buildが通るようになりました! やったか!? (ゴジラ-1.0面白かったですね)
と思ったのもつかの間今度は更新したcircleci/aws-ecrの方でエラーが出現 やってなかった!
Removing login credentials for ************.dkr.ecr.**************.amazonaws.com The config profile (default) could not be found Error: Cannot perform an interactive login from a non TTY device Exited with code exit status 1
4. そして完走へ
CircleCIのbuild + push jobは以下のような構成で
問題のエラーは aws-ecr/ecr_login
で発生していました。
さっきアップデートしたところだ!!!
build-and-push: executor: awsecr/default environment: AWS_ECR_REGISTRY_ID: "<AWS Account ID>" AWS_REGION: <AWS Region> IMAGE_REPO: <ECR Repository> DOCKERFILE: Dockerfile steps: - git-shallow-clone/checkout - aws-ecr/ecr_login # <---- ここでエラー - aws-ecr/build_image: dockerfile: Dockerfile extra_build_args: --build-arg GITHUB_TOKEN=${GITHUB_TOKEN} repo: ${IMAGE_REPO} tag: ${CIRCLE_SHA1}
The config profile (default) could not be found
なのでエラーの通りなのですが
どうやら aws config に defaultの設定がなくて怒られているらしい。
どうもaws-cliのバージョンアップで環境変数でアクセストークン、シークレットトークン設定していた場合でも本来読む必要のない ~/.aws/config
ファイルを読みに行っているようです。
なので aws-cli/setup
でaws configを作成してあげます。
build-and-push: executor: awsecr/default environment: AWS_ECR_REGISTRY_ID: "<AWS Account ID>" AWS_REGION: <AWS Region> IMAGE_REPO: <ECR Repository> DOCKERFILE: Dockerfile steps: - git-shallow-clone/checkout - aws-cli/setup # <----- これを追加 - aws-ecr/ecr_login - aws-ecr/build_image: dockerfile: Dockerfile extra_build_args: --build-arg GITHUB_TOKEN=${GITHUB_TOKEN} repo: ${IMAGE_REPO} tag: ${CIRCLE_SHA1}
これで通るようになりました。めでたしめでたし。
aws-ecr orbのissueを眺めていたら同じ場所で困ってる人がいるみたいで同じ解決策が提案されていました。 aws-ecr orbのDiff眺めても分からなかったのでせめてドキュメントに書いていて欲しいですね。
The docs offer zero context on how to set this up. In our case, we migrated from version 8.x to 9.x.
What solved the same problem for us, was adding AWS_DEFAULT_REGION to the environment and calling aws-cli/setup before the build and push command.
Alpine Linuxからdistrolessへの乗り換え時の注意点
これでおしまい、と思いきや以前にAlpine Linuxからdistrolessへimageを変更して以降、Kubernetesの更新時稀にPodがBackOffしてしまう事象が発生していました。 今回改めて詳しく調査していくとKubernetesのPreStopの際に以下エラーが出ていることがわかりました。
# Rolloutのhelmテンプレート kind: Rollout spec: template: spec: containers: lifecycle: preStop: exec: command: ["sleep", "20"]
podのエラー内容
FailedPreStopHook: Exec lifecycle hook ([sleep 20]) for Container "xxxxxxx" in Pod "xxxxxx" failed - error: rpc error: code = Unknown desc = failed to exec in container: failed to start exec "xxxxxxxxx": OCI runtime exec failed: exec failed: unable to start container process: exec: "sleep": executable file not found in $PATH: unknown, message: ""
Kubernetes PreStopでGraceful Shutdownのために指定している sleep
commandが存在しないというエラーですね。
distroless は極力imageサイズを抑えるため余計なshellやappを含まないimageです。
なので当然 sleep
も存在しないわけです。睡眠の重要性!
そこでdocker buildの際にBuild用のDebian imageからsleepのみを移植するように修正しました。
# Build用image FROM golang:1.21.4-bookworm as builder # 省略 # Deploy用 image FROM gcr.io/distroless/static-debian12 as runner COPY --from=builder /main ./ COPY --from=builder /bin/sleep /bin/sleep # <---- 追加
これでようやく直近出ていたDocker imageの問題はなくなり正常に更新が行えるようになりました。 めでたしめでたし。
preStop は発動するタイミング的に確実にコケるとは限らないので発覚しづらい問題だったかと思います。 preStopフックでのsleepはアルパカ界でも一般的な手法なのでKubernetesを利用しているなら設定している事が多いと思います、distrolessを利用しているor利用しようとしている皆さんも気をつけて下さいませ。
今回の現象に遭遇した際に爆速でエラーを特定して頂いたSREメンバーに猛烈感謝です!
We are hiring!
アンドパッドでは、「幸せを築く人を、幸せに。」というミッションの実現のため、一緒に働く仲間を大募集しています。 数あるユーザーの課題を解決するには何が最善かを考え抜き、よりよいプロダクトを作りたい!と思われる方はぜひぜひご応募ください!