Concourse構築(Lite-VM) @ GCP

# 概要

以前まで、Concourseを1VMで立てる方法はVagrantを使ったものだったが、最近は`bosh`を使った構築方法に統一されたらしく、今回は手元のローカル環境ではなく、クラウドプロバイダでホスティングして常時実行させたくて、GCP上で継続的にホスティングできるような方法を調べて実行してみることにした。

# 構築手段の選び方

Concourseをインターネット上に構築する方法は何通りかあるが、すぐ出てくる手段としては以下3つが多い。

1. **クラウド系IaaSでVMを手動で立てて、そこにSSHしてconcourseのバイナリを放り込む**
    - 流石に原始的すぎるし、ある程度手軽に運用したいとはいえ、なるだけ避けたい。
    - 最近は新しい`concourse quickstart`というコマンドも出たが、継続的に使うにはやや躊躇われる。
2. **concourse-deploymentを使って、boshを使ってVMごとデプロイする**
    - `1.`の方法をboshを使ってdeployment化したもの。本家が作っており安心感がある。
       - [concourse-bosh-deployment/lite at master · concourse/concourse-bosh-deployment](https://github.com/concourse/concourse-bosh-deployment/tree/master/lite)
    - n1-standard-1のVM1台だけなので月額$20くらいで運用できる。
3. **helmを使ってGoogleKubenetesEngine(GKE)の上にコンテナとしてデプロイする**
  - おそらく一番簡単な方法で、資料も豊富にある。
     - [GKEでConcourseを使うには - Qiita](https://qiita.com/watawuwu/items/04d4c8c9cf68b3442355)
     - [Concourse を使用した Kubernetes Engine での Helm チャートの継続的デリバリー  |  ソリューション  |  Google Cloud](https://cloud.google.com/solutions/continuous-integration-helm-concourse?hl=ja)
  - k8sにマシンリソースを一部割くため、コスパはあまり良くない。
     - CPUのコア数が最低2個は求められるので、n1-standard-2だと月額$50くらいで運用できる。
  - 個人でConcourseだけ使える環境が欲しい、分割運用したい、というユースケースにはちょっと大袈裟。

ということで、多少調べれば実現できそう＆コスパも良さそうな `2.`の手段を調べて実行してみることにした。

# 作業環境

- macOS 10.13.4
  - Terminal.app / zsh
  - homebrew
  - git
- concourse 3.11.0
- bosh 3.0.1
- Google Cloud Platform

# 作業内容

## 1.作業環境の構築

事前に、`bosh`, `gcloud`のCLIを手元の端末にインストールする。
Macを使っているならhomebrewだけで完結する。

```shell
brew tap cloudfoundry/tap
brew install bosh-cli
brwe cask install google-cloud-sdk
```

`gloud` でログインし、デフォルトリージョンとゾーンをセットする。

```shell
gcloud auth login
export REGION=asia-northeast1
gcloud config set compute/region $REGION
export ZONE=asia-northeast1-a
gcloud config set compute/zone $ZONE
```

先述のdeploymentのrepoをDLし、`lite`のディレクトリまで移動したら準備完了。

- [concourse-bosh-deployment/lite at master · concourse/concourse-bosh-deployment](https://github.com/concourse/concourse-bosh-deployment/tree/master/lite)

```shell
git clone https://github.com/concourse/concourse-bosh-deployment.git
cd concourse-bosh-deployment/lite
```

## 2. GCPでの事前作業

GCPでの事前準備が必要なものから着手する。

### i. Project作成・課金方法の登録

まずはプロジェクト作成。Project IDを決めて登録しておく。
何度も使うので環境変数に入れておくことにする。

```shell
export PROJECT_ID=your-project-id
gcloud projects create $PROJECT_ID --name $PROJECT_ID --enable-cloud-apis
gcloud config set core/project $PROJECT_ID
```

また、ComputeEngine APIを利用するため、Google APIsにブラウザでアクセスして、
下記からプロジェクトに支払い方法をあらかじめ登録しておく。

- [お支払い - Google APIs](https://console.developers.google.com/billing/linkedaccount)

### ii. サービスアカウントの作成、権限付与

boshからのアクセスに必要なサービスアカウントを作成し、クレデンシャルキーをDLする。
また、このサービスアカウントに対して、boshから操作ができるように編集者の権限を付与する。

```shell
gcloud iam service-accounts create concourse-keys --display-name=concourse-keys
gcloud iam service-accounts keys create gcp-state.json \
    --iam-account concourse-keys@$PROJECT_ID.iam.gserviceaccount.com

gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member serviceAccount:concourse-keys@$PROJECT_ID.iam.gserviceaccount.com \
        --role roles/editor
```

### iii. NW作成、FW設定、静的IP払出し

VPCネットワークを1つと、配下の利用するリージョンのサブネットを1つ作成する。
今回はVM1つなのでそんなにいらないよねー、ということで`/28`の小さめので作成。
ファイアウォールルールとして、22,8080,6868の3ポートのみを指定。
そして公開用に、グローバルの静的外部IPアドレスも忘れずに1つ確保しておく。

```shell
gcloud compute networks create concourse-network --subnet-mode custom
gcloud compute networks subnets create concourse-subnet \
    --network concourse-network --range 10.0.0.0/28 --region $REGION

gcloud compute firewall-rules create concourse-basic \
    --network concourse-network --allow tcp:22,tcp:6868,tcp:8080

gcloud compute addresses create concourse-public-ip --region $REGION

gcloud compute addresses list
NAME                 REGION           ADDRESS        STATUS
concourse-public-ip  asia-northeast1  xx.xxx.xxx.xx  IN_USE

export PUBLIC_IP=xx.xxx.xxx.xx
```

これでGCP側の準備は完了。

## 3. 秘密鍵の作成

3つの秘密鍵の作成が必要になるので、まとめて`ssh-keygen`で作成。
その後、下記で定義されたyamlフォーマットになるように別ファイルとしてまとめて保存しておく。

- [Generating Keys - Concourse](https://concourse-ci.org/install.html#cluster)
  - [`token_signing_key`](https://bosh.io/jobs/atc?source=github.com/concourse/concourse#p=token_signing_key)
  - [`tsa_host_key`](http://bosh.io/jobs/tsa?source=github.com/concourse/concourse&version=3.11.0#p=host_key)
  - [`worker_key`](http://bosh.io/jobs/worker?source=github.com/concourse/concourse&version=3.10.0#p=tsa.worker_key)

```shell
mkdir -p keys \
  && cd keys \
  && ssh-keygen -t rsa -f tsa_host_key -N '' \
  && ssh-keygen -t rsa -f worker_key -N '' \
  && ssh-keygen -t rsa -f token_signing_key -N '' \
  && openssl rsa -in token_signing_key -pubout -out token_signing_key.pub \
  && cd ..

cat << EOF > key-creds.yml
token_signing_key:
  private_key: |+
$(cat keys/token_signing_key | sed -e 's/^/    /')
  public_key: |+
$(cat keys/token_signing_key.pub | sed -e 's/^/    /')
tsa_host_key:
  private_key: |+
$(cat keys/tsa_host_key | sed -e 's/^/    /')
  public_key: $(cat keys/tsa_host_key.pub)
worker_key:
  private_key: |+
$(cat keys/worker_key | sed -e 's/^/    /')
  public_key: $(cat keys/worker_key.pub)
EOF
```

## 4.DBパスワードの決定

Concourse内部で動いているPostgreSQLとmbusのパスワードを決める。
ここは自分が覚えておければ任意のものを設定して構わない。

```shell
export POSTGRES_PASSWORD=password
export MBUS_BOOTSTRAP_PASSWORD=password
```

## 5. デプロイ

- ここまで揃ったらいよいよboshを使ったデプロイが可能になる。
- 下記のコマンドをそのままコピペして、`bosh create-env`を実行。
- LiteにはDirectorがないので、いきなりConcourseを作りに行きます。
- **※30分くらいかかるので、飯でも食いながら気長に待ちましょう:ramen:**

```shell
bosh create-env concourse.yml \
       -l ../versions.yml \
       -l ./key-creds.yml \
       -o infrastructures/gcp.yml \
       -v internal_cidr=10.0.0.0/28 \
       -v internal_gw=10.0.0.1 \
       -v internal_ip=10.0.0.3 \
       -v mbus_bootstrap_password=$MBUS_BOOTSTRAP_PASSWORD \
       -v network=concourse-network \
       -v postgres_password=$POSTGRES_PASSWORD \
       -v project_id=$PROJECT_ID \
       -v public_ip=$PUBLIC_IP \
       -v subnetwork=concourse-subnet \
       -v tags=[] \
       -v zone=$ZONE \
       --var-file gcp_credentials_json=./gcp-state.json
```
**上記で足りない変数がある場合は、更新が入っている可能性があるので、最新の [gcp.yml](https://github.com/concourse/concourse-bosh-deployment/blob/master/lite/infrastructures/gcp.yml) を参照すると良い。**

## 6. 確認

```shell
...
Finished deploying (00:24:48)

Stopping registry... Finished (00:00:00)
Cleaning up rendered CPI jobs... Finished (00:00:00)

Succeeded
```
このログが出たら完了。flyをDLして、パイプラインをセットしつつ動作を確認する。

```shell
curl -o /tmp/fly "http://$PUBLIC_IP:8080/api/v1/cli?arch=amd64&platform=darwin"
chmod +x /tmp/fly
sudo mv /tmp/fly /usr/local/bin/fly
fly -t main login -c http://$PUBLIC_IP:8080

cat << EOF > hello-world-pipeline.yml
jobs:
- name: hello-world
  plan:
  - task: say-hello
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: alpine}
      run:
        path: echo
        args: ["Hello, world!"]
EOF

fly -t main set-pipeline -p hello-world -c hello-world-pipeline.yml
fly -t main unpause-job -p hello-world
fly -t main trigger-job -j hello-world/hello-world
open http://$PUBLIC_IP:8080
```

こんな感じで動いてればOKです。お疲れ様でした。
<img width="561" alt="スクリーンショット 2018-04-15 1.08.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/6529/252478e2-f14b-a05c-c774-bc7e0639c5c0.png">

- デプロイ時に使った環境変数は、メモってから後でunsetしておくこと。
- 環境変数は設定を管理するレポジトリで direnv の .envrc に設定しておくと便利 → [direnvを使おう \- Qiita](https://qiita.com/kompiro/items/5fc46089247a56243a62)

```shell
unset REGION ZONE PROJECT_ID PUBLIC_IP MBUS_BOOTSTRAP_PASSWORD POSTGRES_PASSWORD
```

# その他注意事項

- Bosh Directorがいない構成なので、`bosh ssh`やリザレクション、監視はない。
- 作成したパイプラインの情報は外部ディスクとして保存されているため、`bosh create-env`で再度デプロイしても消えない仕組みになっているようだ。
- f1-microで立てれば無料でConcourse作れるやん！と思って[カスタマイズするyml](https://github.com/legnoh/concourse-lite-deployment/blob/beaf6011b9ae044b820f8f47aa17854c6eb2ddbb/customize.yml#L1-L4)を書いて試してみたところ、正常に動作することを確認できた。ただしかなり貧弱なのは言うまでもなく、どのくらい貧弱かと言うと`go run main.go`のビルドを数回繰り返したところCPUが100%振り切って死ぬ程度には貧弱です。まともなビルド環境が欲しい場合は素直にn1-standard-1使うのが良さそうです。