Github Actions & Google Cloud Run! Part 2

Github Logo

Dieser Beitrag gehört zu einer Serie!

Sollte dir der Beitrag gefallen haben schaue dir gerne noch die anderen Teile an.

Table of Contents

Was ist Google Cloud Run?

Es wird zum skalieren von Containeranwendungen benötigt. Unterstützen tut Cloud Run alles was in ein Docker Image passt. Doch wie genau baue ich nun dieses Docker-Image?

FROM ruby:2.7.4-buster

RUN (curl -sS https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor | apt-key add -) && \
    echo "deb https://deb.nodesource.com/node_14.x buster main"      > /etc/apt/sources.list.d/nodesource.list && \
    apt-get update && apt-get install -y nodejs lsb-release

RUN (curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -) && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

WORKDIR /app

# Application dependencies
COPY Gemfile Gemfile.lock ./

RUN gem install bundler && \
    bundle install


# Copy application code to the container image
COPY . /app

ENV RAILS_ENV=production
ENV RAILS_SERVE_STATIC_FILES=true
ENV SECRET_KEY_BASE=1239081230182390
# Redirect Rails log to STDOUT for Cloud Run to capture
ENV RAILS_LOG_TO_STDOUT=true
# [START cloudrun_rails_dockerfile_key]
ARG MASTER_KEY
ENV RAILS_MASTER_KEY=${MASTER_KEY}
# [END cloudrun_rails_dockerfile_key]

# pre-compile Rails assets with master key
RUN bundle exec rake assets:precompile

EXPOSE 8080
CMD ["sh", "start.sh"]
Diese Datei muss als „Dockerfile“ im Applikations root directory abgelegt werden. In der „start.sh“ ist nur eingetragen, die Rails App Datenbank zu migrieren und Rails zu starten.

bundle exec rails db:migrate
bundle exec rails db:seed

bin/rails server -b "0.0.0.0" -p "8080"
Testen kannst du das Image mithilfe von Docker Lokal. Du kannst das image auch lokal auf deinem Rechner bauen und anschließend starten. Das geht mithilfe von: „docker build .“ zum starten anschließend „docker run (generted container name)“.

Artifact Repository & Cloud Run

Damit Google Cloud Run überhaupt startet müssen wir zuvor das gebaute Docker Image bei einer öffentlichen Container Registry ablegen. Wenn es dein Code ist und es dich nicht stört, dass andere darauf zugreifen kannst du auch ganz einfach im free plan Docker Hub benutzen. Allerdings sind hier im kostenlosen Plan alle „gepushten“ images öffentlich zugänglich.
Da wir mit Kunden Applikationen arbeiten bauen wir natürlich eine private Docker registry in der GCloud.

Erstellung der Container Registry

Eine sogenannte „registry“ ist nichts anderes als eine Verwaltung für gebaute Docker Images. Sie werden automatisiert mit Versionen versehen und bei beliebe können sie über „Tags“ gesteuert werden.
Somit ist eine Registry im Endeffekt nur ein „binary“ speicher mit Versionierung und Zugriffskontrolle.

Wir können sie allerdings nicht automatisiert über die pipeline/Terraform erstellen. Da wir die Images unabhängig von dem Applikations Environment betrieben haben möchten.
Kurzgesagt: die Images und registry soll trotz löschen der App Ressourcen bestehen bleiben. Sonst würden wir ja ein weiteres Terraform file brauchen, da die registry bereits ein fertiges docker image beim start der Pipeline im Hintergrund haben sollte.

Die Lösung hierfür ist, die Docker Container Registry ebenfalls wie den „TFState Storage“ einmalig vorher per Hand zu erstellen. In der GCloud erstellt man hierfür eine „Artifact Registry“ mit dem Format „Docker“.

Github Actions

Da nun alle benötigten Cloud Ressourcen erstellt sind können wir mit dem automatisieren des ganzen Systems anfangen. Für unseren Zweck verwenden wir Github Actions, es funktioniert aber genauso gut mit allen anderen Git Verwaltungssystemen, die eine Pipeline Funktion anbieten also z.B. Gitlab, Jenkins aber auch Bitbucket…   Wichtig! Wie eine Github Actions Pipeline oder Pipelines Allgemein funktionieren erklären wir dir in kürze im ersten Teil dieser Serie! Zu beginn möchten wir eine Github pipeline triggern sobald eine Änderung am Master Branch vorgenommen wurde. Hierfür erstellen wir einen Ordner im Repository namens „mkdir -p .github/workflows“ hier können wir nun beliebig viele YAML Dateien (=“Workflows“) ablegen in diesem Fall nenne ich sie „deploy.yml“:  
name: "Test And Deploy Rails App To Google Cloud Run"
on:
  push:
    branches:
      - main
env:
  PROJECT_ID: test-project-1234
  PROJECT_NAME: test-project
  DOMAIN: test.royalzsoftware.de
  RUBY_VERSION: 2.7.4
Ebenfalls zu einem Namen des Workflows und der Bedingung wann der Workflow ausgeführt werden soll, deklarieren wir Environment Variablen. Die wir im späteren Verlauf noch benötigen werden. Bevor wir nun irgendetwas machen wollen wir das Docker Image bauen und in unserer per Hand angelegten Registry hochladen. Hierfür hat der Alex eine vorgefertigte Action gebaut, die ihr euch hier ansehen könnt. Diese integrieren wir nun in unseren Workflow:    

Bau des Rails Images

  build:
    name: Build and Push Docker Image To GCR
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: royalzsoftware/build-and-push-docker-image-to-gcp@master
        with:
          project_id: ${{env.PROJECT_ID}}
          project_name: ${{env.PROJECT_NAME}}
          google_credentials: ${{secrets.GOOGLE_CREDENTIALS}}
          secret_key_base: ${{secrets.RAILS_MASTER_KEY}}
Wie man nun merkt benötigst du an dieser stelle deinen „.terraform“ Ordner gar nicht mehr. Du solltest nur ein Grundverständnis bekommen wie Terraform mit Ressourcen und z.B. der .TFSTATE Datei im Hintergrund arbeitet.   Wenn du alles aufmerksam verfolgt hast, sollten dir nun zwei Sachen auffallen.

1. Google Credentials
Die Google credentials sind zwingend notwending um eine Verbindung zur Google Cloud zu bekommen. Hierfür musst du nur den Inhalt aus der Service Account Json Datei aus Teil 1 kopieren und als Github Actions Secret zum Repository hinzufügen. In unserem Fall geben wir dem Secret den Namen: „GOOGLE_CREDENTIALS“.

2. Rails Master Key
Wie ein Ruby credentials Manager im Hintergrund funktioniert erklären wir ebenfalls im ersten Teil. Aber sofern du dich ein bisschen mit Ruby und dem Credentials Manager auseinander gesetzt hast, solltest du den „Key“ ja bereits irgendwo bereit liegen haben. Alternativ können wir dir diese Dokumentation wärmstens ans Herz legen.

deploy Cloud Run and load balancer

 
  deploy_branch:
    needs: [build]
    if: ${{ github.ref_name != 'main' && github.ref_name != 'master' }}
    name: Deploy to Branch Environment
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - id: create_environment
        uses: royalzsoftware/rails-cloud-run-action@master
        with:
          project_id: ${{env.PROJECT_ID}}
          project_name: ${{env.PROJECT_NAME}}
          google_application_credentials: ${{secrets.GOOGLE_CREDENTIALS}}
          rails_master_key: ${{secrets.RAILS_MASTER_KEY}}
          environment_name: ${{github.ref_name}}
          environment_tag: ${{github.sha}}
          rails_env: staging
    environment:
      name: ${{github.ref_name}}
      url: ${{steps.create_environment.outputs.deployment-url}}
Nachdem unser Docker Image fertig gebaut wurde kommt nun terraform ins spiel. Die „main.tf“ kannst du hier einsehen.

Wenn du alle variablen sorgfältig überprüft hast solltest du nun beginnen können das ganze zu testen.
Wichtig! Lass dich nicht unterkriegen bei Fehlern und ja du wirst sehr viele verschiedene Fehler bekommen. Und zwar solltest du sofern alles richtig geklappt hat und er nicht vorher schon Fehler ausgibt darauf stoßen, dass Terraform einen DNS Record anlegen möchte aber keine passende Zone dazu findet. Wie du das Problem behebst und deine eigene Custom Domain auf der App integrieren kannst zeigen wir dir im dritten Teil dieser Serie.

Fazit:

Ebenfalls bedanke ich mich erneut fürs lesen und ich hoffe du hast einiges gelernt.
Solltest du weitere Fragen haben, kontaktiere uns gerne per mail, auf Instagram oder vereinbare einen kostenlosen Beratungstermin, falls wir die Arbeit übernehmen sollen.