Closed14

Cloud Run × Gin × TiDBでREST APIを始める

ゆーせいゆーせい

まずは構成から。

  • Cloud RunとTiDBで構成されたWeb APIを一定間隔でラズパイが叩く仕様
  • GCPとTiDBの無料枠を活用
  • 学校のネットワークでの仕様を想定しているので、クラウドからラズパイにメッセージを伝える方法はセキュリティ上困難なため、ラズパイ側からのポーリング処理で通信
  • Web APIは基本的なRESTで実装
ゆーせいゆーせい

TiDBとまずはターミナルからやり取りしてみる

TiDBのコンソール画面から接続のコマンドを簡単にコピーできる

実行してみるとエラーが出た

ERROR 2059 (HY000): Authentication plugin 'mysql_native_password' cannot be loaded

どうやらネイティブパスワードを使用してのMySQLとの通信はデフォルトで無効になっているらしい

bohnenbohnen

mysql-client の9.x だとこのエラーが出るようなので、mysql-clientの8.x を使うようにしてもらうと接続できると思います。

ゆーせいゆーせい

別の通信方法で試す

MySQLWorkbenchで接続することができた
テーブルは作っていないので存在しない

TiDBで起動したDBの中身も確認できるようになったので、APIのベースを作る

ゆーせいゆーせい

ginでサーバーを起動する

package main

import (
	infrastructure "gcp_go_cloud_run/app/infrastructure/mysql"
	"log"
	"os"

	"github.com/gin-gonic/gin"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	infrastructure.InitDB()

	r := gin.Default()

	if err := r.Run(":" + port); err != nil {
		log.Fatalf("Failed to start the server: %v", err)
	}
}
ゆーせいゆーせい

dbを初期化する関数はこんな感じ

db.go
package infrastructure

import (
	"fmt"
	"gcp_go_cloud_run/app/infrastructure/mysql/entity"
	"log"
	"os"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitDB() {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		getEnv("DB_USER", "root"),
		getEnv("DB_PASSWORD", ""),
		getEnv("DB_HOST", "localhost"),
		getEnv("DB_PORT", "4000"),
		getEnv("DB_NAME", "your_db_name"),
	)

	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("failed to connect to database: %v", err)
	}

	DB.AutoMigrate(&entity.Bell{}, &entity.Store{}, &entity.CallLog{})
}
ゆーせいゆーせい

しかし、これでは接続できなかった。
tidbはtls接続のみを許可するようになっている

[error] failed to initialize database, got error Error 1105 (HY000): Connections using insecure transport are prohibited. See https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters

ではどうやってtls接続をするのか
公式ドキュメントにGo言語を使った簡易的なREST APIの実装についてのチュートリアルがあり、そこでは以下のようにdsnを変更すると記述されている

mysql.RegisterTLSConfig("register-tidb-tls", &tls.Config {
    MinVersion: tls.VersionTLS12,
    ServerName: "xxx.tidbcloud.com",
})
dsn := "2aEp24QWEDLqRFs.root:123456@tcp(xxx.tidbcloud.com:4000)/test?charset=utf8mb4&tls=register-tidb-tls"

https://docs.pingcap.com/ja/tidb/v6.5/dev-guide-sample-application-golang#step-3-1-table-initialization

ゆーせいゆーせい

先ほどの記事をもとにtls接続の設定を追加した

package infrastructure

import (
	"crypto/tls"
	"fmt"
	"gcp_go_cloud_run/app/infrastructure/mysql/entity"
	"log"
	"os"

	gormsql "gorm.io/driver/mysql"
	"gorm.io/gorm"

	"github.com/go-sql-driver/mysql" //標準のmysqlドライバーをインポート
)

var DB *gorm.DB

func InitDB() {
    //ここではgoの標準のmysqlドライバーを使用する。gormのドライバーにはRegisterTLSConfigメソッドが存在しないことに注意
	err := mysql.RegisterTLSConfig("tidb", &tls.Config{
		MinVersion: tls.VersionTLS12,
		ServerName: GetEnv("DB_HOST", "localhost"),
	})
	if err != nil {
		log.Fatalf("failed to register TLS config: %v", err)
	}

	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&tls=tidb",
		GetEnv("DB_USER", "root"),
		GetEnv("DB_PASSWORD", ""),
		GetEnv("DB_HOST", "localhost"),
		GetEnv("DB_PORT", "4000"),
		GetEnv("DB_NAME", "your_db_name"),
	)

	DB, err = gorm.Open(gormsql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("failed to connect to database: %v", err)
	}

	DB.AutoMigrate(&entity.Bell{}, &entity.Store{}, &entity.CallLog{})
}
ゆーせいゆーせい

ローカルで実行してみると見事データベースにテーブルを追加できた

ゆーせいゆーせい

しかしCloud Runへのデプロイできない
→環境変数が読み込めていないため正しいdsnが構成できずにtidbとの接続に失敗している

ゆーせいゆーせい

なぜ環境変数が読み込めていないのか?
→デプロイ時ではなく、ビルド時に環境変数を読み込んでいた。

どう解決したか
→デプロイ時に環境変数を渡すように変更した
DB_USERはsecretManagerにて設定済み

 args:
      - run
      - services
      - update
      <--中略-->
       - '--set-secrets=DB_USER=DB_USER:latest'
    id: Deploy
    entrypoint: gcloud
このスクラップは5ヶ月前にクローズされました