Docker のポータビリティにはいくつか注意点がある。
- コラム - クラウド時代のオープンソース実践活用 | 第43回 「Dockerイメージ」のポータビリティとLinuxカーネルのABI|CTC教育サービス 研修/トレーニング
- コンテナってなんだろう― 「コンテナ」の概要を知る | Think IT(シンクイット)
そこで今回は、壊しながらポータビリティについて理解する。
コンテナ型の仮想化
KVM などの仮想化技術(CPU などのハードウェア含めて完全に仮想化する)と異なり、コンテナはメモリやファイルシステムなどのリソースを隔離することで独立した空間を用意している。 このおかげで起動が早かったりリソース効率が良かったりする。
ユーザ空間のソフトウェアはコンテナごとに用意し、ホストのカーネルは共有するイメージ。
システムコールについては以前に調べたので省略する。
ファイルシステムやプロセスがホストと独立しているので、ホストとは異なるディストリビューションを実行できる。例えば、ホストに Ubuntu 20.04LTS を利用していても、コンテナ上では alpine を実行できる。
# docker run alpine cat /etc/os-release Unable to find image 'alpine:latest' locally latest: Pulling from library/alpine 540db60ca938: Pull complete Digest: sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f Status: Downloaded newer image for alpine:latest NAME="Alpine Linux" ID=alpine VERSION_ID=3.13.5 PRETTY_NAME="Alpine Linux v3.13" HOME_URL="https://alpinelinux.org/" BUG_REPORT_URL="https://bugs.alpinelinux.org/"
ポータビリティがないもの
CPU アーキテクチャの異なるコンテナ
カーネル以降のハードウェアは共有してるので、コンテナイメージのバイナリに含まれる命令セットがホストと異なると実行できない。たとえば alpine の arm 用のイメージを実行すると、エラーになる。
# docker run alpine@sha256:9663906b1c3bf891618ebcac857961531357525b25493ef717bca0f86f581ad6 cat /etc/os-release Unable to find image 'alpine@sha256:9663906b1c3bf891618ebcac857961531357525b25493ef717bca0f86f581ad6' locally docker.io/library/alpine@sha256:9663906b1c3bf891618ebcac857961531357525b25493ef717bca0f86f581ad6: Pulling from library/alpine e160e00eb35d: Pull complete Digest: sha256:9663906b1c3bf891618ebcac857961531357525b25493ef717bca0f86f581ad6 Status: Downloaded newer image for alpine@sha256:9663906b1c3bf891618ebcac857961531357525b25493ef717bca0f86f581ad6 WARNING: The requested image's platform (linux/arm) does not match the detected host platform (linux/amd64) and no specific platform was requested standard_init_linux.go:219: exec user process caused: exec format error
※ タグ名を指定して pull すると、適切な CPU アーキテクチャのイメージをダウンロードしてくれるが、今回は実験のために sha256 を直接指定している。
Linux ではないコンテナ
ホストのカーネルを共有するので、Linux カーネル上で動作しないコンテナは実行できない。たとえば windows 用のイメージを実行すると、エラーになる。
# docker run python@sha256:4b5561adc10048ccbe95fdfef486d4a4bb10c7424a347ed9441768d534965098 Unable to find image 'python@sha256:4b5561adc10048ccbe95fdfef486d4a4bb10c7424a347ed9441768d534965098' locally docker.io/library/python@sha256:4b5561adc10048ccbe95fdfef486d4a4bb10c7424a347ed9441768d534965098: Pulling from library/python 3889bb8d808b: Pulling fs layer 2f52abeee6d6: Pulling fs layer c82e82e95dba: Pulling fs layer 43dfa55ab4db: Waiting c630a7e18920: Waiting 7a6418955f2d: Waiting 25623f3f96a5: Waiting 6e43e384dc88: Waiting 19aef2ea307a: Waiting 213dd8e728a1: Waiting 0387077c68d3: Waiting 239fddfee327: Waiting docker: image operating system "windows" cannot be used on this platform.
※ イメージダウンロード時およびコンテナ実行時に OS のチェックをしてくれるので、実行前にエラーになる。
新しすぎるシステムコールを利用するコンテナ
コンテナ作成環境がコンテナ実行環境よりも新しい場合、問題になる場合がある。
最新のシステムコール(今回は v5.3 から追加された pidfd_open
。新しすぎて glibc のラッパーがないので syscall(2) で呼び出す)を利用するアプリケーションを用意し、
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/syscall.h> int main(void) { pid_t fd; fd = fork(); if (fd > 0) { int pidfd; if ((pidfd = syscall(434, fd, 0)) == -1) { perror("failed pidfd_open\n"); } else { printf("succeed pidfd_open\n"); } } }
コンテナ作成環境(今回は Ubuntu 20.04LTS)でビルドし、コンテナイメージを作成する。
FROM ubuntu:focal AS builder WORKDIR /build COPY main.c /build RUN apt-get update && apt-get install -y gcc RUN gcc -o newfeature main.c FROM ubuntu:focal WORKDIR /app COPY --from=builder /build/newfeature . CMD ["/app/newfeature"]
コンテナ作成環境(今回は Ubuntu 20.04LTS)ではコンテナ実行に成功するが、
# docker build -t kimullaa/newfeature -f Dockerfile . # docker run kimullaa/newfeature succeed pidfd_open
このイメージを push して、コンテナ実行環境(今回は CentOS 7)で実行すると、エラーになる。
# docker run -it kimullaa/newfeature Unable to find image 'kimullaa/newfeature:latest' locally latest: Pulling from kimullaa/newfeature 345e3491a907: Pull complete 57671312ef6f: Pull complete 5e9250ddb7d0: Pull complete 1a51d0c3f193: Pull complete b7518134f906: Pull complete Digest: sha256:dc4f30a78726bf889a30567e0b49fdacbe2423392e6cb445561d52311eacd0a6 Status: Downloaded newer image for kimullaa/newfeature:latest failed pidfd_open : Function not implemented
※ 最新の機能を利用するときは注意が必要じゃないかと思う。最近だと io_uring など。アプリケーション側でフォールバックしてたりすればいいけど。ちなみに CentOS7 は uname -a
すると kernel 3.x と表示されるので kernel 4.x で追加されたシステムコール(mlock2 とか copy_file_range とか preadv2 とか)は存在しないと思ったんだけど、だいたいほとんどバックポートされていてさすが Red Hat 様やで…と思った。
補足
逆に コンテナ実行環境 が コンテナ作成環境 よりも新しい場合は、ほぼ問題にならない。これは Linux カーネル開発者がめちゃくちゃ ABI の後方互換性に気を使ってくれているから。ありがてぇ…。
Linuxカーネルの開発においては、基本的には、このようなABIの変更をしないことがルールになっています。特にLinusは厳格で、開発者(メンテナ)の誰かがABIを変更するパッチを取り入れようとすると、頭から湯気が出ているのが見えるぐらいに怒ります。LKML(Linuxカーネル開発者のメーリングリスト)を検索すると、このような例があり、「WE DO NOT BREAK USERSPACE!(ユーザスペースを壊す変更はするな!)」と叫んでいます。
引用元: コラム - クラウド時代のオープンソース実践活用 | 第43回 「Dockerイメージ」のポータビリティとLinuxカーネルのABI|CTC教育サービス 研修/トレーニング
上記で紹介されている LKML: Linus Torvalds: Re: [Regression w/ patch] Media commit causes user space to misbahave (was: Re: Linux 3.8-rc1) を見ると、ひくほどブチギレしている様子がわかる。
まとめ
壊してみると分かることありますね。