ローカルのDockerコンテナをEC2へデプロイしてみる

## やりたいこと
ローカルで構築したDockerコンテナをEC2上にデプロイして実行する

## 材料
* EC2
* FSx　for windows
* Docker
* Amazon Elastic Container Registry (ECR)
* ローカルGo環境
* AWS CLI

## レシピ

### 1.AWS CLIをインストール

```
# 例：Macでhomebrewを使用
% brew install awscli
```

### 2.EC2を作成
![スクリーンショット 2024-05-18 11.01.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/db88bfdc-cea0-f680-a429-cfb8551c59d8.png)

キーペアを作成した場合はダウンロードしたpemファイルのパーミッションを変更
（ローカル環境）
```
% chmod 400 keypar.pem
```

### 3.必要なパッケージをインストール
（EC２）

```
# Amazon Linux 2023でDockerとAWS CLIのインストール
$ sudo yum update -y
$ sudo yum install -y docker
$ sudo service docker start
$ sudo usermod -aG docker ec2-user
$ sudo yum install -y aws-cli cifs-utils
```

### 4.ローカルGO環境構築
Dockerfileを作成します

```dockerfile
# ベースイメージとして公式のGoイメージを使用
FROM golang:1.20

# 作業ディレクトリを作成
WORKDIR /app
```

次に、docker-compose.ymlを作成します。

```dockerfile
version: '3'
services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - "8080:8080"
```

適当なgoファイルを作成
今回は、いつものHello Worldを出力する

```go
package main

import (
    "fmt"
)

func main() {
    fmt.Printf("Hello World\n")
}
```



コンテナをビルドします。
ビルドできたらコンテナに入ります。

``` sh
% docker-compose build
% docker-compose run --rm app sh
```

goの初期化を実施。

```sh
$ go mod init backend
$ exit
```

goの初期化ができたら、Dockerfileに追記します。

```dockerfile
# ベースイメージとして公式のGoイメージを使用
FROM golang:1.20

# 作業ディレクトリを作成
WORKDIR /app

# ここから追記
# ソースコードをコンテナにコピー
COPY . .

CMD ["go", "run", "."]
```

### 5.ECRリポジトリ作成
AWS コンソールに戻り、ECRレポジトリを作成します。
適当な名前を登録します。

![スクリーンショット 2024-05-18 12.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/7bdfe3f9-ca28-4571-c04b-141bdf593c95.png)

リポジトリが作成されました。
![スクリーンショット 2024-05-18 12.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/ac5e6cc7-0005-3480-f114-abcc10d0be71.png)



### 6.IAMロール作成
次にECR用のIAMロールを作成します。
サービスはEC2を選択。
![スクリーンショット 2024-05-18 12.10.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/719dd9f8-4c5e-3cd7-3d38-52462f03d6f7.png)

許可ポリシーにAmazonEC2ContainerRegistryFullAccessを選択。
![スクリーンショット 2024-05-18 12.11.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/09cc1fe9-1606-31b7-1c16-a91eb931e16d.png)

適当なロール名を入力して、作成します。
![スクリーンショット 2024-05-18 12.12.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/c0e9eca5-af5a-49c0-926f-09aaa95a9a94.png)

### 7.作成IAMロールをEC2にアタッチ

作成したIAMロールをEC2にアタッチします。
![スクリーンショット 2024-05-18 12.12.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/f95f5bd0-4e3d-1e18-9111-803c47b41a89.png)

アタッチされたことを確認。
![スクリーンショット 2024-05-18 12.13.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/c252dc90-0a51-e8e4-0313-9d791c4b8afa.png)


### 8.DockerイメージをECRにプッシュ
ローカル環境にて行います。

まずは、Dockerへの認証

```
% aws ecr --region ap-northeast-1 get-login-password | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server
```

dockerイメージをビルドします。

```
% docker image build -t batch-server:v1 .
```

コンテナイメージにタグ付けします。

```
% AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query  'Account' --output text)
% docker image tag batch-server:v1 ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server:v1
```

ECRへコンテナイメージを登録。

```
% docker image push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server:v1
```

プッシュされたイメージを確認
![スクリーンショット 2024-05-18 12.43.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/63c83468-0490-9330-36f0-6806e69db617.png)


### 9.EC2上でdockerイメージをデプロイ
EC2で行います。
EC２でもDockerへの認証を行います。

```
$ aws ecr --region ap-northeast-1 get-login-password | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server
```

***
:::note warn
このDockerへの認証を忘れるとエラーになります。
:::

```
Error response from daemon: Head "https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/v2/batch-server/manifests/v1": no basic auth credentials
```

***

念のため、ビルド済みコンテナイメージの削除

```
$ docker image rm -f $(docker image ls -q)
```

***
:::note warn
docker コマンドでエラーになった場合
:::

sockに権限がないと怒られる
```
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/json": dial unix /var/run/docker.sock: connect: permission denied
```

sockのパーミッションを変更
```
$ sudo chmod 666 /var/run/docker.sock 
```
***

ECRのイメージをEC２へPULLします。
```
$ docker image pull ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server:v1
```

イメージを確認

```
$ docker images
REPOSITORY                                                       TAG       IMAGE ID       CREATED             SIZE
${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server   v1        df9fb8e2e9ed   About an hour ago   852MB
```

イメージからコンテナを実行

```
$ docker container run -it --rm ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server:v1
```

***
:::note warn
エラー
:::

```
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64/v3) and no specific platform was requested
exec /usr/local/go/bin/go: exec format error
```

どうやらM１ Macの環境のコンテナをそのままEC2上では実行できないらしい
手順８のビルドを以下に変更

```
$ docker image build --platform amd64 -t batch-server:v1 .
```
手順をやり直します。
***

コンテナを実行します。

```
$ docker container run -it --rm ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/batch-server:v1
```

無事にコンテナを実行して結果が出力されました。

```
Hello World
```