Search by

    cloudrunでdjangoを構築する

    cloudrunでdjangoを構築するチュートリアルの私的メモ

    概要

    djangoをclould runで動かしましす。

    公式の下記チュートリアルをみやってみました。解説的なものになれば幸いです。 https://cloud.google.com/python/django/run?hl=ja

    アーキテクト

    気が向いたらまとめるかもしれない。

    buildまでの流れ

    気が向いたらまとめるかもしれない。

    その他メモ的なこと

    cloudmigrate.yamlの探索

    cloudmigrate.yaml
    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でcloud builderの指定を行う
        # Cloud Builder
        # 公式イメージ
        # https://github.com/GoogleCloudPlatform/cloud-builders
        name: "gcr.io/google-appengine/exec-wrapper"
        args:
          [
            "-i",
            "gcr.io/$PROJECT_ID/${_SERVICE_NAME}",
            "-s",
            # cloudSQLの接続情報を不可
            "${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}",
            "-e",
            # シークレットマネージャーで管理している
            # シークレット名の読み出し
            # https://cloud.google.com/secret-manager?hl=ja
            "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",
            "--no-input",
          ]
    # substitutions = 置換 
    # イメージをブッシュするときに引数として渡したいモノをここで変数化しておく。
    substitutions:
      _INSTANCE_NAME: django-instance
      _REGION: us-central1
      _SERVICE_NAME: polls-service
      _SECRET_SETTINGS_NAME: django-settings
    
    images:
      - "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"

    google-appengine/exec-wrapper

    SQLproxyを実行するためのbuilder

    cloudrunのbuildでcloudSQLに接続する方法として、下記の方法も利用できそう。 https://cloud.google.com/sql/docs/mysql/connect-docker?hl=ja

    どちらも結構利用シーンがありそうだけど、あまり目立っていない印象。 ベスプラが知りたい。

    動的なスーパーユーザー作成

    今回のコードではDBのmigrationを実行する際に、動的にスーパーユーザーを作成して、その際にシークレットマネージャーからパスワードを取得している。

    https://cloud.google.com/python/django/run?hl=ja#superuser_creation_with_data_migrations

    結構トリッキーだと思う。 こうすることで、デプロイする度に管理者が作成されるが、パスワードは共通とすることができる。 なるほど。

    .envファイルでのシークレットの管理

    .gcloudignoreにて.envファイルは管理対象外になっている。 .env自体の管理方法は別途検討する必要があるが、この方法ならシークレットがソースコードに紛れてデプロイされることはない。

    setting.py
    # If no .env has been provided, pull it from Secret Manager, storing it locally
    # .envが存在しない場合、Secret Managerから値が参照され、.envが作成される。
    if not os.path.isfile(".env"):# 本番環境では存在しない
        # これよくわからない
        if os.getenv('TRAMPOLINE_CI', None):
            payload = f"SECRET_KEY=a\nGS_BUCKET_NAME=none\nDATABASE_URL=sqlite://{os.path.join(BASE_DIR, 'db.sqlite3')}"
        else:
            # [START cloudrun_django_secretconfig]
            import google.auth
            # [クライアントライブラリで読み出す。
            from google.cloud import secretmanager_v1
    
            _, project = google.auth.default()
    
            if project:
                client = secretmanager_v1.SecretManagerServiceClient()
    
                SETTINGS_NAME = os.environ.get("SETTINGS_NAME", "django_settings")
                name = f"projects/{project}/secrets/{SETTINGS_NAME}/versions/latest"
                payload = client.access_secret_version(name=name).payload.data.decode(
                    "UTF-8"
                )
        # open(env_file, "w") as fはファイルが存在しない場合は新規作成
        with open(env_file, "w") as f:
            f.write(payload)
    
    env = environ.Env()
    # ローカル、本番どちらも最終的には、.emvを参照する様にする。
    env.read_env(env_file)

    DATABASE_URLの指定

    下記の様に.envに用意して、 設定

    DATABASE_URL=postgres://django:PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME

    下記の様にsettins.pyかr読み出す。 読み出し

    settins.py
    # [START cloudrun_django_dbconfig]
    # Use django-environ to define the connection string
    DATABASES = {"default": env.db()}
    # [END cloudrun_django_dbconfig]

    この記法にすると、django-environenv.db()DATABASE_URLを解読して、いい感じにしてくれている。 このの記法はdj-database-urlからインスパイアされている模様。(あるいはこちらが本家?)

    Twelve-Factor App

    結構古いけど、webアプリを作成するのに参考になる考え方。

    ハマりポイント

    下記のファイルの62行目が間違っているので、修正が必要です。 run/django/cloudmigrate.yaml

    修正前

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

    修正後

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

    django-settingsdjango_settingsに変更する。

    実行時に明示的にdjango_settingsを指定して実行しても動作するはず

    次に試したい

    ローカル開発の環境も整えたい。