Como executar o Rails no ambiente do Cloud Run

Saiba como implantar um aplicativo Rails de exemplo no Cloud Run e como integrar bancos de dados gerenciados, armazenamento de objetos, secrets criptografados e pipelines de compilação com computação sem servidor.

A implantação de aplicativos do Rails envolve a integração de vários serviços para formar um projeto coeso. Para usar este tutorial, é necessário ter familiaridade com o desenvolvimento da Web com Rails.

Este tutorial requer o Ruby 3.0 ou mais recente (o Ruby 2.7 também é compatível, consulte a seção Entender o código) e o Rails 6 ou mais recente.

Diagrama mostrando a arquitetura da implantação.
O site do Rails é veiculado pelo Cloud Run, que usa vários serviços de backup para armazenar diferentes tipos de dados (informações do banco de dados relacional, mídia recursos, secrets de configuração e imagens de contêiner). Os serviços de back-end são atualizados pelo Cloud Build como parte de uma tarefa de criação e migração.

Objetivos

  • Criar e conectar um banco de dados do Cloud SQL ao Active Record
  • Criar e usar o Secret Manager para armazenar e acessar uma chave mestra do Rails com segurança
  • Hospedar mídia e arquivos enviados pelo usuário do Active Storage no Cloud Storage
  • Usar o Cloud Build para automatizar migrações de builds e bancos de dados
  • Implantar um app Rails no Cloud Run

Custos

Antes de começar

  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. Verifique se as permissões suficientes estão disponíveis para a conta usada neste tutorial.

Como preparar o ambiente

Definir o projeto padrão

Defina a configuração padrão do projeto para a CLI gcloud executando o seguinte comando:

gcloud config set project PROJECT_ID

Substitua PROJECT_ID pelo ID do projeto do Google Cloud recém-criado.

Como clonar o app Rails

O código para o aplicativo de exemplo Rails está no repositório GoogleCloudPlatform/ruby-docs-samples no GitHub.

  1. Clone o repositório:

    git clone https://github.com/GoogleCloudPlatform/ruby-docs-samples.git
    
  2. Acesse o diretório com o exemplo de código e execute os seguintes comandos para garantir que o aplicativo esteja configurado corretamente com gems e dependências necessárias:

    Linux/macOS

    cd ruby-docs-samples/run/rails
    bundle install
    

    Windows

    cd ruby-docs-samples\run\rails
    bundle install
    

Como preparar os serviços de apoio

Neste tutorial, usamos vários serviços do Google Cloud para fornecer o banco de dados, o armazenamento de mídia e o armazenamento de secrets compatíveis com o projeto Rails implantado. Esses serviços são implantados em uma região específica. Para a eficácia entre serviços, é melhor que todos os serviços sejam implantados na mesma região. Para mais informações sobre a região mais próxima de você, consulte Produtos disponíveis por região.

Configurar uma instância do Cloud SQL para PostgreSQL

O Rails é compatível com vários bancos de dados relacionais, incluindo vários oferecidos pelo Cloud SQL. Neste tutorial, usamos o PostgreSQL, um banco de dados de código aberto muito usado por apps Rails.

Nas seções a seguir, descrevemos como criar uma instância, banco de dados e usuário de banco de dados do PostgreSQL para seu app Rails.

Criar uma instância do PostgreSQL

Console

  1. No Console do Cloud, acesse a página Instâncias do Cloud SQL.

    Acessar a página "Instâncias" do Cloud SQL

  2. Clique em Criar instância.

  3. Clique em Escolher PostgreSQL.

  4. No campo ID da instância, digite um nome para a instância (INSTANCE_NAME).

  5. No campo Senha, insira uma senha para o usuário postgres.

  6. Use os valores padrão dos outros campos.

  7. Clique em Criar instância.

gcloud

  • Crie a instância do PostgreSQL:

    gcloud sql instances create INSTANCE_NAME \
        --database-version POSTGRES_12 \
        --tier db-f1-micro \
        --region REGION
    

    Substitua:

    A instância leva alguns minutos para ser criar e para estar pronta para uso.

Crie um banco de dados

Console

  1. No Console do Cloud, acesse a página Instâncias do Cloud SQL.

    Acessar a página "Instâncias" do Cloud SQL

  2. Selecione a instância INSTANCE_NAME.

  3. Acesse a guia Bancos de dados.

  4. Clique em Create database.

  5. Na caixa de diálogo Nome do banco de dados, insira DATABASE_NAME.

  6. Clique em Criar.

gcloud

  • Crie o banco de dados na instância recém-criada:

    gcloud sql databases create DATABASE_NAME \
        --instance INSTANCE_NAME
    

    Substitua DATABASE_NAME por um nome para o banco de dados dentro da instância.

Criar um usuário

Gere uma senha aleatória para o usuário do banco de dados e grave-a em um arquivo chamado dbpassword:

cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1 > dbpassword

Console

  1. No Console do Cloud, acesse a página Instâncias do Cloud SQL.

    Acessar a página "Instâncias" do Cloud SQL

  2. Selecione a instância INSTANCE_NAME.

  3. Acesse a guia Usuários.

  4. Clique em Adicionar conta de usuário.

  5. Na caixa de diálogo Autenticação integrada:

    1. Digite o nome de usuário DATABASE_USERNAME.
    2. Insira o conteúdo do arquivo dbpassword como a senha PASSWORD.
  6. Clique em Adicionar.

gcloud

  • Crie o usuário na instância recém-criada e defina a senha como o conteúdo do dbpassword:

    gcloud sql users create DATABASE_USERNAME \
       --instance=INSTANCE_NAME --password=$(cat dbpassword)
    

    Substitua DATABASE_USERNAME por um nome para o usuário dentro da instância.

Configurar um bucket do Cloud Storage

É possível usar o Cloud Storage para hospedar recursos estáticos e mídia do Rails enviados pelo usuário em um armazenamento de objeto altamente disponível.

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: us-central1
    • 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

A ferramenta de linha de comando gsutil foi instalada como parte da instalação da CLI gcloud.

  • Criar um bucket do Cloud Storage Para criar um nome de bucket exclusivo do Cloud Storage, use o PROJECT_ID e um sufixo de sua escolha, MEDIA_BUCKET_SUFFIX. No Cloud Storage, os nomes dos buckets precisam ser globalmente exclusivos.

    gsutil mb -l REGION gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

Depois de criar um bucket, para tornar públicas as imagens enviadas, altere as permissões dos objetos de imagem para que possam ser lidas por todos.

Console

  1. No Console do Cloud, acesse a página Navegador do Cloud Storage.

    Acessar o navegador

  2. Na lista de buckets, clique no nome daquele que você quer tornar público.

  3. Selecione a guia Permissões na parte superior da página.

  4. Clique no botão Adicionar membros.

    A caixa de diálogo Adicionar membros é exibida.

  5. No campo Novos membros, insira allUsers.

  6. No campo Selecionar uma função, selecione o submenu Cloud Storage e clique na opção Visualizador do objeto de armazenamento.

  7. Clique em Salvar.

Depois que o intervalo for compartilhado publicamente, aparecerá um ícone de link para cada objeto na coluna de acesso público. Clique neste ícone para ver o URL do objeto.

Para informações detalhadas sobre erros de operações com falha no navegador do Ruby, consulte Solução de problemas.

gcloud

  • Use o comando gsutil iam ch para tornar todos os objetos públicos. Use o valor de MEDIA_BUCKET_SUFFIX que você usou quando criou o bucket.

    gsutil iam ch allUsers:objectViewer gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

Armazenar valores do secret no Secret Manager

Agora que os serviços de backup estão configurados, o Rails precisa de informações seguras, como senhas, para acessar esses serviços. Em vez de colocar esses valores diretamente no código-fonte do Rails, este tutorial usa o Rails Credentials e o Secret Manager para armazenar essas informações com segurança.

Criar um arquivo de credenciais criptografado e armazenar a chave como secret do Secret Manager

O Rails armazena secrets em um arquivo criptografado chamado "config/credentials.yml.enc". O arquivo pode ser descriptografado com o local config/master.key ou a variável de ambiente ENV[“RAILS_MASTER_KEY”]. Nesse arquivo, é possível armazenar a senha do banco de dados da instância do Cloud SQL e outras chaves de acesso para APIs externas.

É possível armazenar essa chave com segurança no Secret Manager. Em seguida, conceda ao Cloud Run e ao Cloud Build acesso à chave que permite acessar as respectivas contas de serviço. As contas de serviço são identificadas por um endereço de e-mail que contém o número do projeto.

  1. Gere o arquivo config/credentials.yml.enc com o seguinte comando:

    bin/rails credentials:edit
    

    O comando criará um config/master.key se nenhuma chave mestra for definida, e um arquivo config/credentials.yml.enc se o arquivo não existir. Um arquivo temporário será aberto no $EDITOR padrão com o conteúdo descriptografado para adicionar os secrets.

  2. Copie e cole a senha do banco de dados da instância PostgreSQL recém-criada do arquivo dbpassword no arquivo de credenciais:

    secret_key_base: GENERATED_VALUE
    gcp:
      db_password: PASSWORD
    

    Os secrets podem ser acessados com Rails.application.credentials. Por exemplo, Rails.application.credentials.secret_key_base retorna a base da chave secreta do aplicativo e Rails.application.credentials.gcp[:db_passsword] retorna a senha do banco de dados.

  3. O config/credentials/yml.enc é armazenado criptografado, mas config/master.key pode ser armazenado no Secret Manager.

    Console

    1. No Console do Cloud, acesse a página Secret Manager.

      Acessar a página "Gerenciador de secrets"

    2. Clique em Criar secret.

    3. No campo Nome, insira um nome para o secret RAILS_SECRET_NAME.

    4. Na caixa de diálogo Valor secreto, cole o valor mater.key na caixa.

    5. Clique em Criar secret.

    6. Na página "Detalhes do secret", anote o número do projeto:

      projects/PROJECTNUM/secrets/RAILS_SECRET_NAME

    7. Na guia Permissões, clique em Adicionar membro.

    8. No campo Novos membros, digite PROJECTNUM-compute@developer.gserviceaccount.com e pressione Enter.

    9. No campo Novos membros, digite PROJECTNUM@cloudbuild.gserviceaccount.com e pressione Enter.

    10. No menu suspenso Papel, selecione Acessador de secrets do Secret Manager.

    11. Clique em Salvar.

    gcloud

    1. Crie um novo secret com o valor de config/master.key:

      gcloud secrets create RAILS_SECRET_NAME --data-file config/master.key
      

      Substitua RAILS_SECRET_NAME por um nome para o novo secret.

    2. Para confirmar a criação do secret, verifique-o:

      gcloud secrets describe RAILS_SECRET_NAME
      
      gcloud secrets versions access latest --secret RAILS_SECRET_NAME
      
    3. Encontre o valor do número do projeto:

      gcloud projects describe PROJECT_ID --format='value(projectNumber)'
      
    4. Conceda acesso ao secret para a conta de serviço do Cloud Run:

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

      Substitua PROJECTNUM pelo valor de número do projeto acima.

    5. Conceda acesso ao secret para a conta de serviço do Cloud Build:

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

      Na saída, confirme se bindings lista as duas contas de serviço como membros.

Conectar o app Rails ao armazenamento e ao banco de dados produção

Neste tutorial, uma instância do PostgreSQL é usada como banco de dados de produção e o Cloud Storage como back-end de armazenamento. Para que o Rails se conecte ao banco de dados e ao bucket de armazenamento recém-criados, é necessário especificar todas as informações necessárias para acessá-los no arquivo .env. O arquivo .env contém a configuração das variáveis de ambiente do aplicativo. O aplicativo lerá esse arquivo usando a gem dotenv. Como os secrets são armazenados no credentials.yml.enc e no Secret Manager, o .env não precisa ser criptografado porque não contém nenhuma credencial confidencial.

  1. Para configurar o app Rails e se conectar ao banco de dados e ao bucket de armazenamento, abra o arquivo .env.
  2. Modifique a configuração do arquivo .env para o seguinte. Use o valor de MEDIA_BUCKET_SUFFIX usado quando você criou o bucket.

    PRODUCTION_DB_NAME: DATABASE_NAME
    PRODUCTION_DB_USERNAME: DATABASE_USERNAME
    CLOUD_SQL_CONNECTION_NAME: PROJECT_ID:REGION:INSTANCE_NAME
    GOOGLE_PROJECT_ID: PROJECT_ID
    STORAGE_BUCKET_NAME: PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

    Agora o app Rails está configurado para usar o Cloud SQL e o Cloud Storage na implantação no Cloud Run.

Conceder acesso ao Cloud Build para o Cloud SQL

Para que o Cloud Build aplique as migrações do banco de dados, é preciso conceder permissões para que o Cloud Build acesse o Cloud SQL.

Console

  1. No Console do Cloud, acesse a página Gerenciamento de identidade e acesso.

    Acessar a página "Gerenciamento de identidade e acesso"

  2. Para editar a entrada do membro PROJECTNUM@cloudbuild.gserviceaccount.com, clique em Editar membro.

  3. Clique em Adicionar outro papel.

  4. Na caixa de diálogo Selecionar um papel, selecione Cliente do Cloud SQL.

  5. Clique em Save.

gcloud

  • Conceda permissão para que o Cloud Build acesse o Cloud SQL:

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

Como implantar o app no Cloud Run

Agora que os serviços de backup estão configurados, implante o app como um serviço do Cloud Run.

  1. Usando o cloudbuild.yaml fornecido, use o Cloud Build para criar a imagem, executar as migrações do banco de dados e preencher os recursos estáticos:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    

    Substitua SERVICE_NAME pelo nome do serviço. A primeira versão leva alguns minutos para ser concluída. Se o tempo limite da compilação expirou, aumente a duração do tempo limite inserindo --timeout=2000 no comando de compilação acima.

  2. Quando a criação for bem-sucedida, implante o serviço Cloud Run pela primeira vez, definindo a região de serviço, a imagem de base e a instância do Cloud SQL conectada:

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

    Será exibida a resposta que mostra a implantação bem-sucedida, com o URL de serviço:

    Service [SERVICE_NAME] revision [SERVICE_NAME-00001-tug] has been deployed
     and is serving 100 percent of traffic at https://SERVICE_NAME-HASH-uc.a.run.app

  3. Para ver o serviço implantado, acesse o URL do serviço.

    Captura de tela da página de destino do aplicativo do álbum de gatos.
    Se o URL do serviço mostrar Álbum de fotos de gatos, você estará na página inicial do app.

  4. Tente fazer o upload de uma nova foto. Se o upload da foto for concluído, o aplicativo Rails foi implantado.

    Captura de tela da página de destino do aplicativo de álbum de gatos com uma foto.

Como atualizar o aplicativo

Embora as etapas iniciais de provisionamento e implantação tenham sido complexas, fazer atualizações é um processo mais simples:

  1. Execute o script de criação e migração do Cloud Build:

    gcloud builds submit --config cloudbuild.yaml \
         --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    
  2. Implante o serviço, especificando apenas a região e a imagem:

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

Noções básicas sobre o código

O app de exemplo do Rails foi criado usando comandos padrão do Rails. Os comandos a seguir criam o app cat_album e usam o comando scaffold para gerar um modelo, controlador e visualizações para o recurso de fotos:

rails new cat_album
rails generate scaffold Photo caption:text

Conexão com o banco de dados

O arquivo config/database.yml contém a configuração necessária para acessar seus bancos de dados em diferentes ambientes (desenvolvimento, teste, produção). Por exemplo, o banco de dados de produção é configurado para ser executado no Cloud SQL para PostgreSQL. O nome do banco de dados e o nome de usuário são definidos por meio de variáveis de ambiente no arquivo .env, enquanto a senha do banco de dados está armazenada no arquivo config/credentials.yml.enc, que requer o RAILS_MASTER_KEY para ser descriptografado.

Quando o aplicativo é executado no Cloud Run (totalmente gerenciado), ele se conecta à instância do PostgreSQL usando um soquete fornecido pelo ambiente do Cloud Run. Quando o aplicativo é executado na máquina local, ele se conecta à instância do PostgreSQL usando o proxy do Cloud SQL Auth.

production:
  <<: *default
  database: <%= ENV["PRODUCTION_DB_NAME"] %>
  username: <%= ENV["PRODUCTION_DB_USERNAME"] %>
  password: <%= Rails.application.credentials.gcp[:db_password] %>
  host: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["CLOUD_SQL_CONNECTION_NAME"] %>"

Mídia enviada pelo usuário e armazenada na nuvem

O Rails usa o Active Storage para fazer upload de arquivos para provedores de armazenamento em nuvem. Os arquivos config/storage.yml e config/environments/production.rb especificam o Cloud Storage como o provedor de serviços no ambiente de produção.

google:
  service: GCS
  project: <%= ENV["GOOGLE_PROJECT_ID"] %>
  bucket: <%= ENV["STORAGE_BUCKET_NAME"] %>
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :google

Automação com o Cloud Build

O arquivo cloudbuild.yaml executa não apenas as etapas comuns de criação de imagem (criação da imagem do contêiner e envio da imagem para o registro do contêiner), mas também as migrações de banco de dados do Rails. Eles exigem acesso ao banco de dados, que é realizado usando o app-engine-exec-wrapper, um auxiliar do proxy do Cloud SQL Auth.

steps:
  - id: "build image"
    name: "gcr.io/cloud-builders/docker"
    entrypoint: 'bash'
    args: ["-c", "docker build --build-arg MASTER_KEY=$$RAILS_KEY -t gcr.io/${PROJECT_ID}/${_SERVICE_NAME} . "]
    secretEnv: ["RAILS_KEY"]

  - 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"
    entrypoint: "bash"
    args:
      [
        "-c",
        "/buildstep/execute.sh -i gcr.io/${PROJECT_ID}/${_SERVICE_NAME} -s ${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME} -e RAILS_MASTER_KEY=$$RAILS_KEY -- bundle exec rails db:migrate"
      ]
    secretEnv: ["RAILS_KEY"]

substitutions:
  _REGION: us-central1
  _SERVICE_NAME: rails-cat-album
  _INSTANCE_NAME: cat-album
  _SECRET_NAME: rails-master-key

availableSecrets:
  secretManager:
  - versionName: projects/${PROJECT_ID}/secrets/${_SECRET_NAME}/versions/latest
    env: RAILS_KEY

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

Variáveis de substituição são usadas nessa configuração. Se você alterar os valores no arquivo, significa que a sinalização --substitutions pode ser descartada no momento da migração.

Nesta configuração, somente as migrações atuais do diretório db/migrate são aplicadas. Para criar arquivos de migração, consulte Migrações do Active Record.

Para criar a imagem e aplicar migrações, a configuração do Cloud Build precisa acessar o secret RAILS_MASTER_KEY do Secret Manager. O campo availableSecrets define a versão do secret e as variáveis de ambiente a serem usadas. O secret da chave mestra é transmitido como um argumento na etapa de imagem do build e, em seguida, é definido como o RAILS_MASTER_KEY no Dockerfile durante a criação da imagem.

ARG MASTER_KEY
ENV RAILS_MASTER_KEY=${MASTER_KEY}

Para estender a configuração do Cloud Build para incluir a implantação em uma configuração sem precisar executar dois comandos, consulte Implantação contínua do git usando o Cloud Build. Isso exige alterações no IAM, conforme descrito.

Compatível com Ruby 2.7

Embora este tutorial use o Ruby 3.0, ele também é compatível com o Ruby 2.7. Para usar o Ruby 2.7, altere a imagem base do Ruby no Dockerfile para 2.7

# Pinning the OS to buster because the nodejs install script is buster-specific.
# Be sure to update the nodejs install command if the base image OS is updated.
FROM ruby:3.0-buster

Limpeza

  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.