Cloud Foundry Container Runtime (CFCR) を Azure にデプロイする

この記事は [Cloud Foundry Advent Calendar](https://qiita.com/advent-calendar/2017/cloudfoundry) 22日目の記事です。

# はじめに

この記事では [Cloud Foundry Container Runtime (CFCR)](https://www.cloudfoundry.org/container-runtime/) を [Microsoft Azure](https://azure.microsoft.com) にデプロイする試みを紹介します。
そもそもの話として「CFCR とは何か」「今までの Cloud Foundry (CFAR) はどうなるのか」といった説明が必要かとは思いますが、非常に丁寧な解説記事があるのでここでは割愛させて頂きます。

* [Cloud Foundry Container Runtimeでk8s運用](http://jaco.udcp.info/entry/2017/12/06/180205)
* [CFCRとCFARの使い分けの話](http://jaco.udcp.info/entry/2017/12/02/001539)

2017年12月22日時点において、CFCR がサポートしているクラウドプロバイダは、GCP, vSphere, AWS, OpenStack の 4 つです。
私の職場では Azure を利用することが多いのですが、 残念ながら [CFCR の Azure サポートの予定は直近ではない](https://github.com/cloudfoundry-incubator/kubo-deployment/issues/112#issuecomment-346673144) ようです。
ならば、自分でどうにか動かしてみよう、というのが本記事の趣旨になります。
ニッチな話題であり、どのくらい興味をお持ちの方がいるかわかりませんが、お付き合い頂けますと幸いです。

~~【2018/03/28 更新】 Azure サポートの最初のプルリクエストが発行されました。~~

* ~~https://github.com/cloudfoundry-incubator/kubo-release/pull/191~~
* ~~https://github.com/cloudfoundry-incubator/kubo-deployment/pull/285~~

~~【2018/05/03 更新】~~

~~上記のプルリクエストは [スパイク](http://agiledictionary.com/209/spike/) に利用され、一旦クローズされました。~~

* ~~[**Spike:** CFCR can be deployed on Azure](https://www.pivotaltracker.com/n/projects/2093412/stories/155619338)~~

~~CFCR チームが正式に Azure サポートに着手する際には改めて活用されるかもしれないようです。~~

【2018/7/6 更新】

最初のプルリクエストをベースにした新しいプルリクエストが発行されました。

* https://github.com/cloudfoundry-incubator/kubo-release/pull/223
* https://github.com/cloudfoundry-incubator/kubo-deployment/pull/320

このプルリクエストは [bosh-agent のプルリクエスト](https://github.com/cloudfoundry/bosh-agent/pull/174) に依存しており、こちらがマージされるまではペンディングのようです。
⇒ 2018/10/12 マージされました。
⇒ 2018/10/16 experimental の扱いで Azure 対応された、[CFCR v0.23.0 がリリース](https://docs-cfcr.cfapps.io/overview/release-notes/#v0230) されました。
⇒ 2018/11/9 [CFCR v0.24.0 がリリース](https://docs-cfcr.cfapps.io/overview/release-notes/#v0240) され、Azure 対応はアルファ扱いからベータ扱いになりました。

正式に Azure サポートが完了したら、この記事も書き直そうかと思います。

※ CFCR は本記事執筆時点において開発段階にあり、今後実装が大きく変わる可能性もあります（⇒ 【2018/10/1 更新】： [BOSH Director のデプロイ方法などが変わりました](https://docs-cfcr.cfapps.io/overview/release-notes/#v0220)）。したがって、本記事に記載した手順を実行した結果について、筆者は一切の責任を負いかねます。ただし、誤りの指摘等につきましては歓迎致しますのでよろしくお願いします。

# 0. 前提条件

今回の検証では、[kubo-deployment (7bbf17aef5)](https://github.com/cloudfoundry-incubator/kubo-deployment/tree/7bbf17aef5e2269fd4a8e7d0babe4de426de51ce) をベースに、[Azure 用のカスタマイズを加えた kubo-deployment](https://github.com/a09-capf/kubo-deployment/commit/234bed17a19b973358f5a7ca546ac567dff76c07) を利用します。
また、事前に

* [Azure サブスクリプションの作成](https://azure.microsoft.com/en-us/pricing/free-trial/)
* [サービスプリンシパルの発行](https://github.com/cloudfoundry-incubator/bosh-azure-cpi-release/blob/master/docs/get-started/create-service-principal.md)

を済ませておく必要があります。

# 1. IaaS リソースの準備

CFCR のデプロイに必要な Azure リソースを用意します。
GCP のデプロイ等では、 Terraform が利用されていますが、ここでは Azure Resource Manager Template (AWS の CloudFormation のようなもの) を利用します。

* [CFCR ARM Template](https://github.com/a09-capf/kubo-deployment/tree/azure/docs/user-guide/platforms/azure)

上記のテンプレートを利用すると、 Azure 上に以下のリソースを用意します。

* リソースグループ
* ストレージアカウント
* 仮想ネットワーク
* ネットワークセキュリティグループ ✕ 2
* パブリック IP アドレス ✕ 2
* ロードバランサー
* 仮想 NIC
* 仮想マシン

以下のリンクから Azure Portal にログインすると、テンプレートに設定するパラメータを入力するフォーム画面が現れます。

* [Deploy CFCR ARM Template to Azure](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fa09-capf%2Fkubo-deployment%2Fazure%2Fdocs%2Fuser-guide%2Fplatforms%2Fazure%2Fazuredeploy.json)

| パラメータ名 | 必須項目 | デフォルト値 | 説明 |
|:----:|:--------:|:-------------:|:----------- |
| vmName | **YES** | azure-vm | CFCR のデプロイ作業をおこなう踏み台 VM の名前 |
| ubuntuOSVersion | NO | 16.04.0-LTS | VM の OS バージョン (Ubuntu のみ対応) |
| adminUsername | **YES** | | VM のログインユーザ名 |
| sshKeyData | **YES** | | VM の SSH ログインに使う RSA 公開鍵 |
| tenantID | **YES**  | | サービスプリンシパルのテナント ID |
| clientID | **YES**  | | サービスプリンシパルのクライアント ID |
| clientSecret | **YES** | | サービスプリンシパルのクライアントシークレット |
| \_artifactsLocation | NO | https://raw.githubusercontent.com/a09-capf/kubo-deployment/azure/docs/user-guide/platforms/azure/ | このテンプレートのロケーションのベース URL |

上記のパラメータを入力し、Azure Marketplace の使用条件に同意して、「購入」ボタンをクリックすると、リソースのプロビジョニングが始まります。
10 分程でプロビジョニングは完了します。

# 2. BOSH Director のデプロイ

プロビジョニングされた 仮想マシンに SSH でログインします。ログインに必要な情報は、Azure Portal で当該の仮想マシンを選択して、「概要」 → 「接続」 と進むと確認できます。

<img width="1435" alt="cfcr01.png" src="https://qiita-image-store.s3.amazonaws.com/0/119820/bc549e24-9914-a9bd-1af5-1b97dedc0663.png">

仮想マシンにログインしたら、今回の検証で使う kubo-deployment を clone します。

```bash
$ git clone -b azure https://github.com/a09-capf/kubo-deployment.git
$ cd kubo-deployment
```

デプロイに使ういくつかの環境変数を設定します。

```bash
$ export kubo_env=~/kubo-env
$ export kubo_env_name=kubo
$ export kubo_env_path="${kubo_env}/${kubo_env_name}"
$ export BOSH_ENV=$kubo_env_path
$ export BOSH_NAME=$kubo_env_name
```
デプロイメントの情報を格納するディレクトリを作成します。

```bash
$ mkdir -p "${kubo_env}"
```

BOSH と CFCR のデプロイ時に埋め込むパラメータのテンプレートファイルを生成します。

```bash
./bin/generate_env_config "${kubo_env}" ${kubo_env_name} azure
```
[上記のスクリプト](https://github.com/a09-capf/kubo-deployment/blob/azure/bin/generate_env_config) は、 [IaaS 非依存の共通パラメータ一覧](https://github.com/a09-capf/kubo-deployment/tree/azure/configurations/generic) と [IaaS ごとに異なるパラメータ一覧](https://github.com/a09-capf/kubo-deployment/tree/azure/configurations/azure) を結合して、`${kubo_env_path}/director.yml` と `${kubo_env_path}/director-secrets.yml` という ２ つのテンプレートファイルを生成します。後者は機密情報を記載するファイルなので、間違って git push などしないように十分に注意してください。

生成されたテンプレートファイルをそれぞれ編集します。
基本的に以下の例と同じで構わないので、`xxx` になっている部分のみご自身の環境に合わせて記載してください。

```yaml:director.yml
internal_ip: 10.0.1.252 # Internal ip address of the BOSH director. Please select an available ip address in the network you wish to deploy into.

internal_cidr: 10.0.1.0/24 # CIDR range that BOSH will deploy to
internal_gw: 10.0.1.1 # internal gateway

director_name: bosh # user friendly Director name
dns_recursor_ip: 168.63.129.16 # Azure が提供する DNS リゾルバ

kubo_release_url: https://storage.googleapis.com/kubo-public/kubo-release-latest.tgz

worker_count: 1 # リソース節約のため 3 → 1 に変更

routing_mode: iaas
kubernetes_master_host: xx.xx.xx.xx # ロードバランサー (cfcr-lb) のパブリック IP アドレスを記載する
# master_target_pool: # コメントアウトする

authorization_mode: rbac # なぜか abac だと CFCR デプロイ時に権限エラーが発生したので rbac にする

vnet_name: boshvnet-crp # Azure Virtual Network Name
subnet_name: CFCR # Azure Subnet Name
subscription_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # Azure Subscription ID
resource_group_name: xxx # Azure Resource Group Name
storage_account_name: cfcrsa # Azure Storage Account Name
default_security_group: nsg-cfcr # Azure Network Security Group

iaas: azure
```

```yaml:director-secrets.yml
tenant_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # Azure Service Principal TENANT_ID
client_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # Azure Service Principal CLIENT_ID
client_secret: xxxxxxxxxxxxxxxxxxxxxx # Service Principal CLIENT_SECRET
```

なお、CFCR は [CFAR と ルーティング機能を共用する](https://docs-cfcr.cfapps.io/installing/cf-routing/) こともできますが、今回は CFCR を単体でデプロイするので、 Azure のロードバランサーを使用する設定にしています。

テンプレートを編集したら、 BOSH Director をデプロイします。

```bash
$ ./bin/deploy_bosh "${kubo_env_path}"
```

[上記のスクリプト](https://github.com/a09-capf/kubo-deployment/blob/azure/bin/deploy_bosh) は、内部的には以下のように [Operations Files](https://bosh.io/docs/cli-ops-files.html) を組み合わせてマニフェストを生成して Azure 用の Director をデプロイしています。
（以下の擬似コードは、わかりやすさのために実際の処理を簡略化しています。）

```bash
$ bosh_manifest=$(bosh-cli int ./bosh-deployment/bosh.yml --ops-file ./bosh-deployment/uaa.yml --ops-file ./bosh-deployment/local-dns.yml --ops-file ./configurations/generic/dns-addresses.yml --ops-file ./bosh-deployment/credhub.yml --ops-file ./configurations/generic/bosh-admin-client.yml --vars-file ../kubo-env/kubo/director.yml --vars-file ../kubo-env/kubo/director-secrets.yml --ops-file ./bosh-deployment/azure/cpi.yml)
$ bosh-cli create-env $bosh_manifest --vars-store ../kubo-env/kubo/creds.yml --state ../kubo-env/kubo/state.json
```

10 分程で Director のデプロイは完了します。

# 3. CFCR のデプロイ

ここまで来ると、[公式ドキュメントの手順](https://docs-cfcr.cfapps.io/installing/deploying-cfcr/#step-3-deploy-cfcr) に従って CFCR をデプロイするだけです。

```bash
$ ./bin/deploy_k8s ~/kubo-env/kubo my-cluster public
```

[上記のスクリプト](https://github.com/a09-capf/kubo-deployment/blob/azure/bin/deploy_k8s) は、内部では以下の擬似コードのようにマニフェストを生成しているようです。

```bash
$ bosh-cli int ./manifest/cfcr.yml --ops-file=./manifests/ops-files/misc/dev.yml --ops-file=./manifests/ops-files/misc/bootstrap.yml --ops-file=./manifests/ops-files/use-runtime-config-bosh-dns.yml --vars-file=../kubo-env/kubo/director.yml --var deployment_name=my-kubo --var director_uuid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --vars-file=../kubo-env/kubo/creds.yml --vars-file=../kubo-env/kubo/director-secrets.yml
```

20 分程で CFCR のデプロイは完了します。

bosh-cli をセットアップして、正常に BOSH VM が稼働しているか確認します。

```bash
$ source bin/set_bosh_environment
$ ./bin/set_bosh_alias $kubo_env_path
$ bosh-cli -e $kubo_env_name -d my-cluster vms
```

以下のような画面が出力されれば、デプロイは成功です。

```
Deployment 'my-cluster'

Instance                                     Process State  AZ  IPs       VM CID                                                                                              VM Type
master/36d8a40f-7c17-4130-8822-3acfc377b17e  running        z1  10.0.1.4  agent_id:d6a793ea-b46b-459d-aec3-3b3f28b898a3;resource_group_name:cfcr;storage_account_name:cfcrsa  master
worker/133e0146-0003-4f21-8e1c-214f5aa55041  running        z1  10.0.1.5  agent_id:09dbe3d8-df10-47a9-b232-837de005cd67;resource_group_name:cfcr;storage_account_name:cfcrsa  worker

2 vms

Succeeded
```

デプロイが成功したら、 Azure ロードバランサーから Kuberenetes Master へのルーティングを設定します。
Azure Portal から `cfcr-lb` を選択して、「バックエンド プール」 → 「BackendPool」 と進み、関連付け先に「可用性セット」を選択します。可用性セットに「master」を選択し、ターゲットネットワーク IP 構成に進みます。「ターゲット仮想マシン」と「ネットワーク IP 構成」をそれぞれ選択します（いずれも選択肢は一つしかないはずなので、それを選択する）。

<img width="1027" alt="cfcr02.png" src="https://qiita-image-store.s3.amazonaws.com/0/119820/a30e74f0-12a0-1e3e-f5bc-6d22dd35592a.png">

最後に「保存」ボタンを押せばすべてのデプロイ作業が完了です！

# 4. 動作確認

簡単な動作確認をおこないます。

はじめに kubectl の設定をおこないます。

```bash
$ ./bin/set_kubeconfig $kubo-env_path my-cluster
```

`kube-system` namespace の Pods を確認します。

```bash
$ kubectl get pods --namespace=kube-system
```

以下のような画面が出力されていれば正常な状態です。

```
NAME                                    READY     STATUS    RESTARTS   AGE
heapster-776fb9d89b-bw8wh               1/1       Running   0          27m
kube-dns-9dc479d4f-8n68q                3/3       Running   0          27m
kubernetes-dashboard-789b8cfdb7-zrdws   1/1       Running   0          27m
monitoring-influxdb-c577bbf7-r7kjq      1/1       Running   0          27m
```

Nginx の Deployment を作成してみます。

```bash
$ kubectl run nginx --image=nginx:1.11.3
```

Service を NodePort で公開します。

```
$ kubectl expose deployment nginx --port 80 --type=NodePort
```

Node の IP アドレスと Service の Port をそれぞれ確認して、疎通確認をおこないます。

```bash
$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
10.0.1.5   Ready     <none>    35m       v1.8.4
$ kubectl get services
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.100.200.1     <none>        443/TCP        38m
nginx        NodePort    10.100.200.177   <none>        80:31901/TCP   1m
$ curl 10.0.1.5:31901
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(以下略)
```

アクセスできました！

動作確認が終わったら、 CFCR と BOSH Director を削除します。

```bash
$ bosh-cli -e $kubo_env_name -d my-cluster delete-deployment
$ ./bin/destroy_bosh $kubo_env_path
```

最後に Azure Portal からリソースグループを削除して、終了です。

# おわりに

もう少し突っ込んだ解説をしたかったのですが、時間切れになってしまいました。
ひとまず Azure 上で CFCR が動くというところまでなんとか漕ぎ着けました。ただし、実際にはこれで Azure 対応ができたとは言えません。
例えば、Pod がマウントする Volume として Azure Storage Account を利用したい場合など、Azure リソースを Kuberenetes の オブジェクトとして使うためには、IaaS の API とのやり取りを仲介する [Cloud Provider](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/azure) という機構を用意する必要があります（BOSH の CPI と似てますね！ このあたりの起源も Borg から来ているのでしょうか？）。
この Cloud Provider の Azure 対応をするには、[CFCR 本体](https://github.com/cloudfoundry-incubator/kubo-release/tree/master/jobs/cloud-provider) の方に手を入れる必要がありそうなので、まとまった時間ができたら、腰を据えて挑戦してみたいと思っています。

長くなりましたが、最後までお付き合い頂きありがとうございます。
それでは良い新年をお迎えください！！