Cloud Foundry for Kubernetes を使って Kubernetes native な Cloud Foundry Application Runtime を構築する

# はじめに

[Cloud Foundry for Kubernetes](https://github.com/cloudfoundry/cf-for-k8s) (aka "cf-for-k8s") の最初のアルファリリース [v0.1.0](https://github.com/cloudfoundry/cf-for-k8s/releases/tag/v0.1.0) がリリースされたので動作検証してみました。

cf-for-k8s は、名前の通り [Cloud Foundry Application Runtime](https://www.cloudfoundry.org/application-runtime/) (以降、Cloud Foundry と略します) を Kubernetes 上にデプロイするプロダクトです。

先日 [Cloud Foundry Foundation の Incubating Project に移管された](https://www.cloudfoundry.org/blog/kubecf-now-an-incubating-project-of-cloud-foundry-foundation/) KubeCF というプロダクトも、Cloud Foundry を Kubernetes 上にデプロイするためのものなので、違いがわかりにくいかと思います。

KubeCF は、従来の Cloud Foundry のデプロイ管理に用いていた [BOSH](https://bosh.io/) というツールの機能を[エミュレートする機構](https://github.com/cloudfoundry-incubator/cf-operator)によって、既存の Cloud Foundry のコンポーネントをそのまま Kubernetes 上で稼働させる方式を採っています。

これに対して cf-for-k8s では、BOSH 非依存で Kubernetes のネイティブ機能のみで実行可能な Cloud Foundry リリースを開発しており、一部のコンポーネントは従来の Cloud Foundry 独自に開発されたものから Kubernetes のエコシステムの中で生まれたものに置き換えられています。

具体的には、

* [Cloud Foundry 独自のビルドパック](https://docs.cloudfoundry.org/buildpacks) -> [Cloud Native Buildpacks](https://buildpacks.io) (実際にはそれをラップした [kpack](https://github.com/pivotal/kpack) というプロダクトが採用されています)
* [Cloud Foundry 独自のログシステム](https://docs.cloudfoundry.org/loggregator/architecture.html) -> [Fluentd](https://www.fluentd.org) など
* [Cloud Foundry 独自のルーティング](https://docs.cloudfoundry.org/concepts/cf-routing-architecture.html) -> [Istio](https://istio.io) など

などの置き換えが進められています。

※ KubeCF については、「[cf-operator と KubeCF を使って Kubernetes 上で Cloud Foundry Application Runtime を実行する](https://qiita.com/a09-capf/items/b7e70e433d3763e61dad)」という記事にて紹介していますので、興味がありましたらご参照ください。

## 参考

同じように cf-for-k8s を検証された記事がありました。

こちらの記事では、ローカルマシンの [Kind](https://github.com/kubernetes-sigs/kind) 上にインストールする手順が示されています (私の記事では Azure を使います)。

* [cf-for-k8s(CF4K8s) 0.1.0をKind上にインストールする](https://blog.ik.am/entries/519)

こちらは英語になりますが、GKEでの検証記事も公開されています。
Cloud Foundry コミュニティにおいて、非常に有名な方が執筆しており、解説もわかりやすいです。

* [Deploy Cloud Foundry to Google Kubernetes in 10 minutes](https://starkandwayne.com/blog/deploy-cf-for-k8s-to-google-in-10-minutes/)

## お約束

本記事で紹介する手順は、[v0.1.0 時点の cf-for-k8s リポジトリに記載されたデプロイ手順](https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/docs/deploy.md)をそのまま実行したものになります。

cf-for-k8s は、本記事執筆時点においてはアルファリリースの状態であり、今後実装が大きく変わる可能性もあります。
したがって、本記事に記載した手順を実行した結果について、筆者は一切の責任を負いかねます。ただし、誤りの指摘等のフィードバックにつきましては歓迎致しますのでよろしくお願いします。

# 検証環境

Kuberenetes クラスタの要件については、以下に記載されています。

* https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/docs/deploy.md#kubernetes-cluster-requirements

今回は、[Azure Kubernetes Service (AKS)](https://azure.microsoft.com/ja-jp/services/kubernetes-service/) 上で検証しました。

API サーバのバージョンは以下で検証しました。

```zsh
❯ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.10", GitCommit:"150f36044fe31bee3891b5a3fae69c17237e022c", GitTreeState:"clean", BuildDate:"2020-02-21T10:02:50Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.10", GitCommit:"150f36044fe31bee3891b5a3fae69c17237e022c", GitTreeState:"clean", BuildDate:"2020-02-21T10:02:50Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
```

ノードリソースについては、3 CPU / 7.5 GB RAM 以上のノードが 5 つ以上必要とのことなので、[Standard_F4s_v2 インスタンス](https://docs.microsoft.com/ja-jp/azure/virtual-machines/fsv2-series) (4 vCPU / 8GiB RAM) を 5 つ用意して検証しました。

```zsh
❯ kubectl get nodes -o wide
NAME                       STATUS   ROLES   AGE     VERSION    INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-agentpool-23817061-0   Ready    agent   4m49s   v1.15.10   10.240.0.7    <none>        Ubuntu 16.04.6 LTS   4.15.0-1071-azure   docker://3.0.10+azure
aks-agentpool-23817061-1   Ready    agent   5m7s    v1.15.10   10.240.0.8    <none>        Ubuntu 16.04.6 LTS   4.15.0-1071-azure   docker://3.0.10+azure
aks-agentpool-23817061-2   Ready    agent   5m18s   v1.15.10   10.240.0.5    <none>        Ubuntu 16.04.6 LTS   4.15.0-1071-azure   docker://3.0.10+azure
aks-agentpool-23817061-3   Ready    agent   4m51s   v1.15.10   10.240.0.4    <none>        Ubuntu 16.04.6 LTS   4.15.0-1071-azure   docker://3.0.10+azure
aks-agentpool-23817061-4   Ready    agent   4m46s   v1.15.10   10.240.0.6    <none>        Ubuntu 16.04.6 LTS   4.15.0-1071-azure   docker://3.0.10+azure
```

また、IaaS 側で type:LoadBalancer の Service と default StorageClass が用意されている必要がありますが、AKS を使っている場合、デフォルトで有効になっているので、必要な設定はありません。

# Container Registry の用意

従来の Cloud Foundry では、ビルドパックによってビルドされたコンテナイメージは、Cloud Foundry のコンポーネントとして用意された Blobstore に格納されますが、 cf-for-k8s では外部の OCI 準拠のコンテナレジストリに格納されます。

今回は検証用途なので、[Docker Hub](hub.docker.com) のパブリックリポジトリを使います。

(Google Container Registry などのプライベートレジストリを使うことも可能なようです。)

アカウントを持っていない場合は、[Sign Up](https://hub.docker.com/signup) からアカウントを作成しておきます。

アカウント作成後、リポジトリを作成しておく必要はありません。

# 依存ツールのインストール

cf-for-k8s のデプロイに必要な以下のコマンドラインツールをインストールします。

* [kapp](https://k14s.io/#install)
  * BOSH と同じような操作性で Kubernetes のリソースを管理するツールです。
  * デプロイしたリソースのステータスが ready になるまで待機する点や、リソース更新時に更新差分を確認してから適用できる点などが BOSH の操作性と共通しています。
* [ytt](https://k14s.io/#install)
  * YAML のテンプレートツールです。
  * BOSH の [ops-file](https://bosh.io/docs/cli-ops-files) と同じように、Overlay 方式による設定のカスタマイズを実現可能にします。 
* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl)
  * これは言わずもがなかと思います。
* [BOSH CLI](https://bosh.io/docs/cli-v2-install/)
  * 設定ファイルに埋め込むパスワードおよび自己署名証明書を生成するために使います。
  * [手動で生成・記載する](https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/docs/deploy.md#option-b---create-the-install-values-by-hand)こともできなくはないですが、量が多いので、本記事では BOSH CLI で自動生成する方法を採ります。
* [cf CLI](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html)
  * デプロイに必要なツールではないですが、動作検証の際に必要なので、インストールしていない場合は入れておきましょう。
  * **cf-for-k8s でアプリケーションログを取得するためには、v6.50.0 以上の cf CLI をインストールする必要があると[コメント](https://qiita.com/a09-capf/items/ec6a32bb2b3eed6b0ce8#comment-15b5a55f15a2c2423818)を頂きました。**

# 設定ファイルの生成

cf-for-k8s の Git リポジトリをクローンし、設定ファイルを生成します。

以下の手順では、[設定ファイルの生成スクリプト](https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/hack/generate-values.sh)を実行し、パスワードや自己署名証明書を自動生成していますが、[サンプルの設定ファイル](https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/sample-cf-install-values.yml)を手動で書き換えても良いです。

`cf-domain` には、実際に所持しているドメインを記載します。

```zsh
❯ git clone https://github.com/cloudfoundry/cf-for-k8s.git -b v0.1.0
❯ cd cf-for-k8s
❯ ./hack/generate-values.sh -d <cf-domain> > /tmp/cf-values.yml
```

生成された `cf-values.yml` の末尾に Container Registry の認証情報を追記します。
Docker Hub を使う場合の手順は以下です。
`repository` の設定欄は、リポジトリ名ではなく、Docker Hub のユーザ名で良いです。

```zsh
❯ cat <<EOF >> /tmp/cf-values.yml
app_registry:
   hostname: https://index.docker.io/v1/
   repository: "<my_username>"
   username: "<my_username>"
   password: "<my_password>"
EOF
```

# cf-for-k8s のインストール

用意された[インストールスクリプト](https://github.com/cloudfoundry/cf-for-k8s/blob/v0.1.0/bin/install-cf.sh)を使って、cf-for-k8s をインストールします。

```zsh
❯ ./bin/install-cf.sh /tmp/cf-values.yml
```

上記のスクリプトの実行は、以下のコマンドの実行と等価です。

```zsh
❯ kapp deploy -a cf -f <(ytt -f ./cf-for-k8s/config -f /tmp/cf-values.yml) -y
```

インストールを実行すると、245 個の Kubernetes リソースが作成が開始され、ステータスが ready になるまで待機します。

```
Changes

Namespace             Name                                                            Kind                          Conds.  Age  Op      Wait to    Rs  Ri
(cluster)             adapters.config.istio.io                                        CustomResourceDefinition      -       -    create  reconcile  -   -

(snip...)

^                     metacontroller                                                  StatefulSet                   -       -    create  reconcile  -   -

Op:      245 create, 0 delete, 0 update, 0 noop
Wait to: 245 reconcile, 0 delete, 0 noop

5:01:57PM: ---- applying 36 changes [0/245 done] ----

(snip...)

5:06:24PM: ---- waiting complete [245/245 done] ----

Succeeded
```

5 分もかからずにすべてのリソースが安定し、インストールが完了しました。

インストールが完了したら cf-for-k8s のユーザエンドポイントとなる Istio Ingress Gateway Service (type: LoadBalancer) の External IP を取得し、DNS 設定をおこないます。

```zsh
❯ kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
```

上記コマンドを実行して出力された IP アドレスを `*.<cf-domain>` で名前解決できるように、DNS の A レコードを登録します。

# 動作確認

cf CLI を使って、Cloud Foundry にログインしてみます。

```
❯ cf api --skip-ssl-validation https://api.<cf-domain>
API エンドポイントを https://api.<cf-domain> に設定しています...
OK

API エンドポイント:   https://api.<cf-domain>
api version:          2.148.0
ログインしていません。 'cf login' を使用してログインしてください。

❯ cf auth admin $(bosh interpolate /tmp/<cf-domain>/cf-vars.yaml --path=/cf_admin_password)
API エンドポイント: https://api.<cf-domain>
認証中です...
OK
ターゲットの組織とスペースを表示または設定するには 'cf target' を使用してください。
```

[組織とスペース](https://docs.cloudfoundry.org/concepts/roles.html)を作成し、サンプルアプリケーションをデプロイしてみます。

```
❯ cf create-org test-org
admin として組織 test-org を作成しています...
OK

役割 OrgManager を組織 test-org 内のユーザー admin に割り当てています ...
OK

ヒント: 新しい組織をターゲットにするには、'cf target -o "test-org"' を使用します

❯ cf create-space -o test-org test-space
admin としてスペース test-space を組織 test-org 内に作成しています...
OK
admin として役割 RoleSpaceManager を組織 test-org / スペース test-space 内のユーザー admin に割り当てています...
OK
admin として役割 RoleSpaceDeveloper を組織 test-org / スペース test-space 内のユーザー admin に割り当てています...
OK

ヒント: 新しいスペースをターゲットにするには、'cf target -o "test-org" -s "test-space"' を使用します

❯ cf target -o test-org -s test-space
API エンドポイント:   https://api.<cf-domain>
api version:          2.148.0
ユーザー:             admin
組織:                 test-org
スペース:             test-space

❯ cf push test-node-app -p tests/smoke/assets/test-node-app
admin としてアプリ test-node-app を組織 test-org / スペース test-space にプッシュしています...
アプリ情報を取得しています...
これらの属性でアプリを作成しています...
+ 名前:       test-node-app
  パス:       /private/tmp/cf-for-k8s/tests/smoke/assets/test-node-app
  経路:
+   test-node-app.<cf-domain>

アプリ test-node-app を作成しています...
経路をマップしています...
ローカル・ファイルをリモート・キャッシュと比較しています...
Packaging files to upload...
ファイルをアップロードしています...
 530 B / 530 B [=========================================================================================================================================================] 100.00% 1s

API がファイルの処理を完了するのを待機しています...

アプリをステージングし、ログをトレースしています...

アプリが開始するのを待機しています...

名前:               test-node-app
要求された状態:      started
分離セグメント:       placeholder
経路:               test-node-app.<cf-domain>
最終アップロード日時:   Sun 12 Apr 17:27:21 JST 2020
スタック:
ビルドパック:

タイプ:          web
インスタンス:     1/1
メモリー使用量:   1024M
     状態   開始日時               cpu    メモリー      ディスク      詳細
#0   実行   2020-04-12T08:29:11Z  0.0%   1G の中の 0  1G の中の 0
```

普通の Cloud Foundry と変わりなくアプリケーションが起動しました。
なお、[ステージング中のログが表示されない](https://github.com/cloudfoundry/cf-for-k8s/issues/80)ことは、v0.1.0 時点では Known Issue とのことです。

アプリケーションにも問題なくアクセスできるようです。
ただし、現時点では HTTP のアクセスのみ可能なようです。

```zsh
❯ curl http://test-node-app.<cf-domain>/env
Hello World
```

## (メモ) アプリのステージングについて

`cf push` を実行した際にどのようにアプリケーションがステージングされるのか、(主に自身の理解のために) 記しておきます。

従来の Cloud Foundry におけるステージング時の挙動は、以下のドキュメントに解説があります。

* [How Diego Stages Buildpack Apps](https://docs.cloudfoundry.org/concepts/how-applications-are-staged.html#stage-buildpack)

大まかな流れとしては、

* `cf push` でアップロードされたソースコードは一旦 Blobstore に格納される。
* Cloud Foundry 独自のコンテナオーケストレーターである [Diego](https://docs.cloudfoundry.org/concepts/diego/diego-architecture.html) 上で Buildpack による処理が実行される。
* ビルドされたコンテナイメージ (Droplet) は、再び Blobstore に格納される。
* Blobstore にある Droplet を Diego がアプリコンテナとしてスケジューリングすることで、アプリケーションの実行が開始される。

と、理解しています。

cf-for-k8s においても、最初のステップとして、ソースコードが Blobstore (現状構成では [MinIO](https://min.io/) が使われている) に格納されるところまでは同じです。

以降のステップが Kpack を用いた Kubernetes native なフローに置き換わっていて、

* Kpack の定義する CRD [`images.build.pivotal.io`](https://github.com/pivotal/kpack/blob/master/docs/image.md) を Cloud Controller が発行する ([`source`](https://github.com/pivotal/kpack/blob/master/docs/image.md#source-configuration) には Blobstore のアドレスが指定されている)。
* [`builds.build.pivotal.io`](https://github.com/pivotal/kpack/blob/master/docs/build.md) によって、Cloud Native Buildpacks のビルドが実行される。
* ビルドされたコンテナイメージは、外部のレジストリ (今回の検証では Docker Hub) に push される。
* [Eirini](https://www.cloudfoundry.org/project-eirini/)によって、アプリケーションが Statefulset としてスケジューリングされ、レジストリのイメージを pull して Pod が起動する。
  * Eirini : Cloud Foundry のワークロードを直接 Kubernetes のリソースとして実行するコンポーネントです。

という流れになっているようです。

上記の Kpack の CRD は `cf-workloads-staging` Namespace 上で管理されます。

<details><summary>発行された images.build.pivotal.io の定義内容</summary><div>

```zsh
❯ kubectl -n cf-workloads-staging get images a49ac035-34aa-42d0-865f-95281a1eb91e -o yaml
apiVersion: build.pivotal.io/v1alpha1
kind: Image
metadata:
  annotations:
    sidecar.istio.io/inject: "false"
  creationTimestamp: "2020-04-18T09:12:04Z"
  generation: 1
  labels:
    cloudfoundry.org/app_guid: c311d787-a356-49bf-b620-4911d2091efe
    cloudfoundry.org/build_guid: 7c8d4361-dd5f-4e7a-ab78-dedca86e1fbc
    cloudfoundry.org/source_type: STG
  name: a49ac035-34aa-42d0-865f-95281a1eb91e
  namespace: cf-workloads-staging
  resourceVersion: "5402"
  selfLink: /apis/build.pivotal.io/v1alpha1/namespaces/cf-workloads-staging/images/a49ac035-34aa-42d0-865f-95281a1eb91e
  uid: 48e450ba-a1b6-449c-9f99-42187d409d87
spec:
  builder:
    kind: Builder
    name: cf-autodetect-builder
  failedBuildHistoryLimit: 10
  imageTaggingStrategy: BuildNumber
  serviceAccount: cc-kpack-registry-service-account
  source:
    blob:
      url: http://cf-blobstore-minio.cf-blobstore.svc.cluster.local:9000/cc-packages/a4/9a/a49ac035-34aa-42d0-865f-95281a1eb91e?AWSAccessKeyId=admin&Signature=JNMe2SIPs%2BeLl20Krvqi19Cxjns%3D&Expires=1587204724
  successBuildHistoryLimit: 10
  tag: <my_username>/a49ac035-34aa-42d0-865f-95281a1eb91e
status:
  buildCounter: 1
  conditions:
  - lastTransitionTime: "2020-04-18T09:13:36Z"
    status: "True"
    type: Ready
  - lastTransitionTime: null
    status: "True"
    type: BuilderReady
  latestBuildRef: a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-xm7p7
  latestImage: index.docker.io/<my_username>/a49ac035-34aa-42d0-865f-95281a1eb91e@sha256:5e12f813beaeb454bd7202824deb277dc6ed1ea2370a30ffa2c1836d5d862eea
  latestStack: io.buildpacks.stacks.bionic
  observedGeneration: 1
```
</div></details>

<details><summary>発行された builds.build.pivotal.io の定義内容</summary><div>

```zsh
❯ kubectl -n cf-workloads-staging get builds a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-xm7p7 -o yaml
apiVersion: build.pivotal.io/v1alpha1
kind: Build
metadata:
  annotations:
    image.build.pivotal.io/reason: CONFIG
    sidecar.istio.io/inject: "false"
  creationTimestamp: "2020-04-18T09:12:04Z"
  generateName: a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-
  generation: 1
  labels:
    cloudfoundry.org/app_guid: c311d787-a356-49bf-b620-4911d2091efe
    cloudfoundry.org/build_guid: 7c8d4361-dd5f-4e7a-ab78-dedca86e1fbc
    cloudfoundry.org/source_type: STG
    image.build.pivotal.io/buildNumber: "1"
    image.build.pivotal.io/image: a49ac035-34aa-42d0-865f-95281a1eb91e
  name: a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-xm7p7
  namespace: cf-workloads-staging
  ownerReferences:
  - apiVersion: build.pivotal.io/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: Image
    name: a49ac035-34aa-42d0-865f-95281a1eb91e
    uid: 48e450ba-a1b6-449c-9f99-42187d409d87
  resourceVersion: "5401"
  selfLink: /apis/build.pivotal.io/v1alpha1/namespaces/cf-workloads-staging/builds/a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-xm7p7
  uid: 3c02a751-cdc0-4b5c-9112-c42d21cdf15d
spec:
  builder:
    image: index.docker.io/cloudfoundry/cnb@sha256:0a718640a4bde8ff65eb00e891ff7f4f23ffd9a0af44d43f6033cc5809768945
  resources: {}
  serviceAccount: cc-kpack-registry-service-account
  source:
    blob:
      url: http://cf-blobstore-minio.cf-blobstore.svc.cluster.local:9000/cc-packages/a4/9a/a49ac035-34aa-42d0-865f-95281a1eb91e?AWSAccessKeyId=admin&Signature=JNMe2SIPs%2BeLl20Krvqi19Cxjns%3D&Expires=1587204724
  tags:
  - <my_username>/a49ac035-34aa-42d0-865f-95281a1eb91e
  - index.docker.io/<my_username>/a49ac035-34aa-42d0-865f-95281a1eb91e:b1.20200418.091204
status:
  buildMetadata:
  - id: org.cloudfoundry.node-engine
    version: 0.0.158
  - id: org.cloudfoundry.npm
    version: 0.1.3
  conditions:
  - lastTransitionTime: "2020-04-18T09:13:36Z"
    status: "True"
    type: Succeeded
  latestImage: index.docker.io/<my_username>/a49ac035-34aa-42d0-865f-95281a1eb91e@sha256:5e12f813beaeb454bd7202824deb277dc6ed1ea2370a30ffa2c1836d5d862eea
  observedGeneration: 1
  podName: a49ac035-34aa-42d0-865f-95281a1eb91e-build-1-xm7p7-build-pod
  stack:
    id: io.buildpacks.stacks.bionic
    runImage: index.docker.io/cloudfoundry/run@sha256:bfe49e7d1c2c47d980af9dd684047616db872a982dcb2c5515a960d1a962a599
  stepStates:
  - terminated:
      containerID: docker://c96a229ffd5acea160f6c5dc677ff852bf0e5745fc6e01971595d7ac456de264
      exitCode: 0
      finishedAt: "2020-04-18T09:12:17Z"
      reason: Completed
      startedAt: "2020-04-18T09:12:14Z"
  - terminated:
      containerID: docker://ead6ba611856167980864d288b3b86efa07c60795c804a03a59aa2d04a1485fb
      exitCode: 0
      finishedAt: "2020-04-18T09:12:57Z"
      reason: Completed
      startedAt: "2020-04-18T09:12:57Z"
  - terminated:
      containerID: docker://6a6cf9dedb78e207d8e2e4be5e81babe5903010d79c19495556361116325f252
      exitCode: 0
      finishedAt: "2020-04-18T09:13:02Z"
      reason: Completed
      startedAt: "2020-04-18T09:12:58Z"
  - terminated:
      containerID: docker://2207323ea734479fdb454dc0000e1ec49bb98ce2f46804aec385a9064073736d
      exitCode: 0
      finishedAt: "2020-04-18T09:13:03Z"
      reason: Completed
      startedAt: "2020-04-18T09:13:03Z"
  - terminated:
      containerID: docker://a1f4f03e637d15a83d25e02d038b7fcc027603578d552b9537c3e094c50b17ea
      exitCode: 0
      finishedAt: "2020-04-18T09:13:08Z"
      reason: Completed
      startedAt: "2020-04-18T09:13:04Z"
  - terminated:
      containerID: docker://338a984a0ecdca84bb6d9b74b7ef7dabab10719b7f23f6c37a23eb28df0a338e
      exitCode: 0
      finishedAt: "2020-04-18T09:13:30Z"
      reason: Completed
      startedAt: "2020-04-18T09:13:11Z"
  stepsCompleted:
  - prepare
  - detect
  - analyze
  - restore
  - build
  - export
```
</div></details>

Kpack の理解に際しては、以下などを参考させて頂きました。

* [Dockerfile書きたくないでござる (40 スライド目以降)](https://speakerdeck.com/jacopen/dockerfileshu-kitakunaidegozaru?slide=40)

# 後片付け

`kapp` がリソース管理をしているので、以下のコマンドですべてのリソースを削除できます。

```zsh
❯ kapp delete -a cf
```

# おわりに

KubeCF をインストールした際も、従来手順と比べて非常にシンプルになったことに驚きましたが、こちらの手順もかなり簡単だったので感動しました。

強いて気になった点を挙げるとすれば、デプロイに利用している `kapp` や `ytt` などのツールが、Kubernetes のエコシステムにおいてそこまでメジャーなものではなさそうという点でしょうか。
Kubernetes ユーザが使い慣れたツールとは別に、新しいツールを理解して使ってもらうコストというのは小さくはない気がするので、あえて [Helm](https://helm.sh/) や [kustomize](https://github.com/kubernetes-sigs/kustomize) などを採用しなかった意図があるのであれば、知りたいと思いました (何かしら意図はあるのだと思っています)。
【2020/5/23 追記】
`ytt` の採用に関しては、Cloud Foundry の Slack チャンネルにて良い議論が為されているのを見つけました。
https://cloudfoundry.slack.com/archives/CH9LF6V1P/p1582241748342800
【2021/3/12 追記】
Helm / kustomize と比較した ytt の優位性については、以下の発表にて詳しく紹介されていました。
https://event.cloudnativedays.jp/cndo2021/talks/851

いずれにせよ、cf-for-k8s は現時点ではアルファ版の段階であり、今後の発展が非常に楽しみなプロダクトのひとつなので、動向を注視していきたいと思います。