お肉屋さんがDeveloper Preview版のCopilotでEC-CUBE4を使った商用環境をリリースしてみた 


こちらはAWS Containers Advent Calendar 2020の21日目の記事です＼(^o^)／
※ 「複数の環境を構築していてもPipelineは一つしか構築できない」の欄に関して追記あり(2020/12/25)

# 何がしたかった?

- EC2で動いているECサイトをECS on Fargate上で動かしたいニーズがあった。
- 上記を案件化したタイミングで[リリースされたCopilot](https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/)に勝手に運命を感じた。
- Copilot GA前にProduction環境でリリースして**ドヤりたかった。**
    - 11/23に[generally available](https://aws.amazon.com/jp/blogs/containers/aws-copilot-is-now-generally-available/)してた＼(^o^)／

**(CloudFormationをあまり使いたくなかったのもある。)**

# ここはどこ? 私はだれ?
## ここは
ITリテラシーの低い飲食企業さん。
6年くらい前に~~PHPも知らない~~人に外注して作ったと思われる、EC-CUBE3で構築されたオンライン販売サイトを運営している。
コンテンツは強いので度々テレビに取り上げられるが、その度にECサイトがちょくちょく落ちて商機を逃している。

~~クソコードで~~コア部分にもめちゃめちゃ手が入っていて、バージョンアップも数年単位で出来ていない。
わけわからん高額かつスケールアップもスケールアウトに対応していないサーバー上で稼働していたため、まずはEC2に移行してこれらを手動で行えるようにしたところ。

## 私は
異世界(IT業界)から現世(飲食業界)へ転生してきたエンジニア。
食べるのが好き。特に**肉**が好き。
バックエンドもフロントエンドもコード書くマンで、EC-CUBE3やECS on Fargateはそれぞれ別個に構築経験あり。
インフラはAWSと心中するつもりマン。
今回EC-CUBE4を使って肉屋のサイトをフルリニューアルしていく中で、インフラもちゃんとアップデートしたいと思っている。

# なぜCopilot?
Copilotを使うことで

- 複数の環境(staging, production etc..)のサービス構築
- 複数の環境のCI/CDの構築

が楽に実現できそうな気がした。
以前CloudFormationでECS on Fargateを利用してSpringBootを動かしてた時に感じた
**「もうyamlあんま書きたくないんや！」**
という強い気持ちも動機の一つ。
(このタイミングでは、Copilotのバージョンは**v0.4.0**)

# 使ってみてどうだった？
## 概ね問題無しどころか、割と素敵やん

- CopilotのCLIを用いて新規VPCとECSクラスタ、ALBの作成がサクッとできるのはやはり素敵。まじで楽。
- オートスケールとかの設定変更する場合は、copilot/manifest.yml変えてdeployするだけなので非常に楽にコード管理できるので良き。
- ただ少なくとも着手した時点では**既存のVPCにクラスタを構築するのは不可能っぽかった。**
    - 8/14の[リリース](https://github.com/aws/copilot-cli/releases/tag/v0.3.0)で既存のVPCを指定することができるようになってた＼(^o^)／

使ったコマンド(READMEの一部抜粋)は下記。シンプル！

```md:README.md
#### How to setup copilot cli
brew install aws/tap/copilot-cli
#### How to confirm env list
copilot env ls
#### How to deploy
copilot svc deploy --env <ENV Name>
#### How to add env
copilot env init
#### How to update deployment settings
Update copilot/{アプリ名}/manifest.yml

See:
https://github.com/aws/copilot-cli/wiki/Installation
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started-aws-copilot-cli.html

Check release note:
https://github.com/aws/copilot-cli/releases/tag/v0.4.0
```

ちな、うちの設定はこんな感じ。

```yaml:manifest.yml
# Your service name will be used in naming your resources like log groups, ECS services, etc.
name: {アプリ名}
# The "architecture" of the service you're running.
type: Load Balanced Web Service

image:
  # Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
  build: ./Dockerfile
  # Port exposed through your container to route traffic to it.
  port: 80

http:
  # Requests to this path will be forwarded to your service.
  # To match all requests you can use the "/" path.
  path: "/"
  # You can specify a custom health check path. The default is "/"
  healthcheck: "/health.html"

# Number of CPU units for the task.
cpu: 1024
# Amount of memory in MiB used by the task.
memory: 2048
# Number of tasks that should be running in your service.
count: 20
# Optional fields for more advanced use-cases.
#
variables: # Pass environment variables as key value pairs.
# ENV: dev
#
#secrets:                      # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
#  GITHUB_TOKEN: GITHUB_TOKEN  # The key is the name of the environment variable, the value is the name of the SSM parameter.

# You can override any of the values defined above by environment.
environments:
  prod:
    range: 1-20 # Specify a range for how many tasks you'd like to run in your service.
    cpu_percentage: 70 # To scale on average CPU.
    memory_percentage: 80
    variables:
      ENV: prod

  stg:
    range: 1 # Specify a range for how many tasks you'd like to run in your service.
    cpu_percentage: 70 # To scale on average CPU.
    memory_percentage: 80
    count: 1
    variables:
      ENV: stg

```

## 複数の環境を構築していてもPipelineは一つしか構築できない
正確に言うと、Pipelineに引数を渡せないっぽくて中で向く環境を振り分けることが出来なかった。
最初絶望したけど、対応策としてCodeBuild内でcopilotを実行することで~~ちょっと時間かかるけど~~事なきを得た。
CodeBuildのbuildspecの中で

```copilot svc deploy -a {app名} -e {環境名}```

を実行した。(= Codepipelineは利用していない。)

具体的な設定はこんな感じ。
※ dockerhubのアカウント情報ベタ書きしてたの忘れてた、後でSecrets Manager使うように書き換えて更新しておきます。

```yaml:buildspec.yml
version: 0.2
phases:
  pre_build:
    commands:
       - curl -Lo /usr/local/bin/copilot https://github.com/aws/copilot-cli/releases/download/v0.4.0/copilot-linux-v0.4.0
       - chmod +x /usr/local/bin/copilot
       - copilot --help
       - copilot env ls
       - echo Logging in to Docker Hub...
       - echo {dockerhubパスワード} | docker login -u {dockerhubアカウント名} --password-stdin
  build:
    commands:
       - rm Dockerfile
       - mv Dockerfile.prod Dockerfile
       - copilot svc deploy -a {アプリ名} -e {環境名}
```

// 2020/12/25追記
この記事を書いた後にAWSの[Toriさん](https://twitter.com/toricls)にお尋ねできる機会があったので追記。
一つのApplicationにつき一つのPipelineしか構築できないのは、AWSで採用している

- **Single Source Of Truth**
- **Build once, deploy everywhere**

の思想に基づいた設計だということです。
(CodePipelineが複数ブランチをサポートしてないのも同じ理由)

つまり**リリースブランチは1つ**で

1. masterにpush
2. テスト環境が構築され、テスト実施
3. テストが成功したらステージング環境を構築
4. ステージング環境のチェックが問題なければCodePipeline上でApprove
5. プロダクション環境を構築

という流れを想定しているとのことでした。
自社ではgit-flowを導入していたため**developブランチとステージング環境が1対1で対応する**という前提でCodePipelineの設計を考えていたためにスムーズにcopilotに乗れなかったということですね。

もちろん私はAWSと心中するつもりなので、開発フローを寄せていく努力をするつもりです←

ぱっと気になった点として、弊社では開発を外部に委託している場合にmasterに一切関与しないような権限設計をしているのでそのあたりの再考が必要かなと。
また、Toriさんとも話しましたが、各featureブランチにpushするごとにそれぞれのfeatureブランチのステージング環境が構築されてgithub-flowを実現できる、とかであればスムーズに移行できそうなので今後に期待ですね◎
※ git-flow、github-flowの[参考記事](https://qiita.com/mint__/items/bfc58589b5b1e0a1856a)

Toriさん、改めて開発フローへのツッコミやアドバイス頂きありがとうございました！！
// 2020/12/25追記終わり

# 結論
1ヶ月ほど商用環境として運用しているが特段問題なし。
何度かテレビにも取り上げられてアクセス数が跳ね上がったけど**ご機嫌に稼働し続けてくれてる。**

よく使うサービスのチューニングはmanifest.ymlで全然行えるので、ECS側で複雑な要件がない限りは(CodeBuild内でどうとでもハンドリングできるし)今後も使っていくと思う。CLIでサクサク出来ていく世界観は好き。
諸々の制約は今後回収されていくだろうと期待したい◎