目次
BuildpackとDockerfile
Cloud Foundryのbuildpackは便利で、"Source Code to Container Image"を実現してくれます。
Dockerを用いてアプリケーションをデプロイする場合は、一般的に
- ソースコード作成/修正
Dockerfile
作成/修正- Dockerイメージの作成
- Dockerイメージのデプロイ
- Dockerイメージを使ってアプリケーション(コンテナ)をデプロイ
ですが、Cloud Foundryではbuildpackを使うことにより
- ソースコード作成/修正
cf push
コマンドでアプリケーション(コンテナ)をデプロイ
で完了します。
Dockerの場合、通常Dockerfile
を作ることになりますが、これをメンテナンスするということは、アプリケーションコードのメンテナンスの他に
- OSイメージ(opensshの脆弱性対応など)
- ランタイム/ミドルウェア(Javaの場合JDKやTomcat、PHPの場合Apache HTTPやmod_phpなど)
のメンテナンスもしなくてはなりません。Dockerfile
は自由度が高い反面、ケアすべき点も多いです。
一方、buildpackではrootfsとしてcflinuxfs2というUbuntu14.04の派生のイメージを使用します。定期的にセキュリティパッチがあてられています。 また各言語ごとにランタイム/ミドルウェアがメンテナンスされており、原則としてCVE(Common Vulnerabilities and Exposures)公開後48時間以内にパッチがリリースされます。 これにより、基本的にアプリケーションコードにのみ集中することができます。
個人的には、アプリケーションデプロイの観点ではDockerfile
の作成はあまりやりたい作業でありません。Buildpack方式のほうが好みです。
CF Local Plugin
CF Local Pluginを使用することで、前述のbuildpackによるメリットをDockerイメージ作成にも適用できます。
CF LocalはLaptop内でcf push
をエミュレートするプラグインですが、DockerイメージへのExportに対応しています。
これを使うことにより、Dockerを用いてアプリケーションをデプロイする場合も、
- ソースコード作成/修正
- CF LocalでDockerイメージの作成
- Dockerイメージのデプロイ
- Dockerイメージを使ってアプリケーション(コンテナ)をデプロイ
になり、Dockerfile
のメンテナンスから解放されます。
これでBuildpackのメリットを活かしつつ、DockerイメージをKubernetesにデプロイするといったことが可能になります。
CF Localの利用
では実際にCF Localで"Source Code to Docker Image"を行い、アプリケーションをk8sにデプロイしてみます。
Cloud Foundry CLIのインストール
まずはcf
コマンドが必要です。golangの実行可能バイナリなので、インストールは容易です。
CF CLIのインストールはこちらを参照してください。
Macの場合は、
brew install cloudfoundry/tap/cf-cli
でインストールできます。
CF Local Pluginのインストール
CF Local Pluginのバイナリはこちらからダウンロード可能です。
Macの場合は、
curl -L -J -O https://github.com/sclevine/cflocal/releases/download/v0.13.0/cflocal-0.13.0-macos
cf install-plugin cflocal-0.13.0-macos
でインストールできます。
コンテナイメージの作成
今回は次の簡単なJavaアプリケーション(ただのサーブレット)を題材に使用します。 https://github.com/making/hello-servlet
git clone git@github.com:making/hello-servlet.git
cd hello-servlet
./mvnw package
でtarget/ROOT.war
ファイルが作成されます。
その後、cf local stage
コマンドでステージングを行い、コンテナイメージ(Droplet)を作成します。
cf local stage hello-servlet -p ./target/ROOT.war
次のようなログが出力されます。JDKやTomcatがダウンロードされていることがわかります。またコンテナのメモリサイズ(デフォルトで1GB)に合わせて、JVMのメモリの設定が行われていることもわかります。メモリサイズは-m
で変更可能です。
Buildpack: will detect
[hello-servlet] 2017-08-21T04:32:48.695440393Z -----> Java Buildpack Version: v3.17 | https://github.com/cloudfoundry/java-buildpack.git#87fb619
[hello-servlet] 2017-08-21T04:32:51.033834076Z -----> Downloading Open Jdk JRE 1.8.0_141 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_141.tar.gz (2.1s)
[hello-servlet] 2017-08-21T04:32:51.977072148Z Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (0.9s)
[hello-servlet] 2017-08-21T04:32:52.094011807Z -----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (0.1s)
[hello-servlet] 2017-08-21T04:32:52.117972916Z Memory Settings: -Xss349K -Xmx681574K -XX:MaxMetaspaceSize=104857K -Xms681574K -XX:MetaspaceSize=104857K
[hello-servlet] 2017-08-21T04:32:52.223085215Z -----> Downloading Container Security Provider 1.8.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-security-provider/container-security-provider-1.8.0_RELEASE.jar (0.1s)
[hello-servlet] 2017-08-21T04:32:53.174939975Z -----> Downloading Tomcat Instance 8.0.46 from https://java-buildpack.cloudfoundry.org/tomcat/tomcat-8.0.46.tar.gz (0.9s)
[hello-servlet] 2017-08-21T04:32:53.278926558Z Expanding Tomcat Instance to .java-buildpack/tomcat (0.1s)
[hello-servlet] 2017-08-21T04:32:53.436115761Z -----> Downloading Tomcat Lifecycle Support 2.5.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-lifecycle-support/tomcat-lifecycle-support-2.5.0_RELEASE.jar (0.1s)
[hello-servlet] 2017-08-21T04:32:53.668302543Z -----> Downloading Tomcat Logging Support 2.5.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-logging-support/tomcat-logging-support-2.5.0_RELEASE.jar (0.2s)
[hello-servlet] 2017-08-21T04:32:53.747964428Z -----> Downloading Tomcat Access Logging Support 2.5.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-access-logging-support/tomcat-access-logging-support-2.5.0_RELEASE.jar (0.0s)
Successfully staged: hello-servlet
Gifアニメも貼っておきます。
Java以外の場合、
-p
は不要で、カレントディレクトリからステージングが行われます。
作成されたコンテナイメージはcf local run
コマンドで実行できます。
$ cf local run hello-servlet
Running hello-servlet on port 54921...
[hello-servlet] 2017-08-21T04:51:20.509802947Z [CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Initializing ProtocolHandler ["http-nio-8080"]
[hello-servlet] 2017-08-21T04:51:20.517556491Z [CONTAINER] org.apache.catalina.startup.Catalina INFO Initialization processed in 371 ms
[hello-servlet] 2017-08-21T04:51:20.524758060Z [CONTAINER] org.apache.catalina.core.StandardService INFO Starting service Catalina
[hello-servlet] 2017-08-21T04:51:20.525311765Z [CONTAINER] org.apache.catalina.core.StandardEngine INFO Starting Servlet Engine: Apache Tomcat/8.0.46
[hello-servlet] 2017-08-21T04:51:20.539589930Z [CONTAINER] org.apache.catalina.startup.HostConfig INFO Deploying web application directory /home/vcap/app/.java-buildpack/tomcat/webapps/ROOT
[hello-servlet] 2017-08-21T04:51:20.815559712Z [CONTAINER] org.apache.jasper.servlet.TldScanner INFO At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[hello-servlet] 2017-08-21T04:51:20.869397279Z [CONTAINER] org.apache.catalina.startup.HostConfig INFO Deployment of web application directory /home/vcap/app/.java-buildpack/tomcat/webapps/ROOT has finished in 329 ms
[hello-servlet] 2017-08-21T04:51:20.874792454Z [CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Starting ProtocolHandler ["http-nio-8080"]
[hello-servlet] 2017-08-21T04:51:20.880862674Z [CONTAINER] org.apache.tomcat.util.net.NioSelectorPool INFO Using a shared selector for servlet write/read
[hello-servlet] 2017-08-21T04:51:20.889905003Z [CONTAINER] org.apache.catalina.startup.Catalina INFO Server startup in 371 ms
この例ではlocalhostの54921ポートにアクセスすると、コンテナ内の8080ポートにフォワードされます。
$ curl localhost:54921
██╗ █████╗ ███╗ ███╗ ███████╗████████╗██╗██╗ ██╗ █████╗ ████████╗ ██╗ ██╗ █████╗ ██████╗
██║ ██╔══██╗████╗ ████║ ██╔════╝╚══██╔══╝██║██║ ██║ ██╔══██╗╚══██╔══╝ ██║ ██║██╔══██╗██╔══██╗
██║ ███████║██╔████╔██║ ███████╗ ██║ ██║██║ ██║ ███████║ ██║ ██║ █╗ ██║███████║██████╔╝
██║ ██╔══██║██║╚██╔╝██║ ╚════██║ ██║ ██║██║ ██║ ██╔══██║ ██║ ██║███╗██║██╔══██║██╔══██╗
██║ ██║ ██║██║ ╚═╝ ██║ ███████║ ██║ ██║███████╗███████╗ ██║ ██║ ██║ ╚███╔███╔╝██║ ██║██║ ██║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝
DockerイメージへExport
ここまででCloud FoundryのコンテナイメージフォーマットであるDropletファイルが作成されました。これをcf local export
コマンドでDockerイメージのフォーマットにエクスポートできます。
cf local export hello-servlet -r making/hello-servlet
making/hello-servlet
という名前のDockerイメージが作成できました。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
making/hello-servlet latest 61d311f1bd06 3 seconds ago 951MB
<none> <none> 74a5658980c7 About a minute ago 951MB
cflocal latest 79e3268df4cf 5 minutes ago 953MB
cloudfoundry/cflinuxfs2 latest a7adacf72d2a 3 days ago 893MB
もちろん、このイメージはdocker run
で実行可能です。
$ docker run -p 8080:8080 making/hello-servlet
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Initializing ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.catalina.startup.Catalina INFO Initialization processed in 416 ms
[CONTAINER] org.apache.catalina.core.StandardService INFO Starting service Catalina
[CONTAINER] org.apache.catalina.core.StandardEngine INFO Starting Servlet Engine: Apache Tomcat/8.0.46
[CONTAINER] org.apache.catalina.startup.HostConfig INFO Deploying web application directory /home/vcap/app/.java-buildpack/tomcat/webapps/ROOT
[CONTAINER] org.apache.jasper.servlet.TldScanner INFO At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[CONTAINER] org.apache.catalina.startup.HostConfig INFO Deployment of web application directory /home/vcap/app/.java-buildpack/tomcat/webapps/ROOT has finished in 410 ms
[CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Starting ProtocolHandler ["http-nio-8080"]
[CONTAINER] org.apache.tomcat.util.net.NioSelectorPool INFO Using a shared selector for servlet write/read
[CONTAINER] org.apache.catalina.startup.Catalina INFO Server startup in 458 ms
$ curl localhost:8080
██╗ █████╗ ███╗ ███╗ ███████╗████████╗██╗██╗ ██╗ █████╗ ████████╗ ██╗ ██╗ █████╗ ██████╗
██║ ██╔══██╗████╗ ████║ ██╔════╝╚══██╔══╝██║██║ ██║ ██╔══██╗╚══██╔══╝ ██║ ██║██╔══██╗██╔══██╗
██║ ███████║██╔████╔██║ ███████╗ ██║ ██║██║ ██║ ███████║ ██║ ██║ █╗ ██║███████║██████╔╝
██║ ██╔══██║██║╚██╔╝██║ ╚════██║ ██║ ██║██║ ██║ ██╔══██║ ██║ ██║███╗██║██╔══██║██╔══██╗
██║ ██║ ██║██║ ╚═╝ ██║ ███████║ ██║ ██║███████╗███████╗ ██║ ██║ ██║ ╚███╔███╔╝██║ ██║██║ ██║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝
そして、docker push
でDocker Registryへデプロイします。
docker push making/hello-servlet
k8sへのデプロイ
Docker Registryにデプロイした後は、普通にk8sにデプロイできます。
$ kubectl run hello-servlet --image=making/hello-servlet --port=8080
deployment "hello-servlet" created
$ kubectl expose deployment hello-servlet --type=NodePort
service "hello-servlet" exposed
$ kubectl get service hello-servlet -o wide
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hello-servlet 10.0.0.61 <nodes> 8080:32271/TCP 20s run=hello-servlet
$ curl `minikube ip`:32271
██╗ █████╗ ███╗ ███╗ ███████╗████████╗██╗██╗ ██╗ █████╗ ████████╗ ██╗ ██╗ █████╗ ██████╗
██║ ██╔══██╗████╗ ████║ ██╔════╝╚══██╔══╝██║██║ ██║ ██╔══██╗╚══██╔══╝ ██║ ██║██╔══██╗██╔══██╗
██║ ███████║██╔████╔██║ ███████╗ ██║ ██║██║ ██║ ███████║ ██║ ██║ █╗ ██║███████║██████╔╝
██║ ██╔══██║██║╚██╔╝██║ ╚════██║ ██║ ██║██║ ██║ ██╔══██║ ██║ ██║███╗██║██╔══██║██╔══██╗
██║ ██║ ██║██║ ╚═╝ ██║ ███████║ ██║ ██║███████╗███████╗ ██║ ██║ ██║ ╚███╔███╔╝██║ ██║██║ ██║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝
Buildpackの更新
デフォルトのBuildpackのバージョンはCF Local Pluginに依存するため、Buildpackのバージョンを更新したい場合は、-b
で明示する必要があります。
Java Buildpackのバージョンを、執筆時点で最新の4.5にあげてみます。
$ cf local stage hello-servlet -p ./target/ROOT.war -b https://github.com/cloudfoundry/java-buildpack.git#v4.5
Buildpack: https://github.com/cloudfoundry/java-buildpack.git#v4.5
[hello-servlet] 2017-08-21T05:23:44.725841336Z -----> Java Buildpack v4.5 | https://github.com/cloudfoundry/java-buildpack.git#36205c5
[hello-servlet] 2017-08-21T05:23:45.153024223Z -----> Downloading Jvmkill Agent 1.10.0_RELEASE from https://java-buildpack.cloudfoundry.org/jvmkill/trusty/x86_64/jvmkill-1.10.0_RELEASE.so (0.2s)
[hello-servlet] 2017-08-21T05:23:45.201490613Z -----> Downloading Open Jdk JRE 1.8.0_141 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_141.tar.gz (found in cache)
[hello-servlet] 2017-08-21T05:23:46.099868077Z Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (0.8s)
[hello-servlet] 2017-08-21T05:23:46.937437172Z -----> Downloading Open JDK Like Memory Calculator 3.9.0_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-3.9.0_RELEASE.tar.gz (0.8s)
[hello-servlet] 2017-08-21T05:23:47.235578582Z Loaded Classes: 9742, Threads: 300
[hello-servlet] 2017-08-21T05:23:47.359764715Z -----> Downloading Client Certificate Mapper 1.2.0_RELEASE from https://java-buildpack.cloudfoundry.org/client-certificate-mapper/client-certificate-mapper-1.2.0_RELEASE.jar (0.1s)
[hello-servlet] 2017-08-21T05:23:47.416043793Z -----> Downloading Container Security Provider 1.8.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-security-provider/container-security-provider-1.8.0_RELEASE.jar (found in cache)
[hello-servlet] 2017-08-21T05:23:48.042413872Z -----> Downloading Tomcat Instance 8.5.20 from https://java-buildpack.cloudfoundry.org/tomcat/tomcat-8.5.20.tar.gz (0.6s)
[hello-servlet] 2017-08-21T05:23:48.153498196Z Expanding Tomcat Instance to .java-buildpack/tomcat (0.1s)
[hello-servlet] 2017-08-21T05:23:48.211211569Z -----> Downloading Tomcat Access Logging Support 3.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-access-logging-support/tomcat-access-logging-support-3.0.0_RELEASE.jar (found in cache)
[hello-servlet] 2017-08-21T05:23:48.253126051Z -----> Downloading Tomcat Lifecycle Support 3.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-lifecycle-support/tomcat-lifecycle-support-3.0.0_RELEASE.jar (found in cache)
[hello-servlet] 2017-08-21T05:23:48.300813949Z -----> Downloading Tomcat Logging Support 3.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/tomcat-logging-support/tomcat-logging-support-3.0.0_RELEASE.jar (found in cache)
Successfully staged: hello-servlet
これでソースコードや設定ファイルを変更することなく、イメージが更新され、Tomcatが8.5系に上がりました。また、JVMのMemory Calculatorの仕様も変わりました。 メモリ設定をBuildpackにお任せできて楽です。k8sにJavaアプリケーションをデプロイする場合も、Buildpackの恩恵に与ることができます。
$ cf local run hello-servlet
Running hello-servlet on port 55383...
[hello-servlet] 2017-08-21T05:25:36.151338888Z JVM Memory Configuration: -XX:CompressedClassSpaceSize=15326K -Xss1M -Xmx394118K -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=75931K -XX:ReservedCodeCacheSize=240M
[hello-servlet] 2017-08-21T05:25:36.663399060Z [CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Initializing ProtocolHandler ["http-nio-8080"]
[hello-servlet] 2017-08-21T05:25:36.674031978Z [CONTAINER] org.apache.catalina.startup.Catalina INFO Initialization processed in 373 ms
[hello-servlet] 2017-08-21T05:25:36.681052927Z [CONTAINER] org.apache.catalina.core.StandardService INFO Starting service [Catalina]
[hello-servlet] 2017-08-21T05:25:36.681435114Z [CONTAINER] org.apache.catalina.core.StandardEngine INFO Starting Servlet Engine: Apache Tomcat/8.5.20
[hello-servlet] 2017-08-21T05:25:36.709458734Z [CONTAINER] org.apache.catalina.startup.HostConfig INFO Deploying web application directory [/home/vcap/app/.java-buildpack/tomcat/webapps/ROOT]
[hello-servlet] 2017-08-21T05:25:37.030274533Z [CONTAINER] org.apache.jasper.servlet.TldScanner INFO At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[hello-servlet] 2017-08-21T05:25:37.097099849Z [CONTAINER] org.apache.catalina.startup.HostConfig INFO Deployment of web application directory [/home/vcap/app/.java-buildpack/tomcat/webapps/ROOT] has finished in [387] ms
[hello-servlet] 2017-08-21T05:25:37.101279358Z [CONTAINER] org.apache.coyote.http11.Http11NioProtocol INFO Starting ProtocolHandler ["http-nio-8080"]
[hello-servlet] 2017-08-21T05:25:37.110166410Z [CONTAINER] org.apache.tomcat.util.net.NioSelectorPool INFO Using a shared selector for servlet write/read
[hello-servlet] 2017-08-21T05:25:37.126321558Z [CONTAINER] org.apache.catalina.startup.Catalina INFO Server startup in 451 ms
CF Local PluginはもともとはCloud Foundryを使った開発において、ローカル環境でのイテレーションを円滑に行うためのプラグインですが、
これを使って、Dockerfile
を作ることなくBuildpackからDockerイメージを作成し、k8sにデプロイできることを確認しました。
CF Local Pluginはまだ、Stephen Levineさんの個人プロジェクト扱いですが、Cloud Foundry Summit 2017のKeynoteで紹介されていたので、 Cloud Foundry公式プロジェクトになることを期待しています。