Cloud Run의 Go 애플리케이션을 PostgreSQL용 Cloud SQL 데이터베이스에 연결하는 방법

1. 개요

Cloud SQL Go 커넥터를 사용하면 Go 애플리케이션을 Cloud SQL 데이터베이스에 안전하게 연결할 수 있습니다. Cloud Run은 HTTP 요청을 통해 호출 가능한 스테이트리스(Stateless) 컨테이너를 실행하는 완전 관리형 서버리스 플랫폼입니다. 이 Codelab에서는 IAM 인증을 사용하여 서비스 계정으로 Cloud Run의 Go 애플리케이션을 PostgreSQL용 Cloud SQL 데이터베이스에 안전하게 연결하는 방법을 보여줍니다.

학습할 내용

이 실습에서는 다음 작업을 진행하는 방법을 학습합니다.

  • PostgreSQL용 Cloud SQL 데이터베이스 만들기
  • Cloud Run에 Go 애플리케이션 배포
  • Go 커넥터를 사용하여 Cloud SQL에 애플리케이션 연결

기본 요건

  • 이 실습에서는 Cloud 콘솔 및 Cloud Shell 환경에 익숙하다고 가정합니다.

2. 시작하기 전에

Cloud 프로젝트 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Google 계정이 없다면 계정을 만들어야 합니다.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔이 고유한 문자열을 자동으로 생성합니다. 보통은 그게 뭔지 상관하지 않습니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 무작위로 다른 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스를 삭제하거나 전체 프로젝트를 삭제하면 됩니다. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.

환경 설정

검색창 오른쪽에 있는 아이콘을 클릭하여 Cloud Shell을 활성화합니다.

ecdc43ada29e91b.png

Cloud Shell에서 API를 사용 설정합니다.

gcloud services enable compute.googleapis.com sqladmin.googleapis.com \
  run.googleapis.com artifactregistry.googleapis.com \
  cloudbuild.googleapis.com servicenetworking.googleapis.com

승인하라는 메시지가 표시되면 '승인'을 클릭합니다. 계속하려면

6356559df3eccdda.png

이 명령어를 완료하는 데 몇 분 정도 걸릴 수 있지만 최종적으로 다음과 유사한 성공 메시지가 표시됩니다.

Operation "operations/acf.p2-327036483151-73d90d00-47ee-447a-b600-a6badf0eceae" finished successfully.

3. 서비스 계정 설정

Cloud SQL에 연결할 수 있는 올바른 권한을 갖도록 Cloud Run에서 사용할 Google Cloud 서비스 계정을 만들고 구성합니다.

  1. 다음과 같이 gcloud iam service-accounts create 명령어를 실행하여 새 서비스 계정을 만듭니다.
    gcloud iam service-accounts create quickstart-service-account \
      --display-name="Quickstart Service Account"
    
  2. 다음과 같이 gcloud projects add-iam-policy-binding 명령어를 실행하여 방금 만든 Google Cloud 서비스 계정에 Cloud SQL 클라이언트 역할을 추가합니다. Cloud Shell에서 ${GOOGLE_CLOUD_PROJECT} 표현식은 프로젝트 이름으로 대체됩니다. 원하는 경우 직접 교체해도 됩니다.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.client"
    
  3. 다음과 같이 gcloud projects add-iam-policy-binding 명령어를 실행하여 방금 만든 Google Cloud 서비스 계정에 Cloud SQL 인스턴스 사용자 역할을 추가합니다.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.instanceUser"
    
  4. 다음과 같이 gcloud projects add-iam-policy-binding 명령어를 실행하여 방금 만든 Google Cloud 서비스 계정에 로그 작성자 역할을 추가합니다.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"
    

4. Cloud SQL 설정

gcloud sql instances create 명령어를 실행하여 Cloud SQL 인스턴스를 만듭니다.

  • –database-version: 데이터베이스 엔진 유형 및 버전 지정하지 않으면 API 기본값이 사용됩니다. 사용 가능한 현재 버전을 보려면 gcloud 데이터베이스 버전 문서를 참조하세요.
  • –cpu: 머신에서 필요한 코어 수입니다.
  • –memory: 시스템에서 원하는 메모리의 양을 나타내는 정수 값입니다. 크기 단위 (예: 3072MB 또는 9GB)를 제공해야 합니다. 단위를 지정하지 않으면 GB로 간주됩니다.
  • –region: 인스턴스의 리전 위치 (예: us-central1, asia-east1, us-east1)입니다.
  • –database-flags: 플래그를 설정할 수 있습니다. 이 경우에는 cloudsql.iam_authentication를 사용 설정하여 Cloud Run에서 이전에 만든 서비스 계정을 사용하여 Cloud SQL에 연결할 수 있도록 합니다.
    gcloud sql instances create quickstart-instance \
      --database-version=POSTGRES_14 \
      --cpu=1 \
      --memory=4GB \
      --region=us-central1 \
      --database-flags=cloudsql.iam_authentication=on
    

이 명령어를 완료하는 데 몇 분 정도 걸릴 수 있습니다.

gcloud sql databases create 명령어를 실행하여 quickstart-instance 내에 Cloud SQL 데이터베이스를 만듭니다.

gcloud sql databases create quickstart_db \
  --instance=quickstart-instance

이전에 만든 서비스 계정에 대해 PostgreSQL 데이터베이스 사용자를 만들어 데이터베이스에 액세스합니다.

gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
  --instance=quickstart-instance \
  --type=cloud_iam_service_account

5. 애플리케이션 준비

HTTP 요청에 응답하는 Go 애플리케이션을 준비합니다.

  1. Cloud Shell에서 helloworld라는 새 디렉터리를 만든 후 해당 디렉터리로 변경합니다.
    mkdir helloworld
    cd helloworld
    
  2. go mod init를 실행하여 새 Go 애플리케이션을 초기화합니다.
    go mod init github.com/GoogleCloudPlatform/golang-samples/run/helloworld
    
  3. Cloud SQL Go Connector 종속 항목을 설치합니다.
    go get cloud.google.com/go/cloudsqlconn
    go get cloud.google.com/go/cloudsqlconn/postgres/pgxv4
    
  4. 애플리케이션 코드로 main.go 파일을 만듭니다. 이 코드로 수행할 수 있는 작업은 다음과 같습니다.
    • HTTP 요청 수락
    • 데이터베이스에 연결
    • 데이터베이스에 HTTP 요청 시간 저장
    • 마지막 요청 5개의 시간을 반환합니다.
    를 통해 개인정보처리방침을 정의할 수 있습니다. Cloud Shell에서 다음 명령어를 실행합니다.
    cat > main.go << "EOF"
    package main
    
    import (
      "database/sql"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
      "time"
    
      "cloud.google.com/go/cloudsqlconn"
      "cloud.google.com/go/cloudsqlconn/postgres/pgxv4"
    )
    
    // visitData is used to pass data to the HTML template.
    type visitData struct {
      RecentVisits []visit
    }
    
    // visit contains a single row from the visits table in the database.
    // Each visit includes a timestamp.
    type visit struct {
      VisitTime time.Time
    }
    
    // getDB creates a connection to the database
    // based on environment variables.
    func getDB() (*sql.DB, func() error) {
      cleanup, err := pgxv4.RegisterDriver("cloudsql-postgres", cloudsqlconn.WithIAMAuthN())
      if err != nil {
        log.Fatalf("Error on pgxv4.RegisterDriver: %v", err)
      }
    
      dsn := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable", os.Getenv("INSTANCE_CONNECTION_NAME"), os.Getenv("DB_USER"), os.Getenv("DB_NAME"))
      db, err := sql.Open("cloudsql-postgres", dsn)
      if err != nil {
        log.Fatalf("Error on sql.Open: %v", err)
      }
    
      createVisits := `CREATE TABLE IF NOT EXISTS visits (
        id SERIAL NOT NULL,
        created_at timestamp NOT NULL,
        PRIMARY KEY (id)
      );`
      _, err = db.Exec(createVisits)
      if err != nil {
        log.Fatalf("unable to create table: %s", err)
      }
    
      return db, cleanup
    }
    
    func main() {
      port := os.Getenv("PORT")
      if port == "" {
        port = "8080"
      }
      log.Printf("Listening on port %s", port)
      db, cleanup := getDB()
      defer cleanup()
    
      http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
        // Insert current visit
        _, err := db.Exec("INSERT INTO visits(created_at) VALUES(NOW())")
        if err != nil {
          log.Fatalf("unable to save visit: %v", err)
        }
    
        // Get the last 5 visits
        rows, err := db.Query("SELECT created_at FROM visits ORDER BY created_at DESC LIMIT 5")
        if err != nil {
          log.Fatalf("DB.Query: %v", err)
        }
        defer rows.Close()
    
        var visits []visit
        for rows.Next() {
          var visitTime time.Time
          err := rows.Scan(&visitTime)
          if err != nil {
            log.Fatalf("Rows.Scan: %v", err)
          }
          visits = append(visits, visit{VisitTime: visitTime})
        }
        response, err := json.Marshal(visitData{RecentVisits: visits})
        if err != nil {
          log.Fatalf("renderIndex: failed to parse totals with json.Marshal: %v", err)
        }
        w.Write(response)
      })
      if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
      }
    }
    
    EOF
    

이 코드는 PORT 환경 변수로 정의한 포트를 리슨하는 기본 웹 서버를 생성합니다. 이제 애플리케이션을 배포할 준비가 되었습니다.

6. Cloud Run 애플리케이션 배포

아래 명령어를 실행하여 애플리케이션을 배포합니다.

  • –region: 인스턴스의 리전 위치 (예: us-central1, asia-east1, us-east1)입니다.
  • –source: 배포할 소스 코드입니다. 이 경우 .는 현재 폴더 helloworld의 소스 코드를 참조합니다.
  • –set-env-vars: 애플리케이션이 애플리케이션을 Cloud SQL 데이터베이스로 보내는 데 사용하는 환경 변수를 설정합니다.
  • –service-account: 이 Codelab의 시작 부분에서 생성된 Cloud SQL 데이터베이스에 연결할 권한이 있는 서비스 계정에 Cloud Run 배포를 연결합니다.
  • –allow-unauthenticated: 인터넷에서 애플리케이션에 액세스할 수 있도록 인증되지 않은 요청을 허용합니다.
gcloud run deploy helloworld \
  --region=us-central1 \
  --source=. \
  --set-env-vars INSTANCE_CONNECTION_NAME="${GOOGLE_CLOUD_PROJECT}:us-central1:quickstart-instance" \
  --set-env-vars DB_NAME="quickstart_db" \
  --set-env-vars DB_USER="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam" \
  --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
  --allow-unauthenticated

메시지가 표시되면 yEnter를 눌러 계속 진행할지 확인합니다.

Do you want to continue (Y/n)? y

몇 분 후 애플리케이션에 방문할 수 있는 URL이 제공됩니다.

URL로 이동하여 실제 애플리케이션을 확인합니다. URL을 방문하거나 페이지를 새로고침할 때마다 가장 최근의 방문 내역 5개가 JSON으로 반환됩니다.

7. 축하합니다

Cloud SQL에서 실행되는 PostgreSQL 데이터베이스에 연결할 수 있는 Go 애플리케이션을 Cloud Run에 배포했습니다.

학습한 내용

  • PostgreSQL용 Cloud SQL 데이터베이스 만들기
  • Cloud Run에 Go 애플리케이션 배포
  • Go 커넥터를 사용하여 Cloud SQL에 애플리케이션 연결

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요. 전체 프로젝트를 삭제하려면 다음을 실행합니다.

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}