今回のとことんDevOps勉強会は技術評論社から発売されているSoftware Design 2023年11月号の第1特集「理想のコンテナイメージを作る」の記事の振り返りを、執筆に携わった弊社の田中、水野、遠山がそれぞれお話しさせていただきました。
全5章のうち、弊社では1章、3章、4章を担当させていただきました。
第1章:理想のコンテナを目指す基礎知識 3つの視点とDockerfileの基本を押さえる
第3章:ベースイメージの選び方 セキュリティと効率のために意識したいポイント
第4章:コンテナイメージ作成に役立つツール Docker DesktopやVS Codeを活用しよう
それぞれの視点から振り返りを行いましたが、よりじっくりと理解するためにもぜひ誌面も読んでいただえければと思います。
振り返りも兼ねて当日の配信の様子をYoutubeでアーカイブしていますので、是非ご覧ください。当日参加できなかった方も、これを機会にとことんDevOps勉強会に興味を持っていただければと思います。また、当日いただいたQ&Aに関してもこちらのブログでまとめていますので、ぜひご覧ください。
セミナー動画 - YoutTubeアーカイブ
発表資料 - Speaker Deck
第1章
第3章
第4章
Q&Aまとめ
Q: 先ほど、シークレットの持ち方の話もありましたが、セキュリティの観点で、ベースイメージやツール以外の要素で注意すべき点などありますか?
A: アプリケーションを動かすために必要ないものを入れないという点があると思います。加えて、アプリケーションを動かす権限にも注意する必要があります。そう言った意味で、マルチステージビルドを使用して、シングルバイナリだけをdistrolessに入れるというやり方はおすすめです。
Q: hadolintはaptやyumからはインストールはできないんでしょうか?
A: ディストリビューションによって異なると思います。hadlintはDockerコンテナイメージとしても提供されているので、必要な時にhadlintをコンテナで動かすというのが良いのではないでしょうか。
Q: みんなコンテナイメージをどれぐらいの頻度で更新してるんだろう?動いていればOKみたいなところがありそう。
A: 確かに、動いているシステムでわざわざミドルウェアなどのインフラ部分はアップデートしない傾向にあります。インフラをアップデートすると全テストやり直しになって、やりたくないという心境は理解できます。そこでCIをつかって自動化することが良いかと思います。Dockerのベストプラクティスとしてもビルド時にno-cascheオプションをつけるべきとなっており、頻繁に更新することが推奨されています。その際に開発ステージから徐々に変更してくことでインパクトを減らすことができます。
加えて、更新が億劫になる程のコンテナイメージを使用するのもよくないと思います。先ほどのマルチステージビルドでシングルバイナリを入れたdistrolessの環境にアプリを入れて動かすと言った軽量なコンテナを目指すのが良いかと思います。
Q: マルチステージビルドについてもう少し詳しく知りたいです。初心者なので、どういったアプリがバイナリでビルドすべきか、といったレベルから教えていただけるとありがたいです。
A: マルチステージビルドは、端的にいうとビルド環境と実行環境を分けましょうということです。アプリケーションの実行環境としてビルド環境をそのまま使ってしまうと、ビルドしたあとは使わない開発ツールと依存するライブラリーなどが原因となる脆弱性を作ってしまうことにもなりかねません。
マルチステージビルドではコンテナビルドの過程でビルド用コンテナから実行用コンテナにコピーをするコマンドがあるので、それを使用してビルド環境でビルドされた実行に必要なバイナリだけを実行環境に配置します(以下実行例)。
# syntax=docker/dockerfile:1 FROM golang:1.16 WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go ./ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./ CMD ["./app"]例えばGoやRustは静的リンクでビルドしてしまえば、シングルバイナリで動作するので、glibcやシェルなども不要な空っぽのコンテナで稼働させることができます。その結果、コンテナのメンテナンスも楽になりますし、余計なものがないのでセキュリティ的にも安全です。
最近のDockerの書籍やDockerの公式ドキュメントにはマルチステージビルドに関して詳しく記載されているので、まずはそこを読んでみるのが良いかと思います。
Q: ベースイメージの選び方でUbuntuなどのOSイメージではなく、Pythonなどのプログラミング言語のDockerイメージもベースのOS(alpine、bullseyeなど)を変えるとサイズに影響するという認識でいいでしょうか?
A: 端的にいうと影響はあります。ただし、Pythonなどを動かそうとすると、どうしてもライブラリなどを入れなければならないので、結局あまり変わらない状況になってしまいます。
また、選択するOSによっては挙動が異なったり、パフォーマンスに影響が出ることもあるので、イメージサイズにあまりこだわり過ぎず、アプリケーションが想定通り動くのかという観点でベースイメージを選ぶべきだと思います。
Q: Dockerコンテナを実運用で使っている会社が複数のDockerイメージ使う場合はベースのOSとかは合わせてたりするんですかね?自分が使う場合はあんまりベースOSをそこまで気にしてない・・・逆にベースOSがいっぱいあると、Docker初心者はどれ使って良いか悩む、、
A: 無理に合わせる必要はないと思います。ただし、パッケージ管理コマンドがベースイメージのOSによって異なる場合があるので、そう言った際はメンテナンスの手間になる可能性があります。そう言った意味では、組織の教育コストや使い勝手を考えると合わせるという選択肢もありますが、機能的には揃える必要性はありません。
また、初心者がどのベースOSを選ぶかという観点では、一番使われているディストリビューションを使うのが良いと思います。Debian、Ubuntu、Alpineあたりが鉄板だと思います。もし慣れているOSがあればそれがベストだと思います。
Q: そもそもコンテナイメージの選定とかDockerfileを書くことに対してオーナーシップを発揮すべきはDev側?Ops側?ミドルウェアあたりを考えるとDev側の気もするけど、そうすると塩漬けになりがち?
A: Dockerfileはアプリケーションを作るDev側が書くべきではないかと思います。使いたいミドルウェアやライブラリを判断するのが開発者なので、開発者がオーナーシップをもつべきだと思います。ただ、実際にDockerfileで環境を設定するためにはフルスタックエンジニアのスキルが必要となるので、特に開発初期段階ではそう言ったスキルを持ったエンジニアがしっかりとサポートする形でDockerfileを作成する必要があります。
一方で、Ops側はCIのプロセスの中でhadolintなどの静的解析ツールやyamoryなどの脆弱性管理クラウドを使用して、Dockerfileが適切な状態になっているのかをチェックする仕組みを用意する必要があります。