トーク内容の目次

伊藤裕一氏(以下、伊藤):「脱 Dockerfile! Cloud Native Buildpacksとkpackを使った簡単で安全なイメージ」という内容について、伊藤がお話しします。

目次です。最初にDockerfileのおさらいと、問題点を話します。そして、Dockerfileを使わずにビルドを実施するCloud Native Buildpacks(CNB)の概要とデモについて話し、その次にKubernetes上でCNBを実施するkpackの概要とデモを実施します。

最後に、CNBとkpackを使ってCIパイプラインを構築する例を説明し、最終的にkpackの製品版であるVMware Tanzu Build Serviceの紹介を実施します。

Dockerfileのおさらい

最初にDockerfileのおさらいです。Dockerfileについては多くの方が知っているかと思いますが、コンテナのイメージをビルドするための定義ファイルです。左下に例が書いてあります。イメージを構築するためのベースイメージがこのOSだと思ってもらうと、簡単に言えば、このOSの上にどういったパッケージを入れるのか。

YAMLやaptを入れたり。あとはアプリケーションのソースコードを取り込んで、それでライブラリをインストールしてコードをビルドして、実行可能ファイルが内部的にできたら、実行可能ファイルを実行するための実行コードを定義する。このようなかたちでコンテナのイメージを作成します。

このDockerfileですが、その中身に1、2、3、4、5、6とあります。各手順を順番に羅列するようなかたちのため、Bashのスクリプトに近く、AnsibleやKubernetesなどで使うYAMLのように定義しているようなものとは、ちょっと異なります。導入する敷居が非常に低く、多くのユーザーがいるツールです。

Dockerfileの問題点

このDockerfileですが、少し問題点があります。まず、Dockerfileを多くの方が実際に使った際に、どういう感じで使い始めたかを考えます。私もそうだと思いますが、例えば自分がJavaのアプリケーションで「このフレームワークを使いたい」と思った際に、その名前でインターネット検索をして、作りたいものに近いDockerfileを探してくる。

その見つけてきたDockerfileを、自分のアプリケーションで動くように、修正を何回も何回も繰り返しながら、ちゃんと動くものを作る。そういったかたちで実行する方が多いと思います。そうやって作ったDockerfileですが、実際にアプリケーションを動かせるものの、本当にそれがプロダクションレディなのかは疑わしい場面が多いです。

最初に使っているイメージや、利用しているライブラリ。これは脆弱性が報告されているものを利用してしまっている可能性があります。仮に今はちゃんと問題がないと把握した上で使っていたとしても、そのDockerfileを2年後に使った際に、2年前の状態のまま使ってしまう。そうすると、今はないトラブルが、将来的に発生する可能性はありえます。

他にも、例えばビルドする際のパラメーターがちゃんと適切になっているか。そういったことを保証できないので、Dockerfileで作られているイメージは問題がある場合が多いです。

例にあるJavaの場合でも、非常に詳しい方がDockerfileを書けば、そのDockerfileは信頼性が高いものが作れると思います。ただ、開発者が50人いて、それぞれの開発者が10個のDockerfileを書くシナリオだと、合計500のDockerfileを更新しながらずっと使い続けていく。このようなことを想定すると、本当に全部のDockerfileが正しく作られているのかは、非常に怪しいのではと私は思います。

このようなDockerfileの問題ですが、基本的に問題の根っこにはDockerfileを属人的に運用して、それを陳腐化しないためには、ちゃんと更新し続けないといけない問題があります。それを解決するためには、Dockerfileのように自分たちで細かいステップを管理するのではなく、例えばJavaのアプリケーションやPythonなどが、ソースコードの中身に応じてベストプラクティスに沿った自動ビルドの仕組みを実施する。

そうすると、属人的な問題が入り込む余地がなくなります。ビルドを行う際にも、例えば今ビルドするものと2年後にビルドするものは、同じものでビルドすれば同じ動きをします。しかし、脆弱性の観点を考えると、2年後にちゃんと更新していないといけないシナリオが多いと思います。

そういった際に、Dockerfileを自分でいじって更新するのではなく、先ほどお話したベストプラクティスに沿って自動ビルドをする仕組みのビルドの定義。これさえ新しくすれば、必要なイメージを使うベースOSやライブラリを、新しいバージョンでビルドし直せる。こういった方法で解決が図れます。

Dockerfileの問題点を解決する1つの方法がCloud Native Buildpacks

この解決を図る1つの手法として挙げられるものが、CNBです。CNBは簡単に言うと、Dockerfileで実現していたビルドなどをどうビルドを定義するのかを定義されているスクリプトファイルです。

それを使うことによって、自動でコードから、例えば「これはJava」「これはPython」と判定して、その判定に沿ってJava用のベストプラクティスに沿ったビルドをする、Python用のベストプラクティスに沿ったビルドをする、といったかたちでイメージを作成するためのツールになります。

これを採用すると、Dockerfileを書く手間がまずかからなくなる。当然Buildpacksのバージョンやビルド定義が古ければ脆弱性が入ってしまう可能性がありますが、きちんと更新して、ちゃんと管理されているものを使う限りは、そのリスクが大幅に減ります。

あとは、Dockerfileに比べてキャッシュを利用する仕組みが優秀なので、何回も何回もいろいろな場所でビルドするシナリオでは、より高速にビルドできる可能性が高くなります。

Cloud Native Buildpacksの使い方デモ

このCNBですが、どう使うのかをデモで見せたいと思います。

今、/javaのディレクトリにいて、フォルダの中身一覧を出しています。これがpom.xmlといって、どういったライブラリを使うかを定義しているものです。今回だとSpring Bootを使っています。

pack build java-app --builder gcr.ioとありますが、これはCNBでビルドをするときのコマンドで、このpack buildでビルドする。java-appがイメージ名で、--builderがそのビルドに使う定義ファイル。これはコンテナですが、Google上のBuildpacksのbuilderです。

ビルドが終わったあと、すぐdockerコマンドでイメージを起動するように指定しています。このコマンドを発行すると、このようにビルドが開始されます。これは事前に1回ビルドしておいたので一瞬で終わりましたが、最終的にはEXPORTINGでイメージを出力して、そのイメージをdockerコマンドで起動して、Spring Bootが起動しているかたちになっています。

その起動したアプリケーションに対して、ターミナルのブラウザのlinksでアクセスすると、ちゃんと先ほどのようにレスポンスが返ってきて、アプリが動いていることがわかりました。同じようにディレクトリを/goに変更して、Goのソースコードと使うパッケージを定義したgo.modがある状態でgo-appをビルドする。

ビルドコマンドはさっきと同じです。それでビルドしたあと、dockerコマンドをすぐ動かす。指定してビルドすると、イメージを出力してイメージが走り始めました。

出力したいGoのアプリに対してまたlinksでアクセスすると、このようにGinというフレームワークを使っているGoのWebサーバーが動いていることが確認できました。

その次にPythonでアプリのディレクトリの構成。これはFlaskを使っているので、Flaskを使う宣言はrequirements.txtに書いています。Pythonでもまったく同じコマンドです。ビルドコマンドを発行してビルドして、今Flaskのライブラリをダウンロードしています。

イメージがビルドできたので、dockerコマンドで起動。そしてまたlinksでアクセス。

そうするとFlaskが動いています。レスポンスが返ってきたので、Dockerのイメージがちゃんとビルドできて起動できていることがわかりました。他に.NETのアプリや、Ruby、PHPもビルドできます。

(次回につづく)