はじめに
Cloud Foundry for Kubernetes (aka "cf-for-k8s") の最初のアルファリリース v0.1.0 がリリースされたので動作検証してみました。
cf-for-k8s は、名前の通り Cloud Foundry Application Runtime (以降、Cloud Foundry と略します) を Kubernetes 上にデプロイするプロダクトです。
先日 Cloud Foundry Foundation の Incubating Project に移管された KubeCF というプロダクトも、Cloud Foundry を Kubernetes 上にデプロイするためのものなので、違いがわかりにくいかと思います。
KubeCF は、従来の Cloud Foundry のデプロイ管理に用いていた BOSH というツールの機能をエミュレートする機構によって、既存の Cloud Foundry のコンポーネントをそのまま Kubernetes 上で稼働させる方式を採っています。
これに対して cf-for-k8s では、BOSH 非依存で Kubernetes のネイティブ機能のみで実行可能な Cloud Foundry リリースを開発しており、一部のコンポーネントは従来の Cloud Foundry 独自に開発されたものから Kubernetes のエコシステムの中で生まれたものに置き換えられています。
具体的には、
- Cloud Foundry 独自のビルドパック -> Cloud Native Buildpacks (実際にはそれをラップした kpack というプロダクトが採用されています)
- Cloud Foundry 独自のログシステム -> Fluentd など
- Cloud Foundry 独自のルーティング -> Istio など
などの置き換えが進められています。
※ KubeCF については、「cf-operator と KubeCF を使って Kubernetes 上で Cloud Foundry Application Runtime を実行する」という記事にて紹介していますので、興味がありましたらご参照ください。
参考
同じように cf-for-k8s を検証された記事がありました。
こちらの記事では、ローカルマシンの Kind 上にインストールする手順が示されています (私の記事では Azure を使います)。
こちらは英語になりますが、GKEでの検証記事も公開されています。
Cloud Foundry コミュニティにおいて、非常に有名な方が執筆しており、解説もわかりやすいです。
お約束
本記事で紹介する手順は、v0.1.0 時点の cf-for-k8s リポジトリに記載されたデプロイ手順をそのまま実行したものになります。
cf-for-k8s は、本記事執筆時点においてはアルファリリースの状態であり、今後実装が大きく変わる可能性もあります。
したがって、本記事に記載した手順を実行した結果について、筆者は一切の責任を負いかねます。ただし、誤りの指摘等のフィードバックにつきましては歓迎致しますのでよろしくお願いします。
検証環境
Kuberenetes クラスタの要件については、以下に記載されています。
今回は、Azure Kubernetes Service (AKS) 上で検証しました。
API サーバのバージョンは以下で検証しました。
❯ 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 インスタンス (4 vCPU / 8GiB RAM) を 5 つ用意して検証しました。
❯ 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 のパブリックリポジトリを使います。
(Google Container Registry などのプライベートレジストリを使うことも可能なようです。)
アカウントを持っていない場合は、Sign Up からアカウントを作成しておきます。
アカウント作成後、リポジトリを作成しておく必要はありません。
依存ツールのインストール
cf-for-k8s のデプロイに必要な以下のコマンドラインツールをインストールします。
-
kapp
- BOSH と同じような操作性で Kubernetes のリソースを管理するツールです。
- デプロイしたリソースのステータスが ready になるまで待機する点や、リソース更新時に更新差分を確認してから適用できる点などが BOSH の操作性と共通しています。
-
ytt
- YAML のテンプレートツールです。
- BOSH の ops-file と同じように、Overlay 方式による設定のカスタマイズを実現可能にします。
-
kubectl
- これは言わずもがなかと思います。
-
BOSH CLI
- 設定ファイルに埋め込むパスワードおよび自己署名証明書を生成するために使います。
- 手動で生成・記載することもできなくはないですが、量が多いので、本記事では BOSH CLI で自動生成する方法を採ります。
-
cf CLI
- デプロイに必要なツールではないですが、動作検証の際に必要なので、インストールしていない場合は入れておきましょう。
- cf-for-k8s でアプリケーションログを取得するためには、v6.50.0 以上の cf CLI をインストールする必要があるとコメントを頂きました。
設定ファイルの生成
cf-for-k8s の Git リポジトリをクローンし、設定ファイルを生成します。
以下の手順では、設定ファイルの生成スクリプトを実行し、パスワードや自己署名証明書を自動生成していますが、サンプルの設定ファイルを手動で書き換えても良いです。
cf-domain
には、実際に所持しているドメインを記載します。
❯ 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 のユーザ名で良いです。
❯ 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 のインストール
用意されたインストールスクリプトを使って、cf-for-k8s をインストールします。
❯ ./bin/install-cf.sh /tmp/cf-values.yml
上記のスクリプトの実行は、以下のコマンドの実行と等価です。
❯ 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 設定をおこないます。
❯ 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' を使用してください。
組織とスペースを作成し、サンプルアプリケーションをデプロイしてみます。
❯ 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 と変わりなくアプリケーションが起動しました。
なお、ステージング中のログが表示されないことは、v0.1.0 時点では Known Issue とのことです。
アプリケーションにも問題なくアクセスできるようです。
ただし、現時点では HTTP のアクセスのみ可能なようです。
❯ curl http://test-node-app.<cf-domain>/env
Hello World
(メモ) アプリのステージングについて
cf push
を実行した際にどのようにアプリケーションがステージングされるのか、(主に自身の理解のために) 記しておきます。
従来の Cloud Foundry におけるステージング時の挙動は、以下のドキュメントに解説があります。
大まかな流れとしては、
-
cf push
でアップロードされたソースコードは一旦 Blobstore に格納される。 - Cloud Foundry 独自のコンテナオーケストレーターである Diego 上で Buildpack による処理が実行される。
- ビルドされたコンテナイメージ (Droplet) は、再び Blobstore に格納される。
- Blobstore にある Droplet を Diego がアプリコンテナとしてスケジューリングすることで、アプリケーションの実行が開始される。
と、理解しています。
cf-for-k8s においても、最初のステップとして、ソースコードが Blobstore (現状構成では MinIO が使われている) に格納されるところまでは同じです。
以降のステップが Kpack を用いた Kubernetes native なフローに置き換わっていて、
- Kpack の定義する CRD
images.build.pivotal.io
を Cloud Controller が発行する (source
には Blobstore のアドレスが指定されている)。 -
builds.build.pivotal.io
によって、Cloud Native Buildpacks のビルドが実行される。 - ビルドされたコンテナイメージは、外部のレジストリ (今回の検証では Docker Hub) に push される。
-
Eiriniによって、アプリケーションが Statefulset としてスケジューリングされ、レジストリのイメージを pull して Pod が起動する。
- Eirini : Cloud Foundry のワークロードを直接 Kubernetes のリソースとして実行するコンポーネントです。
という流れになっているようです。
上記の Kpack の CRD は cf-workloads-staging
Namespace 上で管理されます。
発行された images.build.pivotal.io の定義内容
❯ 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
発行された builds.build.pivotal.io の定義内容
❯ 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
Kpack の理解に際しては、以下などを参考させて頂きました。
後片付け
kapp
がリソース管理をしているので、以下のコマンドですべてのリソースを削除できます。
❯ kapp delete -a cf
おわりに
KubeCF をインストールした際も、従来手順と比べて非常にシンプルになったことに驚きましたが、こちらの手順もかなり簡単だったので感動しました。
強いて気になった点を挙げるとすれば、デプロイに利用している kapp
や ytt
などのツールが、Kubernetes のエコシステムにおいてそこまでメジャーなものではなさそうという点でしょうか。
Kubernetes ユーザが使い慣れたツールとは別に、新しいツールを理解して使ってもらうコストというのは小さくはない気がするので、あえて Helm や 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 は現時点ではアルファ版の段階であり、今後の発展が非常に楽しみなプロダクトのひとつなので、動向を注視していきたいと思います。