docker buildできない
宮崎からお送りします。yoshinoです。
私が担当しているプロジェクトでは、デプロイするために、お客様の管理するサーバー上でdockerイメージをbuildする必要があります。
先日、ローカルでは問題なくdocker buildできるのに、お客様サーバー上ではbuildできないという事象が起きて、試行錯誤しながら解決できましたので共有します(同僚のtaijuさんに助けてもらい解決できました)。
ローカルとサーバーではDockerfileの定義も少し異なり、本番用のDockerfileではhttps_proxy
オプションを利用しており、
はじめはこの差異が原因かと考えたのですが、原因は別のところにありました。
docker build
で生じるエラー内容は以下のようになっていました。
Step 11/22 : RUN apt-get update ---> Running in 66ec271bf6ce Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB] Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB] Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB] . . W: GPG error: http://deb.debian.org/debian bookworm InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY ....... E: The repository 'http://deb.debian.org/debian bookworm InRelease' is not signed.
GPG error?
the public key is not available
と怒られているので、GPGのpublic keyまわりで何か問題が起きていることがわかります。
ローカルではエラーが起きていないので、単純なpublic key不足ではないだろうなと思いつつ調査します。
Dockerのコンテナに入りGPGのpublic keyが存在するかを確認すると、問題の原因につながりそうなヒントが得られました。
> apt-key list
ローカルでは登録されているpublic key が表示されているのですが、サーバーでは何も返ってこなかったのです(エラーはこの時点では起きませんでした。エラー起きて欲しかった。)
改めてpublic keyが入っているはずのディレクトリをサーバー上で確認してみます。
> ls /etc/ ls: cannot access '/etc/': Operation not permitted
!!!!?
ディレクトリにパーミッションの関連でアクセスできない事象が発生していることがわかりました。
調べてみると、Dockerへの権限付与を行う箇所の問題であるようで、すべてのシステムコールが利用可能になるようなオプション(--security-opt="seccomp=unconfined"
)を付与して実行すると、buildが成功することを確認できました!!!
> docker run --security-opt="seccomp=unconfined" -it test bash > apt-get update
原因と修正方法
原因と修正方法の話の前に、seccompというカーネル機能について知っておく必要があります(突然の「Operation not permitted」—Dockerが採用するセキュリティ機構「Seccomp」とは何か?を参考にさせていただきました)。
- Seccompとは、Linuxカーネルが持つセキュリティ機構の一つで、Secure Computing Modelの略です。簡単に言うと、Seccompはシステムコールの許可・不許可を設定できるようにし、危険なシステムコールを実行できなくするためのものです。
- Seccompはカーネルの機能であり、Dockerはカーネルとの仲介にlibseccompライブラリを使っています。
つまり、原因は以下のように推察されます。
Dockerで使用しているruby:3.2.2
のコンテナではls
コマンドは「lstat」ではなく「statx」というシステムコールを使用しており、Dockerが起動しているサーバーではこのシステムコールが禁止されているため、operation not permitted
が出力された。
Dockerが起動しているサーバーのlibseccompのバージョンが古いことにより、システムコールのルール制御も古くなり、Docker上で利用するシステムコールとのミスマッチが起きていることが原因だと考えられます。したがって、修正方法は2つあることがわかります。
1つ目は「linuxのバージョンを下げる」ことです。
例えば、Dockerのimageをruby:3.2.2-buster
に指定するだけで対応可能です。
この対応でも動くことは確認できましたが、LTS のサポート期間は2024-06-30 までなので、一時的な対応となってしまいます。
2つ目は「対象サーバーのlibseccomp
のバージョンを上げる」ことです。
今回はこちらの方法を採用して、古かったバージョン(libseccomp-2.3.1-3.amzn2.0.1.x86_64
)を最新バージョン(libseccomp-2.5.2-1.amzn2.0.1.x86_64
)にアップデートすることで対応しました。
おわりに
実はこの問題が生じたのは、imageのバージョンを変更した時点ではありませんでした(そのため原因推測がより難しかった)。
はっきりと認識できているわけではないのですが、問題が生じたタイミングはdocker のcacheを削除した時点だと思います。
以下は推測になります。
ruby:3.1
-> ruby:3.2
のようにイメージを変更した場合、低レイヤの部分(linux部分?)はcacheされ、そのまま使われ上位レイヤだけ変更される。そのため、例えばruby:3.1
で古いlinuxバージョンがcacheされruby:3.2
では新しいバージョンのlinuxが使われているにもかかわらず、build時はcacheが使われて気がつかない。そして、cacheを削除した段階で、新しいlinuxバージョンが利用されるようになり、問題が顕在化する。
運用という最上流工程にたずさわる*1、同士の皆様の助けに少しでもなることがあれば、 著者としてこれ以上の喜びはありません。 現場からは以上です。
*1: オブジェクト倶楽部: 2005-17号より引用