CodePipeline + Fargate + FastAPIで本番・開発・ローカル毎に環境変数が変わるCI/CDを構築してみた

## はじめに
初めまして。現在、新卒1年目で[グーホーム](https://goohome.jp/)チームに所属している[プロトソリューション](https://www.protosolution.co.jp/service/goo_home/goo_home.html)の川崎です。
今回は業務でAPIサーバーのCI/CDを構築する業務があり、その中で得た知見を書いていこうと思います。


## 環境
【筆者ローカル環境】
・Windows
・WSL(コントリビューション：Ubuntu-20.04)
・Docker 20.10.23

【使用した主なAWSサービス】
・CodeCommit
・CodeBuild
・CodePipeline
・Elastic Container Registry
・Elastic Container Service(ECS on Fargate)



## この記事で分かること
CodePipelineを使用した、ECS on Fargateにおける開発環境・本番環境における各環境変数の設定方法が分かります。
今回紹介する方法以外にも環境変数の設定の仕方は様々あるかと思いますが、個人的にはフォルダ直下に環境変数がある方が見やすいので今回はそのような構成をお伝えします。
また、CI/CDを主軸にしているので、基本的なGitの使用・運用方法、Docker、VPC、IAM、FastAPIの説明は省略しているのでご容赦ください。

## 手順や本論
フォルダ構成とファイルは以下になります。
appspec.ymlとtaskdef.jsonは途中で書くため、最初には記載しません。

```:フォルダ構成
tech_press/
　├ backend/
　│　├ app/
　│　│   ├ main.py
　│　│ 　└ requirements.txt
　│　└ Dockerfile
　├ pipeline/
　│　├ dev/
　│　│   ├ appspec.yml
　│　│ 　└ taskdef.json
　│　└ product/
　│　    ├ appspec.yml
　│　  　└ taskdef.json
　├ .env.dev
　├ .env.local
　├ .env.product
　├ buildspec.yml
　└ docker-compose.yaml

```

```python:tech_press/backend/app/main.py
import os
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/check")
async def root():
    container_env = os.environ['CONTAINER_ENV']
    return {"Check": container_env + " is healthy"}
```
```txt:tech_press/backend/app/requirements.txt
fastapi==0.75.0
uvicorn==0.16.0
```
```Dockerfile:tech_press/backend/Dockerfile
FROM tiangolo/uvicorn-gunicorn:python3.7

COPY ./app /app

RUN pip install -U pip &&\
    pip install --no-cache-dir -r /app/requirements.txt

ARG CONTAINER_ENV

ENV CONTAINER_ENV=${CONTAINER_ENV}
```

```env:tech_press/.env.dev
# COMMON
TIMEZONE=Asia/Tokyo

# API
API_HOST=0.0.0.0
API_PORT=8080

CONTAINER_ENV=Devel
```

```env:tech_press/.env.local
# COMMON
TIMEZONE=Asia/Tokyo

# API
API_HOST=0.0.0.0
API_PORT=8080

CONTAINER_ENV=Local
```

```env:tech_press/.env.product
# COMMON
TIMEZONE=Asia/Tokyo

# API
API_HOST=0.0.0.0
API_PORT=8080

CONTAINER_ENV=Product
```

```yaml:tech_press/docker-compose.yaml
version: '3.8'
services:
  fastapi:
    container_name: tech-press-api
    image: tech-press-api
    build:
      context: ./backend
      dockerfile: Dockerfile
      args:
        - CONTAINER_ENV
    volumes:
      - ./backend/app:/app
    environment:
      - TZ=${TIMEZONE}
    command: uvicorn main:app --reload --host ${API_HOST} --port ${API_PORT}
    ports:
      - ${API_PORT}:${API_PORT}
```

### 1.まずはローカルで実行
まずはローカル環境下でFastAPIを立ち上げます。

(初回実行時)
```shell:
docker compose --env-file .env.local up -d --build
```
(2回目以降)
```shell:
docker compose --env-file .env.local up -d 
```

下記画像のように
http://localhost:8080/check に
「{"Check":"Local is healthy"}」のJSONレスポンスが返ってきたらOKです
![コメント 2023-01-24 132745.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/ff6015cc-1408-d1b3-be39-000ca7b5ae7c.png)

### 2.CodeCommit リポジトリ作成
CodeCommit -> リポジトリを作成 でリポジトリを作成します
今回は「tech-press」というリポジトリを作成しました
![コメント 2023-01-24 135410.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/c1d422cf-f841-aa46-3163-3a9629c2c0de.png)


### 3.開発環境パイプラインを構築
ここからは、まず開発環境を構築していきます。

#### 3-1.開発ECRのリポジトリ作成
Elastic Container Registry　-> 「リポジトリを作成」でリポジトリを作成します。
![コメント 2023-01-25 100743.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/0786bca7-c122-3d38-c051-0c93610957c2.png)


他の設定はデフォルトのまま、「リポジトリを作成」を押下します。

次にCodeBuildからECRリポジトリにアクセス許可を与える設定をする必要があります。
Elastic Container Registry　-> 作成したリポジトリ(今回でいうと「dev-tech-press-api」)の横にあるラジオボタンを選択 -> アクション -> 許可 -> ポリシーJSONの編集 を選択し、以下を追加します。

```json:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CodeBuildAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<AWSアカウントID>:root",
        "Service": "codebuild.amazonaws.com"
      },
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ],
      "Condition": {
        "StringEquals": {
          "aws:SourceArn": "arn:aws:codebuild:ap-northeast-1:<AWSアカウントID>:project/dev-tech-press",
          "aws:SourceAccount": "<AWSアカウントID>"
        }
      }
    }
  ]
}
```
Conditionのaws:SourceArnのプロジェクト名はまだ作成していないので、
ここで付けた名前をCodeBuildを作成するときのプロジェクト名にしてください(今回でいうと「dev-tech-press」)

#### 3-2.buildspec.ymlの作成
まずはビルドが成功しているかどうかを見たいので、artifacts以下はコメントアウトしております。
デプロイフェーズで再度コメントアウトを外します

```yml:tech_press/buildspec.yml
version: 0.2
env:
  variables:
    DOCKERHUB_USER: "<Docker Hubのユーザー名>"
    DOCKERHUB_PASS: "<Docker Hubのパスワード>"

phases:
  install:
    runtime-versions:
      docker: 20
  pre_build:    
    commands:
      # ECR へのログイン
      - echo Logging in to Amazon ECR...
      - $(aws ecr get-login --no-include-email --region ap-northeast-1)
      # DockerHub へのログイン(pull回数制限回避のため)
      - echo Logging in to Docker Hub...
      # ID/PWはAWS Systems Manager > パラメータストアを参照
      - echo ${DOCKERHUB_PASS} | docker login -u ${DOCKERHUB_USER} --password-stdin
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - TAG_HOST=<AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
      - TAG_NAME=dev-tech-press-api
      - LOCAL_IMAGE_NAME=tech-press-api
      - TAG_VERSION=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 8)
      - docker-compose --env-file .env.dev up -d
      - docker tag ${LOCAL_IMAGE_NAME}:latest ${TAG_HOST}/${TAG_NAME}:${TAG_VERSION}
      - docker tag ${LOCAL_IMAGE_NAME}:latest ${TAG_HOST}/${TAG_NAME}:latest
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker images
      - docker push ${TAG_HOST}/${TAG_NAME}:${TAG_VERSION}
      - docker push ${TAG_HOST}/${TAG_NAME}:latest
      - printf '{"ImageURI":"%s"}' ${TAG_HOST}/$TAG_NAME:latest > imageDetail.json
# artifacts:
#     files:
#       - ${TASK_DEF_PATH}
#       - ${APPSPEC_PATH}
```

#### 3-3.CodeBuildの構築
次にBuild環境の構築をしていきます。
CodeBuild -> 「ビルドプロジェクトを作成する」でビルドプロジェクトを作成します。
![コメント 2023-01-24 140426.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/39e511c0-424b-7de4-c563-faf0e2cf9629.png)

プロジェクト名は「dev-tech-press」です。
※先ほど作成したECRリポジトリのポリシーJSONのプロジェクト名と一致させてください
![コメント 2023-01-24 140437.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/c5050e68-57e4-e216-dc7e-c3cdbf545d06.png)

ソースは先ほど作成した「tech-perss」リポジトリを選択します。
ブランチは現時点では「master」ブランチのままにしておき、後ほど変更します。
![コメント 2023-01-24 142714.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/2f1157a7-625c-7b67-d6af-998b31884c79.png)


環境はマネージド型イメージを使用します。
他、細かい部分は画像にある通りです。ロール名はデフォルトのまま変更していません。

![コメント 2023-01-24 140841.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/7fb6100b-d0c2-7a6f-e086-adad12a3d7b8.png)

Buildspecでは、「buildspecファイルを使用する」を選択し、Buildspec名は「buildspec.yml」と記載します。

その他の設定はデフォルトのままで、「ビルドプロジェクトを作成する」を押下します。
![コメント 2023-01-24 141036.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/9348df2b-7995-1abf-f622-291d9f76dd6f.png)


ビルドプロジェクトが作成されたので、一度「ビルドを開始」を押下して、ビルドを開始しましょう。
すると、以下の画像のようにビルドが失敗します。
```error:エラー文
COMMAND_EXECUTION_ERROR: Error while executing command: $(aws ecr get-login --no-include-email --region ap-northeast-1). Reason: exit status 255
```
![コメント 2023-01-24 153614.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/25893741-aac1-9de6-29ee-af2caf6c25eb.png)


理由は「環境」で新しく作成したロールにECRのポリシーが設定されていないためです。
![コメント 2023-01-24 143233.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/11472854-e8f1-e67d-302e-8dea558e774e.png)

先ほど作成したロール(codebuild-dev-tech-press-service-role)に「AmazonEC2ContainerRegistryPowerUser」ポリシーを追加します。
![コメント 2023-01-24 143458.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/8ee47dc3-fde2-cf42-36ad-30d5bed81d84.png)

この状態になっていればOKです。
再度、ビルドを開始します。
![コメント 2023-01-24 153914.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/037895f6-f7b9-24ad-e0a1-47325407c318.png)


成功していればビルドフェーズはOKです。


#### 3-4.ECS on Fargate + CodeDeployの構築
##### 3-4-1. クラスターの作成

まずはクラスターを作成します。
Elastic Container Service -> クラスター -> クラスターの作成 でクラスター作成画面に遷移します。

「ネットワーキングのみ」を選択して「次のステップ」を押下します
![コメント 2023-01-24 154451.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/a84d0484-1303-fcd8-b019-8ca12af0b341.png)

クラスター名は「dev-tech-press」とし、その他はデフォルトのままとします。
![コメント 2023-01-24 154613.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/920e138a-bc2a-21cd-0590-e53ea5e2bc25.png)

「作成」を押下するとクラスターができます
![コメント 2023-01-24 154757.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/eb8a1467-dea2-8d9d-3187-8c22ff7cef0f.png)


##### 3-4-2. タスク定義の作成

次に新しいタスク定義を作成します。
Elastic Container Service -> タスク定義 -> 新しいタスク定義の作成 で新しいタスク定義を作成します。
Fargateを選択し、「次のステップ」を押下します。

![コメント 2023-01-24 161520.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/d5a49e00-e250-85f9-1c80-774a00f71027.png)

タスク定義名は「dev-tech-press」とし、オペレーティングシステムファミリーは「Linux」を選択します。
![コメント 2023-01-24 161451.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/d2bae4c1-52df-3a8c-c2dd-e343d0e620bc.png)


タスクサイズはタスクメモリを「0.5GB」・タスクCPUを「0.25vCPU」とします。
※必要に応じてスペックを調整します

次にコンテナの設定をします。
![コメント 2023-01-25 092702.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/8a0c0016-6f1a-2621-1080-18617b04fa83.png)

コンテナ名はdocker-compose.yamlで定義しているコンテナ名を使用し、イメージは「<ECRリポジトリのURI>+:latest」とします。
ポートマッピングは80番に設定します。
そのほかの設定はデフォルトのままで「追加」を押下します。

その他のタスク定義もデフォルトのままで「作成」を押下します。

タスク定義が完成したら、タスク定義 -> <作成したタスク定義名> -> <作成したタスク定義名>:1 -> JSON を押下します
背景がグレーになっている部分がタスク定義のJSONなので、pipeline/dev　直下にtaskdef.jsonを作成し、背景がグレーになっている部分をコピーし、codecommitにプッシュします。

##### 3-4-3. サービス・CodeDeployの作成
次にサービスを作成し、その過程でCodeDeployも同時に作成します。
まずは、サービスにアタッチするロールにCodeDeployの権限を付与する必要があります。
IAM -> ロール -> ロールの作成 -> でロールの作成画面に遷移します。そこから、
AWSのサービス -> ユースケースを表示するサービスを選択する -> 「CodeDeploy」と検索 -> 「CodeDeploy - ECS」をチェック -> 次へ -> 次へ とします。
ロール名は「TechPressCodeDeployRoleForECS」とします。
![コメント 2023-01-24 183036.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/600e9a87-1c04-af08-0a76-7479aa21e0fe.png)

「ロール名を作成」でロールを作成します。

次に、サービスを作成します。
Elastic Container Service -> 先ほど作成したクラスター(今回でいうと「dev-tech-press」) -> 「サービス」タブを選択した状態で「作成」を押下 でサービスの作成画面に遷移します。
![コメント 2023-01-24 184148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/952cdfd8-8e4d-b92b-0d0d-35a582f94827.png)

サービスの設定は
起動タイプ：Fargate
タスク定義：dev-tech-press
サービス名：dev-tech-press-service
タスク数：1
と、そのほかはデフォルトで設定します。

![コメント 2023-01-24 184202.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/ceae0d7d-f9ed-ff86-35e5-6b019c8fc844.png)

デプロイメントは
・Blue/Greenデプロイメントを選択
・CodeDeployのサービスロールは先ほど作成した「TechPressCodeDeployRoleForECS」を選択します。

VPCとセキュリティグループはこの記事で作成していないので、割愛します。
パブリック IP の自動割り当ては「ENABLED」を選択します。

ロードバランシングはALBを選択します。

![コメント 2023-01-24 185047.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/eeeead77-ef56-d81a-19b9-e80b45bf0e76.png)

ここでロードバランサを作成する場合は、ロードバランサのリスナーの設定をなしにしましょう。(サービス作成時に自動的に作成されるため)
![コメント 2023-01-24 185214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/a1e8c023-c4a6-e695-d060-012804f7f1c1.png)

また、ロードバランシングの上にあるヘルスチェックもここで変更します。
![コメント 2023-01-24 185442.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/057d2a98-73c0-34cc-6192-31b9db9b80e7.png)

次にロードバランス用のコンテナで「ロードバランサーに追加」を押下し、
![コメント 2023-01-24 185522.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/3d3bab1a-bc75-40fb-fad2-99d696e03ebb.png)

ロードバランス用のコンテナのプロダクションリスナーポートを「80」に設定します。
![コメント 2023-01-24 185634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/26a278d4-3bfb-41eb-b9a9-98d97a541f70.png)

Additional configurationは
ターゲットグループ 1 の名前：dev-tech-press-01
ターゲットグループ 2 の名前：dev-tech-press-02
にし、そのほかはデフォルトのまま設定し、「次のステップ」を押下します
![コメント 2023-01-24 190001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/569c5a34-809e-4412-0d62-7d6c6073631e.png)

Auto Scalingは特に設定しません。次のステップに行きます

内容を確認し、「サービスの作成」を押下します。

7つのサービス作成ステータスが成功していればOKです。
![コメント 2023-01-24 190247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/0bbc370a-2056-ad6b-eb9b-f07ea85ee367.png)
![コメント 2023-01-24 190256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/a8b83e36-952d-09f5-f0c2-c1dde071e742.png)

しばらくすると、サービスにてタスクが実行されています。
![コメント 2023-01-24 190426.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/ae6ba7f0-8ae8-6206-4f45-44aa4466ad09.png)


タスク画面に遷移すると、
![コメント 2023-01-25 093650.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/ad9d0620-13d5-9632-3c37-136a97599cdf.png)


パブリックIPが見れます。
このIPアドレスを開くと
http://<パブリックIP>/check

![コメント 2023-01-25 093802.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/9740323e-fbc5-703a-8ded-9771c73f8ab0.png)


開発環境の環境変数である「Devel」が反映されていればOKです。


##### 3-4-4. CodePipelineの構築
開発環境の最後にCodePipelineの構築をします。
CodePipeline -> パイプラインを作成する でパイプラインの構築画面に遷移します。
パイプラインの名前を「dev-tech-press」とします。
![コメント 2023-01-24 191244.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/6bfb7c23-eb70-a1c3-bfc8-226a40effbfe.png)


ソースステージは
・ソースプロバイダー：AWS CodeCommit
・リポジトリ名：tech-press
・ブランチ名：master
とします。
※ブランチは後ほど開発用を作成します。
![コメント 2023-01-24 191358.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/1b58029c-3d4d-72bc-a8d2-4737ae46cee3.png)


ビルドステージは
・プロバイダー：AWS CodeBuild
・プロジェクト名：dev-tech-press
・環境変数を
TECH_PRESS_ECR_NAME：dev-tech-press-api
ENV_FILE：./.env.dev
TASK_DEF_PATH：pipeline/dev/taskdef.json
APPSPEC_PATH：pipeline/dev/appspec.yml
と設定します。
![コメント 2023-01-24 192033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/1b49d6ed-a0ad-b6b1-38ae-47622afdb403.png)


デプロイステージは
・デプロイプロバイダー：Amazon ECS(ブルー/グリーン)
・アプリケーション名：サービス作成時に構築されたもの
・デプロイグループ：サービス作成時に構築されたもの
Amazon ECS タスク定義は「BuildArtifact」を選択し、「pipeline/dev/taskdef.json」
AWS CodeDeploy AppSpec ファイルは「BuildArtifact」を選択し、「pipeline/dev/appspec.yml」とします。
![コメント 2023-01-24 192509.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/ba507300-f7f3-502e-a007-7526e7b0a6bc.png)



最後にレビューを確認し、パイプラインを作成します。

作成した後、パイプラインが自動で起動しますが、この段階ではDeploy時にエラーが発生します。理由はbuildspec.ymlがビルド段階までのソースになっているからです。
buildspec.ymlを変更します

```yml:tech_press/buildspec.yml
version: 0.2
env:
  variables:
    DOCKERHUB_USER: "<Docker Hubのユーザー名>"
    DOCKERHUB_PASS: "<Docker Hubのパスワード>"

phases:
  install:
    runtime-versions:
      docker: 20
  pre_build:    
    commands:
      # ECR へのログイン
      - echo Logging in to Amazon ECR...
      - $(aws ecr get-login --no-include-email --region ap-northeast-1)
      # DockerHub へのログイン(pull回数制限回避のため)
      - echo Logging in to Docker Hub...
      # ID/PWはAWS Systems Manager > パラメータストアを参照
      - echo ${DOCKERHUB_PASS} | docker login -u ${DOCKERHUB_USER} --password-stdin
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - TAG_HOST=<AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
      - TAG_NAME=${TECH_PRESS_ECR_NAME}
      - LOCAL_IMAGE_NAME=tech-press-api
      - TAG_VERSION=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 8)
      - docker-compose --env-file ${ENV_FILE} up -d
      - docker tag ${LOCAL_IMAGE_NAME}:latest ${TAG_HOST}/${TAG_NAME}:${TAG_VERSION}
      - docker tag ${LOCAL_IMAGE_NAME}:latest ${TAG_HOST}/${TAG_NAME}:latest
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker images
      - docker push ${TAG_HOST}/${TAG_NAME}:${TAG_VERSION}
      - docker push ${TAG_HOST}/${TAG_NAME}:latest
      - printf '{"ImageURI":"%s"}' ${TAG_HOST}/$TAG_NAME:latest > imageDetail.json
artifacts:
    files:
      - ${TASK_DEF_PATH}
      - ${APPSPEC_PATH}
```

buildspec.ymlを変更し、プッシュし、再度「dev-tech-press」パイプラインを走らせます。
![コメント 2023-01-24 195236.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/0c5f3f4d-d298-fc91-683d-9f79b0dad856.png)

この段階でパイプラインが成功していれば、一旦開発パイプラインの構築は完了です。


### 4.本番環境パイプラインを構築
開発環境と構築方法は同じなので、変更している名前等を書き記します。

#### 4-1.ECRのリポジトリ作成
リポジトリ名：product-tech-press-api 

#### 4-2.buildspec.ymlの作成
ここで作成するbuildspec.ymlは3-4-4.で作成したものでOKなので何もしなくて大丈夫です

#### 4-3.CodeBuildの構築
プロジェクト名：product-tech-press
他の設定は3-3と同様です
また、この段階で「ビルドを開始」は不要です

#### 4-4.ECS on Fargate + CodeDeployの構築

##### 4-4-1. クラスターの作成
クラスター名：product-tech-press

##### 4-4-2. タスク定義の作成
タスク定義名：product-tech-press
イメージ：「<本番ECRリポジトリのURI>+:latest」

##### 4-4-3. サービス・CodeDeployの作成
ロールは3-4-3で作成した「「TechPressCodeDeployRoleForECS」でOKです
タスク定義：product-tech-press
サービス名：product-tech-press-service
ロードバランサ名：product-tech-press
ターゲットグループ 1 の名前：product-tech-press-01
ターゲットグループ 2 の名前：product-tech-press-02
この段階でサービスは立ち上がりますが、タスクは起動→停止を繰り返します。
パイプラインを構築することで、解消されます。

##### 4-4-4. CodePipelineの構築
**ビルドステージ**
プロジェクト名：product-tech-press
環境変数を
TECH_PRESS_ECR_NAME：product-tech-press-api
ENV_FILE：./.env.product
TASK_DEF_PATH：pipeline/product/taskdef.json
APPSPEC_PATH：pipeline/product/appspec.yml

**デプロイステージ**
AWS CodeDeploy アプリケーション名：AppECS-product-tech-press-product-tech-press-service

AWS CodeDeploy デプロイグループ：DgpECS-product-tech-press-product-tech-press-service

3-4と同様に構築ができれば最終的にパイプラインが成功するかと思います。
Elastic Container Service -> 本番用クラスター ->  サービス名 -> タスク で、タスクが正常に動いていたらOKです
![コメント 2023-01-24 204911.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/dafaf055-2f9d-ae89-90cd-6e2e43b37bed.png)


前回のステータスが「RUNNING」が作動していたらタスクが正常に動いています。
本番用のパブリックIPで本番用の環境変数が適用されているか確認します。
![コメント 2023-01-25 094729.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/b520bf61-8ee5-a482-a5f3-583cf27496d9.png)

問題なく動いているので、本番用のパイプラインの構築も完了です

### 5.パイプラインのトリガーをブランチ毎に変更

最後に、今の状態だと両パイプラインがmasterブランチが変更するとパイプラインが起動する設定になっています。
「staging」ブランチと「production」ブランチを作成し、「staging」ブランチに変更がかかると開発パイプラインが、「production」ブランチに変更がかかると本番パイプラインが起動する設定に変更します。

まずは、stagingブランチとproductionブランチを作ります
![コメント 2023-01-24 210248.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/5fde632c-8822-186f-47d2-ef84bf55a337.png)


次に開発のパイプラインの設定をします。
CodePipeline -> 開発用パイプライン -> 編集 -> Sourceの「ステージを編集する」 -> 編集のアイコンをクリック -> ブランチ名を「staging」に変更 -> 完了 -> 保存
![コメント 2023-01-24 210635.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/27f052ac-958a-b108-2f73-ea90825eeb44.png)

本番パイプラインも設定をします。
![コメント 2023-01-24 210832.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/50cd1c6b-bad6-6e28-0d02-a08af22a6f86.png)


これで「staging」ブランチに変更がかかると開発パイプラインが、「production」ブランチに変更がかかると本番パイプラインが起動する設定に変更されました。

最後に実際に変更がかかるのか、プルリクエストをして確認します。
まずは開発機の変更を確認するためのブランチ、「develop/開発パイプライン確認ブランチ」を作成します。
「develop/開発パイプライン確認ブランチ」のブランチには以下の変更を加えます

```diff_apache:tech_press/.env.dev
# COMMON
TIMEZONE=Asia/Tokyo

# API
API_HOST=0.0.0.0
API_PORT=8080

- CONTAINER_ENV=Devel
+ CONTAINER_ENV=開発
```

プッシュして、プルリクエストを「staging」ブランチに投げ、マージします
![コメント 2023-01-24 211455.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/7ac59528-53f1-efd6-f482-1d692278927c.png)


パイプラインが終了すると、環境変数が変更されているのが分かります。
![コメント 2023-01-25 095750.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/8a03e0ad-13ab-e4fc-a879-3798b9e1677c.png)


同様に「develop/本番パイプライン確認ブランチ」を作成し、.env.productを変更し、「production」にマージします
```diff_apache:tech_press/.env.product
# COMMON
TIMEZONE=Asia/Tokyo

# API
API_HOST=0.0.0.0
API_PORT=8080

- CONTAINER_ENV=Product
+ CONTAINER_ENV=本番
```

本番も反映されていますね。めでたしめでたし。

![コメント 2023-01-25 095833.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388107/aedcc640-112c-6d55-7f5d-091c93f3b1e9.png)



### おわりに

気が付いたら、中々のボリュームになっていましたｗ
今回は省きましたが、所属しているチームではパイプラインにテストを入れていたり、承認を入れたりして各フェーズでのチェックを行っています。
CI/CDは構築されたらその後は基本的に触らないため、今回の執筆でも思い出す部分が多かったです。

まだまだ1年目で浅くしか経験していないので、知識をより深めていきたいです！