Go + react + ansibleでサービスを作ってOSSにしてみた

タイトルのOSSは
ソースコードを公開している=OSSという解釈で付けています


# 作ったサービス
![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/170824/598a47a5-1c88-af73-ae23-a4c936296cb1.png)

URL: https://dotstamp.com/


- **公開しているサービス内容自体は、この記事の本質では無い**ので、クオリティの参考程度だと思って下さい。
- サービス云々の話はブログで記事にしているので、こちらをどうぞ（ http://wheatandcat.hatenablog.com/ ）
    - ~~なので内容が、ただのオタクの掃き溜めなのはスルーして下さい~~

## 作ったサービスの内容
- 記事を投稿できる
- 書いた記事を読み上げる機能がある(読み上げはopen-Jtalk)
- SPAである
- サービスとして運営できる最低限の機能は実装済み（のはず）

# 公開ソースコード

**サーバーサイド**
・https://github.com/wheatandcat/dotstamp_server
**クライアントサイド**
・https://github.com/wheatandcat/dotstamp_client
**環境構築**
・https://github.com/wheatandcat/dotstamp_ansible
**デプロイスクリプト**
・https://github.com/wheatandcat/dotstamp_deploy_script
**デプロイ環境構築**
・https://github.com/wheatandcat/dotstamp_deploy_ansible

※実行手順はREADME.mdに記載

# この記事を読む前に
このサービスは一人で開発したものです

go言語、react、ansibleは、今回初めて触れました（開発期間は学習も含めて半年くらい）
コードは、その程度の信頼度です

**公開していソースも実装内容も、あくまで個人開発規模での内容です（ここ重要）**

ってことで、この記事では
各リポジトリの説明と実装方法について記載します

# 全体構成(ザックリ)
- **サーバーサイド**
    - 言語
        - Go
    - フレームワーク
        - beego
    - テスト
        - gocheck
        - goconvey
- **クライアントサイト**
    - 言語
        - es6
    - jsライブラリ
        - react
        - redux
    - テスト
        - jest
    - css
        - post-css
    - コンパイラ
        - webpack 
- **環境構築**
    - ansible
- **web**
    - nginx
- **db**
    - MariaDB
- **デプロイ方法**
    - supervisor + nginxで、ブルーグリーンデプロイ

# サーバーサイド
## 概要
リポジトリ：https://github.com/wheatandcat/dotstamp_server

言語：Go言語
APIサーバ（+トップのhtmlのみview活用）

# サーバーサイド構成
## フレームワーク
### beego
https://github.com/astaxie/beego

フルスタックフレームフーク
ドキュメント豊富

　**が、今回の用途はAPIサーバなのでフルスタックなフレームワークを選んだのはミスですね ・・・orz**

**注意**
日本語化されているドキュメントに、beedbの使用例がありますが、
beedbは既に非推奨になっているので注意
~~beedb: https://github.com/astaxie/beedb~~

## DB
### gorm
https://github.com/jinzhu/gorm

ORMラッパー
ドキュメント豊富

## バリデーション
### validator
https://github.com/go-playground/validator/

バリデーション。
以下みたいに書くと、フォームパラメータをコードを汚さずバリデートできてイケてる

```
// NewRequest 新規リクエスト
type NewRequest struct {
	Email    string `form:"email" validate:"required,email"`
	Password string `form:"password" validate:"min=8,max=100"`
}

func (c *NewController) Post() {
	request := NewRequest{}

	// ここでフォームの値を取得
	if err := c.ParseForm(&request); err != nil {
		c.ServerError(err, controllers.ErrCodeCommon, 0)
		return
	}

	// ここでバリデーション
	validate := validator.New()
	if err := validate.Struct(request); err != nil {
		c.ServerError(err, controllers.ErrCodeCommon, 0)
		return
	}
```
## テスト
### 実装状況
 - モデルとライブラリはユニットテスト
 - コントローラはhttpテスト（現状は正常系のみ実装）
 - テストDBデータはフィクスチャで挿入
 - カバレッジは、40%くらい
    - Go Mockとか使えば、カバレッジ100%にできるかも？ここは、まだ手つかず
        - https://github.com/golang/mock

```ruby:フォルダ構造
├── conf  
├── controllers  
├── db
├── logs
├── models
├── resources
├── routers
├── static
├── tasks
├── tests
│   ├── controllers ← コントローラーのテスト
│   ├── database
│   ├── files
│   └── resources
│       └── fixture ← テスト前に挿入するDBデータ
├── tool
├── utils
├── vendor
└── views
```

### gocheck
https://github.com/go-check/check

フィクスチャをサポートしているのでDBの初期設定に使用
データは、ymlで用意してテスト前にインサート

**注意**
フィクスチャでデータ挿入を実装すると、並走は使用できなくなるので、
go testのオプションに「**-p 1**」が必須になる

### goconvey
https://github.com/smartystreets/goconvey

goの自動テスト
ソースコードがでかくなったので、最近はあまり使ってない

## その他ツール系

| パッケージ名       |内容 |   リンク    |
|:-----------------|:------------------|:------------------:|
| glide             |       パッケージ管理 |        https://github.com/Masterminds/glide        |
| goose           |            Go言語製のマイグレーション |       https://bitbucket.org/liamstask/goose/       |
| dbweb             |              Go言語製のdbクライアント |        https://github.com/go-xorm/dbweb        |

# クライアントサイド
## 概要
リポジトリ：https://github.com/wheatandcat/dotstamp_client

言語：es6

# クライアントサイド構成
## jsライブラリ
### react + redux
表示制御に使用

### react-bootstrap
https://github.com/react-bootstrap/react-bootstrap
reactのBootstrapコンポーネント
サイトのクオリティがある程度に保てているのは、これのおかげ
### react-router
https://github.com/ReactTraining/react-router

reactのルーティング制御。react使うなら、ほぼ必須

また、react-bootstrapと合わせて使うなら、
react-router-bootstrapも必須

https://github.com/react-bootstrap/react-router-bootstrap

### isomorphic-fetch
https://github.com/matthew-andrews/isomorphic-fetch

httpアクセス
reduxのチュートリアルで、使っていたので採用。

でも、よく読んだだらGraphQLのサンプルだったので、RESTは、他にも選択肢があるかも

## css
### postCss
https://github.com/postcss/postcss

cssパーサー。sassよりも手軽

### css-loader
https://github.com/webpack-contrib/css-loader

webpackの一機能。jsにcssを組み込める
実装例は検索すればHITするので省略

「react-bootstrap + css-loader」の組み合わせで、
デザインによるコード汚染を抑制できる

## テスト
### jest
https://github.com/facebook/jest

テストフレームワーク
起動が遅いけど、基本「--watch」しているので、あまり気にならない

css-loader使用時は、以下の通りに実装
https://facebook.github.io/jest/docs/webpack.html

実装方法はreduxのドキュメントが一番参考になった
http://redux.js.org/docs/recipes/WritingTests.html

# 環境構築
## 概要
リポジトリ：https://github.com/wheatandcat/dotstamp_ansible

ローカル環境構築と本番（デプロイ関係以外）の環境構築
ローカル環境は、vagrantで構築

ツール：ansible

## 構築している内容一覧
基本的には、「galaxy.ansible」から導入。無いものは自作
https://galaxy.ansible.com/

| 名前       | 使用内容        |　環境        |　備考       |
|:-----------------|:------------------|:------------------|:------------------|
| MariaDB             | データベース。アプリのデータ管理 | 両方       |全文検索にMroongaを使用                | 
| redis           |キーバリューのDB。セッション管理に使用 |　両方       |                |
| open-jtalk           | 日本語読み上げ| 両方       |                |
| sox           | 音声ファイル結合 | 両方       |                |
| lame           | wav→mp3に変換 | 両方       |                |
| Go             | サーバーサイドのビルド | ローカル        |               本番はバイナリ実行なので不要|  
| nodejs               | クライアントサイドのコンパイル |ローカル|         本番はビルド済みのファイルを同期するので不要|  
| ntp          | vagrantの時間合わせ | ローカル       |      |
| nginx             | webサーバ | 本番 |         ローカルはbeegoに直接アクセスするので不要|
| supervisor          | Goのバイナリ実行をデーモン化 | 本番       |       同上|
| firewall          | アクセス制限 | 本番       |       |
| logrotate          | ログのローテション | 本番       |       |
| mysqldump          | DBバックアップ |  本番       |       |
## Mroonga
MySQLのプラグイン
DBの全文検索に使用

# デプロイスクリプト
## 概要
リポジトリ：https://github.com/wheatandcat/dotstamp_deploy_script

デプロイに必要なファイルを同期するバッチを管理
デプロイしているリポジトリはプライベートにしているので、アクセス不可

## 内容
以下のコマンドを実行で、デプロイリポジトリにファイルを同期

```
sh ./bin/deploy.sh
```

- 実行内容
    - goをビルドしてバイナリを同期
    - goのバイナリと外部で呼んでいる以下のファイルを同期
        - viwe
        - config（環境設定ファイル）
        - static（外部から参照できる静的ファイル：画像等）
        - resources(内部から参照するファイル：csvファイル等)
    - jsをwebpackでコンパイル
        - 本番設定を適用
        - jsを難読化
        - jsをgzipで圧縮
    - コンパイルしたjsファイルを同期

ここで同期したファイルをコミットして、本番環境に反映しています

# デプロイ環境構築
## 概要
リポジトリ：https://github.com/wheatandcat/dotstamp_deploy_ansible

デプロイするために必要な構築系を管理
デプロイはブルーグリーンデプロイを採用

対象は本番サーバー


ツール：ansible
## 構築内容
- 本番環境にブルーとグリーンの環境構築（デプロイリポジトリから２つのフォルダ作成）
- supervisorにブルーとグリーンのデーモンを設定
    -  ブルー or グリーンは、環境変数で切り替えている
- 構築時はブルーは起動。グリーンは停止状態 
- ブルーとグリーン切り替え用のnginxの設定を追加
- 上記のnginxの設定を切り替えるバッチを作成


## デプロイ実行
以下のコマンドを実行で、デプロイする
※以下は、サーバー１台構成用の内容です。ロードバランサの使用は考慮していないので、ご了承下さい

```
sh ./deploy.sh
```

- 実行内容
    - **グリーンの環境を起動**
        - supervisorctl start green
    - **nginxの設定をグリーンの環境書き換えてreload**
        - cp -r /xxx/nginx_green.conf /etc/nginx/conf.d/xxx.conf
        - nginx -s reload
    - **ブルーの環境を停止**
        - supervisorctl stop blue
    - **ブルーの環境に最新のバイナリ + 静的ファイルを同期**
    - **ブルーの環境を起動**
        - supervisorctl start blue
    - **nginxの設定をブルーの環境書き換えてreload**
        - cp -r /xxx/nginx_blue.conf /etc/nginx/conf.d/xxx.conf
        - nginx -s reload
    - **グリーンの環境を停止**
        - supervisorctl stop green
    - **グリーンの環境に最新のバイナリ + 静的ファイルを同期**

**ただし**
上記だけだと、利用中のユーザーのjsが最新にならないので、
真面目にやるなら、jsのバージョン管理 + サーバーから最新のjsバージョンを返却
ローカルのjsのバージョンと差異があったらリロードするを実装する必要があります
（リポジトリは、そこまでは実装していない）

# まとめ

以上、ここまで実装する + 本番vpsの設定 + gitのREADMEの整理で、
OSSで個人開発規模サービス運営が開始できます！！(たぶん)

また、今回の開発では時間の制約で省きましたが、本来ならば以下も考慮したいところですね

- 負荷検証
- サーバー監視
- MySqlチューニングツール
- クライアントのレンダリング測定ツール

時間は無限にある訳では無いので、個人開発では難しいところではありますが・・・