はじめに
この記事は Kubernetes3 Advent Calendar 2019 23日目の記事です。
Project Quarks の cf-operator v1.0.0 と KubeCF v0.1.0 がリリースされたので動かしてみていたら、ちょうど Advent Calendar に空きができたようなので、せっかくの機会と思い投稿させて頂きました。
【2020/03/23 更新】KubeCF v1.0.0 がリリースされ、また Cloud Foundry Foundation の Incubating Project に移管されたので、最新情報を記事に反映しました。
Project Quarks は、Cloud Foundry Application Runtime (以降、Cloud Foundry と略します) を Kubernetes のワークロードとして実行できるようにするための試みです。
従来、Cloud Foundry をインストールするためには、BOSH という管理ツールを扱う必要があった (※1) のですが、helm install
を実行するだけでインストールができる (※2) ようになりました。
- ※1 (余談) BOSH 自体は IaaS レイヤを抽象化して管理・運用するためのツールとして非常に優れており、例えば Managed Kubernetes を使わず、自前で Kubernetes クラスタを本番運用する場合などに有用なツールです。また、BOSH も Kubernetes もルーツは Google の Borg なので、思想が似通っています。
- ※2 デフォルト設定でインストールする場合。一部の設定については
values.yaml
でカスタマイズ可能ですが、それ以外の設定をカスタマイズするためには (現時点では) BOSH の知識が依然として必要になります。将来的には CF-for-K8s プロジェクトにて開発中の Kubernetes native なコンポーネントに置き換えられていくことで、BOSH への依存性は段階的に取り除かれていく見込みのようです (詳細後述)。
Project Quarks の狙いやより技術的な詳細については、Proposal 原文が参考になるかと思います。
なお、Kubernetes 上で Cloud Foundry を実行する取り組みは Project Quarks 以前からあり、その取り組みで生まれた技術が Project Quarks でも活かされています。
経緯を知るにあたっては、以下の記事を参照させて頂きました。
- https://qiita.com/jyoshise/items/88a57c9b46760a839c87
- http://jyoshise.hatenablog.com/entry/2017/12/22/234527
本記事で紹介する手順は、基本的には KubeCF Documentation の Getting Started の記載に従っていますが、一部の手順はアレンジしています。
また、手順の途中でところどころ、BOSH 上で実現していたことをどのように Kubernetes で扱っているのかの説明を (主に自身の理解の整理のために) 挟んでいますが、BOSH に詳しくない方や、とりあえず動かしてみたいという方は読み飛ばして頂いて問題ありません。
お約束
Project Quarks は、本記事執筆時点においてはインキュベータプロジェクトであり、今後実装が大きく変わる可能性もあります。
したがって、本記事に記載した手順を実行した結果について、筆者は一切の責任を負いかねます。ただし、誤りの指摘等のフィードバックにつきましては歓迎致しますのでよろしくお願いします。
検証環境
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"}
ノードリソースについてですが、cf-oprator と KubeCF 合わせて 20 個ほどの Pod (1 Pod に複数コンテナが入った Pod が多く、コンテナ数にすると 100 個ほど) が起動するので、十分に大きめのリソースを確保しておく必要があります。
具体的にどの程度必要なのかは調べられていないのですが、Standard_D2_v3 インスタンス (2 vCPU / 8GiB RAM) を 5 つ用意して検証したところ、問題なく稼働することは確認できました。
❯ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
aks-agentpool-31661883-0 Ready agent 20m 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-31661883-1 Ready agent 19m 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-31661883-2 Ready agent 19m v1.15.10 10.240.0.6 <none> Ubuntu 16.04.6 LTS 4.15.0-1071-azure docker://3.0.10+azure
aks-agentpool-31661883-3 Ready agent 19m 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-31661883-4 Ready agent 19m v1.15.10 10.240.0.8 <none> Ubuntu 16.04.6 LTS 4.15.0-1071-azure docker://3.0.10+azure
(準備) Helm のインストール
KubeCF v0.1.0 の時点では Helm v3 に対応していませんでしたが、現在は v3 に対応しているので Tiller のインストールは不要です。
❯ helm version
version.BuildInfo{Version:"v3.1.2", GitCommit:"d878d4d45863e42fd5cff6743294a11d28a9abce", GitTreeState:"clean", GoVersion:"go1.13.8"}
cf-operator のインストール
cf-operator は、BOSH の概念を Custom Resource Definition (CRD) として定義して Kubernetes 上で扱えるようにするための Kubernetes Operator です。
KubeCF をインストールする namespace を決め、global.operator.watchNamespace
パラメータの値に指定して helm install
を実行します。
その他の設定は基本的にデフォルトのままで問題ありません。
❯ kubectl create namespace cf-operator
❯ helm install cf-operator \
--namespace cf-operator \
--set "global.operator.watchNamespace=kubecf" \
https://s3.amazonaws.com/cf-operators/release/helm-charts/cf-operator-3.3.0%2B0.gf32b521e.tgz
NAME: cf-operator
LAST DEPLOYED: Mon Mar 23 11:57:12 2020
NAMESPACE: cf-operator
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Running the operator will install the following CRD´s:
- boshdeployments.quarks.cloudfoundry.org
- quarksjobs.quarks.cloudfoundry.org
- quarksecrets.quarks.cloudfoundry.org
- quarkstatefulsets.quarks.cloudfoundry.org
You can always verify if the CRD´s are installed, by running:
$ kubectl get crds
Interacting with the cf-operator pod
1. Check the cf-operator pod status
kubectl -n cf-operator get pods
2. Tail the cf-operator pod logs
export OPERATOR_POD=$(kubectl get pods -l name=cf-operator --namespace cf-operator --output name)
kubectl -n cf-operator logs $OPERATOR_POD -f
3. Apply one of the BOSH deployment manifest examples
kubectl -n kubecf apply -f docs/examples/bosh-deployment/boshdeployment-with-custom-variable.yaml
4. See the cf-operator in action!
watch -c "kubectl -n kubecf get pods"
インストールが完了すると、確かに以下の CRD が作成されていることが確認できます。
❯ kubectl get crds
NAME CREATED AT
boshdeployments.quarks.cloudfoundry.org 2020-03-23T02:57:37Z
quarksjobs.quarks.cloudfoundry.org 2020-03-23T02:57:36Z
quarkssecrets.quarks.cloudfoundry.org 2020-03-23T02:57:37Z
quarksstatefulsets.quarks.cloudfoundry.org 2020-03-23T02:57:37Z
各 CRD と BOSH の概念のマッピングは、以下のとおりです。
- boshdeployments.quarks.cloudfoundry.org : BOSH Deployment
- quarksjobs.quarks.cloudfoundry.org : BOSH Errands
- quarkssecrets.quarks.cloudfoundry.org : BOSH Variables
- quarksstatefulsets.quarks.cloudfoundry.org : BOSH Instance Groups
cf-operator の利用者がメインで意識する必要があるリソースは boshdeployments.quarks.cloudfoundry.org で、これは Kubernetes の ConfigMap と Secret として定義した、BOSH manifest と Ops File をまとめて一つの BOSH Deployment として管理するためのリソースです。
KubeCF のインストール
KubeCF は、Project Quarks 仕様にビルドされた cf-deployment 用の BOSH Releases とマニフェスト群を Helm Chart にパッケージングしたものです。
Cloud Foundry が使うシステムドメインを values.yaml に記載します。
(your domain
は実際に所持しているドメインを記載します。)
system_domain: kubecf.<your domain>
上記設定は helm install
を実行するために最低限必要な設定です。
その他に values.yaml
にて定義可能な設定は、以下から参照できます。
values.yaml
を記載したら helm install
で KubeCF をインストールします。
❯ helm install kubecf \
--namespace kubecf \
--values values.yaml \
https://github.com/cloudfoundry-incubator/kubecf/releases/download/v1.0.1/kubecf-v1.0.1.tgz
NAME: kubecf
LAST DEPLOYED: Mon Mar 23 12:13:33 2020
NAMESPACE: kubecf
STATUS: deployed
REVISION: 1
TEST SUITE: None
インストールすると、ほどなくして type: LoadBalancer
の Service に EXTERNAL-IP が割り当たるので、この IP を指し示すように、所持しているドメインの DNS レコードに A レコードを登録しておきます。
❯ kubectl -n kubecf get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubecf-database ClusterIP 10.0.205.218 <none> 3306/TCP 2m43s
kubecf-database-repl ClusterIP None <none> 4567/TCP,4568/TCP,4444/TCP 2m43s
kubecf-router-public LoadBalancer 10.0.89.230 XX.YY.ZZ.228 80:30145/TCP,443:30657/TCP 2m43s
kubecf-ssh-proxy-public LoadBalancer 10.0.125.82 XX.YY.ZZ.227 2222:30584/TCP 2m43s
kubecf-tcp-router-public LoadBalancer 10.0.134.85 XX.YY.ZZ.142 80:31190/TCP,20000:30919/TCP,20001:30658/TCP,20002:31593/TCP,20003:32107/TCP,20004:30992/TCP,20005:30670/TCP,20006:30967/TCP,20007:31352/TCP,20008:30825/TCP 2m43s
-
kubecf.<your domain>
->kubecf-router-public
の EXTERNAL-IP (上記の例だとXX.YY.ZZ.228
) -
*.kubecf.<your domain>
->kubecf-router-public
の EXTERNAL-IP (上記の例だとXX.YY.ZZ.228
) -
ssh.kubecf.<your domain>
->kubecf-ssh-proxy-public
の EXTERNAL-IP (上記の例だとXX.YY.ZZ.227
) -
tcp.kubecf.<your domain>
->kubecf-tcp-router-public
の EXTERNAL-IP (上記の例だとXX.YY.ZZ.142
)
インストールが完了するまで少し時間がかかるので、待ち時間でどのようなリソースが構成されたのか見てみましょう。
cf-operator の説明で少し触れたように boshdeployments CRD にて、KubeCF の BOSH Deployment が定義されていることが確認できます。
❯ kubectl -n kubecf get boshdeployments.quarks.cloudfoundry.org kubecf -o yaml
apiVersion: quarks.cloudfoundry.org/v1alpha1
kind: BOSHDeployment
metadata:
creationTimestamp: "2020-03-23T03:13:36Z"
generation: 1
labels:
app.kubernetes.io/instance: kubecf
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: kubecf
app.kubernetes.io/version: v1.0.1
helm.sh/chart: kubecf-v1.0.1
name: kubecf
namespace: kubecf
resourceVersion: "9557"
selfLink: /apis/quarks.cloudfoundry.org/v1alpha1/namespaces/kubecf/boshdeployments/kubecf
uid: 3dd148d3-a537-4211-a643-5712429571bb
spec:
manifest:
name: kubecf-cf-deployment
type: configmap
ops:
- name: kubecf-ops-move-auctioneer
type: configmap
- name: kubecf-ops-move-routing-api
type: configmap
- name: kubecf-ops-set-suse-buildpacks
type: configmap
- name: kubecf-ops-acceptance-tests
type: configmap
- name: kubecf-ops-adapter
type: configmap
- name: kubecf-ops-api
type: configmap
- name: kubecf-ops-app-autoscaler
type: configmap
- name: kubecf-ops-auctioneer
type: configmap
- name: kubecf-ops-bits
type: configmap
- name: kubecf-ops-brain-tests
type: configmap
- name: kubecf-ops-cc-worker
type: configmap
- name: kubecf-ops-credhub
type: configmap
- name: kubecf-ops-database
type: configmap
- name: kubecf-ops-diego-api
type: configmap
- name: kubecf-ops-diego-cell
type: configmap
- name: kubecf-ops-doppler
type: configmap
- name: kubecf-ops-eirini
type: configmap
- name: kubecf-ops-log-api
type: configmap
- name: kubecf-ops-nats
type: configmap
- name: kubecf-ops-router
type: configmap
- name: kubecf-ops-routing-api
type: configmap
- name: kubecf-ops-scheduler
type: configmap
- name: kubecf-ops-singleton-blobstore
type: configmap
- name: kubecf-ops-smoke-tests
type: configmap
- name: kubecf-ops-sync-integration-tests
type: configmap
- name: kubecf-ops-tcp-router
type: configmap
- name: kubecf-ops-uaa
type: configmap
- name: kubecf-ops-sizing
type: configmap
- name: kubecf-ops-azs
type: configmap
- name: kubecf-ops-addons
type: configmap
- name: kubecf-ops-releases
type: configmap
- name: kubecf-ops-set-deployment-name
type: configmap
- name: kubecf-ops-set-opensuse-stemcells
type: configmap
- name: kubecf-user-provided-properties
type: configmap
status:
lastReconcile: "2020-03-23T03:14:23Z"
ConfigMap で定義されている BOSH manifest を確認してみます。
❯ kubectl -n kubecf get configmaps kubecf-cf-deployment -o yaml | head
apiVersion: v1
data:
manifest: |-
---
name: cf
manifest_version: v12.33.0
update:
canaries: 1
canary_watch_time: 30000-1200000
max_in_flight: 1
ConfigMap の data の中に BOSH manifest の YAML 定義がそのまま格納されていることが確認できます。
helm install
実行後、1 時間程度で、すべての Pod が Running になりました。
❯ kubectl -n kubecf get pod
NAME READY STATUS RESTARTS AGE
kubecf-adapter-0 4/4 Running 0 47m
kubecf-api-0 15/15 Running 1 46m
kubecf-auctioneer-0 4/4 Running 2 46m
kubecf-bosh-dns-86784b95f7-q7wg4 1/1 Running 0 47m
kubecf-bosh-dns-86784b95f7-v6spg 1/1 Running 0 47m
kubecf-cc-worker-0 4/4 Running 0 46m
kubecf-credhub-0 6/6 Running 0 46m
kubecf-database-0 2/2 Running 0 66m
kubecf-diego-api-0 6/6 Running 4 46m
kubecf-diego-cell-0 7/7 Running 2 46m
kubecf-doppler-0 9/9 Running 0 46m
kubecf-log-api-0 7/7 Running 0 46m
kubecf-nats-0 4/4 Running 0 47m
kubecf-router-0 5/5 Running 4 46m
kubecf-routing-api-0 4/4 Running 1 46m
kubecf-scheduler-0 10/10 Running 5 46m
kubecf-singleton-blobstore-0 6/6 Running 0 46m
kubecf-tcp-router-0 5/5 Running 0 46m
kubecf-uaa-0 7/7 Running 5 46m
helm install
を 2 回実行するだけで Cloud Foundry が立ち上がりました!
各 Pod は BOSH Instance Group を表しているようです。
quarksstatefulsets の状態に従って、StatefulSet が生成され、StatefulSet で定義された Pod が上記になっています。
多数のコンテナで構成される Pod も見受けられますが、これは各コンテナが BOSH の Job の単位で立ち上がっているためのようです。
試しに一つのコンテナの中に入って確認してみます。
❯ kubectl -n kubecf exec -it kubecf-api-0 -c cloud-controller-ng-cloud-controller-ng /bin/bash
/:/var/vcap/jobs/cloud_controller_ng# ps -alx
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 4172 700 sigtim Ss ? 0:00 /usr/bin/dumb-init -- /var/vcap/all-releases/container-run/container-run --post-start-name /var/vcap/jobs/cl
0 0 6 1 20 0 106444 8508 futex_ Ssl ? 0:00 /var/vcap/all-releases/container-run/container-run --post-start-name /var/vcap/jobs/cloud_controller_ng/bin/
4 0 11 6 20 0 1312760 158988 poll_s Sl ? 0:19 ruby /var/vcap/packages/cloud_controller_ng/cloud_controller_ng/bin/cloud_controller -c /var/vcap/jobs/cloud
4 0 1465 0 20 0 18328 6180 wait Ss pts/0 0:00 /bin/bash
0 0 1520 1465 20 0 36544 1740 - R+ pts/0 0:00 ps -alx
コンテナ内では単一の Job プロセスだけが稼働しているようです。
ただし、Instance Group の単位で Pod を構成しているため、ファイルシステムとしては各 Job の設定やログなどは見られるようです。
/:/var/vcap/jobs/cloud_controller_ng# ls /var/vcap/jobs/
binary-buildpack go-buildpack nginx-buildpack python-buildpack statsd_injector suse-nginx-buildpack suse-staticfile-buildpack
cc_uploader java-buildpack nodejs-buildpack r-buildpack suse-binary-buildpack suse-nodejs-buildpack
cloud_controller_ng loggregator_agent php-buildpack route_registrar suse-dotnet-core-buildpack suse-php-buildpack
dotnet-core-buildpack loggr-forwarder-agent policy-server ruby-buildpack suse-go-buildpack suse-python-buildpack
file_server loggr-udp-forwarder policy-server-internal staticfile-buildpack suse-java-buildpack suse-ruby-buildpack
/:/var/vcap/jobs/cloud_controller_ng# tail -n1 /var/vcap/sys/log/cloud_controller_ng/cloud_controller_ng.log
{"timestamp":1584937427.0345204,"message":"Completed 200 vcap-request-id: aa9c2b94-8c66-4c2e-9ac2-2abe60479dbc","log_level":"info","source":"cc.api","data":{"request_guid":"aa9c2b94-8c66-4c2e-9ac2-2abe60479dbc"},"thread_id":47375851013980,"fiber_id":47375849848360,"process_id":11,"file":"/var/vcap/packages/cloud_controller_ng/cloud_controller_ng/middleware/request_logs.rb","lineno":24,"method":"call"}
動作確認
cf CLI を使って、Cloud Foundry にログインしてみます。
❯ cf api --skip-ssl-validation "https://api.kubecf.<your domain>"
API エンドポイントを https://api.kubecf.<your domain> に設定しています...
OK
API エンドポイント: https://api.kubecf.<your domain>
api version: 2.146.0
ログインしていません。 'cf login' を使用してログインしてください。
❯ admin_pass=$(kubectl get secret \
--namespace kubecf kubecf.var-cf-admin-password \
-o jsonpath='{.data.password}' \
| base64 --decode)
❯ cf auth admin "${admin_pass}"
API エンドポイント: https://api.kubecf.<your domain>
認証中です...
OK
ターゲットの組織とスペースを表示または設定するには 'cf target' を使用してください。
スペースを作成し、サンプルアプリケーションをデプロイしてみます。
❯ cf create-space -o system kubecf
❯ cf target -o "system" -s "kubecf"
❯ git clone https://github.com/cloudfoundry-samples/test-app.git
❯ cd test-app
❯ cf push test-app
(snip...)
要求された状態: started
インスタンス: 1/1
使用: 256M x 1 インスタンス
URL: test-app-thioacetic-nonvariance.kubecf.<your domain>
最終アップロード日時: Mon Mar 23 04:26:21 UTC 2020
スタック: cflinuxfs3
ビルドパック: go
状態 開始日時 CPU メモリー ディスク 詳細
#0 実行 2020-03-23 01:32:03 PM 6.5% 256M の中の 15.5M 1G の中の 11.7M
普通の Cloud Foundry と変わりなくアプリケーションが起動しました。
アプリケーションにアクセスしてみます。
❯ curl -k https://test-app-thioacetic-nonvariance.kubecf.<your domain>/env
(snip...)
<body class="">
<div class="envs"><dl><dt>CF_INSTANCE_ADDR</dt><dd>10.244.3.9:61001</dd><dt>LANG</dt><dd>en_US.UTF-8</dd><dt>OLDPWD</dt><dd>/home/vcap</dd><dt>CF_INSTANCE_PORT</dt><dd>61001</dd><dt>VCAP_APPLICATION</dt><dd>{"application_id":"d9461e75-f4a3-4007-abe2-d687adccf893","application_name":"test-app","application_uris":["test-app-thioacetic-nonvariance.kubecf.<your domain>"],"application_version":"cc6339c5-e27b-4010-b1f1-53b56e3b78f8","cf_api":"https://api.kubecf.<your domain>","host":"0.0.0.0","instance_id":"8d40f86c-eca2-4f53-41e3-0cdd","instance_index":0,"limits":{"disk":1024,"fds":16384,"mem":256},"name":"test-app","organization_id":"5998979e-4f00-4bfc-9f62-75fde744ff80","organization_name":"system","port":8080,"process_id":"d9461e75-f4a3-4007-abe2-d687adccf893","process_type":"web","space_id":"5acc1fc7-94f0-4b33-8fb7-ff6306c6907b","space_name":"kubecf","uris":["test-app-thioacetic-nonvariance.kubecf.<your domain>"],"version":"cc6339c5-e27b-4010-b1f1-53b56e3b78f8"}</dd><dt>MEMORY_LIMIT</dt><dd>256m</dd><dt>USER</dt><dd>vcap</dd><dt>CF_INSTANCE_INTERNAL_IP</dt><dd>10.38.0.2</dd><dt>VCAP_APP_PORT</dt><dd>8080</dd><dt>PWD</dt><dd>/home/vcap/app</dd><dt>HOME</dt><dd>/home/vcap/app</dd><dt>CF_INSTANCE_KEY</dt><dd>/etc/cf-instance-credentials/instance.key</dd><dt>PORT</dt><dd>8080</dd><dt>TMPDIR</dt><dd>/home/vcap/tmp</dd><dt>DEPS_DIR</dt><dd>/home/vcap/deps</dd><dt>CF_INSTANCE_GUID</dt><dd>8d40f86c-eca2-4f53-41e3-0cdd</dd><dt>CF_INSTANCE_PORTS</dt><dd>[{"external":61001,"internal":8080,"external_tls_proxy":61003,"internal_tls_proxy":61001},{"external":61002,"internal":2222,"external_tls_proxy":61004,"internal_tls_proxy":61002}]</dd><dt>CF_SYSTEM_CERT_PATH</dt><dd>/etc/cf-system-certificates</dd><dt>CF_INSTANCE_IP</dt><dd>10.244.3.9</dd><dt>INSTANCE_INDEX</dt><dd>0</dd><dt>CF_INSTANCE_INDEX</dt><dd>0</dd><dt>SHLVL</dt><dd>1</dd><dt>INSTANCE_GUID</dt><dd>8d40f86c-eca2-4f53-41e3-0cdd</dd><dt>VCAP_SERVICES</dt><dd>{}</dd><dt>VCAP_APP_HOST</dt><dd>0.0.0.0</dd><dt>PATH</dt><dd>/usr/local/bin:/usr/bin:/bin:/home/vcap/app/bin</dd><dt>CF_INSTANCE_CERT</dt><dd>/etc/cf-instance-credentials/instance.crt</dd><dt>_</dt><dd>/home/vcap/app/bin/test-app</dd></dl></div>
</body>
</html>
ログの取得や SSH アクセスも問題なさそうです。
❯ cf logs --recent test-app
Retrieving logs for app test-app in org system / space kubecf as admin...
(snip...)
2020-03-23T13:34:16.25+0900 [APP/PROC/WEB/0] OUT test-app. Says Hello. on index: 0
❯ cf ssh test-app
vcap@8d40f86c-eca2-4f53-41e3-0cdd:~$ ls
app deps logs profile.d staging_info.yml tmp
vcap@8d40f86c-eca2-4f53-41e3-0cdd:~$ cd app/
vcap@8d40f86c-eca2-4f53-41e3-0cdd:~/app$ ls
bin build.sh Dockerfile go.mod go.sum handlers helpers LICENSE main.go Procfile README.md routes vendor
Eirini を有効にしてみる
デフォルト設定で KubeCF をインストールした場合、Cloud Foundry にデプロイしたアプリケーションは、Cloud Foundry 独自のコンテナオーケストレーターである Diego によってスケジューリングされます。
この場合、Diego のワーカーノードに相当する Diego Cell というコンポーネントが Pod として稼働しており、デプロイしたアプリケーションはさらにその上で稼働するという状態になります。
このようなオーバヘッドをなくし、Cloud Foundry のワークロードを直接 Kubernetes のリソースとして実行できるようにする機構が Eirini です。
KubeCF v1.0.1 では Eirini v1.0 を取り込んでいるそうなので、試してみます。
現行の Eirini なしの構成からありの構成にスイッチできるのかわからなかったので、確実のために一旦 KubeCF をアンインストールしてから、新しい構成でインストールし直します。
❯ helm -n kubecf delete kubecf
先ほどの values.yaml
に features.eirini.enabled=true
の設定を追加します。
system_domain: kubecf.<your domain>
features:
eirini:
enabled: true
再度 helm install
で KubeCF をインストールします。
先ほどとリソース構成が少し変わっていることが確認できます。
❯ helm install kubecf \
--namespace kubecf \
--values values.yaml \
https://github.com/cloudfoundry-incubator/kubecf/releases/download/v1.0.1/kubecf-v1.0.1.tgz
NAME: kubecf
LAST DEPLOYED: Mon Mar 23 13:51:14 2020
NAMESPACE: kubecf
STATUS: deployed
REVISION: 1
TEST SUITE: None
今度は 30 分程度ですべての Pod が Running になりました。
❯ kubectl -n kubecf get pod
NAME READY STATUS RESTARTS AGE
kubecf-adapter-0 4/4 Running 0 17m
kubecf-api-0 15/15 Running 1 16m
kubecf-bits-0 6/6 Running 0 16m
kubecf-bosh-dns-86784b95f7-tcx29 1/1 Running 0 17m
kubecf-bosh-dns-86784b95f7-zbkmz 1/1 Running 0 17m
kubecf-cc-worker-0 4/4 Running 0 16m
kubecf-credhub-0 6/6 Running 0 16m
kubecf-database-0 2/2 Running 0 25m
kubecf-diego-api-0 6/6 Running 2 17m
kubecf-doppler-0 9/9 Running 0 16m
kubecf-eirini-0 9/9 Running 0 16m
kubecf-log-api-0 7/7 Running 0 16m
kubecf-nats-0 4/4 Running 0 17m
kubecf-router-0 5/5 Running 2 16m
kubecf-routing-api-0 4/4 Running 0 16m
kubecf-scheduler-0 8/8 Running 0 16m
kubecf-singleton-blobstore-0 6/6 Running 0 17m
kubecf-tcp-router-0 5/5 Running 0 16m
kubecf-uaa-0 7/7 Running 2 16m
前回と同様に一般的な Cloud Foundry の操作が実行できるか確認します。
❯ cf api --skip-ssl-validation "https://api.kubecf.<your domain>"
❯ admin_pass=$(kubectl get secret \
--namespace kubecf kubecf.var-cf-admin-password \
-o jsonpath='{.data.password}' \
| base64 --decode)
❯ cf auth admin "${admin_pass}"
❯ cf create-space -o system kubecf
❯ cf target -o "system" -s "kubecf"
❯ git clone https://github.com/cloudfoundry-samples/test-app.git
❯ cd test-app
❯ cf push test-app
❯ curl -k https://test-app-unnauseating-underzealot.kubecf.<your domain>/env
❯ cf logs --recent test-app
上記までは問題なく実行できましたが、リリースノートにある通り、cf ssh
は機能しませんでした。
❯ cf ssh test-app
失敗
エラー: SSH session allocation failed: EOF
Eirini を有効にしたので、デプロイしたアプリケーションは Kubernetes のリソースとして稼働していることが確認できます。
デプロイしたアプリケーションは、Cloud Foundry のシステムコンポーネントとは別の Namespace で稼働するようです。
❯ kubectl -n kubecf-eirini get all
NAME READY STATUS RESTARTS AGE
pod/test-app-kubecf-5ae5449c68-0 1/1 Running 0 9m19s
NAME READY AGE
statefulset.apps/test-app-kubecf-5ae5449c68 1/1 9m19s
StatefulSet として定義されているアプリケーションの状態を見てみると、Cloud Foundry のアプリケーションとして振る舞うための情報がぎっしり詰め込まれていることが確認できます。
❯ kubectl -n kubecf-eirini get statefulsets.apps test-app-kubecf-5ae5449c68 -o yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
cloudfoundry.org/application_id: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/application_name: test-app
cloudfoundry.org/application_uris: '[{"hostname":"test-app-dubitable-flesh.kubecf.<your domain>","port":8080}]'
cloudfoundry.org/last_updated: "1584946309.0"
cloudfoundry.org/original_request: '{"guid":"1c559be1-5534-4bc2-a11e-357a462edf7b","version":"b06cbcf3-595e-43f2-857a-a2dd6ba6a78d","process_guid":"1c559be1-5534-4bc2-a11e-357a462edf7b-b06cbcf3-595e-43f2-857a-a2dd6ba6a78d","process_type":"web","app_guid":"1c559be1-5534-4bc2-a11e-357a462edf7b","app_name":"test-app","space_guid":"e1afaffc-3381-4c7c-8c8e-957102aa2406","space_name":"kubecf","organization_guid":"79c9de03-3dbb-4a1b-a7d2-de7c999cc0a5","organization_name":"system","environment":{"VCAP_APPLICATION":"{\"cf_api\":\"https://api.kubecf.<your domain>\",\"limits\":{\"fds\":16384,\"mem\":256,\"disk\":1024},\"application_name\":\"test-app\",\"application_uris\":[\"test-app-dubitable-flesh.kubecf.<your domain>\"],\"name\":\"test-app\",\"space_name\":\"kubecf\",\"space_id\":\"e1afaffc-3381-4c7c-8c8e-957102aa2406\",\"organization_id\":\"79c9de03-3dbb-4a1b-a7d2-de7c999cc0a5\",\"organization_name\":\"system\",\"uris\":[\"test-app-dubitable-flesh.kubecf.<your domain>\"],\"process_id\":\"1c559be1-5534-4bc2-a11e-357a462edf7b\",\"process_type\":\"web\",\"application_id\":\"1c559be1-5534-4bc2-a11e-357a462edf7b\",\"version\":\"b06cbcf3-595e-43f2-857a-a2dd6ba6a78d\",\"application_version\":\"b06cbcf3-595e-43f2-857a-a2dd6ba6a78d\"}","MEMORY_LIMIT":"256m","VCAP_SERVICES":"{}","PORT":"8080","VCAP_APP_PORT":"8080","VCAP_APP_HOST":"0.0.0.0"},"egress_rules":[{"protocol":"all","destinations":["0.0.0.0-9.255.255.255"],"annotations":["security_group_id:94bc433e-54f6-4e06-91ce-b5819eeec621"]},{"protocol":"all","destinations":["11.0.0.0-169.253.255.255"],"annotations":["security_group_id:94bc433e-54f6-4e06-91ce-b5819eeec621"]},{"protocol":"all","destinations":["169.255.0.0-172.15.255.255"],"annotations":["security_group_id:94bc433e-54f6-4e06-91ce-b5819eeec621"]},{"protocol":"all","destinations":["172.32.0.0-192.167.255.255"],"annotations":["security_group_id:94bc433e-54f6-4e06-91ce-b5819eeec621"]},{"protocol":"all","destinations":["192.169.0.0-255.255.255.255"],"annotations":["security_group_id:94bc433e-54f6-4e06-91ce-b5819eeec621"]},{"protocol":"tcp","destinations":["0.0.0.0/0"],"ports":[53],"annotations":["security_group_id:5ad109f0-96c9-48a3-9f9a-3c97d8ac0f84"]},{"protocol":"udp","destinations":["0.0.0.0/0"],"ports":[53],"annotations":["security_group_id:5ad109f0-96c9-48a3-9f9a-3c97d8ac0f84"]},{"protocol":"tcp","destinations":["10.244.4.27/32"],"ports":[8443],"annotations":["security_group_id:193773f1-315d-4d70-988b-4604c4d34a24"]},{"protocol":"tcp","destinations":["10.244.1.15/32"],"ports":[8844],"annotations":["security_group_id:f2463f0c-4db6-4220-9889-9aaef8ac13b1"]}],"placement_tags":[],"instances":1,"memory_mb":256,"disk_mb":1024,"cpu_weight":3,"health_check_type":"port","health_check_http_endpoint":null,"health_check_timeout_ms":0,"start_timeout_ms":60000,"last_updated":"1584946309.0","volume_mounts":[],"ports":[8080],"routes":{"cf-router":[{"hostname":"test-app-dubitable-flesh.kubecf.<your domain>","port":8080}]},"lifecycle":{"buildpack_lifecycle":{"start_command":"test-app","droplet_hash":"fe97c9c416f0ead6fa59fe3581bb94d22ab03d24","droplet_guid":"14ee9069-03b9-4cc2-ad5e-903f57ba43c7"}},"user_defined_annotations":{}}'
cloudfoundry.org/process_guid: 1c559be1-5534-4bc2-a11e-357a462edf7b-b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
cloudfoundry.org/routes: '[{"hostname":"test-app-dubitable-flesh.kubecf.<your domain>","port":8080}]'
cloudfoundry.org/space_name: kubecf
cloudfoundry.org/version: b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
creationTimestamp: "2020-03-23T06:51:49Z"
generation: 1
labels:
cloudfoundry.org/app_guid: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/guid: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/process_type: web
cloudfoundry.org/rootfs-version: ""
cloudfoundry.org/source_type: APP
cloudfoundry.org/version: b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
name: test-app-kubecf-5ae5449c68
namespace: kubecf-eirini
resourceVersion: "43813"
selfLink: /apis/apps/v1/namespaces/kubecf-eirini/statefulsets/test-app-kubecf-5ae5449c68
uid: df890530-43f9-4284-b6db-54ba2c75907a
spec:
podManagementPolicy: Parallel
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
cloudfoundry.org/guid: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/source_type: APP
cloudfoundry.org/version: b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
serviceName: ""
template:
metadata:
annotations:
cloudfoundry.org/application_id: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/process_guid: 1c559be1-5534-4bc2-a11e-357a462edf7b-b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
seccomp.security.alpha.kubernetes.io/pod: runtime/default
creationTimestamp: null
labels:
cloudfoundry.org/app_guid: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/guid: 1c559be1-5534-4bc2-a11e-357a462edf7b
cloudfoundry.org/process_type: web
cloudfoundry.org/rootfs-version: ""
cloudfoundry.org/source_type: APP
cloudfoundry.org/version: b06cbcf3-595e-43f2-857a-a2dd6ba6a78d
spec:
automountServiceAccountToken: false
containers:
- command:
- dumb-init
- --
- /lifecycle/launch
env:
- name: PORT
value: "8080"
- name: VCAP_APP_PORT
value: "8080"
- name: VCAP_APPLICATION
value: '{"cf_api":"https://api.kubecf.<your domain>","limits":{"fds":16384,"mem":256,"disk":1024},"application_name":"test-app","application_uris":["test-app-dubitable-flesh.kubecf.<your domain>"],"name":"test-app","space_name":"kubecf","space_id":"e1afaffc-3381-4c7c-8c8e-957102aa2406","organization_id":"79c9de03-3dbb-4a1b-a7d2-de7c999cc0a5","organization_name":"system","uris":["test-app-dubitable-flesh.kubecf.<your domain>"],"process_id":"1c559be1-5534-4bc2-a11e-357a462edf7b","process_type":"web","application_id":"1c559be1-5534-4bc2-a11e-357a462edf7b","version":"b06cbcf3-595e-43f2-857a-a2dd6ba6a78d","application_version":"b06cbcf3-595e-43f2-857a-a2dd6ba6a78d"}'
- name: PATH
value: /usr/local/bin:/usr/bin:/bin
- name: TMPDIR
value: /home/vcap/tmp
- name: MEMORY_LIMIT
value: 256m
- name: VCAP_APP_HOST
value: 0.0.0.0
- name: CF_INSTANCE_PORT
value: "8080"
- name: START_COMMAND
value: test-app
- name: HOME
value: /home/vcap/app
- name: CF_INSTANCE_ADDR
value: 0.0.0.0:8080
- name: VCAP_SERVICES
value: '{}'
- name: CF_INSTANCE_PORTS
value: '[{"external":8080,"internal":8080}]'
- name: LANG
value: en_US.UTF-8
- name: USER
value: vcap
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: CF_INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: CF_INSTANCE_INTERNAL_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
image: 127.0.0.1:32123/cloudfoundry/14ee9069-03b9-4cc2-ad5e-903f57ba43c7:fe97c9c416f0ead6fa59fe3581bb94d22ab03d24
imagePullPolicy: Always
livenessProbe:
failureThreshold: 4
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
name: opi
ports:
- containerPort: 8080
protocol: TCP
readinessProbe:
failureThreshold: 1
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 8080
timeoutSeconds: 1
resources:
limits:
ephemeral-storage: 1024M
memory: 256M
requests:
cpu: 30m
memory: 256M
securityContext:
allowPrivilegeEscalation: false
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: bits-service-registry-secret
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
status:
collisionCount: 0
currentReplicas: 1
currentRevision: test-app-kubecf-5ae5449c68-6f56989b98
observedGeneration: 1
readyReplicas: 1
replicas: 1
updateRevision: test-app-kubecf-5ae5449c68-6f56989b98
updatedReplicas: 1
cf ssh
コマンドが使えない代わりに kubectl exec
を使うことでアプリケーション内部に入ることができます。
ただし、cf ssh
よりも強い権限を持った状態でコンテナにログインできてしまうので、セキュリティ上好ましくはありません。
❯ kubectl -n kubecf-eirini exec -it test-app-kubecf-5ae5449c68-0 /bin/bash
vcap@test-app-kubecf-5ae5449c68-0:/$ ls /app
bin build.sh Dockerfile go.mod go.sum handlers helpers LICENSE main.go Procfile README.md routes vendor
後片付け
検証が終わったら helm delete
でリソースを削除します。
(削除があっという間に終わることも地味に嬉しいポイントです。)
❯ helm -n kubecf delete kubecf
❯ helm -n cf-operator delete cf-operator
おわりに
従来の BOSH を使った Cloud Foundry のインストール手順に比べて、あまりにも簡単にインストールができてしまったので、従来の方式を知る者としては大変感動しました。
ただし、今回検証した KubeCF の構成はあくまでデフォルト構成であり、構成定義自体は ConfigMap に BOSH manifest がそのまま書いてあるだけなので、
構成をカスタマイズしようと思うと従来どおり Ops File をゴリゴリ書いていく必要が (少なくとも現状では) ありそうです。
【2020/03/23 追記】Project Quarks は、互換性維持の観点から既存の BOSH Releases を (cf-operator によって) そのまま利用する方式を採用していますが、これとは別の動きとして、BOSH 非依存で Kubernetes のネイティブ機能のみで実行可能な Cloud Foundry リリースを開発する CF-for-K8s というプロジェクトが立ち上がっています。これらの 2 プロジェクトは競合関係にはなく、KubeCF のコンポーネントを CF-for-K8s のコンポーネントに段階的に置き換えていくことによって、既存ユーザーにスムーズな移行パスを提供することを図っていくようです。近いうちに CF-for-K8s のアルファ版がリリースされるようなので、リリースされた際にはそちらも動作検証してみようと思います。
【2020/04/18 追記】CF-for-K8s のアルファ版がリリースされたので検証しました。
Kubernetes の上に PaaS のレイヤを提供しようという取り組みは、Project Quarks 以外にも Knative など多数存在しており、今後の展望を見通すことは難しいですが、少なくとも Cloud Foundry の「ソースコードをアップロードしたらすぐ動く」という、Developer Experience を重要視する思想は大変素晴らしいと思っているので、この思想がうまく Kubernetes の世界にも統合されていくことを願っております。
それでは、良い新年をお迎えください!