在谷歌应用引擎(GAE)上灵活地构建Rails6应用和异步处理环境(sidekiq)

はじめに

ちょっとした興味からGCPの勉強を初めて3日目にしてやっと Google App Engine(以降GAE)に当初のゴールだった Railsアプリとsidekiqのワーカー両方をデプロイし起動するところまで出来ました。ググってみるとRails アプリ単体をGAEにデプロイする記事はたくさんあったのですが、sidekiq も合わせてGAEにデプロイする手順までまとめた記事は見かけなかったのでここにまとめたいと思います。

整体图

    • GAEで別サービスで App(Rails), Worker(Sidekiq) の両方をデプロイし起動させる

 

    • DBにはCloud SQLのPostgres を利用する

 

    Cloud MemorystoreのRedisを利用してSidekiqのジョブ管理を行う
App.png

前提 tí)

    • 簡単なRailsアプリ、Sidekiqのワーカーは実装済みとする

 

    • GCPでプロジェクをは作成済みとする。今回のプロジェクトIDは (仮) rails-app-sample とする。

 

    • 環境

Rails 6.1.0
Sidekiq 6.1.2

不解释/不说明

    sidekiq の初期設定

样例应用程序 lì xù)

    • id, name, created_at, updated_at カラムを保持する items テーブル

 

    • RailsのAPIモード上で scaffold を利用しAPIを実装

GET /items
POST /items

数据库架构.rb

  create_table "items", force: :cascade do |t|
    t.string "name", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

config/routes.rb

Rails.application.routes.draw do
  resources :items
end

app/controllers/items_controller.rb

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :update, :destroy]

  # GET /items
  def index
    @items = Item.all

    render json: @items
  end

  # POST /items
  def create
    ItemCreatorWorker.perform_in(3.seconds, item_params.to_h)
    render json: { message: 'success' }, status: :accepted
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_item
      @item = Item.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def item_params
      params.fetch(:item, {}).permit(:name)
    end
end

app/workers/item_creator_worker.rb

class ItemCreatorWorker
  include Sidekiq::Worker

  def perform(options = {})
    @item = Item.new(options)
    @item.save
  end
end

操作步骤

安装 Cloud SDK

为了部署GAE,需要使用gcloud命令,因此需要安装Cloud SDK。请参考Google Cloud SDK的安装 | Cloud SDK文档,了解详细信息。

使用gcloud命令进行认证。

# Google アカウントの選択とアクセスの許可を行う認証画面がブラウザ上で開かれるので許可をする
$ gcloud auth login

# プロジェクト一覧を確認する
$ gcloud projects list

# gclould コマンドで操作したいプロジェクトのプロジェクトIDを指定する
$ gcloud config set project [PROJECT ID]

安装appengine gem

appengine gemはDBマイグレーションを App Engine から実行するために必要なので追加してください。

gem "appengine"

これで以下のようなコマンドが実行できるようになります。

$ bundle exec rake appengine:exec -- bundle exec rake db:migrate

准备GAE环境

gcloud app create を実行し、リージョンを選択することでApp Engineアプリケーションが作成できます。これでいつでもデプロイは可能な状態となりました。

$ gcloud app create
You are creating an app for project [my-project2-300306].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.

Please choose the region where you want your App Engine application
located:

 [1] asia-east2
 [2] asia-northeast1
 [3] asia-northeast2
 [4] asia-northeast3
 [5] asia-south1
 [6] asia-southeast2
 [7] australia-southeast1
 [8] europe-west
 [9] europe-west2
 [10] europe-west3
 [11] europe-west6
 [12] northamerica-northeast1
 [13] southamerica-east1
 [14] us-central
 [15] us-east1
 [16] us-east4
 [17] us-west2
 [18] us-west3
 [19] us-west4
 [20] cancel
Please enter your numeric choice:  2

Creating App Engine application in project [my-project2-300306] and region [asia-northeast1]....done.
Success! The app is now created. Please use `gcloud app deploy` to deploy your first app.

参考: ロケーション – リージョンとゾーン  |  Google Cloud

使用Cloud SQL准备PostgreSQL版。

    1. 在全局菜单中选择“SQL”,然后选择“创建实例”。

 

    1. 选择“PostgreSQL”。

 

    1. 输入实例ID和密码,选择位置和数据库版本。这次我们选择从公共IP连接。机器类型和存储之后可以根据个人喜好选择。

 

    点击“创建”按钮,大约10分钟后将创建PostgreSQL。

创建DB用户

    1. SQL -> ユーザーメニューを選択後、「ユーザーアカウントを追加」ボタンを押す

 

    1. インスタンスに固有のユーザーを作成するため、ユーザー名・パスワードを入力後、「追加」ボタンを押す

 

    これでDB接続ユーザーが作成されました

创建数据库

    选择数据库后,点击“创建数据库”按钮。输入数据库名称后,点击“创建”按钮即可完成。

在云端存储上准备 Redis。

$ gcloud services enable redis.googleapis.com を実行して Google Cloud Memorystore for Redis API を有効にする
Memorystore -> Redis から「インスタンスを作成」ボタンを押す
インスタンスID, 表示名を入力、リージョン・ゾーン・バージョンなどを選択し、「作成」ボタンを押します。これで Redis が作成されます

在应用程序的根目录下准备以下的Dockerfile。

FROM ruby:2.6.6

ENV LANG C.UTF-8

RUN apt-get update -qq && apt-get install -y build-essential

RUN gem install bundler

RUN mkdir /app
WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install

COPY . /app

EXPOSE 8080

# アプリとWorker両方でDockerfileを兼用するため起動スクリプトを別途実行するようにする
CMD ["/bin/sh", "bootstrap.sh"]

app.yaml: web側サービスの構成ファイルを用意する

# 独自のDockerイメージを利用するため custom を入力
runtime: custom

# フレキシブル環境を利用するため flex を入力
env: flex

# env_variables で環境変数の設定ができる。このサービス側は web として認識し、bootstrap.sh 内で実行するコマンドを切り替えるため、 `SERVICE_TYPE: 'web'` としました。
# 他にも良い方法があれば知りたいです。
env_variables:
  SERVICE_TYPE: 'web'

# 今回は検証環境として最低限のスペックで構築します。
manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

# 【重要】 GAE から Cloud SQLのDBに接続できるようにするため、 cloud_sql_instances に CLOUD SQL で作成した該当DBの接続名(例: bamboo-striker-300213:asia-northeast1:sample-app-db )を入力します。
beta_settings:
  cloud_sql_instances: [YOUR_INSTANCE_CONNECTION_NAME]

#  DBパスワードなどの秘匿情報を
includes:
  - secret.yaml

worker.yaml: 非同期処理側サービスの構成ファイルを用意する

runtime: custom
env: flex
# 用途がわかるようにサービス名をつけておきます
service: worker

env_variables:
  # bootstrap.shで実行コマンドを切り替えるため、明示的に以下を定義します。
  SERVICE_TYPE: 'worker'

manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

beta_settings:
  cloud_sql_instances: [YOUR_INSTANCE_CONNECTION_NAME]

includes:
  - secret.yaml

secret.yaml: 秘匿情報のみを記載した環境変数を定義するファイルを用意する

アプリのサービス構成ファイルでは includes ディレクティブ を使用することで、別ファイルを読み込むことが出来ます。今回は秘匿情報を定義した構成ファイルを用意し、以下のように定義ます。

env_variables:
  SECRET_KEY_BASE: [SECRET_KEY_BASE]
  DATABASE_USER: 'sample-app-user'
  DATABASE_PASSWORD: 'sample-app-user-password'
  DATABASE_NAME: 'sample-app-db'
  # /cloudsql/[YOUR_INSTANCE_CONNECTION_NAME] の形式で定義することにより、GAE から Cloud SQL へ UNIX ドメインソケットで接続できるようになります。
  DATABASE_HOST: '/cloudsql/[YOUR_INSTANCE_CONNECTION_NAME]'
  RAILS_ENV: 'production'
  # web/workerのサービスから Reis に接続できるようにするため以下も定義します。
  REDIS_URL: 'redis://[REDIS_IP_ADDRESS]:6379'

参考: App Engine から Cloud SQL への接続  |  Google Cloud

bootstrap.sh: web, worker用の起動スクリプトを用意する

#!/bin/bash

if [ "$SERVICE_TYPE" = "web" ]; then
  bundle exec rackup -p 8080 -o '0.0.0.0'
elif [ "$SERVICE_TYPE" = "worker" ]; then
  bundle exec sidekiq -C config/sidekiq.yml
fi

在这里先部署web端

この後データベースの作成、マイグレーションを実施するためweb側をデプロイしておきます。

$ gcloud app deploy

准备迁移

Cloud SQL Admin APIの有効

执行以下操作以启用Cloud SQL Admin API。您将能够创建和迁移数据库等功能。

$ gcloud services enable sqladmin.googleapis.com

向Cloud Build中添加Cloud SQL客户端控制。

Cloud Build から Cloud SQL Admin API 経由でアクセスできるように Cloud Build のサービスアカウントに Cloud SQL クライアント のロールを追加します。これでアクセスできるようになります。

image.png

DBの接続状態を確認

$ bundle exec rake appengine:exec -- bundle exec rake db:migrate:status

特にCloud Buildや認証関連のエラーが発生しなければOK!

---------- CONNECT CLOUDSQL ----------
cloud_sql_proxy is running.
Connections: my-project2-300306:asia-northeast1:sample-app-db.

---------- EXECUTE COMMAND ----------
bundle exec rake db:migrate:status
Schema migrations table does not exist yet.
ERROR
ERROR: build step 0 "gcr.io/google-appengine/exec-wrapper:latest" failed: step exited with non-zero status: 1
--------------------------------------------------------------------------------------------------------------

マイグレーションを実行!!

$ bundle exec rake appengine:exec -- bundle exec rake db:migrate

这样需要的表格也顺利创建了。

最后再次部署web/worker并完成

時間はかかりますが待っていればこれでweb, worker構成の完了です! うおおおお。

$ gcloud app deploy app.yaml worker.yaml

动作验证

Item数据的注册处理已经在工作方面异步注册,等待几秒后就会被注册!

$ curl -s https://my-project2-300306.an.r.appspot.com/items | jq .
[]
$ curl -X POST -H "Content-Type: application/json" -d '{"name":"ageage"}' https://my-project2-300306.an.r.appspot.com/items
{"message":"success"}
$ curl -s https://my-project2-300306.an.r.appspot.com/items | jq .
[
  {
    "id": 1,
    "name": "ageage",
    "created_at": "2020-12-31T09:58:19.770Z",
    "updated_at": "2020-12-31T09:58:19.770Z"
  }
]

来到这里花了整整三天,不可避免地遇到了各种各样的事情…

トラブルシューティング

ERROR: (gcloud.app.deploy) NOT_FOUND: Unable to retrieve P4SA: [service-XXXX@gcp-gae-service.iam.gserviceaccount.com] from GAIA. Could be GAIA propagation delay or request from deleted apps.

初回時に gcloud app deploy をした際にこのエラーが表示されることがあります。ググっても GAIA が何者か分からず原因不明なのですが、もう一度コマンドを再実行すると発生しなくなります。謎…。

ERROR: (gcloud.app.deploy) Error Response: [8] The region asia-northeast1 does not have enough resources available to fulfill the request. Please try again later.

これは結構な頻度で発生します。内容としては「該当のリージョンで該当のリクエストを満たす十分なリソースが無い。後でやり直してください」とのこと。文字通り時間をおいてからやり直すとうまくいきます

最后

年末年始の自由研究としてちょっとした興味がてら GAE 上でのRailsアプリケーション環境の構築方法を調査して構築してみました。ざっと触れることでGAE フレキシブルを利用した環境構築方法はざっと把握することができました。GCPから日本語の Rails 環境構築方法、Cloud SQLでのPostgreSQLへの接続方法などの資料が丁寧に提供されておりとても助かりました。一方構築し始めて気づいたことは、意外と非同期処理環境の構築方法は日本語・英語共にあまりなく、本記事は珍しい方の内容になるかと思います。あくまで自分用のメモとして列挙した内容であり読みづらい点は申し訳ないです。

GAEには、その他タスクキュー、cronジョブ、ファイアーウォールルール、Memcacheなどのなどの機能もあるので、全容把握のためざっくりでも良いので触れてみたいです。

顺便提一下,到目前为止的成本是910日元(不包括免费部分)。

image.png

请将以下内容以中文进行本地化,只需给出一个选项:

参考传统文化

    • GoogleCloudPlatform/appengine-ruby: Optional integration library for the Ruby runtime for Google App Engine

 

    • 今回利用したサンプルRailsアプリケーション hypermkt/rails-playground

Rails 5 での Cloud SQL for PostgreSQL の使用  |  Ruby  |  Google Cloud
app.yaml によるアプリの構成  |  App Engine フレキシブル環境用カスタム ランタイム  |  Google Cloud
App Engine から Cloud SQL への接続  |  Google Cloud

广告
将在 10 秒后关闭
bannerAds