1つのリポジトリから複数のCloudRunにデプロイする構成考えた
はじめに
「Goで1つのリポジトリで2つのAPIを実装し、そのAPIをそれぞれ別のCloudRunにデプロイするようなことができるのか?」ということが気になり、実際にできるのか試してみました。
やりたいことはできたので、どういう方法で実現したのかについて記録に残します。
なぜこのような構成を考えたのか
「複数のAPIがあり、そのAPI群を任意の単位で分割して複数のCloudRunにデプロイする運用をしたい。しかしアプリケーションのコードは一つのリポジトリで管理したい。」
このような課題に対して、解決方法の1つになるかもしれないと思い、考えてみた次第です。
リポジトリ
実際に実装したリポジトリはこちらに用意してあります。
https://github.com/choimake/multi-deploy-by-cloudbuild
使用した技術
- Go 1.17.11
- Docker
- Cloud Run
- Cloud Build
要件
以下のような要件があるものとして、構成を考えます。
- Fizz Buzzを実行するAPIを2つのCloudRun(片方をalpha、もう片方をbetaとする)でデプロイしたい
- Fizz Buzzはalpha、beta共に同じロジックを利用したい
- 各APIのレスポンスは、alphaは
alpha: {Fizz Buzzの結果}
、betaはbeta: {Fizz Buzzの結果}
としたい
実現方法
alpha用のエントリーポイント(main.go)とbeta用のエントリーポイントを用意し、そのエントリーポイントをそれぞれ別のCloud Runへデプロイする方法を取りました。
パッケージ構成
具体的に、以下のようなパッケージ構成にしました。
.
| # Imageに関する処理を置くところ。alphaとbetaそれぞれのものを用意。
├── build
│ ├── alpha
│ │ └── Dockerfile
│ └── beta
│ └── Dockerfile
|
| # エントリーポイントの置くところ。alphaとbetaそれぞれのものを用意。
├── cmd
│ ├── alpha
│ │ └── main.go
│ └── beta
│ └── main.go
|
| # デプロイスクリプトを置くところ。alphaとbetaそれぞれのものを用意。
├── deployment
│ ├── alpha
│ │ └── cloudbuild.yaml
│ └── beta
│ └── cloudbuild.yaml
|
| # アプリケーションの処理を置くところ。alpha、beta双方で使うFizz Buzzのコードを置いている。
└── pkg
└── fizzbuzz
└── fizzbuzz.go
コードの内容
cmd
エントリーポイントです。
fuzzbizzの処理を呼び出し、alpha: {Fizz Buzzの結果}
orbeta: {Fizz Buzzの結果}
というフォーマットで表示する処理を実装しています。
alphaとbetaの違いは表示処理の部分のみです。
package main
import (
"fmt"
"log"
"multi-deploy/pkg/fizzbuzz"
"net/http"
"os"
"strconv"
)
func handler(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
n := q.Get("n")
num, _ := strconv.Atoi(n)
fmt.Fprintf(w, "alpha: %s\n", fizzbuzz.Fizzbuzz(num))
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/fizzbuzz", handler)
err := http.ListenAndServe(":"+port, nil)
fmt.Printf("[START] server. port: %s\n", port)
if err != nil {
log.Fatal(err)
}
}
build
Dockerfileは以下になります。
alphaとbetaの違いはRUN go build ~
の部分だけです。
FROM golang:1.17.8-bullseye as builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
RUN go build multi-deploy/cmd/alpha && \
mv alpha server
FROM gcr.io/distroless/base:latest
COPY /app/server /server
# start server
CMD ["/server"]
pkg/fizzbuzz
FizzBuzzの実装処理です。alpha、beta共にこの処理を利用しています。
package fizzbuzz
import "strconv"
func Fizzbuzz(number int) string {
if number%15 == 0 {
return "Fizz Buzz"
}
if number%5 == 0 {
return "Buzz"
}
if number%3 == 0 {
return "Fizz"
}
return strconv.Itoa(number)
}
deployment
CloudBuildの設定ファイルを配置します。
alpha用、beta用それぞれ用意し、alphaにデプロイする際にはalphaの設定を、betaにデプロイする際にはbetaの設定を利用します。
alphaはこちら
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/${PROJECT_ID}/alpha', '-f', 'build/alpha/Dockerfile', '.']
# Push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/${PROJECT_ID}/alpha']
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: [ 'run', 'deploy', 'service-alpha', '--allow-unauthenticated', '--image', 'gcr.io/${PROJECT_ID}/alpha', '--region', 'asia-northeast1' ]
betaはこちらです
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/${PROJECT_ID}/beta', '-f', 'build/beta/Dockerfile', '.']
# Push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/${PROJECT_ID}/beta']
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: [ 'run', 'deploy', 'service-beta', '--allow-unauthenticated', '--image', 'gcr.io/${PROJECT_ID}/beta', '--region', 'asia-northeast1' ]
alphaとbetaの違いは
- 使用するDockerfile
- 指定するサービス名
になります。
デプロイ方法
alphaをデプロイしたい時は、alphaの設定でデプロイを実行します。
gcloud builds submit --config=deployment/alpha/cloudbuild.yaml .
betaをデプロイしたい時は、betaの設定でデプロイすればOKです。
gcloud builds submit --config=deployment/alpha/cloudbuild.yaml .
この構成によって何が嬉しいのか
- デプロイ単位でリポジトリを分ける必要がなくなる
- 1つのリポジトリに存在する複数の処理を、任意の単位でデプロイできる
おわりに
もしかしたら当たり前にあるテクニックなのかも知れませんが、自分の周りでは見た事なかったので試しに作ってみました。
何かの参考に立てば幸いです。
Discussion