【インフラ刷新】Elastic Beanstalk から ECS on Fargate への乗り換え

## 概要
長年Elastic Beanstalk(EB)で動いていたWebアプリケーションをECS on Fargateに乗り換えました。
EBのデプロイ時に行っていた下記は、Dockerイメージのビルド時に実行しています。
- 依存関係のインストール (bundle installやyarn install)
- アセットのコンパイル

一方で、データベースのマイグレーションは、コンテナが実行される際に行うよう変更しました。
作業手順とハマったことを残しておきます。

## Dockerfileの準備
開発環境はDockerで動かしていたのでそこまで苦労はしませんでした。
開発用と本番用で若干の差異があったこと、本番環境は`Nginx`を利用するので下記のようなディレクトリ構成にして開発と本番を分けました。

```bash
.
├── docker
│   ├── app
│   │   ├── Dockerfile      # 本番環境で使用するアプリ用Dockerfile
│   │   └── Dockerfile.dev  # 開発環境で使用するアプリ用Dockerfile
│   └── nginx
│       ├── Dockerfile      # 本番環境のみで使用するNginx用Dockerfile
│       ├── conf.d
│       │   └── myapp.conf  # Nginxの仮想ホスト設定ファイル
│       └── nginx.conf      # Nginxのメイン設定ファイル
├── docker-compose.yml      # 開発環境の起動で使用
├── start_prod.sh           # 本番環境の起動で使用
│
│ # その他アプリファイル...
```

開発環境立ち上げ用 `docker-compose.yml` 抜粋
```yaml:docker-compose.yml
version: "3.8"
services:
  web:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile.dev # 開発用のDockerfileを指定
    working_dir: /app # Gemfile があるディレクトリ
    command: bash -c "rm -f tmp/pids/server.pid && bundle install && yarn install && bundle exec rails db:migrate && bundle exec rails s -p 3009 -b '0.0.0.0' -e development" # マウントするボリュームの問題でここでも bundle install する
```

本番環境立ち上げ用 `start_prod.sh`
```shell:start_prod.sh
#!/bin/bash
# 本番環境のDockerコンテナ起動用シェルスクリプト。ECSタスク定義により実行される。
# bundle install と assets:precompile は Dockerfile 内で実行済み
set -e

# マイグレーションの実行（アプリの特性上タスクが複数台起動していてマイグレーションがバッティングすることは想定しない）
# 複数台タスクがあるアプリの場合はマイグレーションタイミングを別途考慮しないとダメ
echo "Exec rails db:migrate..."
bundle exec rails db:migrate

# Puma のソケットディレクトリを作成
echo "Making puma directory..."
mkdir -p /app/sockets

# Rails サーバー（Puma）の起動
echo "Starting the puma..."
bundle exec puma -C /app/config/puma.rb
```

## 開発環境での実行
アプリの`Dockerfile.dev`イメージのビルドを行い、`docker-compose.yml`から立ち上げを行う。
**開発環境では nginx を通さずに直接 puma を起動している**

```bash
# 依存関係に変更があった場合に開発環境アプリ用イメージをビルド
docker build --no-cache -f docker/app/Dockerfile.dev .

# コンテナを立ち上げ オプションで -d をつけてデーモン化もあり
docker-compose up

# コンテナを落とす
docker-compose down
```

## ECRイメージを準備
### リポジトリ作成からイメージ格納までのコマンド手順
#### ECRリポジトリの作成
```bash
# アプリ用のリポジトリ
aws ecr create-repository --repository-name hoge_app --profile hoge-sso

# nginx用のリポジトリ
aws ecr create-repository --repository-name hoge_nginx --profile hoge-sso
```


#### Dockerイメージのビルド
以降の手順は後ほど`CodeBuild`から行えるようにする
```bash
# アプリ用イメージのビルド
docker build --no-cache -t hoge_app -f docker/app/Dockerfile .

# nginx用イメージのビルド 
docker build --no-cache -t hoge_nginx -f docker/nginx/Dockerfile . 
```

#### ECRへのログイン
```bash
# $ECR_REGISTRY は各環境のECRリポジトリ
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ECR_REGISTRY
```

#### Dockerタグをつける
```bash
# アプリイメージのタグ付け
docker tag hoge_app:latest $ECR_REGISTRY/hoge_app:latest

# nginxイメージのタグ付け
docker tag hoge_nginx:latest $ECR_REGISTRY/hoge_nginx:latest
```

#### ECRへpushする
```bash
# アプリイメージのpush
docker push $ECR_REGISTRY/hoge_app:latest

# nginxイメージのpush
docker push $ECR_REGISTRY/hoge_nginx:latest
```

#### プルスルーキャッシュリポジトリ機能を利用して、パブリックリポジトリのイメージをプライベートリポジトリ経由で利用する

※参考 こちらの記事通り実行すれば問題なく設定できるかと思います

https://dev.classmethod.jp/articles/ecr-pull-through-cache-repositories/

上記記事を参考にして設定を行う。
ECR -> Private registry -> Features & Settings -> Pull through cache

public.ecr.aws のリポジトリを ecr-public の名前空間で利用できるようにする
[![Image from Gyazo](https://i.gyazo.com/39e95363c3c9f7a57eb61caa32ff3d4a.png)](https://gyazo.com/39e95363c3c9f7a57eb61caa32ff3d4a)

この状態でプライベートリポジトリからPullを実行してみる
※`aws-for-fluent-bit`イメージをPullする
```bash
# ECRにログイン
aws ecr get-login-password --region ap-northeast-1 --profile hoge-sso | docker login --username AWS --password-stdin $ECR_REGISTRY

# プライベートリポジトリからイメージのPull
docker image pull $ECR_REGISTRY/ecr-public/aws-observability/aws-for-fluent-bit:stable
```

プライベートリポジトリに `aws-for-fluent-bit` が登録されている
[![Image from Gyazo](https://i.gyazo.com/c50893e1a2aa00c0a4a41311fe5f519c.png)](https://gyazo.com/c50893e1a2aa00c0a4a41311fe5f519c)

## ECSを構築
### ログ保管用のS3バケットを作成
rails (puma) と nginx のログを保管するために `hoge-ecs-log` バケットを作成
後ほど設定するログルーティングコンテナ（FireLens）のタスク定義により、それぞれのタスク内部のファイルには出力せず直接このバケットにログが出力される
このためログファイルがタスクのディスクを圧迫する、ということは起きないのでログローテーションの設定は不要

### Secrets Managerを作成
ECSタスクから利用する秘匿情報を保管するSecrets Managerを作成しておく
新しいシークレットを保存する -> その他のシークレットのタイプ
秘匿情報のキーペアを保存
シークレットの名前：`hoge`

[![Image from Gyazo](https://i.gyazo.com/7453969faa7fd2748d3d42afabab3856.png)](https://gyazo.com/7453969faa7fd2748d3d42afabab3856)

### ECSサービス用のセキュリティグループを作成
**❗️このSGのインバウンド & アウトバウンドグループが重要**
正しく設定できていないとコンテナにアクセスできなかったりリポジトリをpullできなくなるハマりポイントになる

- インバウンドルール
  - HTTP 80 作成した自分のセキュリティグループ自身
  - HTTPS 443 CloudFront のみ許可
- アウトバウンドルール
  - すべてのトラフィック 0.0.0.0/0

[![Image from Gyazo](https://i.gyazo.com/881a0672cc42c7fbcf77fbc128cf46f7.png)](https://gyazo.com/881a0672cc42c7fbcf77fbc128cf46f7)

[![Image from Gyazo](https://i.gyazo.com/25b9e01c6d1e44974754fd060701f64d.png)](https://gyazo.com/25b9e01c6d1e44974754fd060701f64d)

**❗️RDSのセキュリティグループでアクセスを制限している場合**
RDSのセキュリティグループのインバウンドルールにて、今回作成したセキュリティグループを追加してECSからRDSに接続できるようにする

### タスク定義を準備
- タスク定義：`hoge-web`
- 起動タイプ：`AWS Fargate`
- オペレーティングシステム/アーキテクチャ：`Linux/X86_64`
- タスクサイズ：`2vCPU 4GiB`
  - 現行EBで動いている t3.medium と同等スペック
- タスクロール：`ecsTaskExecutionRole`
- タスク実行ロール：`ecsTaskExecutionRole` 

#### タスクロール`ecsTaskExecutionRole` には下記ポリシーをつける
- `AmazonEC2ContainerRegistryReadOnly`
  - リポジトリからイメージをpullする用
- ログ用S3バケットインラインポリシー 
  - ログをS3に出力するため
- シークレットマネージャーのインラインポリシー
  - タスクからSecrets Managerにアクセスするため
- SSMのインラインポリシー
  - SSMを使ってSSHするため

※インラインポリシーの例
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::hoge-ecs-log",
                "arn:aws:s3:::hoge-ecs-log/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:secretsmanager:ap-northeast-1:123456789123:secret:hoge-secret",
                "arn:aws:kms:ap-northeast-1:123456789123:key/hogehoge"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel",
                "ecs:ExecuteCommand",
                "ssm:StartSession",
                "ecs:DescribeTasks"
            ],
            "Resource": "*"
        }
    ]
}
```

#### コンテナ1 アプリ用
- 名前：`rails`
- イメージURI： `$ECR_REGISTRY/hoge_app:latest`
  - latest を指定することで常に最新のイメージを元にタスクが立ち上がる
- 必須コンテナ：`はい`
- プライベートレジストリ：オフ
- ポートマッピング：`なし`
  - **Nginx経由でアクセスするのでrailsコンテナには不要**
- 読み取り専用：チェックなし
- リソースの割り当て制限：初期値
- 環境変数：環境変数を引っ張ってくる
  - 秘匿情報は`ValueFrom`にて`Secrets Manager`から
  - ValueFrom のSecrets Managerの値は **`シークレットのarn:キー名::`** の形 
    - 例`arn:aws:secretsmanager:ap-northeast-1:123456789123:secret:hoge-secret:APP_KEY::`
- ログの収集：`ON`  `AWS Firelens 経由で S3 にログをエクスポートする`
  - bucket：`hoge-ecs-log`
    - 先ほど作成したバケット
  - exclude-pattern：`^\s*$` 
    - 空白はログ記録しない意図
  - s3_key_format：`/hoge-web/rails/%Y/%m/%d/%H/%M/%H-%M-%S-$UUID`
    - バケットに `/hoge-web/rails/日付` という構成でログを保管していく
    - このフォルダ構成でrailsとnginxのログをわかりやすくしておく
- 再起動ポリシー：初期値
- HealthCheck：初期値
- スタートアップの依存関係の順序：初期値
- コンテナタイムアウト：初期値
- コンテナネットワーク設定：初期値
- Docker設定：
  - エントリポイント：空白
  - コマンド：`bash,/app/start_prod.sh`
    - プロジェクトルートに用意したシェルを起動する 
  - 作業ディレクトリ：`/app`
- リソース制限：初期値
- Dockerラベル：初期値

#### コンテナ2 Nginx用
- 名前：`nginx`
- イメージURI： `$ECR_REGISTRY/hoge_nginx:latest`
  - latest を指定することで常に最新のイメージを元にタスクが立ち上がる
- 必須コンテナ：`はい`
- プライベートレジストリ：オフ
- ポートマッピング：
  - コンテナポート：`80` 
  - プロトコル：`TCP`
  - ポート名：`nginx-80-tcp`
- 読み取り専用：チェックなし
- リソースの割り当て制限：初期値
- 環境変数：なし
- ログの収集：`ON`  `AWS Firelens 経由で S3 にログをエクスポートする`
  - bucket：`hoge-ecs-log`
    - 先ほど作成したバケット
  - exclude-pattern：`^\s*$` 
    - 空白はログ記録しない意図
  - s3_key_format：`/hoge-web/nginx/%Y/%m/%d/%H/%M/%H-%M-%S-$UUID`
    - バケットに `/hoge-web/nginx/日付` という構成でログを保管していく
    - このフォルダ構成でrailsとnginxのログをわかりやすくしておく
- 再起動ポリシー：初期値
- HealthCheck：初期値
- スタートアップの依存関係の順序 
  - コンテナ名：`rails`
  - 条件：`start`
    - rails コンテナが立ち上がってからnginxを立ち上げる
- コンテナタイムアウト：初期値
- コンテナネットワーク設定：初期値
- Docker設定：初期値
- リソース制限：初期値
- Dockerラベル：初期値

#### ログルーティングコンテナ（FileLens）
:::note
コンテナのログの収集を `AWS Firelens 経由で S3 にログをエクスポートする` にすると表示される
:::

- 名前：`log_router`
- イメージURI： `$ECR_REGISTRY/ecr-public/aws-observability/aws-for-fluent-bit:stable`
  - プルスルーキャッシュ機能を使いプライベートリポジトリを使用する
- 必須コンテナ：`はい`
- プライベートレジストリ：オフ
- ポートマッピング：初期値
- 読み取り専用：チェックなし
- リソースの割り当て制限：初期値
- 環境変数：なし
- ログの収集：`ON`  `AWS CloudWatch` 
  - awslogs-group：`/aws/ecs/hoge-ecs/firelens`
    - このロググループに「S3へ保存したよ」というFirelensの仕事ログが格納されるようになる
- 再起動ポリシー：初期値
- HealthCheck：初期値
- スタートアップの依存関係の順序：  初期値
- コンテナタイムアウト：初期値
- コンテナネットワーク設定：初期値
- Docker設定：初期値
- リソース制限：初期値
- Dockerラベル：初期値

#### ストレージオプション
:::note
Nginxが受けたリクエストをPumaへ流すために、両コンテナで共通のソケットファイルを準備する
この設定によりNginxとPumaが同じソケットファイルを通じて通信できるようになる
:::
- エフェメラルストレージ 量：空白
- ボリューム：
  - ボリューム名： `sockets`
  - 設定タイプ：`タスク定義の作成時に設定`
  - ボリュームタイプ：`バインドマウント`
- コンテナマウントポイント：
  - コンテナ：`rails` ソースボリューム：`sockets` コンテナパス：`/app/tmp/sockets`
  - コンテナ：`nginx` ソースボリューム：`sockets` コンテナパス：`/app/tmp/sockets`

### ECSクラスターの作成
- クラスター名：`hoge`
- インフラストラクチャ：`AWS Fargate`
- モニタリング：
  - Container Insights の使用：`ON`

### ECSサービスの作成
- コンピューティングオプション：`起動タイプ`
- 起動タイプ：`FARGATE`
- プラットフォームバージョン：`LATEST`
- アプリケーションタイプ：`サービス`
- リビジョンの手動指定：`OFF`
- ファミリー：`hoge-web`
- リビジョン：`最新のもの`
- サービス名：`hoge-web`
- サービスタイプ：`レプリカ`
- 必要なタスク：`1`
  - 何台構成で動かすか
- デプロイタイプ：`ローリングアップデート`
  - 最小実行タスク：`100%`
  - 最大実行タスク：`200%`
    - デプロイ中は新旧タスクで2台構成になる
- デプロイ不具合の検出：
  - Amazon ECS デプロイサーキットブレーカーを使用する：`ON`
  - 失敗時のロールバック：`ON`
  - CloudWatchアラームの使用：`OFF`
- Service Connect：初期値
- サービス検出：初期値
- ネットワーキング
  - VPC：`hoge-vpc`
  - サブネット： `-a` `-c` `-d` パブリックサブネットを全選択
  - セキュリティグループ：`既存のセキュリティグループ` 
    - `hoge-ecs` 
      - 先ほど作成したもの
  - パブリックIP：`ON`
- ロードバランシング
  - ロードバランサーの種類：`Application Load Balancer`
  - コンテナ：`nginx 80:80`
  - Application Load Balancer：`新しいロードバランサーの作成`
  - ロードバランサー名：`hoge-ecs-lb`
  - ヘルスチェックの猶予期間：`60`
  - 新しいリスナーを作成：`選択`
    - ポート：`80`
    - プロトコル：`HTTP`
  - 新しいターゲットグループの作成：`選択`
    - ターゲットグループ名：`ecs-hoge-web`
    - プロトコル：`HTTP`
    - 登録解除の遅延：`300`
    - ヘルスチェックプロトコル：`HTTP`
    - ヘルスチェックパス：`/health`
      - アプリケーションのヘルスチェック用ルーティングに合わせて設定
- サービスの自動スケーリング：初期値
- Volume：初期値
- タグ：初期値

### ロードバランサーの設定
ECSサービスにて作成した`hoge-ecs-lb`ロードバランサーの設定を変更する
リスナーとルールにてHTTP:80を削除しHTTPS:443を追加する

[![Image from Gyazo](https://i.gyazo.com/a0514ad9e7bf8840b6b60a96eaeb8656.png)](https://gyazo.com/a0514ad9e7bf8840b6b60a96eaeb8656)

## デプロイパイプラインを構築
GitHubへのpushを契機に、CodeBuildを動かして自動でデプロイをさせる
CodeBuildeでは下記の一連動作を行う

:::note
1. ECRにログイン
1. Docker Hub にログイン
1. アプリとnginxのイメージをビルド
1. イメージにタグ付け
1. ECRリポジトリにpush
1. ECSサービスに更新コマンドを投げる
:::

### CodePipelineの設定
**GitHubバージョン2を使用するのであらかじめGitHubとの接続を作成しておく**
  - 参考
  
  https://dev.classmethod.jp/articles/aws-codepipeline-github-connection-v2/

- パイプライン名：`hoge-ecs-pipeline`
- 実行モード：キュー (パイプラインタイプ V2 が必須)
- サービスロール：新しいサービスロール
- ソースプロバイダー：`GitHub（バージョン2）` 
- 接続：作成済みの接続
- リポジトリ名：任意のGitHubリポジトリ
- デフォルトブランチ：任意のブランチ
- 出力アーティファクト形式：CodePipelineのデフォルト
- トリガータイプ：フィルターを指定
- イベントタイプ：プッシュ
- フィルタータイプ：ブランチ
- ブランチ：含める 任意のブランチ
- プロバイダーを構築する：Other build providers
  - AWS CodeBuild
  - プロジェクトを作成する
- **ビルドステージでCodeBuildからECSをアップデートするのでデプロイステージはスキップする**

### buildspec.yml の準備
```yaml
version: 0.2
env:
  variables:
    DOCKER_BUILDKIT: '1' # ビルド高速化の意図

phases:
  pre_build:
    commands:
      # ECRにログインする
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ECR_REGISTRY
      # Docker Hub へのログイン(匿名だとDockerHubからのイメージpull回数の制限に引っかかることがあるため)
      - echo Logging in to Docker Hub...
      - echo $DOCKER_HUB_PASSWORD | docker login -u $DOCKER_HUB_USER --password-stdin
  build:
    commands:
      # アプリとnginxのDockerイメージをキャッシュ無効化してビルド
      - echo Building the Docker images with no cache...
      - docker build --no-cache -t hoge_app -f docker/app/Dockerfile .
      - docker build --no-cache -t hoge_nginx -f docker/nginx/Dockerfile .
      # イメージにタグ付け
      - docker tag hoge_app:latest $ECR_REGISTRY/hoge_app:latest
      - docker tag hoge_nginx:latest $ECR_REGISTRY/hoge_nginx:latest
  post_build:
    commands:
      # ECRリポジトリにイメージをそれぞれpush
      - echo Pushing the Docker images to ECR...
      - docker push $ECR_REGISTRY/hoge_app:latest
      - docker push $ECR_REGISTRY/hoge_nginx:latest
      - echo Docker images have been pushed to ECR.
      # ECSサービスを更新して新しいデプロイを強制
      - echo Updating ECS service...
      - aws ecs update-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --force-new-deployment --region ap-northeast-1
      - echo ECS service has been updated.

artifacts:
  files:
    - '**/*'
```

### CodeBuild ビルドプロジェクトを作成
:::note
CodeBuild上でDockerHubからのイメージpull回数の制限を避ける目的でDockerHubにログインさせる
:::
  - 参考

https://dev.classmethod.jp/articles/codebuild-has-to-use-dockerhub-login-to-avoid-ip-gacha/

- Secrets Manager に DockerHub の認証情報 `DOCKER_HUB_USER` と `DOCKER_PASSWORD` を追加
- プロジェクト名：`hoge-build`
- 同時ビルド制限の有効化：ON
  - 同時ビルド制限：`1`
- プロビジョニングモデル：オンデマンド
- 環境イメージ：マネージド型イメージ
- コンピューティング：EC2
- オペレーティングシステム：Amazon Linux
- ランタイム：スタンダード
- イメージ：最新イメージ
- イメージのバージョン：最新バージョン
- GPU強化コンピューティングを使用：OFF
- サービスロール：新しいサービスロール
  - ロールにつけるポリシーは後ほど記載
- タイムアウト：初期値
- キュータイムアウト：初期値
- 特権付与：`ON` 
- レポート自動検出を無効にする：初期値
- 自動検出ディレクトリ：初期値
- 証明書：初期値
- VPC：初期値
- コンピューティング：初期値
- 環境変数として下記を設定
  - ECR_REGISTRY：`123456789123.dkr.ecr.ap-northeast-1.amazonaws.com` 
  - CLUSTER_NAME：`clusterName`
  - SERVICE_NAME：`serviceName`
  - DOCKER_HUB_USER：`シークレットマネージャーのARN:DOCKER_HUB_USER`
    - タイプはSECRETS_MANAGER
  - DOCKER_HUB_PASSWORD：`シークレットマネージャーのARN:DOCKER_HUB_PASSWORD`
    - タイプはSECRETS_MANAGER`
- ビルド使用：`buildspec ファイルを使用する`
- バッチ設定：初期値
- ログ初期値
- 作成されたIAMロールを用意して下記権限をつける
  - AmazonEC2ContainerRegistryPowerUser
  - ECSのアップデートインラインポリシー
  - Secrets Managerのインラインポリシー

CodeBuildロールのインラインポリシー例
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:UpdateService",
                "ecs:DescribeServices",
                "ecs:DescribeClusters"
            ],
            "Resource": [
                "arn:aws:ecs:ap-northeast-1:123456789123:service/clusterName/serviceName",
                "arn:aws:ecs:ap-northeast-1:123456789123:cluster/clusterName"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:secretsmanager:ap-northeast-1:123456789123:secret:secretsName",
                "arn:aws:kms:ap-northeast-1:123456789123:key/hoge"
            ]
        }
    ]
}
```

## ドメインでECS環境にアクセスできるように
Route53の向き先になっているCloudFrontのオリジンに、ECSサービスで作成したロードバランサーを登録する。
登録後にビヘイビアにて対象を新しいロードバランサーに向ける。
旧環境から新環境に向くまでダウンタイムはなし。

## ログ設計について
Elastic Beanstalk時代と違い `puma.log` や `nginx/access.log` のファイルには直接ログを出力していない。
標準のログ出力に任せており、そのログはタスクコンテナからS3`hoge-ecs-log`バケットにアップロードされている。
ログ検索はAthenaを使って行うことになる。
`puma.log` や `nginx/access.log` に書き込まれてディスクを圧迫するということもないので、今まで行っていたログローテションの仕組みは不要になる

## ECSタスクへのSSHについて
SSMを用いてSSHする
アプリログやビルドログが出力されないのでほとんどSSHはしないかもしれない

### SSHまでのコマンド手順
SSMプライグインをクライアントマシンにダウンロード(初回のみ)
```bash
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
```
 解凍(初回のみ)
```bash
unzip sessionmanager-bundle.zip
```
インストール実行(初回のみ)
```bash
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
```
インストール確認(初回のみ)
```bash
session-manager-plugin

# 下記successが返ってくる
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
```
AWS CLIのリージョン設定(初回のみ)
```bash
aws configure set default.region ap-northeast-1
```
ECRへのログイン
```bash
aws ecr get-login-password --region ap-northeast-1 --profile hoge-sso | docker login --username AWS --password-stdin $ECR_REGISTRY
```
railsコンテナへのSSH
```bash
aws ecs execute-command --region ap-northeast-1 --cluster $CLUSTER_NAME --task $TASK_ARN --interactive --container rails --command "/bin/bash" --profile hoge-sso
```
nginxコンテナへのSSH
```bash
aws ecs execute-command --region ap-northeast-1 --cluster $CLUSTER_NAME --task $TASK_ARN --interactive --container nginx --command "/bin/bash" --profile hoge-sso
```

## おまけ Elastic Beanstalk用のファイルを削除
`.ebextensions`配下や`.platform`を削除しました。
`Procfile`を利用して`Puma`を起動していたのでそちらも削除しました。
```diff:Procfile
- web: mkdir -p /var/app/current/tmp/pids && bundle exec puma -C /var/app/current/config/puma.rb
```

`puma.rb`も若干修正を加えています
```diff:puma.rb
- directory '/var/app/current'
- bind 'unix:///var/run/puma/my_app.sock'
+ directory '/app'
+ bind 'unix:///app/tmp/sockets/puma.sock'

- stdout_redirect '/var/log/puma/puma.log', '/var/log/puma/puma.log', true
```
`stdout_redirect`を削除して`puma.log`にログを出力させなくしています。


## おまけ ハマった所
### ECSタスクからECRをPullできない
下記のエラーが発生
```bash
ResourceInitializationError: unable to pull secrets or registry auth: The task cannot pull registry auth from Amazon ECR: There is a connection issue between the task and Amazon ECR. Check your task network configuration. RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-1.amazonaws.com/": dial tcp 3.112.65.177:443: i/o timeout
```
ECS実行タスクにECRの権限はちゃんとついている
`AmazonEC2ContainerRegistryFullAccess`と`AmazonElasticContainerRegistryPublicFullAccess`つけてみる→ダメ
ECSサービスのSGアウトバウンドグループにて通信を全て許可する→成功⭕️

### Nginxを準備していない時に立ち上がったタスクのIPでアクセスできない
ヘルスチェックに失敗し何度もタスクが再起動する
SGをみたが問題なさそう
`puma.rb` の bind で 80 ポートで待つようにしてとりあえず解消

```diff:puma.rb
- bind 'unix:///app/sockets/my_app.sock'
+ bind 'tcp://0.0.0.0:80'
```

第二段階として、その後はNginxを挟んでバインドするように改修

```diff:ruby
- bind 'tcp://0.0.0.0:80'
+ bind 'unix:///app/tmp/sockets/puma.sock'
```

```diff:nginx/conf.d/myapp.conf
+ upstream myapp {
+   server unix:///app/tmp/sockets/puma.sock;
+ }
```

コンテナ間でデータ共有するためにタスク定義でボリュームを設定
[![Image from Gyazo](https://i.gyazo.com/c08b63e8106b3dc41f24647a2f374211.png)](https://gyazo.com/c08b63e8106b3dc41f24647a2f374211)