---
title: Cloud Foundryのbuildpackを使ってDockerイメージを作成しKubernetesにデプロイ
tags: ["Cloud Foundry", "Docker", "Kubernetes"]
categories: ["Dev", "CaaS", "Kubernetes"]
date: 2017-08-21T05:32:40Z
updated: 2017-08-21T05:53:23Z
---

**目次**
<!-- toc -->

### BuildpackとDockerfile

Cloud Foundryの[buildpack](https://docs.cloudfoundry.org/buildpacks/)は便利で、"Source Code to Container Image"を実現してくれます。


Dockerを用いてアプリケーションをデプロイする場合は、一般的に

1. ソースコード作成/修正
1. `Dockerfile`作成/修正
1. Dockerイメージの作成
1. Dockerイメージのデプロイ
1. Dockerイメージを使ってアプリケーション(コンテナ)をデプロイ

ですが、Cloud Foundryではbuildpackを使うことにより


1. ソースコード作成/修正
1. `cf push`コマンドでアプリケーション(コンテナ)をデプロイ

で完了します。

Dockerの場合、通常`Dockerfile`を作ることになりますが、これをメンテナンスするということは、アプリケーションコードのメンテナンスの他に

* OSイメージ(opensshの脆弱性対応など)
* ランタイム/ミドルウェア(Javaの場合JDKやTomcat、PHPの場合Apache HTTPやmod_phpなど)

のメンテナンスもしなくてはなりません。`Dockerfile`は自由度が高い反面、ケアすべき点も多いです。

一方、buildpackではrootfsとして[cflinuxfs2](https://github.com/cloudfoundry/cflinuxfs2)というUbuntu14.04の派生のイメージを使用します。定期的にセキュリティパッチがあてられています。
また各言語ごとにランタイム/ミドルウェアがメンテナンスされており、原則としてCVE（Common Vulnerabilities and Exposures）公開後48時間以内にパッチがリリースされます。
これにより、基本的にアプリケーションコードにのみ集中することができます。

個人的には、アプリケーションデプロイの観点では`Dockerfile`の作成はあまりやりたい作業でありません。Buildpack方式のほうが好みです。

### CF Local Plugin

[CF Local Plugin](https://github.com/sclevine/cflocal)を使用することで、前述のbuildpackによるメリットをDockerイメージ作成にも適用できます。
CF LocalはLaptop内で`cf push`をエミュレートするプラグインですが、DockerイメージへのExportに対応しています。

これを使うことにより、Dockerを用いてアプリケーションをデプロイする場合も、

1. ソースコード作成/修正
1. CF LocalでDockerイメージの作成
1. Dockerイメージのデプロイ
1. Dockerイメージを使ってアプリケーション(コンテナ)をデプロイ

になり、`Dockerfile`のメンテナンスから解放されます。

これでBuildpackのメリットを活かしつつ、DockerイメージをKubernetesにデプロイするといったことが可能になります。

### CF Localの利用

では実際にCF Localで"Source Code to Docker Image"を行い、アプリケーションをk8sにデプロイしてみます。

#### Cloud Foundry CLIのインストール
まずは`cf`コマンドが必要です。golangの実行可能バイナリなので、インストールは容易です。

CF CLIのインストールは[こちら](https://github.com/cloudfoundry/cli/releases)を参照してください。

Macの場合は、

```
brew install cloudfoundry/tap/cf-cli
```

でインストールできます。


#### CF Local Pluginのインストール

CF Local Pluginのバイナリは[こちら](https://github.com/sclevine/cflocal/releases)からダウンロード可能です。

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アニメも貼っておきます。

![cflocal](https://user-images.githubusercontent.com/106908/29503885-8d602192-8676-11e7-9a10-3b730cfaf689.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](https://github.com/cloudfoundry/java-buildpack/releases/tag/v4.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の仕様も[変わりました](https://www.cloudfoundry.org/just-released-java-buildpack-4-0/)。
メモリ設定を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](https://github.com/sclevine)さんの個人プロジェクト扱いですが、Cloud Foundry Summit 2017のKeynoteで紹介されていたので、
Cloud Foundry公式プロジェクトになることを期待しています。
