Cloud Run 환경에서 Django 실행

Django와 같이 Cloud Run에 스테이트풀(Stateful) 애플리케이션을 배포하려면 여러 서비스를 서로 상호작용하여 통합 프로젝트를 형성해야 합니다.

이 튜토리얼에서는 사용자가 Django 웹 개발에 익숙하다고 가정합니다. Django 개발이 처음이면 계속하기 전에 첫 번째 Django 앱 작성 연습을 진행하는 것이 좋습니다.

이 가이드에서는 Django를 구체적으로 보여주지만 이 배포 프로세스를 WagtailDjango CMS와 같은 다른 Django 기반 프레임워크와 함께 사용할 수 있습니다.

이 튜토리얼에서는 Python 3.8 이상이 필요한 Django 4를 사용합니다.

목표

이 튜토리얼에서는 다음 단계를 진행합니다.

  • Cloud SQL 데이터베이스를 만들고 연결합니다.
  • Secret Manager 보안 비밀 값을 만들고 사용합니다.
  • Cloud Run에 Django 앱 배포

  • Cloud Storage에 정적 파일을 호스팅합니다.

  • Cloud Build를 사용하여 배포를 자동화합니다.

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

시작하기 전에

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine APIs.

    Enable the APIs

  5. Install the Google Cloud CLI.
  6. To initialize the gcloud CLI, run the following command:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  8. Make sure that billing is enabled for your Google Cloud project.

  9. Enable the Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine APIs.

    Enable the APIs

  10. Install the Google Cloud CLI.
  11. To initialize the gcloud CLI, run the following command:

    gcloud init
  12. 이 가이드에 사용된 계정에 충분한 권한이 있는지 확인합니다.

개발 환경 준비

샘플 앱 클론

Django 샘플 앱용 코드는 GitHub의 GoogleCloudPlatform/python-docs-samples 저장소에 있습니다.

  1. ZIP 파일로 샘플을 다운로드하고 이를 추출하거나 저장소를 로컬 머신에 클론할 수 있습니다.

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. 샘플 코드가 있는 디렉터리로 이동합니다.

    Linux/macOS

    cd python-docs-samples/run/django
    

    Windows

    cd python-docs-samples\run\django
    

Python 설정 확인

이 튜토리얼에서는 Python을 사용하여 머신에서 샘플 애플리케이션을 실행합니다. 샘플 코드에는 종속 항목 설치도 필요합니다.

자세한 내용은 Python 개발 환경 가이드를 참조하세요.

  1. Python 버전이 3.7 이상인지 확인합니다.

     python -V
    

    Python 3.7.3 이상이 표시되어야 합니다.

  2. Python 가상 환경을 만들고 종속 항목을 설치합니다.

    Linux/macOS

    python -m venv venv
    source venv/bin/activate
    pip install --upgrade pip
    pip install -r requirements.txt
    

    Windows

    python -m venv env
    venv\scripts\activate
    pip install --upgrade pip
    pip install -r requirements.txt
    

Cloud SQL 인증 프록시를 다운로드하여 로컬 머신에서 Cloud SQL에 연결

배포된 앱은 Cloud Run 환경에 내장된 Cloud SQL 인증 프록시를 사용하여 Cloud SQL 인스턴스와 통신합니다. 하지만 앱을 로컬에서 테스트하려면 프록시의 로컬 사본을 개발 환경에 설치하여 사용해야 합니다. 자세한 내용은 Cloud SQL 인증 프록시 가이드를 참조하세요.

Cloud SQL 인증 프록시는 Cloud SQL API를 사용하여 SQL 인스턴스와 상호작용합니다. 이렇게 하려면 gcloud를 통한 애플리케이션 인증이 필요합니다.

  1. API에 대한 사용자 인증 정보를 인증하고 가져옵니다.

    gcloud auth application-default login
    
  2. Cloud SQL 인증 프록시를 다운로드하여 로컬 머신에 설치합니다.

    Linux 64비트

    1. Cloud SQL 인증 프록시를 다운로드합니다.
      wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
    2. Cloud SQL 인증 프록시 실행 파일을 만듭니다.
      chmod +x cloud_sql_proxy

    Linux 32비트

    1. Cloud SQL 인증 프록시를 다운로드합니다.
      wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.386 -O cloud_sql_proxy
    2. wget 명령어를 찾을 수 없으면 sudo apt-get install wget을 실행하고 다운로드 명령어를 반복합니다.
    3. Cloud SQL 인증 프록시 실행 파일을 만듭니다.
      chmod +x cloud_sql_proxy

    macOS 64비트

    1. Cloud SQL 인증 프록시를 다운로드합니다.
      curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
    2. Cloud SQL 인증 프록시 실행 파일을 만듭니다.
      chmod +x cloud_sql_proxy

    macOS 32비트

    1. Cloud SQL 인증 프록시를 다운로드합니다.
      curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.386
    2. Cloud SQL 인증 프록시 실행 파일을 만듭니다.
      chmod +x cloud_sql_proxy

    Mac M1

    1. Cloud SQL 인증 프록시를 다운로드합니다.
        curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.arm64
        
    2. Cloud SQL 인증 프록시 실행 파일을 만듭니다.
        chmod +x cloud_sql_proxy
        

    Windows 64비트

    https://dl.google.com/cloudsql/cloud_sql_proxy_x64.exe를 마우스 오른쪽 버튼으로 클릭하고 다른 이름으로 링크 저장을 선택하여 Cloud SQL 인증 프록시를 다운로드합니다. 파일 이름을 cloud_sql_proxy.exe로 바꿉니다.

    Windows 32비트

    https://dl.google.com/cloudsql/cloud_sql_proxy_x86.exe를 마우스 오른쪽 버튼으로 클릭하고 다른 이름으로 링크 저장을 선택하여 Cloud SQL 인증 프록시를 다운로드합니다. 파일 이름을 cloud_sql_proxy.exe로 바꿉니다.

    Cloud SQL 인증 프록시 Docker 이미지

    편의를 위해 GitHub의 Cloud SQL 인증 프록시 저장소에 Cloud SQL 인증 프록시가 포함된 여러 컨테이너 이미지가 제공되고 있습니다. 다음 명령어로 Docker를 사용하여 최신 이미지를 로컬 머신으로 가져올 수 있습니다.
    docker pull gcr.io/cloudsql-docker/gce-proxy:1.30.1
    

    기타 OS

    여기에 포함되지 않은 다른 운영체제의 경우 소스에서 Cloud SQL 인증 프록시를 컴파일하면 됩니다.

    다운로드한 파일을 PATH의 위치 또는 홈 디렉터리와 같이 일반적인 위치로 이동할 수 있습니다. 이 방법을 선택하는 경우 튜토리얼 뒷부분에서 Cloud SQL 인증 프록시를 시작하면 cloud_sql_proxy 명령어를 사용할 때 선택한 위치를 참조해야 합니다.

지원 서비스 만들기

이 튜토리얼에서는 여러 Google Cloud 서비스를 사용하여 배포된 Django 프로젝트를 지원하는 데이터베이스, 미디어 스토리지, 보안 비밀 스토리지를 제공합니다. 이러한 서비스는 특정 리전에 배포됩니다. 서비스 간 효율성을 높이려면 모든 서비스가 동일한 리전에 배포되어야 합니다. 가장 가까운 리전에 대한 자세한 내용은 리전별 제공 제품을 참조하세요.

이 튜토리얼에서는 Cloud Run의 통합 정적 애셋 호스팅 메커니즘을 사용합니다.

PostgreSQL용 Cloud SQL 인스턴스 설정

Django는 여러 관계형 데이터베이스를 공식적으로 지원하지만 PostgreSQL을 가장 많이 지원합니다. PostgreSQL은 Cloud SQL에서 지원하므로 이 튜토리얼에서는 해당 유형의 데이터베이스를 사용합니다.

다음 섹션에서는 앱의 PostgreSQL 인스턴스, 데이터베이스 및 데이터베이스 사용자를 만드는 방법에 대해 설명합니다.

  1. PostgreSQL 인스턴스를 만듭니다.

    Console

    1. Cloud Console에서 Cloud SQL 인스턴스 페이지로 이동합니다.

      Cloud SQL 인스턴스 페이지로 이동

    2. 인스턴스 만들기를 클릭합니다.

    3. PostgreSQL을 클릭합니다.

    4. 인스턴스 ID 필드에 INSTANCE_NAME을 입력합니다.

    5. postgres 사용자의 비밀번호를 입력합니다.

    6. 다른 필드에는 기본값을 사용합니다.

    7. 만들기를 클릭합니다.

    인스턴스를 만들고 사용할 수 있도록 준비하는 데 몇 분 정도 소요됩니다.

    gcloud

    • PostgreSQL 인스턴스를 만듭니다.

      gcloud sql instances create INSTANCE_NAME \
          --project PROJECT_ID \
          --database-version POSTGRES_13 \
          --tier db-f1-micro \
          --region REGION
      

    다음을 바꿉니다.

    • INSTANCE_NAME: Cloud SQL 인스턴스 이름
    • PROJECT_ID: Google Cloud 프로젝트 ID
    • REGION: Google Cloud 리전

    인스턴스를 만들고 사용할 수 있도록 준비하는 데 몇 분 정도 소요됩니다.

  2. 생성된 인스턴스 내에서 데이터베이스를 생성합니다.

    Console

    1. 인스턴스 페이지에서 데이터베이스 탭으로 이동합니다.
    2. 데이터베이스 만들기를 클릭합니다.
    3. 데이터베이스 이름 대화상자에 DATABASE_NAME을 입력합니다.
    4. 만들기를 클릭합니다.

    gcloud

    • 최근에 만든 인스턴스 내에 데이터베이스를 만듭니다.

      gcloud sql databases create DATABASE_NAME \
          --instance INSTANCE_NAME
      

      DATABASE_NAME을 인스턴스 내부의 데이터베이스 이름으로 바꿉니다.

  3. 데이터베이스 사용자를 만듭니다.

    Console

    1. 인스턴스 페이지에서 사용자 탭으로 이동합니다.
    2. 사용자 계정 추가를 클릭합니다.
    3. '기본 제공 인증' 아래 인스턴스에 사용자 계정 추가 대화상자에서 다음을 수행합니다.
    4. 사용자 이름 DATABASE_USERNAME을 입력합니다.
    5. 비밀번호 DATABASE_PASSWORD를 입력합니다.
    6. 추가를 클릭합니다.

    gcloud

    • 최근에 만든 인스턴스 내에 사용자를 만듭니다.

      gcloud sql users create DATABASE_USERNAME \
          --instance INSTANCE_NAME \
          --password DATABASE_PASSWORD
      

      PASSWORD를 안전한 비밀번호로 바꿉니다.

Cloud Storage 버킷 설정

Cloud Storage를 사용하여 Django의 포함된 정적 애셋뿐만 아니라 사용자가 업로드한 미디어를 가용성이 높은 객체 스토리지에 저장할 수 있습니다. django-storages[google] 패키지는 Django와 이 스토리지 백엔드와의 상호작용을 처리합니다.

Console

  1. In the Google Cloud console, go to the Cloud Storage Buckets page.

    Go to Buckets page

  2. Click Create bucket.
  3. On the Create a bucket page, enter your bucket information. To go to the next step, click Continue.
    • For Name your bucket, enter a name that meets the bucket naming requirements.
    • For Location, select the following: MEDIA_BUCKET
    • For Choose a default storage class for your data, select the following: Standard.
    • For Choose how to control access to objects, select an Access control option.
    • For Advanced settings (optional), specify an encryption method, a retention policy, or bucket labels.
  4. Click Create.

gcloud

gsutil 명령줄 도구는 gcloud CLI 설치의 일부로 설치되었습니다.

  • Cloud Storage 버킷을 만듭니다.

    gsutil mb -l REGION gs://PROJECT_ID_MEDIA_BUCKET
    

    MEDIA_BUCKET을 미디어 버킷의 서픽스로 바꿉니다. 프로젝트 ID와 결합하면 고유한 버킷 이름이 생성됩니다.

Secret Manager에 보안 비밀 값 저장

이제 지원 서비스가 구성되었으므로 Django에는 이러한 서비스에 대한 정보가 필요합니다. 이 튜토리얼에서는 이러한 값을 Django 소스 코드에 직접 입력하는 대신 Secret Manager를 사용하여 이 정보를 안전하게 저장합니다.

Cloud Run 및 Cloud Build는 해당 서비스 계정을 사용하여 보안 비밀과 상호작용합니다. 서비스 계정은 프로젝트 번호를 포함하는 이메일 주소로 식별됩니다.

Secret Manager 보안 비밀로 Django 환경 파일 만들기

Django를 시작하는 데 필요한 설정은 보안 적용된 env 파일에 저장합니다. 샘플 앱에서는 Secret Manager API를 사용하여 보안 비밀 값을 검색하고, django-environ 패키지를 사용하여 값을 Django 환경에 로드합니다. 보안 비밀은 Cloud Run 및 Cloud Build에서 액세스할 수 있도록 구성됩니다.

  1. 데이터베이스 연결 문자열, 미디어 버킷 이름, 새 SECRET_KEY 값을 정의하여 .env라는 파일을 만듭니다.

    echo DATABASE_URL=postgres://DATABASE_USERNAME:DATABASE_PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME > .env
    echo GS_BUCKET_NAME=PROJECT_ID_MEDIA_BUCKET >> .env
    echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1) >> .env
    
  2. Secret Manager에 보안 비밀을 저장합니다.

    Console

    1. Cloud Console에서 Secret Manager 페이지로 이동합니다.

      Secret Manager 페이지로 이동

    2. 보안 비밀 만들기를 클릭합니다.

    3. 이름 필드에 django_settings를 입력합니다.

    4. 보안 비밀 값 대화상자에 .env 파일의 콘텐츠를 붙여넣습니다.

    5. 보안 비밀 만들기를 클릭합니다.

    6. django_settings의 세부정보에서 프로젝트 번호를 확인합니다.

      projects/PROJECTNUM/secrets/django_settings
      
    7. 로컬 파일을 삭제하여 로컬 설정 재정의를 방지합니다.

    gcloud

    1. .env 파일의 값으로 새 보안 비밀 django_settings를 만듭니다.

      gcloud secrets create django_settings --data-file .env
      
    2. 보안 비밀 생성을 확인하려면 다음을 확인합니다.

      gcloud secrets describe django_settings
      
      gcloud secrets versions access latest --secret django_settings
      
    3. 프로젝트 번호 값을 가져옵니다(PROJECTNUM).

      export PROJECTNUM=$(gcloud projects describe PROJECT_ID --format='value(projectNumber)')
      
    4. 로컬 파일을 삭제하여 로컬 설정 재정의를 방지합니다.

      rm .env
      
  3. 보안 비밀에 대한 액세스 구성

    Console

    1. 권한 탭을 클릭합니다.
    2. 추가를 클릭합니다.
    3. 새 구성원 필드에 PROJECTNUM-compute@developer.gserviceaccount.com을 입력하고 Enter를 누릅니다.
    4. 새 구성원 필드에 PROJECTNUM@cloudbuild.gserviceaccount.com을 입력하고 Enter를 누릅니다.
    5. 역할 드롭다운 메뉴에서 Secret Manager 보안 비밀 접근자를 선택합니다.
    6. 저장을 클릭합니다.

    gcloud

    1. Cloud Run 서비스 계정에 보안 비밀에 대한 액세스 권한을 부여합니다.

      gcloud secrets add-iam-policy-binding django_settings \
          --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      
    2. Cloud Build 서비스 계정에 보안 비밀에 대한 액세스 권한을 부여합니다.

      gcloud secrets add-iam-policy-binding django_settings \
          --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      출력에서 bindings가 두 서비스 계정을 구성원으로 나열하는지 확인합니다.

Django 관리자 비밀번호의 보안 비밀 만들기

Django 관리자는 일반적으로 대화형 관리 명령어 createsuperuser를 실행하여 생성됩니다.

이 튜토리얼에서는 데이터 마이그레이션을 사용하여 Secret Manager에서 관리자 비밀번호를 검색합니다.

Console

  1. Cloud Console에서 Secret Manager 페이지로 이동합니다.
  2. 보안 비밀 만들기를 클릭합니다.

  3. 이름 필드에 superuser_password을 입력합니다.

  4. 보안 비밀 값 필드에 임의의 고유 비밀번호를 입력합니다.

  5. 보안 비밀 만들기를 클릭합니다.

  6. superuser_password에 대한 세부정보에서 프로젝트 번호(projects/PROJECTNUM/secrets/superuser_password)를 기록해 둡니다.

  7. 권한 탭을 클릭합니다.

  8. 추가를 클릭합니다.

  9. 새 구성원 필드에 PROJECTNUM@cloudbuild.gserviceaccount.com을 입력하고 Enter를 누릅니다.

  10. 역할 드롭다운 메뉴에서 Secret Manager 보안 비밀 접근자를 선택합니다.

  11. 저장을 클릭합니다.

gcloud

  1. 무작위로 생성된 비밀번호에서 새 보안 비밀 superuser_password를 만듭니다.

    echo -n "$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 30 | head -n1)" | gcloud secrets create superuser_password --data-file -
    
  2. 보안 비밀에 대한 액세스 권한을 Cloud Build에 부여합니다.

    gcloud secrets add-iam-policy-binding superuser_password \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    

    출력에서 bindings에 Cloud Build만 구성원으로 나열되는지 확인합니다.

Cloud Build에 Cloud SQL 액세스 권한 부여

Cloud Build에서 데이터베이스 마이그레이션을 적용하려면 Cloud Build에 Cloud SQL 액세스 권한을 부여해야 합니다.

Console

  1. Cloud Console에서 Identity and Access Management 페이지로 이동합니다.

    Identity and Access Management 페이지로 이동

  2. PROJECTNUM@cloudbuild.gserviceaccount.com 항목을 수정하려면 수정을 클릭합니다.

  3. 다른 역할 추가를 클릭합니다.

  4. 역할 선택 대화상자에서 Cloud SQL 클라이언트를 선택합니다.

  5. 저장을 클릭합니다.

gcloud

  1. Cloud Build에 Cloud SQL 액세스 권한을 부여합니다.

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/cloudsql.client
    

로컬 컴퓨터에서 앱 실행

지원 서비스가 구성되었으면 이제 컴퓨터에서 앱을 실행할 수 있습니다. 이 설정을 사용하면 로컬 개발, 데이터베이스 마이그레이션 적용을 수행할 수 있습니다. 데이터베이스 마이그레이션은 Cloud Build에도 적용되지만 makemigrations하려면 이 로컬 설정이 필요합니다.

  1. 별도의 터미널에서 Cloud SQL 인증 프록시를 시작합니다

    Linux/macOS

    ./cloud_sql_proxy -instances="PROJECT_ID:REGION:INSTANCE_NAME"=tcp:5432
    

    Windows

    cloud_sql_proxy.exe -instances="PROJECT_ID:REGION:INSTANCE_NAME"=tcp:5432
    

    이 단계에서는 로컬 테스트를 위해 로컬 컴퓨터에서 Cloud SQL 인스턴스로 연결을 설정합니다. 앱을 로컬에서 테스트하는 동안 Cloud SQL 인증 프록시를 계속 실행합니다. 별도의 터미널에서 이 프로세스를 실행하면 프로세스가 실행되는 동안 작업을 계속할 수 있습니다.

  2. 새 터미널에서 프로젝트 ID를 로컬로 설정합니다(Secret Manager API에서 사용).

    Linux/macOS

      export GOOGLE_CLOUD_PROJECT=PROJECT_ID
    

    Windows

      set GOOGLE_CLOUD_PROJECT=PROJECT_ID
    
  3. Cloud SQL 인증 프록시를 사용 중임을 환경 변수를 설정합니다(이 값은 코드에서 인식됨).

    Linux/macOS

      export USE_CLOUD_SQL_AUTH_PROXY=true
    

    Windows

      set USE_CLOUD_SQL_AUTH_PROXY=true
    
  4. Django 마이그레이션을 실행하여 모델과 애셋을 설정합니다.

    python manage.py makemigrations
    python manage.py makemigrations polls
    python manage.py migrate
    python manage.py collectstatic
    
  5. Django 웹 서버를 시작합니다.

    python manage.py runserver
    
  6. 브라우저에서 http://localhost:8000으로 이동합니다.

    페이지에 다음과 같은 텍스트가 표시됩니다.'Hello, world. 사용자는 설문조사 색인에 있습니다.' 컴퓨터에서 실행되는 Django 웹 서버는 샘플 앱 페이지를 전송합니다.

  7. Ctrl/Cmd+C를 눌러서 로컬 웹 서버를 중지합니다.

Cloud Run에 앱 배포

이제 지원 서비스 설정을 사용하여 Cloud Run 서비스를 배포할 수 있습니다.

  1. 제공된 cloudmigrate.yaml을 사용해 Cloud Build를 사용하여 이미지를 빌드하고, 데이터베이스 마이그레이션을 실행하고, 정적 애셋을 채웁니다.

    gcloud builds submit --config cloudmigrate.yaml \
        --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
    

    이 첫 번째 빌드를 완료하는 데 몇 분 정도 걸립니다.

  2. 빌드가 성공하면 Cloud Run 서비스를 처음 배포할 때 서비스 리전, 기본 이미지, 연결된 Cloud SQL 인스턴스를 설정합니다.

    gcloud run deploy polls-service \
        --platform managed \
        --region REGION \
        --image gcr.io/PROJECT_ID/polls-service \
        --add-cloudsql-instances PROJECT_ID:REGION:INSTANCE_NAME \
        --allow-unauthenticated
    

    서비스 URL과 함께 배포가 성공한 것을 보여주는 출력이 표시됩니다.

    Service [polls-service] revision [polls-service-00001-tug] has been deployed
    and is serving 100 percent of traffic at https://polls-service-<hash>-uc.a.run.app
    
  3. 이제 서비스 URL을 알았으므로 이 값을 환경 변수로 설정하도록 서비스를 업데이트합니다.

    SERVICE_URL=$(gcloud run services describe polls-service --platform managed \
        --region REGION --format "value(status.url)")
    
    gcloud run services update polls-service \
        --platform managed \
        --region REGION \
        --set-env-vars CLOUDRUN_SERVICE_URL=$SERVICE_URL
    
  4. 배포된 서비스를 확인하려면 서비스 URL로 이동합니다.

  5. Django 관리자에 로그인하려면 /admin을 URL에 추가하고 사용자 이름 admin과 이전에 설정된 비밀번호를 사용하여 로그인합니다.

애플리케이션 업데이트

초기 프로비저닝 및 배포 단계는 복잡했지만 업데이트는 더 간단한 프로세스입니다.

  1. Cloud Build 빌드 및 마이그레이션 스크립트를 실행합니다.

    gcloud builds submit --config cloudmigrate.yaml \
        --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
    
  2. 리전과 이미지만 지정하여 서비스를 배포합니다.

    gcloud run deploy polls-service \
        --platform managed \
        --region REGION \
        --image gcr.io/PROJECT_ID/polls-service
    

프로덕션 구성

이제 Django 배포가 작동하고 있지만 애플리케이션을 프로덕션에 즉시 사용할 수 있도록 추가 단계를 수행할 수 있습니다.

디버깅 사용 중지

mysite/settings.pyDEBUG 변수가 False로 설정되었는지 확인합니다. 이렇게 하면 사용자에게 자세한 오류 페이지가 표시되지 않아 구성에 관한 정보가 유출될 수 있습니다.

데이터베이스 사용자 권한 제한

Cloud SQL을 사용하여 생성된 모든 사용자는 cloudsqlsuperuser 역할과 연결된 권한(CREATEROLE, CREATEDB, LOGIN)이 부여됩니다.

Django 데이터베이스 사용자가 이러한 권한을 갖지 않도록 하려면 PostgreSQL에서 수동으로 사용자를 생성합니다. psql 대화형 터미널이 설치되어 있거나 이 도구가 사전 설치된 Cloud Shell을 사용해야 합니다.

Console

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  2. Cloud Shell에서 기본 제공 터미널을 사용하여 INSTANCE_NAME 인스턴스에 연결합니다.

    gcloud sql connect INSTANCE_NAME --user postgres
    
  3. postgres 사용자 비밀번호를 입력합니다.

    현재 psql을 사용 중입니다. postgres=> 프롬프트가 표시됩니다.

  4. 사용자를 생성합니다.

    CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
    

    PASSWORD를 임의의 고유한 비밀번호로 바꿉니다.

  5. 새 데이터베이스에 대한 전체 권리를 새 사용자에게 부여합니다.

    GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
    
  6. psql을 종료합니다.

    \q
    

gcloud

  1. SQL 인스턴스에 대한 연결을 시작합니다.

    gcloud sql connect INSTANCE_NAME --user postgres
    

    INSTANCE_NAME을 생성된 Cloud SQL 인스턴스로 바꿉니다.

  2. postgres 사용자 비밀번호를 입력합니다.

    현재 psql을 사용 중입니다. postgres=> 프롬프트가 표시됩니다.

  3. 사용자를 생성합니다.

    CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
    
  4. 새 데이터베이스에 대한 전체 권리를 새 사용자에게 부여합니다.

    GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
    
  5. psql을 종료합니다.

    \q
    

최소 권한 설정

기본적으로 이 서비스는 기본 Compute 서비스 계정을 사용하여 배포됩니다. 하지만 기본 서비스 계정을 사용하면 너무 많은 권한이 제공되는 경우도 있습니다. 보다 제한이 적용되도록 하려면 자체 서비스 계정을 만들고 서비스에 필요한 권한만 할당해야 합니다. 필요한 권한은 특정 서비스에서 사용하는 리소스에 따라 서비스마다 다를 수 있습니다.

이 서비스에 필요한 최소 프로젝트 역할은 다음과 같습니다.

  • Cloud Run 호출자
  • Cloud SQL 클라이언트
  • 미디어 버킷의 스토리지 관리자
  • Django 설정 보안 비밀의 Secret Manager 접근자 Django 관리자 보안 비밀에 대한 액세스는 서비스 자체에서 필요하지 않습니다.

필요한 권한이 있는 서비스 계정을 만들고 서비스에 할당하려면 다음을 실행합니다.

  1. gcloud CLI에서 필요한 역할이 있는 서비스 계정을 만듭니다.

    gcloud iam service-accounts create polls-service-account
    SERVICE_ACCOUNT=polls-service-account@PROJECT_ID.iam.gserviceaccount.com
    
    # Cloud Run Invoker
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/run.invoker
    
    # Cloud SQL Client
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/cloudsql.client
    
    # Storage Admin, on the media bucket
    gsutil iam ch \
        serviceAccount:${SERVICE_ACCOUNT}:roles/storage.objectAdmin \
        gs://MEDIA_BUCKET
    
    # Secret Accessor, on the Django settings secret.
    gcloud secrets add-iam-policy-binding django_settings \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/secretmanager.secretAccessor
    
  2. 서비스를 새 서비스 계정과 연결하여 배포합니다.

    gcloud run services update polls-service \
        --platform managed \
        --region REGION \
        --service-account ${SERVICE_ACCOUNT}
    

코드 이해하기

샘플 애플리케이션

Django 샘플 앱은 표준 Django 도구를 사용하여 생성되었습니다. 다음 명령어는 프로젝트와 설문조사 앱을 만듭니다.

django-admin startproject mysite
python manage.py startapp polls

기본 보기, 모델 및 경로 구성이 첫 번째 Django 앱 작성(1부2부 참조)에서 복사되었습니다.

Secret Manager의 보안 비밀

settings.py 파일에는 Secret Manager Python API를 사용하여 명명된 보안 비밀의 최신 버전을 검색하고 이를 환경에 가져오는 코드가 포함됩니다(django-environ 사용).

# SECURITY WARNING: don't run with debug turned on in production!
# Change this to "False" when you are ready for production
env = environ.Env(DEBUG=(bool, True))
env_file = os.path.join(BASE_DIR, ".env")

# Attempt to load the Project ID into the environment, safely failing on error.
try:
    _, os.environ["GOOGLE_CLOUD_PROJECT"] = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError:
    pass

if os.path.isfile(env_file):
    # Use a local secret file, if provided

    env.read_env(env_file)
# ...
elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
    # Pull secrets from Secret Manager
    project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")

    client = secretmanager.SecretManagerServiceClient()
    settings_name = os.environ.get("SETTINGS_NAME", "django_settings")
    name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
    payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")

    env.read_env(io.StringIO(payload))
else:
    raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")

보안 비밀은 구성해야 하는 다양한 보안 비밀의 수를 줄이기 위해 여러 보안 비밀 값을 저장하는 데 사용되었습니다. 명령줄에서 직접 superuser_password를 만들 수도 있지만 대신 파일 기반 메서드가 사용되었습니다. 명령줄에서 생성된 경우 파일 끝에 새 줄 문자가 없는지 확인하면서 head -c를 사용하여 무작위로 생성된 문자열 길이를 확인합니다. 새 줄 문자가 있으면 Django 관리자에 비밀번호가 입력될 때 문제가 발생합니다.

CSRF 구성

Django에는 교차 사이트 요청 위조(CSRF)에 대한 보호 기능이 내장되어 있습니다. Django 4.0부터, 작동 방식에 대한 변경사항은 호스팅된 URL을 Django에게 알리는 것이 중요함을 의미합니다. 이렇게 하면 데이터를 제출하는 사용자에게 최상의 보호를 제공할 수 있습니다.

settings.py 파일에 앱의 URL을 환경 변수로 제공합니다. 이 값은 Django가 관련 설정에 사용하는 값입니다.

# SECURITY WARNING: It's recommended that you use this when
# running in production. The URL will be known once you first deploy
# to Cloud Run. This code takes the URL and converts it to both these settings formats.
CLOUDRUN_SERVICE_URL = env("CLOUDRUN_SERVICE_URL", default=None)
if CLOUDRUN_SERVICE_URL:
    ALLOWED_HOSTS = [urlparse(CLOUDRUN_SERVICE_URL).netloc]
    CSRF_TRUSTED_ORIGINS = [CLOUDRUN_SERVICE_URL]
    SECURE_SSL_REDIRECT = True
    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
else:
    ALLOWED_HOSTS = ["*"]

로컬 보안 비밀 재정의

로컬 파일 시스템에서 .env 파일이 발견되면 Secret Manager의 값 대신 사용됩니다. .env 파일을 로컬에서 만들면 로컬 테스트(예: SQLite 데이터베이스에 대한 로컬 개발 또는 기타 로컬 설정)에 도움이 될 수 있습니다.

데이터베이스 연결

settings.py 파일에는 SQL 데이터베이스에 대한 구성이 포함되어 있습니다. USE_CLOUD_SQL_AUTH_PROXY를 설정하면 Cloud SQL 인증 프록시 사용을 유추하도록 DATABASES 설정이 변경됩니다.

# Use django-environ to parse the connection string
DATABASES = {"default": env.db()}

# If the flag as been set, configure to use proxy
if os.getenv("USE_CLOUD_SQL_AUTH_PROXY", None):
    DATABASES["default"]["HOST"] = "127.0.0.1"
    DATABASES["default"]["PORT"] = 5432

클라우드 저장 정적

또한 settings.py 파일은 django-storages를 사용하여 Cloud Storage 미디어 버킷을 프로젝트에 직접 통합합니다.

# Define static storage via django-storages[google]
GS_BUCKET_NAME = env("GS_BUCKET_NAME")
STATIC_URL = "/static/"
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
STATICFILES_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_DEFAULT_ACL = "publicRead"

Cloud Build로 자동화

cloudmigrate.yaml 파일은 일반적인 이미지 빌드 단계(컨테이너 이미지를 만들고 Container Registry로 내보내기) 뿐만 아니라 Django migratecollectstatic 명령어도 수행합니다. 이렇게 하려면 Cloud SQL 인증 프록시 도우미인 app-engine-exec-wrapper를 사용하여 데이터베이스에 액세스해야 합니다.

steps:
  - id: "build image"
    name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}", "."]

  - id: "push image"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"]

  - id: "apply migrations"
    name: "gcr.io/google-appengine/exec-wrapper"
    args:
      [
        "-i",
        "gcr.io/$PROJECT_ID/${_SERVICE_NAME}",
        "-s",
        "${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}",
        "-e",
        "SETTINGS_NAME=${_SECRET_SETTINGS_NAME}",
        "--",
        "python",
        "manage.py",
        "migrate",
      ]

  - id: "collect static"
    name: "gcr.io/google-appengine/exec-wrapper"
    args:
      [
        "-i",
        "gcr.io/$PROJECT_ID/${_SERVICE_NAME}",
        "-s",
        "${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}",
        "-e",
        "SETTINGS_NAME=${_SECRET_SETTINGS_NAME}",
        "--",
        "python",
        "manage.py",
        "collectstatic",
        "--verbosity",
        "2",
        "--no-input",
      ]

substitutions:
  _INSTANCE_NAME: django-instance
  _REGION: us-central1
  _SERVICE_NAME: polls-service
  _SECRET_SETTINGS_NAME: django_settings

images:
  - "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"

이 구성에는 대체 변수가 사용됩니다. 파일의 값을 직접 변경하면 마이그레이션 시 --substitutions 플래그가 삭제될 수 있습니다.

이 구성에서는 기존 마이그레이션만 적용됩니다. 마이그레이션은 '로컬에서 앱 실행'에 정의된 Cloud SQL 인증 프록시 메서드를 사용하여 로컬로 만들어야 합니다. 필요에 따라 이 템플릿을 확장하여 다른 manage.py 명령어를 실행할 수 있습니다.

두 개의 명령어를 실행하지 않고 단일 구성에 배포를 포함하도록 Cloud Build 구성을 확장하려면 Cloud Build를 사용하여 git에서 지속적 배포를 참조하세요. 이렇게 하려면 설명된 대로 IAM을 변경해야 합니다.

데이터 마이그레이션을 통해 수퍼유저 생성

Django 관리 명령어 createsuperuser는 사용자가 프롬프트에 대한 응답으로 정보를 입력할 수 있는 경우에만 대화형으로 실행할 수 있습니다. 이 명령어를 Cloud SQL 프록시와 함께 사용하고 로컬 Docker 설정 내에서 명령어를 실행할 수도 있지만 또 다른 방법은 수퍼유저를 데이터 마이그레이션으로 만드는 것입니다.

import os

from django.contrib.auth.models import User
from django.db import migrations
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps

import google.auth
from google.cloud import secretmanager

def createsuperuser(apps: StateApps, schema_editor: DatabaseSchemaEditor) -> None:
    """
    Dynamically create an admin user as part of a migration
    Password is pulled from Secret Manger (previously created as part of tutorial)
    """
    if os.getenv("TRAMPOLINE_CI", None):
        # We are in CI, so just create a placeholder user for unit testing.
        admin_password = "test"
    else:
        client = secretmanager.SecretManagerServiceClient()

        # Get project value for identifying current context
        _, project = google.auth.default()

        # Retrieve the previously stored admin password
        PASSWORD_NAME = os.environ.get("PASSWORD_NAME", "superuser_password")
        name = f"projects/{project}/secrets/{PASSWORD_NAME}/versions/latest"
        admin_password = client.access_secret_version(name=name).payload.data.decode(
            "UTF-8"
        )

    # Create a new user using acquired password, stripping any accidentally stored newline characters
    User.objects.create_superuser("admin", password=admin_password.strip())

class Migration(migrations.Migration):

    initial = True
    dependencies = []
    operations = [migrations.RunPython(createsuperuser)]

삭제

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

프로젝트 삭제

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

다음 단계