Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
19
Help us understand the problem. What is going on with this article?
@pokotyan

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

はじめに

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

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

https://connpass.net/
スクリーンショット 2020-05-03 12.02.51.png

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

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

フロント

アプリケーション

アプリケーションの土台はcreate-react-appを使いtypescriptで書きました。
stateの管理にはredux、ミドルウェアにredux sagaを使いました。
また、reduxの型定義にtypescript-fsa、reducerでのオブジェクトのマージにimmerを使っています。
SSRをせずに、動的なOGPに対応するためにprerender-spa-pluginを使ったため、customize-crareact-app-rewiredを使ってCRAのデフォルトのwebpack設定を上書きしたりもしました。prerender-spa-pluginのことは、別記事にも書いています
また、apiのfetch中に出てくるアニメーションはlottieにあったものを使いました。

環境構築

cloudfront、s3の環境をterraformで作成しました。
別記事に詳しく書いています。

CI/CDの流れ

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

バックエンド

アプリケーション

golangを使いました。フレームワークにgin、cuiにcobraを使っています。
apiでもcuiでも共通のロジックを使うようにしています。クリーンアーキテクチャを意識はしましたができてるかはよくわからないです。

docker

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

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を使ってホットリロードができるようにしています。

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である必要があります。(3.5とかだと無理でした)

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で作成しました。
別記事に詳しく書いています。

CI/CD

  • circleci

    • featureやmasterブランチにpushがされる
    • そのコミットのSHA-1をタグにしてアプリのイメージをビルド
    • ECRにイメージをpush
  • デプロイするスクリプト(ローカルで実行)

    • どのブランチでリリースをしたいか選択
    • ecs-cliを使ってecsのサービス、タスクをリリース

近道

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

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

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

ecsならリリースにecs-cliを使えば、docker-compose.ymlをそのまま流用する形でecsのタスクが作れる。(dockerは勉強する必要あるがecsの独自のタスク定義の書き方とかは勉強しなくてよくなる)

terraformならいい本があるのでベーシックな環境ならこの本の写経でなんとかなる。

circleciなら便利な処理をパッケージにしてくれているorbsを使えば結構楽できる。

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

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

19
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
pokotyan
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
19
Help us understand the problem. What is going on with this article?