Dockerのイメージの中で、
サイズが小さくてセキュリティが高い印象を受ける有名どころとしては、
Alpineなどがあるかと思いますが、
もっとこれらに特化したイメージがあります。
それが本記事で取り上げるChainguardです。
もしかすると、そこまで知名度があるわけではないかもしれませんが、
イメージサイズとCVEの少なさは随一です。
高度のセキュリティを誇ることが、Chainguardの特徴です。
本記事では、そんなChainguardとは何か?から、
実際に一般的なDockerイメージと比べた特徴を、
脆弱性スキャンを行いつつハンズオンします。
日常的にDockerを使用していて、
より良いDockerイメージを模索している人にとって、
参考になれば幸いです。
そもそもDockerがよくわからんっていう人は、
下記の記事と動画で詳細に解説しているので、
そちらを先にご覧になってからのほうが、
本記事の理解がスムーズかもしれません。
Chainguardとは何か?概要解説
Chainguard(イメージ)は、Chainguard社によって提供されている、
高いセキュリティを誇るコンテナイメージ群です。
dockerhubでChainguardと検索すると、
500以上のイメージがヒットすることがわかります。
https://hub.docker.com/search?q=Chainguard
Chainguardの特徴は、やはり従来のコンテナイメージの構築と運用に伴う
セキュリティリスクを軽減していることでしょう。
下記が公式のページです。
0 CVE containerと打ち出していることからも分かる通り、
セキュリティやコンテナイメージサイズの縮小などに強みを持った、
イメージであることがわかります。
ちなみに、CVEというのは、
Common Vulnerabilities and Exposures の略称で、共通脆弱性識別子と言ったりする、
要するに脆弱性のリストのようなものかと思います。
https://www.sompocybersecurity.com/column/glossary/cve
https://www.redhat.com/ja/topics/security/what-is-cve
そんな脆弱性のリストが0ということからも分かる通り、
Chainguardが一般的なDockerイメージと比べて高いセキュリティを誇っていることが伺えます。
Chainguardのセキュリティ
すでに述べたようにChainguardの大きな魅力は、
そのセキュリティ機能です。
その中でも、Chainguardイメージは、
Wolfiというアンディストリビューション(undistro)なOSをベースとしていることが
大きな特徴でしょう。
https://edu.chainguard.dev/open-source/wolfi/overview
また、distrolessなイメージとして、
不要なソフトウェアが入っていないことも特徴です。
https://edu.chainguard.dev/chainguard/chainguard-images/getting-started-distroless
Alpineとかを使ったことがある方は、
コンテナサイズが小さい分、何も入っていないから、
自分で結構色々インストールしなきゃいけなかったみたいない印象があるかもしれません。
ChainguardイメージはそんなAlpineよりもさらに無駄が削ぎ落とされています。
というのも、Chainguardは、下記のような特徴をもっています。
(※公式のやつを訳して貼ってあるだけなので、公式の方を見てもらったほうがいいかもです)
ポイント
・不要なソフトウェアを排除したミニマルなデザイン
・自動化ビルドにより、イメージの最新性と利用可能なセキュリティパッチを確保していること
・イメージ内のすべてのアーティファクトの出所を証明する高品質のビルドタイム SBOM (ソフトウェア部品表)
・Sigstore によって提供される検証可能な署名
・Cosign と apko による再現可能なビルド
とまあ、基本的にはもともと不要なソフトウェアを排除したミニマルデザインなのに、
その中でも使われている技術の信頼性は担保しているという、
本当にセキュリティに特化しているなという印象を受けました。
こうした特徴から、Chainguardはセキュリティに特化していることはもちろん、
コンテナサイズの縮小にも寄与したコンテナプラットフォームとして、非常に魅力的だと考えます。
よく使用されるDockerイメージと何が違うのか?
さて、そんなChainguardですが、
よく使用されるDockerイメージと何がどれぐらい違うのでしょう。
なんとなくセキュリティがすごそうということはわかりましたが、
まだいまいちイメージがわかないかと思うので、
ここでRubyのChainguardイメージ、RubyのAlpineイメージ、一般的なRubyのDockerイメージの
3つのコンテナを立ててみようと思います。
Rubyの一般的なDockerイメージの環境構築
まずは、普通のDockerイメージの方を環境構築します。
簡易的なもの(というか脆弱性スキャンがメイン)なので、
今回はsinatraを使おうと思います。
まずは起動するapp.rbを作成します
require 'sinatra'
set :bind, '0.0.0.0'
get '/' do
'Hello, from Docker!'
end
次にGemfileを作成します。
sinatraだけだとエラーになるので、rackupも追加します
source 'https://rubygems.org'
gem 'sinatra'
gem 'rackup'
次にDockerfile.rubyを作成します
FROM ruby:latest
WORKDIR /app
COPY Gemfile ./
RUN bundle install
COPY . .
CMD ["ruby", "app.rb"]
まあよくあるDockerファイルの記法という感じですね。
あとは、buildとrunをします。
docker build -t ruby-demo -f Dockerfile.ruby .
docker run -d -p 5000:4567 --name ruby-demo-container ruby-demo
docker ps
を行って、問題なくコンテナが立ち上げっていればOKです
RubyのChainguardイメージの環境構築
次にChainguardの方も環境構築します。
Gemfileとapp.rbは共通なので、
Dockerfile.chainguardだけ作成します
FROM cgr.dev/chainguard/ruby:latest-dev as builder
ENV GEM_HOME=/work/vendor
ENV GEM_PATH=${GEM_PATH}:/work/vendor
COPY Gemfile /work/
RUN gem install bundler && bundle install
FROM cgr.dev/chainguard/ruby:latest
ENV GEM_HOME=/work/vendor
ENV GEM_PATH=${GEM_PATH}:/work/vendor
COPY --from=builder /work/ /work/
COPY app.rb /work/
ENTRYPOINT [ "ruby", "app.rb" ]
さて、Chainguardの方ですが、
こちらは少し書き方がトリッキーになります。
普通のRubyイメージの書き方と同じようにすると、
画像のようにshがない的なエラーになって
bundle installとかができません。
これは最初にちらっと触れましたが、
distrolessなイメージとして
シェルとかパッケージマネージャーも省かれているので、
まあ当然といえば当然ですね。
なのでFROMでは、cgr.dev/chainguard/ruby:latest
ではなくて、cgr.dev/chainguard/ruby:latest-dev
を指定します。
ただ、これだけでもダメで、
今度はrubyコマンドがない的なエラーにも遭遇するので、
multi stage build構成にします。
この辺の詳細はこのあたりに書かれています。
https://edu.chainguard.dev/chainguard/chainguard-images/getting-started/ruby
書きなれたDockerfileの記法とは少し異なる書き方が必要なので、
このあたりは慣れが必要かもしれません。
docker build -t chainguard-ruby-demo -f Dockerfile.chainguard .
docker run -d -p 5001:4567 --name chainguard-ruby-demo-container chainguard-ruby-demo
さて、docker ps
で無事立ち上がればOKです
RubyのAlpineイメージの環境構築
さて、3つ目にAlpineの方も行おうと思います。
Gemfileとかapp.rbは同じく共通なので、
Dockerfile.alpineをまず作成します
FROM ruby:alpine
WORKDIR /app
COPY Gemfile ./
RUN bundle install
COPY . .
CMD ["ruby", "app.rb"]
alpineの場合、何か別途インストールしないとダメかなと思いましたが、
意外にも普通のRubyイメージと同じ構成のDockerfileで問題なく立ち上がりました。
docker build -t ruby-demo-alpine -f Dockerfile.alpine .
docker run -d -p 4567:4567 --name ruby-demo-alpine-container ruby-demo-alpine
何も入っていないように思えたAlpineも、
比べてみるとまだ色々入っている方なのかもしれないと感じる瞬間でした
各イメージの脆弱性スキャン
さて、各イメージの環境構築をしたので、
脆弱性スキャンやイメージサイズを比較してみます。
まず、イメージのサイズを比較してみます。
docker images | grep demo
一般的なRubyイメージが以下に巨大かが伺えますね。
alpineも結構小さいですが、Chainguardはそれを更に上回ってサイズが小さいですね。
trivyを使った脆弱性スキャン
次に、trivyを使用して、イメージのセキュリティスキャンも実行してみます
trivyがなければ、homebrewとかでインストールしておきます。
https://github.com/aquasecurity/trivy
brew install trivy
trivy image ruby-demo
trivy image chainguard-ruby-demo
chainguard-ruby-demoが圧倒的に脆弱性が少ないことが伺えます
grypeを使った脆弱性スキャン
grypeの方でも、脆弱性スキャンを行ってみようと思います。
https://github.com/anchore/grype
まずは公式に従ってcurlでインストールしときます
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
とやってみたが、
curlだと上手いことインストールできなかったので、
homebrewでインストールしました。
brew tap anchore/grype
brew install grype
インストールしたら、早速スキャンしてみます。
grype ruby-demo
grype ruby-demo-alpine
grype chainguard-ruby-demo
ruby-demoの方は、
CVEがたくさん出てきましたが、
chainguard-ruby-demoの方は、
なんと驚きのNo vulnerabilities found
でした。
(alpineもmediumが1つだけだったのでかなり少ない...!)
すごい!!
ていうか、これをやるまで
知りませんでした、普通のRubyイメージの方は、
gitとかpython3まで入っているんですね...
(このgitに脆弱性があるようでしたが)
Docker Scoutでの比較
もうtrivyやgrypeを使って比較したので、
結果は見えていますが、
最後にdockerhubのScoutを使って見ようと思います。
まずは、dockerhubのアカウントを作成して、
cliでログインします。
docker login
次に、それぞれのイメージをタグ付けし、
pushします。
docker tag ruby-demo <your-dockerhub-username>/ruby-demo
docker tag ruby-demo-alpine <your-dockerhub-username>/ruby-demo-alpine
docker tag chainguard-ruby-demo <your-dockerhub-username>/chainguard-ruby-demo
docker push <your-dockerhub-username>/ruby-demo
docker push <your-dockerhub-username>/ruby-demo-alpine
docker push <your-dockerhub-username>/chainguard-ruby-demo
pushしたら、docker-hubにアクセスします
chainguardの脆弱性は0でした。
alpineでも5つぐらいありますね。
普通のRubyイメージは115個...
Dockerイメージと比べたChainguardの魅力
本記事では、有名どころのDockerイメージと
Chainguardイメージを主にイメージサイズと脆弱性の観点で
比較してみました。
Chainguardイメージは、やはりその高いセキュリティと
極限にまで縮小されたイメージサイズが特徴であり、
それらが大きな魅力だと私は感じました。
何気なく使っている、Dockerの普通のイメージとかが、
以下に肥大化したソフトウェアを内包していて、
多くの脆弱性を抱えているかが本記事で明らかになったかと思います。
Chainguardは、日本語での情報がまだ多くはなく、
通常のDockerfileに比べ、ややトリッキーな記法や、
Wolfi OSやdistroless、undistroといった、
人によっては馴染みが薄い概念も多くあるので、
技術的キャッチアップコストは、通常のDockerイメージよりは大きくなるかもしれませんが、
それを払ってでも、積極的に採用したい魅力的なDockerイメージだと思います。
本記事が、より良いDockerイメージの技術選定の参考になれば幸いです。