Cloud RunGoで作成したAPIをデプロイしようと思ったところ、うまくいかなかった。

調査してみたところ、データーベースに接続できずに落ちているっぽいとのこと。

ちょっとなんでかわからないけど(今思い返せば、Docker起動していた? という不安がある)、ローカルのDBに接続できないので、いずれ接続する予定だったので、Cloud SQLに接続し、それでCloud Runにデプロイしてみようという風に思い立った。

Cloud SQLに登録する

Cloud Run から Cloud SQL for MySQL に接続するがある。

公式があるので、こちらを参考にする。

なお、今回サーバーはローカル(net/http)で起動することにする。

インスタンスを作成する

  1. Google Cloud コンソールで Cloud SQL の [インスタンス] (https://console.cloud.google.com/sql?hl=ja&_ga=2.145632700.968950133.1716041931-758618644.1700836939)ページに移動する。
  2. インスタンスを作成 をクリック。
  3. [MySQL を選択] をクリック。

インスタンスを作成するには、まず Compute Engine API を有効にする必要があります。

という警告が出るので、APIを有効にする。

インスタンスID、パスワード、を登録。

My SQLのバージョンを8にし、Cloud SQL のエディションはEnterpriseにする。

プリセットをいったん開発環境にして、リレージョンを東京、ゾーンをシングルに設定する。

構成オプションを開き、とりあえず一番小さい1 vCPU3.75 GBを選択してみる。

で、インスタンスを作成する。

構成オプションはとりあえず、最小で不満があればあげていく方法で問題ないらしい。

DBの作成

インスタンスのデータベースにいき、データーベースの作成をクリックする。

今回インスタンスの作成をし、データーベースの作成をしていなかったので、エラーが出続け、悩まされた。

接続を試みる

まず接続をしてみたところ、

$ go run main.go
2024/05/26 00:41:05 cloudsqlconn.NewDialer: failed to create default credentials: google: could not find default credentials. See https://cloud.google.com/docs/authentication/external/set-up-adc for more information
exit status 1

というエラーが出た。

どうやら認証情報が見つかりませんとのこと。

対象のディレクトリでgcloud auth application-default loginを実行し、ログインを完了させた。

ソースコードは公式を参考に下記のように。

package db

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net"
	"os"

	"cloud.google.com/go/cloudsqlconn"
	"github.com/go-sql-driver/mysql"
	gmysql "gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

type Model struct {
	gorm.Model
}

func mustGetenv(k string) string {
	v := os.Getenv(k)
	if v == "" {
		log.Fatalf("Fatal Error in connect_connector.go: %s environment variable not set.", k)
	}
	return v
}

func InitDB() {

	var (
		dbUser                 = mustGetenv("DB_USER")                  // e.g. 'my-db-user'
		dbPwd                  = mustGetenv("DB_PASS")                  // e.g. 'my-db-password'
		dbName                 = mustGetenv("DB_NAME")                  // e.g. 'my-database'
		instanceConnectionName = mustGetenv("INSTANCE_CONNECTION_NAME") // e.g. 'project:region:instance'
		usePrivate             = os.Getenv("PRIVATE_IP")
	)
	d, err := cloudsqlconn.NewDialer(context.Background())
	if err != nil {
		fmt.Errorf("cloudsqlconn.NewDialer: %w", err)
		return
	}
	var opts []cloudsqlconn.DialOption
	if usePrivate != "" {
		opts = append(opts, cloudsqlconn.WithPrivateIP())
	}
	mysql.RegisterDialContext("cloudsqlconn",
		func(ctx context.Context, addr string) (net.Conn, error) {
			return d.Dial(ctx, instanceConnectionName, opts...)
		})

	dbURI := fmt.Sprintf("%s:%s@cloudsqlconn(localhost:3306)/%s?parseTime=true",
		dbUser, dbPwd, dbName)

	sqlDB, err := sql.Open("mysql", dbURI)
	if err != nil {
		log.Fatalf("sql.Open: %v", err)
	}

	// Open a connection to the database using the gorm package
	DB, err = gorm.Open(gmysql.New(gmysql.Config{
		Conn: sqlDB,
	}), &gorm.Config{})

	if err != nil {
		log.Fatalf("gorm.Open: %v", err)
	}

	fmt.Println("データベースに接続しました")
}

これで実行したところ、

データベースに接続しました
2024/05/28 00:26:14 defaulting to port 8080
2024/05/28 00:26:14 listening on port 8080

無事に接続できた。

課金具合が予想を超えている

Cloud SQLの課金体系がいまいち理解できていない。

ただコンソールを見る限り、想像を超える課金が行われている。

使用していない時はインスタンスを停止してその場を凌ぐけど、しっかりと理解をしたい。