GCPにCloud Foundryを構築する


[Deploying Cloud Foundry on Google Compute Engine](https://github.com/cloudfoundry-incubator/bosh-google-cpi-release/tree/master/docs/cloudfoundry)に従ってGCP上にCloud Foundryの環境を構築します。

前提として必要なBOSHの動作環境は[前回](http://qiita.com/tommarute/items/3ad1f3204e72933185c6)構築しています。

## 関連ソフトウェア

* [BOSH](http://bosh.io/)
	* 分散システムの構成管理ツール

* [Terraform](https://www.terraform.io/)
	* オーケストレーションツール

## Cloud Foundryの用語

* cf
	* Cloud Foundryを操作するコマンドラインツール
* runtime
	* プログラミング言語の動作環境（JavaやGoなど）
* service
	* DBなどのミドルウェア（MySQLやRedisなど）

## Cloud Foundry環境構築

前回作成したBOSH CLIを実行するbosh-bastionにログインし、必要な環境変数を設定します。

```
gcloud compute ssh bosh-bastion
cd /share/docs/cloudfoundry
export region_compilation=us-central1
export zone_compilation=us-central1-b
```

terraform planで作成するリソース内容を確認します。

```
$ terraform plan \
>   -var network=${network} \
>   -var projectid=${project_id} \
>   -var region=${region} \
>   -var region_compilation=${region_compilation} \
>   -var zone=${zone} \
>   -var zone_compilation=${zone_compilation}
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.


The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ google_compute_address.cf
    address:   "<computed>"
    name:      "cf"
    self_link: "<computed>"

+ google_compute_firewall.cf-public
    allow.#:                   "1"
    allow.3583880081.ports.#:  "4"
    allow.3583880081.ports.0:  "80"
    allow.3583880081.ports.1:  "443"
    allow.3583880081.ports.2:  "2222"
    allow.3583880081.ports.3:  "4443"
    allow.3583880081.protocol: "tcp"
    name:                      "cf-public"
    network:                   "bosh"
    project:                   "<computed>"
    self_link:                 "<computed>"
    target_tags.#:             "1"
    target_tags.2977945361:    "cf-public"

+ google_compute_forwarding_rule.cf-http
    ip_address:  "${google_compute_address.cf.address}"
    ip_protocol: "TCP"
    name:        "cf-http"
    port_range:  "80"
    project:     "<computed>"
    region:      "<computed>"
    self_link:   "<computed>"
    target:      "${google_compute_target_pool.cf-public.self_link}"

+ google_compute_forwarding_rule.cf-https
    ip_address:  "${google_compute_address.cf.address}"
    ip_protocol: "TCP"
    name:        "cf-https"
    port_range:  "443"
    project:     "<computed>"
    region:      "<computed>"
    self_link:   "<computed>"
    target:      "${google_compute_target_pool.cf-public.self_link}"

+ google_compute_forwarding_rule.cf-ssh
    ip_address:  "${google_compute_address.cf.address}"
    ip_protocol: "TCP"
    name:        "cf-ssh"
    port_range:  "2222"
    project:     "<computed>"
    region:      "<computed>"
    self_link:   "<computed>"
    target:      "${google_compute_target_pool.cf-public.self_link}"

+ google_compute_forwarding_rule.cf-wss
    ip_address:  "${google_compute_address.cf.address}"
    ip_protocol: "TCP"
    name:        "cf-wss"
    port_range:  "4443"
    project:     "<computed>"
    region:      "<computed>"
    self_link:   "<computed>"
    target:      "${google_compute_target_pool.cf-public.self_link}"

+ google_compute_http_health_check.cf-public
    check_interval_sec:  "30"
    healthy_threshold:   "10"
    host:                "api.${google_compute_address.cf.address}.xip.io"
    name:                "cf-public"
    port:                "80"
    project:             "<computed>"
    request_path:        "/info"
    self_link:           "<computed>"
    timeout_sec:         "5"
    unhealthy_threshold: "2"

+ google_compute_subnetwork.cf-compilation-subnet-1
    gateway_address: "<computed>"
    ip_cidr_range:   "10.200.0.0/16"
    name:            "cf-compilation-us-central1"
    network:         "https://www.googleapis.com/compute/v1/projects/cf-test/global/networks/bosh"
    region:          "us-central1"
    self_link:       "<computed>"

+ google_compute_subnetwork.cf-private-subnet-1
    gateway_address: "<computed>"
    ip_cidr_range:   "192.168.0.0/16"
    name:            "cf-private-us-east1"
    network:         "https://www.googleapis.com/compute/v1/projects/cf-test/global/networks/bosh"
    self_link:       "<computed>"

+ google_compute_target_pool.cf-public
    health_checks.#: "1"
    health_checks.0: "cf-public"
    instances.#:     "<computed>"
    name:            "cf-public"
    project:         "<computed>"
    region:          "<computed>"
    self_link:       "<computed>"
Plan: 10 to add, 0 to change, 0 to destroy.
```

実際にリソースを作成します。

```
terraform apply \
  -var network=${network} \
  -var projectid=${project_id} \
  -var region=${region} \
  -var region_compilation=${region_compilation} \
  -var zone=${zone} \
  -var zone_compilation=${zone_compilation}
```

Cloud FoundryのVMが使うアカウントを作成します。

```
gcloud iam service-accounts create cf-component
```

アカウントに権限を割り当てます。

```
gcloud projects add-iam-policy-binding ${project_id} \
    --member serviceAccount:cf-component@${project_id}.iam.gserviceaccount.com \
    --role "roles/editor" \
    --role "roles/logging.logWriter" \
    --role "roles/logging.configWriter"
```

BOSH CLIにリモートの環境を指定します。
（前回に引き続き作業をしているので本来必要ないと思います）

```
bosh target 10.0.0.6
```

Cloud Foundryのデプロイで使用する情報を環境変数に設定します。

```
export vip=$(terraform output ip)
export zone=$(terraform output zone)
export zone_compilation=$(terraform output zone_compilation)
export region=$(terraform output region)
export region_compilation=$(terraform output region_compilation)
export private_subnet=$(terraform output private_subnet)
export compilation_subnet=$(terraform output compilation_subnet)
export network=$(terraform output network)
export director=$(bosh status --uuid)
```

ステンシルをアップロードします。

```
bosh upload stemcell https://bosh.io/d/stemcells/bosh-google-kvm-ubuntu-trusty-go_agent?v=3263.7
```

必要なリリースをアップロードします。
Releaseはバージョンごとに取得したJobのスナップショットです。


DiegoはCloud Foundryが内部で使うコンテナベースの実行環境です。
GardenはLinuxコンテナの実装です。


```
bosh upload release https://bosh.io/d/github.com/cloudfoundry/cf-mysql-release?v=23
bosh upload release https://bosh.io/d/github.com/cloudfoundry-incubator/garden-linux-release?v=0.340.0
bosh upload release https://bosh.io/d/github.com/cloudfoundry-incubator/etcd-release?v=36
bosh upload release https://bosh.io/d/github.com/cloudfoundry-incubator/diego-release?v=0.1454.0
bosh upload release https://bosh.io/d/github.com/cloudfoundry/cf-release?v=231
```

manifest.ymlをデプロイする前に中身を覗いてみます（長いので一部だけ）。
先ほどアップロードしたリリースでJobが定義されてます。


```yml:manifest.yml
releases:
  - name: cf-mysql
    version: "23"
  - name: cf
    version: "231"
  - name: diego
    version: "0.1454.0"
  - name: garden-linux
    version: "0.340.0"
  - name: etcd
    version: "36"

compilation:
  workers: 12
  network: compilation
  reuse_compilation_vms: true
  cloud_properties:
    zone: <%= google_zone_compilation%>
    region: <%= google_region_compilation%>
    machine_type: n1-standard-2
    root_disk_size_gb: 100
    root_disk_type: pd-ssd
    preemptible: true
    service_account: <%= cf_service_account%>

resource_pools:
  - name: common
    network: private
    stemcell:
      name: bosh-google-kvm-ubuntu-trusty-go_agent
      version: latest
    cloud_properties:
      zone: <%= google_zone%>
      machine_type: n1-standard-1
      root_disk_size_gb: 20
      root_disk_type: pd-standard
      service_account: <%= cf_service_account%>

jobs:
  - name: consul
    templates:
      - name: consul_agent
        release: cf
      - name: metron_agent
        release: cf
    instances: 1
    resource_pool: common
    persistent_disk_pool: consul
    networks:
      - name: private
        default: [dns, gateway]
    properties:
      consul:
        agent:
          mode: server

  - name: mysql
    templates:
      - name: mysql
        release: cf-mysql
    instances: 1
    resource_pool: common
    persistent_disk_pool: mysql
    networks:
      - name: private
        default: [dns, gateway]
```

manifest.ymlをデプロイします。

```
bosh deployment manifest.yml
bosh deploy
```

task 7がtimeoutでエラーになってしまいました。

```
Error 450002: Timed out sending 'compile_package' to 6d82e114-ee06-4d5b-8569-2a10f7f0f722 after 45 seconds
Task 7 error
For a more detailed error report, run: bosh task 7 --debug
```

debugしてみましたがめぼしい情報もないのでもう一度実行してみます。
今度は正常終了しました。

```
...
Task 14 done
Started         2016-11-14 10:43:53 UTC
Finished        2016-11-14 11:42:36 UTC
Duration        00:58:43
Deployed 'cf' to 'micro-google'
```

Cloud Foundryの環境にログインします。

```
$ cf api https://api.${vip}.xip.io --skip-ssl-validation
Setting api endpoint to https://api.123.45.67.89.xip.io...
OK
                
API endpoint:   https://api.123.45.67.89.xip.io (API version: 2.51.0)
Not logged in. Use 'cf login' to log in.

$ cf login
API endpoint: https://api.123.45.67.89.xip.io
Email> admin
Password> 
Authenticating...
OK
Targeted org system
                
API endpoint:   https://api.123.45.67.89.xip.io (API version: 2.51.0)
User:           admin
Org:            system
Space:          No space targeted, use 'cf target -s SPACE'
```

環境構築はここまで。
せっかく作ったので少し触ってみましょう。

簡単なアプリケーションをデプロイして動作を確認します。

以降は[Deploying Cloud Foundry on Google Compute Engine](https://github.com/cloudfoundry-incubator/bosh-google-cpi-release/tree/master/docs/cloudfoundry)に載っていない内容です。

スペースを作れと言われているので作ります。

```
$ cf create-space develop
Creating space develop in org system as admin...
OK
Assigning role RoleSpaceManager to user admin in org system / space develop as admin...
OK
Assigning role RoleSpaceDeveloper to user admin in org system / space develop as admin...
OK
TIP: Use 'cf target -o "system" -s "develop"' to target new space
```

スペースと組織を紐付けます。

```
$ cf target -o "system" -s "develop"
                
API endpoint:   https://api.123.45.67.89.xip.io (API version: 2.51.0)
User:           admin
Org:            system
Space:          develop
```

使えるbuildpackを確認します。

```
$ cf buildpacks
Getting buildpacks...

buildpack                position   enabled   locked   filename
staticfile_buildpack     1          true      false    staticfile_buildpack-cached-v1.3.1.zip
java_buildpack_offline   2          true      false    java-buildpack-offline-v3.6.zip
ruby_buildpack           3          true      false    ruby_buildpack-cached-v1.6.13.zip
nodejs_buildpack         4          true      false    nodejs_buildpack-cached-v1.5.5.zip
go_buildpack             5          true      false    go_buildpack-cached-v1.7.2.zip
python_buildpack         5          true      false    python_buildpack-cached-v1.5.4.zip
php_buildpack            6          true      false    php_buildpack-cached-v4.3.5.zip
binary_buildpack         7          true      false    binary_buildpack-cached-v1.0.1.zip
```

とりあえずphpでさっと試します。

```
$ mkdir -p /cf_app_test/php
$ cd /cf_app_test/php
# ホスト名とIPアドレスを返す簡単な処理を書く
$ cat index.php
<?php
    echo "Hostname: " . gethostname() . "</br>";
    echo "IP Address: " . $_SERVER["REMOTE_ADDR"];
?>
```

アプリケーションをデプロイします。

```
$ cf push show-system-info-php -b php_buildpack
```

デプロイしたアプリケーションを確認します。

```
$ cf apps
Getting apps in org system / space develop as admin...
OK

name                   requested state   instances   memory   disk   urls
show-system-info-php   started           1/1         1G       1G     show-system-info-php.123.45.67.89.xip.io
```

表示されたURLにブラウザからアクセスすると、正常に表示されました。
ちなみにこちらはcurlの実行結果です。

```
$ curl http://show-system-info-php.123.45.67.89.xip.io
Hostname: 1olr0hrk30q</br>IP Address: XXX.XXX.XXX.XXX
```

なお、123.45.67.89はcf-publicというロードバランサのIPアドレスです。
この時点でフロントの負荷分散までできているようですね。

ひとまずCloud Foundryの構築から簡単な動作確認までができました。

## 参考

* https://github.com/cloudfoundry-incubator/bosh-google-cpi-release/tree/master/docs/cloudfoundry
* http://www.publickey1.jp/blog/15/cloud_foundry_diego.html