Tinderで右スワイプするためにCI/CD環境を作ってみたお話

# はじめに
## きっかけは汚部屋から
我が家ではTinderで右スワイプを延々を実施するコードをラズパイ上で定期実行しています。TinderのOauth2.0対応、細かいバグFixなどで何度かコードを修正してきたのですが、修正の際は大体以下のフローで動いていました。
<img width="937" alt="スクリーンショット 2019-09-22 12.14.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/cf85820f-80b7-10c2-8e50-c153fe1985f9.png">
自分用の適当なツールのため適当にコード管理していたこともあり、このフローには以下の問題点がありました。

- 最新版が分からない：VCSなど導入していなかったため`tinder_swipe.py`、`tinder_swipe_new.py`の様なコードが複数存在している最悪の状態
- 実行サーバと開発機のバージョンのズレ：時々実行サーバにsshしてコードを手で弄ってしまうことがあり、実行サーバと開発機の間には常に記述のズレがあった
- ②、③をいちいち実施するのが煩わしい

上記の様な汚部屋チックな状態では効率的なコード修正ができず、ストレスが溜まる一方でした。

## CI/CDとの出会い
どうしたもんかな〜と考えてる時に[Effective DevOps](https://www.amazon.co.jp/Effective-DevOps-%E2%80%954%E6%9C%AC%E6%9F%B1%E3%81%AB%E3%82%88%E3%82%8B%E6%8C%81%E7%B6%9A%E5%8F%AF%E8%83%BD%E3%81%AA%E7%B5%84%E7%B9%94%E6%96%87%E5%8C%96%E3%81%AE%E8%82%B2%E3%81%A6%E6%96%B9-Jennifer-Davis/dp/4873118352)に出会い、ペラペラめくってる内に上記の問題を解決するにはCI/CDツールを導入すれば良さそうという考えに至りました。

CI（継続的インテグレーション）とは開発者が書いた新しいコードをマスターブランチに頻繁に統合するプロセスであり、コードへの変更を速やかに本番環境に適用することをCD（継続的デリバリー）といいます。

変更をマスターブランチへマージする際は必ずテストを実施します。当然テストを手動実行するとオーバーヘッドが発生するため、テストの自動化まで実現する必要があります。また、状況によってはコードを従来のバージョンに戻してトラブルシューティングする必要もあります。つまり、CI/CD環境を作るために最低限必要なのは、

- テスト・最新版のリリースを実行可能なツール
- バージョン管理ツール

の2つと言えるでしょう（ほんとか？）。今回は前者の実現のためJenkinsを、後者のためにGitLabを採用しました。（タダで導入可能＆オンプレで導入可能＆他のサービスより柔軟にカスタマイズ可能＆日本語のドキュメントが多そうなため）

# システム構成
Jenkins、GitLabを使い、今回は以下のCI/CD環境を構築します。~~Windows7使ってるのはちょうどその辺に使ってないラップトップが転がってたからです~~

![スクリーンショット 2019-09-23 18.27.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/b05f1751-2876-ac4c-e1dd-e3709aeb7feb.png)


この環境を構成することで、以下のメリットが期待されます。

- コードのバージョン管理をちゃんと行える
- コードの修正を実行サーバに反映させる手間が減る：図の赤字の作業は全て自動化されているため、コードを書いてpushすることに専念できる。

# 作り方
では作っていきましょう。ここではJenkinsの導入、Nested ESXiの作成、VMの作成、ラズパイのセットアップについては省略します。

## 今回テスト対象のコード
[tinpy](https://github.com/FullteaOfEEIC/tinpy2)を使って実装しました。~~パスワードが平文で書いてあるゴミコードなのでおいおいその辺は修正したいです……~~

```python:tinde_auto.py
# coding: utf-8
# Tinder右スワイプ用スクリプト

from time import sleep
from numpy import random
import sys
import traceback
from tinpy import getAccessToken
import tinpy

# ユーザ数の計算
counter = 0
# スワイプ上限数の指定（テスト用）
try:
    limit = int(sys.argv[1])
except IndexError:
    limit = 10000000

# トークンの取得
token = getAccessToken("<Facebookの登録に使ったEmailアドレス>", "<Facebookのパスワード>")

# スワイプ
api = tinpy.API(token)
try:
    USERS = api.getNearbyUsers()
    for user in USERS:
        if counter == limit:
            break
        user.like()
        counter += 1
        sleep(random.randint(1, 5))
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    traceback.print_exception(exc_type, exc_value, exc_traceback)

print("**********総スワイプ数：{0}**********".format(counter))

```

## GitLabの導入・プロジェクトの作成
- CentOS上で、[公式のインストールガイド](https://about.gitlab.com/install/#centos-7)の1-2を参考に、GitLabをインストールします。

```bash
# HTTPS、HTTP、SSHによる通信を許可
sudo yum install -y curl policycoreutils-python openssh-server
sudo systemctl enable sshd
sudo systemctl start sshd
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo systemctl reload firewalld

# メールで通知を送る場合のみ（今回は設定しない）
sudo yum install postfix
sudo systemctl enable postfix
sudo systemctl start postfix

# GitLabパッケージ用リポジトリを追加
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash

# GitLabのインストール
sudo EXTERNAL_URL="http://<GitLabサーバのURLかIPアドレス>" yum install -y gitlab-ee
```

- `/etc/gitlab/gitlab.rb`の記述を以下の様に修正します。このファイルを元にChefが動いて設定が変更されるみたいです。

```ruby:gitlab.rb
# 13行目
external_url "http://<GitLabサーバのURLかIPアドレス>"
```
- 記述の修正を反映させた後、GitLabを再起動します。

```bash
gitlab-ctl reconfigure
gitlab-ctl restart
```

- 指定したURLにブラウザからアクセスし、GUIをぽちぽちしてProjectを作成します。今回は`tinder_auto_swipe`というプロジェクトを作成しました。外部公開はしたくないので公開設定はinternalにしてあります。このProjectに`tinder_auto.py`など各種コードを入れておきましょう。

![スクリーンショット 2019-09-23 19.23.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/aee81445-0db0-5803-fe94-c13212aefad4.png)

## ユーザアクセストークンの取得
Jenkinsでリモートリポジトリをcloneするため、ユーザのアクセストークンが必要になります。

- 「User Settings」→「Access Token」を選択して下さい
- トークン名、有効期限、スコープ（今回は全てチェックを入れましたが、必要に応じて不要なものを除いて下さい）を記入・選択して下さい
- 以下の様にアクセストークンが生成されます。一度しか表示されないため、どこかにコピーしておいて下さい。

![スクリーンショット 2019-09-23 20.50.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/28196053-ddcb-712d-7cb0-1a13d52ee33a.png)


## Jenkinsの設定
「Jenkinsの管理」→「システム設定」を選択して下さい。

### GitLabへのアクセス設定
JenkinsでGitLabからリポジトリの操作を行うための設定を実施します。
「Gitlab」の項目で、適当な接続名、GitLabのURL、ユーザアクセストークンを指定します。Test Connectionを押して「Success」と表示されたらOKです。

![スクリーンショット 2019-09-23 19.43.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/b6ca984f-0693-8927-f927-6bf538517be5.png)


### SSHエージェント設定
#### 鍵ペアの生成
- `ssh-keygen`で公開鍵・秘密鍵を生成しておきます。

#### SSHサイト設定
GitLab→ラズパイにSSHするための設定を行います。

- ホスト名、SSHポートを指定します。
- Credentialsの「追加」を押し、認証情報を登録します。  
  ユーザ名はSSH先のユーザ名、秘密鍵は~~正直アレかと思いますが~~先程生成した**秘密鍵**の中身をコピペして下さい。パスフレーズは設定している場合のみ記入して下さい。  

<img width="1082" alt="スクリーンショット 2019-09-23 21.11.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/d2eb1a74-74db-89d8-cace-e8d48caabaca.png">

- Check Connectionを押して「Successful connection」と表示されたらOKです。

![スクリーンショット 2019-09-23 19.42.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/79073fd9-3a51-76a5-fc97-b4cc6848c9de.png)

### Slack通知設定
[JenkinsとSlack連携](https://qiita.com/haruto830/items/7a9798a57d9b2471a15c)を見て設定しました。

### Jenkinsジョブの作成
テスト・コード配布のためのJenkinsジョブを作成します。今回は以下の3つのジョブを作成することにします。

- `tinder_autoswipe`：自動右スワイプ用スクリプトのテスト用ジョブ
- `tinder_pull`：ラズパイでリモートリポジトリをpullするためのジョブ
- `tinder_swipe_test`：上記2ジョブのパイプライン

パイプラインを使わなくてもジョブは作れますが、ジョブに不具合が生じた場合の問題の切り分けを行いやすい＆各ジョブのビルドに要した時間をひと目で確認できるため、今回はパイプラインを使いました（パイプライン使う方が普通、と聞きますが、実際どうなんでしょう？）

#### tinder_autoswipe
- ソースコード管理はGitを選択し、リポジトリURLはProjectのURLを、認証情報にはGitLabのユーザ名・パスワードを使用します。

<img width="944" alt="スクリーンショット 2019-09-23 19.30.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/73dfa39a-711c-6e5d-9b86-5583b161aea4.png">

- 「ビルド開始前にワークスペースを削除する」にチェックを入れておきます（入れなくてもいいかも？）
- ビルドは「Windowsバッチコマンドの実行」を指定し、以下のコマンドを記入しておきます。ここでは、10ユーザに対して自動で右スワイプを行います。

```
<python3のパス>\python tinder_auto.py 10
```

- ビルド後の処理に「ビルド終了後にワークスペースを削除する」、「Slack Notifications」を加えます。ここではビルドが成功or失敗した場合のみ通知する様に設定します。

<img width="943" alt="スクリーンショット 2019-09-23 19.31.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/dc7722ef-7a46-6545-3796-7616ba6de4c7.png">

#### tinder_pull
- ビルド環境の「リモートホストでシェルを実行」でSSHサイトに「<ユーザ名>@<接続先>」、ビルド前スクリプトに以下を記入します。

```bash
cd tinder_auto_swipe;git pull
```

#### tinder_swipe_test
- ビルド・トリガで「Build when a change is pushed to GitLab」を選択します。この時表示されたGitLab Webhook URLは後で使うのでメモしておきましょう。
- ビルドのトリガにしたいアクションにチェックを入れましょう。（今回はpushのみ）
- 「高度な設定」を押し、Secret tokenを「Generate」ボタンを押し生成します。これも後で使うのでメモ必須です。
- パイプラインには以下のスクリプトを記入します。

```
node{
    stage("テスト実行"){
        build job: "tinder_autoswipe"
    }
    stage("コード配布"){
        build job: "tinder_pull"
    }
}
```

## GitLab Webhookの設定
- Projectのサイドバーから「Settings」→「Integration」を押してWebhookの設定を行います。
- ①に先程メモった（はずの）GitLab Webhook URLを、②にSecret tokenを記入して、トリガーにしたいイベントにチェックを付けて下さい。必要ならSSL verificationにもチェックを入れるといいでしょう。

![スクリーンショット 2019-09-23 19.26.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/7d334f9d-7890-108f-d791-80a93bb42377.png)

- Add webhookを押せば`tinder_swipe_test`用のWebhookが作成されます。
- 作成したWebhookの「test」→「<イベント>（ここではpush）」をクリックし、「Hook executed successfully: HTTP 200」と表示されればOKです。403 Errorを吐く場合はURL・トークンが間違ってないか確認しましょう。

## ラズパイの設定
- 先程生成した**公開鍵**の中身をコピーし、SSH先アカウントの`.ssh/authorized_keys`に追記しておきましょう。
- Projectをcloneしておきます。このProjectはinternalのためclone、pullの度にパスワードを要求されてしまうので、以下の処理を実行しておきます。

```bash
git clone http://<GitLabサーバのURLかIPアドレス>:<ユーザ名>/tinder_auto_swipe.git
git config credential.helper store # パスワードを聞かずgit pull出来る様にするための処理
git pull --all # このpullのみパスワードが聞かれる
```

# 動かしてみようのコーナー
パイプラインはこんな感じで動きます。

![スクリーンショット 2019-09-23 22.17.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/c21bc432-279c-20de-7f40-f4d718e2288d.png)

GitLabへのpushをトリガーにテストが動いてコード配布までされるまでの一連のプロセスにどれだけ時間がかかったか、どのジョブは成功したのかがひと目で分かりますね。
ちなみにSlackにはこんな感じで通知が飛びます。

<img width="365" alt="スクリーンショット 2019-09-23 22.19.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266022/13063b48-b6ce-b87d-1d9c-2a521a57e0a3.png">

# 感想と所感の間
CI/CD環境を導入したことで、余分な手作業に時間を割くことなくコードを書けハッピーになったように思います。不要な手作業はどんどん無くしていかねばという気持ちが強まりました。
また、Windows上にJenkinsを立てた場合、鍵の管理をしにくい、そのままだとshellを実行できない等問題点が多くあることが分かったので、できればLinuxマシンに移行してその辺楽にしたいです。いずれはパブリッククラウド等他の環境に移植したり、CircleCIなど他のCI/CDツールに差し替えるのも試してみたいとも思いますが、当分先かなぁ……

# その他
- そろそろWindows7のサポートも終わるので新しいWin10機買いたいけどお金がない（かなしい）
- ガチ初心者のため皆さんのツッコミ・アドバイス等随時お待ちしております🙇