はじめまして。VMware の伊藤です。Tanzu 製品のプラットフォームアーキテクトとして働いており、開発と運用双方の経験があります。この記事では Tanzu Build Service を使って、コンテナイメージのビルドをする方法と、コード更新およびセキュリティ更新に起因する自動ビルドについて扱います。
Tanzu Build Service (以下 TBS)の概要について把握されていない場合は、先に TBS 環境の構築を扱った以下の記事をご参照ください。
TBS の概要としては、Kubernetes (以下 K8s)のアプリケーションとして TBS を構築すると、
- Dockerfile なしでビルドを実施できる
- 一度ビルドしたアプリはコード更新/セキュリティ定義更新をトリガに自動ビルドされる
- ビルドされたイメージは Dockerfile に比べて一般的にセキュアとなる
- 上記に起因して属人性の排除や、開発/運用の時間的コストを削減する
というメリットがあります。
TBS シンプルな CI 基盤(自動でビルドをする仕組み)として動作をしますので、本格的な CI パイプラインの部品として利用したり、メンテナンスフリーな CI を個人ごとやブランチごとに大量に作る場合の管理コスト圧縮に使えます。
この記事では以下の流れで TBS の主要機能の解説をします。
- 初回のアプリケーションビルド
- コードコミットを起点とする自動ビルド
- セキュリティアップデートを起点とする自動ビルド
比較対象としてのDockerfileによるビルド
TBS のビルドを実施するまえに、参考情報として Dockerfile のビルドと、その成果物の確認を実施します。
ここでは以下の流れでビルドと確認をします。
- Spring Music という Spring Boot の有名なサンプルアプリのコードを取得
- そこそこ妥当な Dockerfile を記述
- docker image build コマンドでビルド
- コンテナレジストリにイメージをPush
- レジストリ上でPushされたイメージの脆弱性検査を実施
以下に上記ステップの詳細を図として記載します。
まず、コンテナイメージをビルドする図の左下の Dockerfile ですが、その内容は以下となります。
- Docker Hub の Java の公式リポジトリから、Java11 の最新版ビルド用イメージを取得
- ソースコードを Gradle を使ってビルド
- 作成した成果物の Jar を保存
- Docker Hub の Java 公式リポジトリから、Java11 の最新版実行用イメージを取得
- 先ほど作成した Jar をコピーする
- コンテナ起動時に Jar を java コマンドで実行する
この内容から分かるようにステージングビルドにより、コンテナのベース OS イメージをそのまま使って、特に細かな指定なく Gradle を使ってJavaアプリをビルドして実行させています。作業内容を見てもらうとわかるように、これらの工程では特に脆弱性を発生させる意図的な操作はしていませんし、使っているイメージも最新のものを使っています。
ただし、このようにして構築した「妥当そうに見える」イメージを脆弱性スキャンにかけてみると、図の右のスキャン結果にあるように「Critical2つ。High17個」と重大な脆弱性が多く含まれていることがわかります。本来であれば、上記の Dockerfile を調整して、これらの脆弱性を潰す作業がこの「とりあえずビルドができた」という状態から開始します。
この脆弱性を潰す作業はなかなか大変ですし、古い脆弱性対応の作業内容は時間がたつにつれて陳腐化します。そのため、定期的にセキュリティチェックを実施して、その対策を更新していく必要があります。
TBS の初回のアプリケーションビルド
次に Dockerfile でビルドしたものと同じ Java アプリケーションを TBS でビルドをします。TBS でビルドをする手法はいくつかありますが、多くの場合は以下の手法を使います。
- ビルドするアプリケーション用の Git リポジトリやブランチを用意
- ビルドしたイメージを保存するコンテナレジストリを用意
- 「どこ(Git の URL)からコードを取得するか」と「どこ(コンテナリポジトリ)にイメージを保存するか」を指定して初回ビルド
TBS は多くの作業を自動化していますが、「なにをビルドするか」と「どこにイメージを保存するか」はユーザーが指定する必要があるため、それを初回ビルドで指定します。この作業を以下に図示します。
作業内容としては単純で、Git とコンテナレジストリが用意できたら、それらを指定して図の「kpコマンド」でビルド指示を TBS にたいして発行するだけです。こうすると、TBS が Git からコードを取得してイメージにビルドし、できたイメージをコンテナリポジトリに Push します。
図の右下に TBS でビルドしたコンテナイメージを脆弱性検査にかけた結果を記載しています。Dockerfile でビルドしたときの「Critical2つ。High17個」に比べると、「Criticalが0。Highが2つ」なので、脆弱性の観点では TBS でビルドされたイメージのほうが大幅に優れていることが分かるかと思います。実はこの残る脆弱性は古いセキュリティ定義を TBS に適用することでわざと含ませたものなので、後でセキュリティ定義を更新させることで解決させます。
本来はこの重大な脆弱性が少ない状態を作ることを開発者なり運用者なりが頑張って実現する必要がありますが、TBS を使うことで「セキュリティを高める」という作業をシステムにアウトソースしています。つまり、ユーザーの代わりに製品開発元の VMware がコンテナのセキュリティ対策を実施しているということです。
多くのユーザーにとっては、セキュリティの実現は必要な作業ではあるものの、アプリケーションの優位性を生み出す作業ではあるため、コストがかかる作業とみなされています。こういったコンテナビルドにセキュリティを付与する製品を採用すると、セキュリティ対策にあてるコストをアプリそのものの開発リソースや運用リソースに割り振ることができるようになります。
コードの更新(コミット)による自動ビルド
手動でのコンテナアプリの開発では「コードを更新してからイメージをビルドし、イメージを Push する」という作業を頻繁に実施します。この作業は面倒なので、スクリプト化したり CI パイプラインを構築することが多いです。TBSはこの作業を完全に自動化します。
図にあるように開発者が更新したソースコードを「TBSで初回ビルドで指定したリポジトリ」にたいしてコミットすると、TBSは「1. 新規コミットの検知、2.コードの取得、3.ビルド、4. 新しいタグでイメージをコンテナリポジトリに Push」という流れで自動ビルドを実施します。
たとえば、先程の Spring Music のアプリケーションを変更すると以下のようになります。
ここでは、ソースコードのテンプレートにかかれているアプリのヘッダ(ページ上部のバー)のテキストを変更しています。変更内容を Git リポジトリにコミットすると、TBS はコミットを検知してビルドをしなおした新しいイメージをリポジトリに Push します。
その Push されたイメージを展開すると、右下のようにコードの更新がコンテナイメージに反映されていることがわかります。
セキュリティ定義の更新による自動ビルド
コンテナイメージのセキュリティの問題は、多くの場合は以下から発生します
- コンテナイメージのベース OS に含まれる脆弱性
- ベース OS 上に構築したアプリ自体の脆弱性
TBS はこれらの脆弱性が少なくなるように開発されていますが、それでも古いベース OS イメージを使ってビルドをしたり、古いビルド定義でアプリケーションをビルドをしたりすると、それぞれの脆弱性が紛れ込んでしまう危険性があります。この「古いイメージや古い定義を使うと新規に発見された脆弱性が入り込む」という問題は、新しいセキュリティ定義を使えば解決します。
TBS は新しい定義の更新を「新しいセキュリティ定義ファイルを TBS に適用」というかたちで実現します。ここで重要なのは、セキュリティ定義の更新後にビルドをされたイメージに新定義が適用されるだけでなく、「既存のイメージにもセキュリティ定義が適用される」という点です。つまり、セキュリティ定義を適用後に自分たちで既存アプリを全てビルドしなおして脆弱性を解決する作業が不要となります。これは組織が管理するコンテナイメージが数十個、数百個あるようなシナリオでは開発者と運用者の負担を大きく減らします。
このセキュリティ定義の更新作業は非常に簡単で、VMware のページからファイルをダウンロードし、それを TBS にコマンドで適用するだけです。
古いセキュリティ定義で発生していた図右上にある2つの重大脆弱性は、新しい定義を適用したあとで自動ビルドされたイメージ(図右下)では解決されています。
このセキュリティ定義の更新は今回の例のように運用者が管理することが一般的ですが、それすら面倒という場合はセキュリティ定義の更新を自動化することも可能です。詳しくは公式ドキュメントの構築情報をご参照ください。
以上で TBS の機能紹介の記事を終了したいと思います。ご拝読ありがとうございました。