プルリクに対して検証環境を自動で起動/終了するプログラムを作ったら、検証が捗った話

# 記事の概要

GitHub Flowでの開発、つまり単純なプルリク運用での開発を、営業も巻き込んで実践したいと思い、そのような環境を作りました。その際、いくつか足りない機能を補うウェブアプリを作って公開したので、それに関する様々な話を書きます。
（実際にこのウェブアプリを使えるかどうかというよりは、似たようなフローで開発を改善できるといいなというような目的の話です。）

# ウェブアプリのリポジトリ

https://github.com/uniaim-event-team/pullre-kun

このウェブアプリの使い方と機能については、一応README.mdに書いていますが、この記事では少し背景的な話も含めて順番に書きます。
issue対応やその他追加開発などは絶賛募集中です。

# 背景

## 開発に関するよくある課題

これまで、既存のウェブアプリ（サービス）の機能追加開発において、以下のような課題がありました。

- 検証が十分にできていない機能がある
- 追加した機能の使い方を十分にレクチャーできていない/一部の人にしか認知されず**隠し機能**になる
- 時間が経過するとみんなその機能の存在を忘れて**隠し機能**になる
- 実は画面を使えば普通に登録ができるデータ設定作業も開発側でやらざるを得なくなる
- ~~そもそもデータを普通の画面から登録できない**隠し機能**を作って運用してしまう~~

## GitHub フロー（を外部的な部分で充実させたフロー）の導入

このような課題を解決するために、以下のようなフローでの運用を検討しました。

<img width="903" alt="スクリーンショット 2020-03-29 16.20.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94255/6d8f6ed3-605f-4477-4f2c-aaefc8622e54.png">

一般的な類似の作業フローと比較して、すこし強いのは「チケット登録者がテストシナリオまで書く」というところです。
これは色々と迷う部分があったのですが、以下のような点において、技術的に可能か不可能かで言えば可能と思い、信頼して踏み切ることにしました。

- いかに営業の人といえども、お客様に提供する際に、管理画面の最低限の操作説明などは、求められればできるべき。
- 末端のエンドユーザーや、その他システムを使う人が満足に利用できる事の確認は、むしろ技術に関する知識がない状態で実施する方が自然である。
- その際の確認項目を列挙することについて、カバレッジ等の観点は営業側には無いにしても、実際にお客様の前でやることのシミュレーションぐらいは、技術知識が無くても可能なはずである。

ただし、当然ですが、要求・要件を出した人が必ずしも網羅的な試験を書ききるということは不可能だと思うので、必要なものがあれば開発側で増やします。システム内部的に、どのような条件による分岐に依存しているかというような事は、要求・要件を出した時点で洗い出すのは難しく、また技術的な意味でそれらを網羅する試験を書くのは難しいと判断したからです。

## フロー導入にあたっての課題

このフローの導入にあたっての、技術的な課題がいくつかあったのですが、概ね次の課題に集約されました。

- チケット登録者は、どの環境で検証するのか

例えば、テーブルの項目を追加したりする改修内容の場合、そのプルリクで定義されているスキーマを参照する必要があります。
開発者の環境でテストをするという方法も無くはないですが、開発者の開発作業は継続しているので、テスト中に使えなくなるという事も往々にして想定されます。また、別改修の影響で正常に動作しない、というような事もあり得て、これはお互いのためによくありません。そもそも、別の改修が一部含まれているソースでのテストは、純粋なプルリク内容に対するテストになっていないという問題もあります。

では、それ用の検証環境を手動で作るのかというと、一日に一人が何個もプルリクを作る場合がある状況で、毎回手動でそれ用の検証環境を作るというのはちょっと大変です。

修正がJSだけで済むならばnetlifyのようなものもありますが、スキーマやデータセットを用意する部分に多くの課題があり、またそもそも対象プロジェクトの場合はサーバー側がPythonなので、ちょっと適用できませんでした。

### dockerで解決しないのか

原理的にはdockerでも解決できるもので、実際にdockerを使って同様のことをやった話が、少なくとも3年前には存在しています！
[Pull Request発行時にそのコミットIDでデプロイされた環境を自動構築してレビュー時/マージ前に確認しやすくする仕組み](https://tech.toreta.in/entry/2017/02/24/210755)

もともと自動テストを書いて、CircleCIで動かしていたので、ある程度はdockerでも動くのですが、一部の処理は単純にdockerを作るだけではうまく動かず（具体的にはwkhtmlを使ってhtmlをpdfに変換している処理で、途中でxvfbを使ったりしているところをすぐにdockerに載せることができなかった...そこはCircleCIでも動かしていない）、それ用のdockerを作ったりするのがめんどくさくて今に至ります。

# 実際にやったこと

方針は以下の通りです。

- 事前に検証用インスタンスをたくさん作って、ロードバランサーやDNSの設定をして、サーバープロセスが起動したら普通のアクセスができる状態にしておいた上で、インスタンスをoffにしておく
- DBはAuroraを**開発環境含めて共有**することにして、そのAuroraを作っておく
- プルリクがあれば、それを拾って検証用インスタンスをonにする
- 検証用インスタンス側で、そのインスタンスがcheckoutすべきコミットを識別して、そのコミットでサーバープロセスを起動する
- プルリクがクローズされたら、対応する検証サーバをoffにする

この、事前作業を除いたプルリクを処理する部分が、ウェブアプリで対応する部分です。

システム構成イメージは以下のとおりです。Controller Instanceでウェブアプリを動かし、Staging Instancesでは定期的にサーバープロセス起動用のバッチ処理を実行します。

<img width="640" alt="スクリーンショット 2020-03-22 23.25.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94255/2761b800-124d-678e-ea66-1a3979489eea.png">

# 実際に運用してみての所見

まだ運用を初めて数週間で、現状は新型コロナの影響などで通常の状態と違っている部分がありますが、とりあえず実画面でのテストは相当しやすくなったと思いました。開発検証者としても、従来、自分の環境でcheckoutしてスキーマ直したりなんやかんやしながらテストしていたものについて、自分の環境を全く触らずに検証できるようになり、とても楽です。
また、リリース前に非開発者を巻き込みやすくなったのも、大きな進歩かなと思います。

# ウェブアプリのインストール（セットアップ）方法

これはREADME.mdの記載内容と同じです。

## ec2インスタンスを作成する

EC2インスタンスを作成します。そして、そのうちの1つにポート5250を許可します。 1つは「コントローラーインスタンス」と呼ばれます。
その他は「ステージングインスタンス」と呼ばれます。

## IAMポリシーとユーザーを作成する

次のアクションを許可するIAMポリシーを作成します。

「ec2：DescribeInstances」
「ec2：StartInstances」
「ec2：StopInstances」

ポリシーのリソースは、作成したインスタンスです。（注：DescribInstancesは全てのリソース対象になります）
そして、ポリシーをユーザー/ロールにアタッチし、アクセスキーとシークレットキーを保存します。

## gitをインストールする

インスタンス全体にgitをインストールします。
注：amazon linux2を使用している場合、

```bash
$ sudo yum install git
```

##ステージングサーバーへのアプリケーションのセットアップ

アプリケーションをステージングサーバーに設定します。

## python3をインストールする

インスタンス全体にpython3をインストールします。
注：amazon linux2を使用している場合、

```bash
$ sudo yum install python3
```

## mysql-clientをインストールする

インスタンス全体にmysql-clientをインストールします。
注：amazon linux2を使用している場合、

```bash
$ sudo yum install mysql
```

## MySQLのようなデータベースを実行またはインストールします

MySQLのようなデータベースを実行またはインストールします。

##プルレくん(pullre-kun)のクローン

プルレくんをクローンします。

## install "requirements"

requirements(requirements.txtの内容)をインストールします。

## get_basic_token.pyを実行します

コントローラインスタンスには基本認証があります。パスワードのトークン（ハッシュ）を作成し、app.iniに保存する必要があります。

## app.iniファイルを作成する

app.iniファイルを作成します。サンプルはapp.ini.defaultです。そしてそれをインスタンス全体にデプロイします。

##コントローラーサーバーのcrontabを編集する

**コントローラサーバー**のcrontabに次の行を追加します。

```
* * * * * cd /home/ec2-user/pullre-kun; python3 update_pull.py
```

##ステージングサーバーのcrontabを編集する

**ステージングサーバー**のcrontabsに次の行を追加します。

```
* * * * * cd /home/ec2-user/pullre-kun; python3 client.py
```

## init.pyを実行する

**コントローラーサーバー**で次のコマンドを実行します。

```bash
$ cd ~/pullre-kun
$ python3 init.py
```

## pullre-kunアプリケーションを実行する

**コントローラーサーバー**で次のコマンドを実行します。

```bash
$ cd ~/pullre-kun
$ nohup python3 app.py＆
```

##サーバーを登録する

`https://<your-domain>/server/list`にアクセスすると、サーバー全体が表示されます。
次に、ステージングサーバーの登録ボタンをクリックします。
そして、`https://<your-domain>/master/server`にアクセスし、各レコードのdb_schemaを更新します。

##ユーザーを登録する

`https://<your-domain>/master/git_hub_users`にアクセスし、ユーザーを登録します。
「login」はgithubユーザーログイン、db_schemaはクローンの元のスキーマです。

以上の手順で、プルリクがあれば自動的に検証環境が立ち上がるようになります。

# その他の技術的な解説について

全体的にFlask + SQLAlchemyで、ただしサーバーとしてはCherryPyを使うという構成になっています。
希望がたくさんあれば、そのうち解説も書くかもしれません。（SQLAlchemyのmodelに合わせた汎用的なWTFormの使い方が少し特殊）
（この記事にコメントか、GitHubにissueを作って+1でお願いします。）