【はじめての】Google CloudでDocker コンテナイメージを管理する

# はじめに
こんにちは！masa-asaです。
クラウドサービスを用いて開発を行っていると、手元で開発したコードをDockerイメージにビルドしてそれをクラウド上のサーバーで実行したい。というニーズはよく出てくることと思います！
そこで今回はGoogle Cloud（GC）でコンテナイメージを管理し、どのようにクラウド上でデプロイするかについて見ていきたいと思います！

# Artifact Registry と Container Registry
GCではコンテナイメージを管理するためのサービスとしてArtifact Registry（AR） と Container Registry（CR）の2つがあります。従来利用されていたものがContainer Registryで、このサービスを拡張したものがArtifact Registryです。
そのため、今から利用する場合はArtifact Registryを利用します。
Container Registryは**非推奨**で、**2025 年 3 月 18 日以降サービスが終了します。**

https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr?hl=ja

Artifact Registry はアーティファクトという名前が示すように、Dockerコンテナイメージのみを管理するサービスではなく、Dockerイメージを含めた多種多様な（何らかの成果物としての）モノを保存・管理できるサービスです。Dockerコンテナイメージはもちろん、npmやaptで管理するパッケージなどを保存・管理できます。

# 前提条件
 - Dockerの導入：DockerをWSLに導入している必要があります

 https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository

- Node.jsとnpmの導入：asdfでNode.js 20.18.0を導入しています

https://asdf-vm.com/guide/getting-started.html

- サンプルアプリの準備：次のコマンドで、サンプルのプロジェクトを作成しました
`npx create-react-app my-react-app`

# Artifact Registryの作成
terraformを使ってArtifact Registryの作成を行います。コードは次通りです。

```terraform
# ./modules/artifact_registry/main.tf
resource "google_artifact_registry_repository" "docker-repo" {
  location      = "asia-northeast1"
  repository_id = "ar-repo-asa-poc"
  description   = "example docker repository"
  format        = "DOCKER"


  docker_config {
    immutable_tags = true
  }
}

```

```terraform
# ./main.tf
module "artifact_registry" {
    source = "./modules/artifact_registry"
}

```

```terraform
# ./provider.tf
provider "google" {
  project = "my-sandbox"
}

```

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/artifact_registry_repository

実行結果です。ARが作成できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/07d9a6b4-d9f7-4eed-b155-3ffa6c93ff6f.png)


# Dockerコンテナイメージのプッシュ
Artifact Registryが作成できたら、手元のDockerイメージをARにプッシュします。
ARにアップロードするソースイメージに対して、アップロード先のARの情報を含んだイメージ名を付けてあげる必要があります。また、識別のためにタグも付けます。そのために次のようなコマンドを実行します。

- `docker tag mytest asia-northeast1-docker.pkg.dev/my-sandbox/ar-repo-asa-poc/docker-image-asa-poc:latest`

上記コマンドについて、ARでDockerのリポジトリを扱う場合には、対象のARのリポジトリ名を用いる必要があります。命名は次のような形式で表されます。
- `LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY`
  - LOCATION:ARのリソースが存在するロケーションです。ここでは上のコマンドでは「asia-northeast1」が相当します
  - PROJECT-ID:ARのリソースが存在するプロジェクトのDです。GCのホーム画面から確認できます。注意すべきは数字で構成されたプロジェクト番号**ではない**ということです。コマンドの「my-sandbox」に相当します
  - REPOSITORY:ARのリポジトリ名で、作成したARの名前です。コマンドの「ar-repo-asa-poc」に相当します

また、このリポジトリにあるコンテナイメージ名は以下のようなパターンの命名で表されます。
- `LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE`
`LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE:TAG`
`LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE@IMAGE-DIGEST`
  - IMAGE:イメージはリポジトリ内のコンテナイメージ名です。コマンドの「docker-image-asa-poc」に相当します
  - TAG:イメージのタグです。コマンドの「latest」に相当します
  - IMAGE-DIGEST:イメージのsha256ハッシュ値です。今回は使用していません


### DockerイメージのARへのプッシュ
手元にあるDockerイメージをARにプッシュするには以下のコマンドを実行します。
- `docker push asia-northeast1-docker.pkg.dev/my-sandbox/ar-repo-asa-poc/docker-image-asa-poc:latest`

AR画面を見てみるとイメージがアップロードできています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/5ac31e12-c7fd-4723-b51d-d2dccd0d5f1f.png)


https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling?hl=ja

https://cloud.google.com/artifact-registry/docs/docker/names?hl=ja

# Cloud Runにアップロードする
アップロードしたイメージのURLを取得します。画面から簡単にデプロイすることもできますが、せっかくですのでterraformから作成したいと思います。

作成イメージからCloud Runにデプロイするを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/17b2cc82-6ec1-49aa-83f7-c03e828dc12b.png)

「コンテナイメージのURL」からイメージのURLを取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/f5b2534d-431a-4654-9131-8cb09a1d4f12.png)

template:containers:imageに取得したコンテナイメージのを指定します
```terraform
resource "google_cloud_run_v2_service" "default" {
    name     = "crun-sandbox-asa"
    location = "asia-northeast1"
    deletion_protection = false
    ingress = "INGRESS_TRAFFIC_ALL"
    template {
        max_instance_request_concurrency = 20
        scaling {
        max_instance_count = 1
        }
        containers {
        image = "asia-northeast1-docker.pkg.dev/my-sandbox/ar-repo-asa-poc/docker-image-asa-poc:latest"
        resources {
            limits = {
              cpu = "1"
              memory = "512Mi"
                }
            }
        }
    }
}


data "google_iam_policy" "noauth" {
  binding {
    role = "roles/run.invoker"
    members = [
      "allUsers",
    ]
  }
}

resource "google_cloud_run_service_iam_policy" "noauth" {
  location    = google_cloud_run_v2_service.default.location
  project     = google_cloud_run_v2_service.default.project
  service     = google_cloud_run_v2_service.default.name

  policy_data = data.google_iam_policy.noauth.policy_data
}

```

terraformを実行し、Cloud Runが作成できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/4a88db80-02bd-4f26-9bd5-461df7f42c0c.png)

Cloud RunのURLをたたいてみると以下のように表示されます。うまく動いていそうです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3942439/6d743f0e-ec25-40fd-b604-35a429c980c5.png)

# まとめ
今回はGoogle CloudでDockerコンテナイメージを管理する方法を見ました。内容としては以下のようなものでした。

- イメージの保存・管理にはArtifact Registryを用いる
- ARからCloud RunをはじめとするGCサービスに簡単にデプロイできる

これらのサービスはCI/CDと組み合わせることでビルド～デプロイまでをより効率的に行うことができるようになりそうです！機会があればCI/CDとの連携についても書けたらと思います。
お読みいただきありがとうございました！

# おまけ1：ARへのイメージプッシュ時の権限エラー

イメージをプッシュしようとした際に以下のようなエラーが出ました。権限が不足しているようです。
`denied: Unauthenticated request. Unauthenticated requests do not have permission "artifactregistry.repositories.uploadArtifacts"`

以下のコマンドを実行することで解決できました。指定したリージョンに対して、Dockerの認証を構成します。
`gcloud auth configure-docker asia-northeast1-docker.pkg.dev`


https://cloud.google.com/artifact-registry/docs/docker/authentication?hl=ja


# おまけ2 サンプルのDockerfile
今回のサンプルプロジェクトのDockerイメージを作成するために用いたDockerFileです。

```Dockerfile
FROM node:23-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 8080

CMD [ "npm" , "start" ]

```