gitlab-ciとansibleでgolangのデプロイ環境を作る

この記事は [富士通クラウドテクノロジーズ Advent Calendar 2017]
 (https://qiita.com/advent-calendar/2017/fjct)の17日目の記事です。

16日目は@clutterさんの[「OK, Google. サーバーをスペックアップして」](https://qiita.com/clutter/items/905a0560ad862e96d293)でした。
最近はやりの音声アシスタントをサーバのスペックアップに使うという発想面白くていいですね。

今回私はgitlab-ciとansibleでgolangのデプロイ環境を整えるお話をしたいと思います。

# gitlab-ciとは
- gitlab バージョン８以降にデフォルトで付いているCIツールです。
- 特別な設定が不要で、リポジトリに`.gitlab-ci.yml`を追加するだけで利用できます。

# デプロイ環境の概要

<img width="910" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/70427/92971ed9-d3ad-b292-f0d8-497cce397273.png">

- gitlabにpushするとgitlab runnerが動き出します。
- gitlab runnerがデプロイのため以下の動作をするように`.gitlab-ci`を作成します。
   1. ソースをビルドしgitlab上に保存
   2. ビルドでできたバイナリをvmへ転送
   3. AnsibleでVMにinit.dでサービス登録

#### ディレクトリ構成

```
gitlab.com/UminoShohei05/go-gitlabci
├── .gitlab-ci.yml
├── ansible
│   ├── inventories
│   └── roles
└── server/
```
- `.gitlab-ci`はビルドからAnsibleの実行まで行うジョブが定義されています。
- `ansible`にはgoのバイナリをserviceとして登録するplay-bookが入っています。
- `server`にはHelloWorld!を返すだけのgo+echoでできたAPIが入っています。
- OSはCentos6.7で試しています。

# やってみる
### まずはビルドジョブの作成
`build_job`では`server/`にあるソースをビルドし、できたバイナリをgitlab上に保存します。
ビルドジョブは以下のようになります。

```yaml:.gitlab-ci.yml
build_job:
  image: golang:1.8
  stage: build
  script:
    - mkdir -p $GOPATH/src/gitlab.com/UminoShohei05/go-gitlabci/server
    - mv $CI_PROJECT_DIR/server/* $GOPATH/src/gitlab.com/UminoShohei05/go-gitlabci/server
    - cd $GOPATH/src/gitlab.com/UminoShohei05/go-gitlabci/server/
    - go get -u github.com/golang/dep/cmd/dep
    - dep ensure
    - go build -o test-api
    - cp test-api $CI_PROJECT_DIR
  artifacts:
    paths:
    - test-api
```
ジョブの内容を解説していきます。

#### image
使いたいdockerイメージを指定します。

#### script
実行したい命令を記述していきます。
今回はビルドするため一度GOPATHに移動し、ビルドでできた成果物を`artifacts`で使うために`$CI_PROJECT_DIR`にコピーしています。
`$CI_PROJECT_DIR`は、CI実行時にリポジトリがクローンされるディレクトリを表しています。
これはPredefined variablesというCI上で使うために元から定義されている変数で、詳細は[こちら](https://docs.gitlab.com/ce/ci/variables/README.html#predefined-variables-environment-variables)で確認することができます。
#### artifacts
ジョブの中で作成した成果物などを`path`で指定してgitlab上に保存できます。
`path`は$CI_PROJECT_DIRからの相対パスとなっています。
今回`artifacts`では`path`のみ指定していますが、成果物の名前を変更したり、保存期間を指定することもできます。(何も指定しないと無限に残り続けます)
保存されたartifactsは画像のようにgitlab上からzip形式でダウンロードすることもできます。

Repository
<img width="1026" alt="artifacts1" src="https://qiita-image-store.s3.amazonaws.com/0/70427/0b4ba610-3f09-87a4-a946-388b6dbd8a40.png">

CI/CD -> Pipeline
<img width="1189" alt="artifacts２" src="https://qiita-image-store.s3.amazonaws.com/0/70427/8342b585-1f66-88a6-0956-1fbb1fc5c97e.png">

### デプロイジョブの作成

`deploy_job`では`build_job`でできたバイナリのサーバへの転送と、Ansibleを使いバイナリをinit.dでサーバにサービス登録します。
デプロイジョブは以下のようになります。

```yaml:.gitlab-ci.yml
deploy_job:
  image: webdevops/ansible
  stage: deploy
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - ssh-keyscan $SERVER_IP  >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - scp test-api root@$SERVER_IP:/opt/api/bin/release
    - cd ansible
    - ansible-playbook -i inventories/production.yaml deploy-api.yaml
```

#### before script
`script`を実行する前の準備を記述していきます。
今回のデプロイジョブではscpやAnsibleでSSH keyを使うためssh-agentへの鍵の登録を行い、known_hostsへホストの登録をしています。

また、SSH keyはリポジトリに入れることが憚られるため、gitlabのSecret variablesという機能を使い参照できるようにしています。
画像のようにgitlab上でKeyとValueを入れ登録すると、CI上で`$Key`で参照することができます。
APIのアクセスキーやシークレットキーの登録などもこちらで行うと安全に使えます。
<img width="1033" alt="secret_variables" src="https://qiita-image-store.s3.amazonaws.com/0/70427/9df75c90-4aa6-e3ce-0ebd-895dc3264acd.png">


#### script
scpコマンドを見てもらうとわかるように、前のジョブで`artifacts`に保存した`test-api`をそのまま使っています。このように前のジョブの成果物を引き継ぎたい時に`artifacts`は便利です。
デプロイのジョブは最後にserviceにtest-apiを登録するansibleを流して終了です。
Ansibleの内容については今回使ったリポジトリを貼っておくので、[こちら](https://gitlab.com/UminoShohei05/go-gitlabci/tree/master)で確認をしてみてください。

### 動作確認
ちゃんとデプロイされているか一応動作確認をしてみます。
<img width="628" alt="動作確認" src="https://qiita-image-store.s3.amazonaws.com/0/70427/01c8feda-a3f6-623c-3e10-f42c1229dd59.png">

## さいごに
今回はデプロイをするだけの簡単なgitlab-ciですが、実際に仕事で使うにはbuildの前にlintやtestを追加するとより良いと思います。
また、今回は最低限の機能しか使っていませんが、指定されたブランチのpush時のみジョブを指定する`only`やジョブの障害時や失敗した場合に実行されるジョブを定義する`when`など様々な機能があります。
一方で、gitlab-ciはpassphraseのついたssh keyが使えなかったり、パラメータを取るようなジョブの場合は向いていなかったりもします。
個人的にはgitlab-ciの機能面だけでなく、設定いらずで管理するツールも増えない部分にメリットを感じているので、gitlab-ciをこれからもっと使っていきたいなと思います。


あしたは@cooの「ニフクラの物理ネットワークを支える人たち」です。宜しくお願いします！