インフラが苦手な人の個人開発（ECS、terraform）

# はじめに
本記事は 個人開発 Advent Calendar 2019 25日目の記事です。

最近connpassに掲載されている勉強会を地図を見ながら検索できるようなサイトを作りました。
（もともと同僚が勉強会でこんな感じのアプリを作っておりそれに触発された）

https://connpass.net/
<img width="1440" alt="スクリーンショット 2020-05-03 12.02.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131625/5b2b7b88-eae5-0e71-a45e-bc2ae6bba721.png">


使った技術としては
フロント: typescript、react、redux、circleci、cloudfront、s3、terraform
バックエンド: go、gin、docker、nginx、circleci、ecs、terraform
な感じです。

仕事ではあまりインフラまわりを触ることがなく、CI/CD環境の知見があまりないが、それっぽいものを自分で作ってみようとしました。


# フロント
https://github.com/pokotyan/connpass-map-front

### アプリケーション
アプリケーションの土台は[create-react-app](https://github.com/facebook/create-react-app)を使いtypescriptで書きました。
stateの管理には[redux](https://github.com/reduxjs/react-redux)、ミドルウェアに[redux saga](https://github.com/redux-saga/redux-saga)を使いました。
また、reduxの型定義に[typescript-fsa](https://github.com/aikoven/typescript-fsa)、reducerでのオブジェクトのマージに[immer](https://github.com/immerjs/immer)を使っています。
SSRをせずに、動的なOGPに対応するために[prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin)を使ったため、[customize-cra](https://github.com/arackaf/customize-cra)、[react-app-rewired](https://github.com/timarney/react-app-rewired)を使ってCRAのデフォルトのwebpack設定を上書きしたりもしました。prerender-spa-pluginのことは、[別記事](https://qiita.com/pokotyan/items/11806b8b77f4a3527951)にも書いています
また、apiのfetch中に出てくるアニメーションは[lottie](https://lottiefiles.com/)にあったものを使いました。

### 環境構築
cloudfront、s3の環境をterraformで作成しました。
[別記事](https://qiita.com/pokotyan/items/036930bf6a4b7233817a)に詳しく書いています。

### CI/CDの流れ
- circleci
    - pushされるたびにビルド
    - masterへのコミットならビルドしたファイルをs3にあげる

# バックエンド
https://github.com/pokotyan/connpass-map-api

### アプリケーション
golangを使いました。フレームワークに[gin](https://github.com/gin-gonic/gin)、cuiに[cobra](https://github.com/spf13/cobra)を使っています。
apiでもcuiでも共通のロジックを使うようにしています。クリーンアーキテクチャを意識はしましたができてるかはよくわからないです。

### docker
マルチステージビルドを使って、ローカルで開発する時に利用するイメージと、ECRにあげる本番用イメージを別にしています。

```Dockerfile:Dockerfile
# 開発用
FROM golang:alpine as builder

RUN apk update \
  && apk add --no-cache git \
  && go get github.com/oxequa/realize

WORKDIR /app
COPY go.mod .
COPY go.sum .

RUN go mod download
COPY . .

RUN GOOS=linux GOARCH=amd64 go build -o /main

# 本番用
FROM alpine:3.9

COPY --from=builder /main .

ENTRYPOINT ["/main"]
EXPOSE 7777
```

開発用のdocker-composeです。
[realize](https://github.com/oxequa/realize)を使ってホットリロードができるようにしています。

```yml:docker-compose.dev.yml
version: "3.5"

services:
  app:
    build:
      context: .
      target: builder
    volumes:
      - ./:/app
    command: realize start --server
    environment:
      - API_VERSION=development
    ports:
      - 7777:7777
  nginx:
    build: nginx
    ports:
      - 80:80
    depends_on:
      - app
```

本番用のdocker-composeです。ECRにあげたイメージを使っています。
また、現在のリリースバージョンの確認ができるようにコミットのSHA-1を環境変数（API_VERSION）として渡しています。ALBのヘルスチェック用に用意しているAPIを叩くと、このSHA-1が返ってくるようにしてます。
このdocker-composeはecs-cliに引数として渡すものになるため、[ecs-cliが対応しているversion](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI.html)である必要があります。（3.5とかだと無理でした）

```yml:docker-compose.prod.yml
version: "3"

services:
  app:
    image: "882275384674.dkr.ecr.ap-northeast-1.amazonaws.com/connpass-map-api-app:${SHA1}"
    environment:
      - API_VERSION="${SHA1}"
    ports:
      - 7777:7777
  nginx:
    image: "882275384674.dkr.ecr.ap-northeast-1.amazonaws.com/connpass-map-api-nginx:${SHA1}"
    ports:
      - 80:80
    depends_on:
      - app
```

### 環境構築
vpc、サブネットなどのNW環境とALBやターゲットグループ、ECSのクラスターなどをterraformで作成しました。
[別記事](https://qiita.com/pokotyan/items/3ca0e3a78a1475aa1b16)に詳しく書いています。

### CI/CD
- circleci
    - featureやmasterブランチにpushがされる
    - そのコミットのSHA-1をタグにしてアプリのイメージをビルド
    - ECRにイメージをpush

- デプロイするスクリプト（ローカルで実行）
    - どのブランチでリリースをしたいか選択
    - ecs-cliを使ってecsのサービス、タスクをリリース



# 近道
上記の環境を作るに当たってインフラまわりが苦手な人がひっかかりそうな所としては

- ecs
- terraform
- circleci
- デプロイするシェルスクリプト

な気がしてます。これらは僕自身も今も苦手ですが、

ecsならリリースに[ecs-cli](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ECS_CLI.html)を使えば、docker-compose.ymlをそのまま流用する形でecsのタスクが作れる。（dockerは勉強する必要あるがecsの独自のタスク定義の書き方とかは勉強しなくてよくなる）

terraformなら[いい本](https://www.amazon.co.jp/dp/B07XT7LJLC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)があるのでベーシックな環境ならこの本の写経でなんとかなる。

circleciなら便利な処理をパッケージにしてくれている[orbs](https://circleci.com/docs/ja/2.0/orb-intro/)を使えば結構楽できる。

シェルスクリプトは勉強すればいい。（これは近道がない気がしている）

という感じでどれも今なら便利なツールや本が揃っているので個人開発でそれっぽい環境を作るハードルはかなり低くなってると思います。