Wie mit Terraform und Gitlab Rancher konfiguriert werden kann

Habt ihr gewusst, dass Gitlab als Terraform remote Backend verwendet werden kann? Ich jedenfalls bis vor kurzem nicht. Wir versuchen bei uns grundsätzlich alles als Infrastructure as code zu beschreiben. Dabei setzen wir in der Regel auf Ansible. Da es aber für Rancher einen sehr ausführlichen Terraform Provider gibt, bietet sich die Kombination aus Gitlab und Terraform bestens an.

Infrastructure as code (IaC) is the process of managing and provisioning computer data centers through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools.

Wittig, Andreas; Wittig, Michael (2016). Amazon Web Services in Action. Manning Press. p. 93. ISBN 978-1-61729-288-0.

Unsere Puzzle interne Rancher Installation wird mehrheitlich mit Hilfe von Ansible erstellt. Das beinhaltet die Provisionierung der benötigten VMs bei unserem IaaS Provider cloudscale.ch, der Installation von Rancher mit RKE und Helm sowie dem anschliessenden Hinzufügen eines Kubernetes Cluster basierend auf custom Nodes. Diverse weitere post-Installation Tasks sind zu diesem Zeitpunkt aber noch nicht vollständig automatisiert. Dazu gehört z.B. die Konfiguration des Authentication Provider für Rancher wie aber auch das Hinzufügen von diversen Applikationen wie z.B. der Nginx Ingress Controller oder Loki/Promtail/Grafana als Logging Stack.

Rancher Terraform Provider

Mit dem Rancher Terraform Provider lassen sich viele dieser Tasks sehr einfach automatisieren. Die Konfiguration des Rancher Terraform Provider ist mit wenigen Zeilen HCL Code erledigt:

provider "rancher2" {
  api_url    = var.rancher2_api_url
  access_key = var.rancher2_access_key
  secret_key = var.rancher2_secret_key
}

Der Rancher Terraform Provider stellt viele Terraform Ressourcen zur Verfügung um ganz unterschiedliche Bereiche von Rancher zu konfigurieren. Ein Beispiel dafür ist der Authentication Providers. Mit einigen weiteren Zeilen HCL Code und der rancher2_auth_config_keycloak  Ressourcen können wir Keycloak als Authentication Backend verwenden:

resource "rancher2_auth_config_keycloak" "keycloak" {
  display_name_field        = "displayName"
  groups_field              = "memberOf"
  idp_metadata_content      = file("${path.module}/resources/keycloak-metadata.xml")
  rancher_api_host          = "<rancherapiurl>"
  sp_cert                   = file("${path.module}/resources/keycloak.cert")
  sp_key                    = data.vault_generic_secret.keycloak-cert.data["keycloak.key"]
  uid_field                 = "uuid"
  user_name_field           = "uid"
  access_mode               = "restricted"
}

Ein weiteres Beispiel ist die rancher2_app Ressource. Damit kann, wie der Name schon vermuten lässt, eine Rancher App verwaltet werden. Am Beispiel unseres NGINX Ingress Controller sieht das so aus:

resource "rancher2_app" "nginx-ingress-public" {
  catalog_name = "helm"
  name = "nginx-ingress"
  project_id = data.rancher2_project.k8s-staging-cluster-infra.id
  template_name = "nginx-ingress"
  template_version = "1.41.1"
  target_namespace = "nginx-ingress-public"
  values_yaml = filebase64("${path.module}/values_yaml/infra-nginx-ingress-public.yaml")
}

Die Helm Values für diese Rancher App werden als YAML File im Repository abgelegt. Ein Upgrade des Ingress Controller geschieht durch das Anpassen der Template Version oder der Values.

Gitlab CI/CD und Terraform Remote Backend

Damit Terraform Gitlab als remote Backend verwenden kann, muss dieses entsprechend konfiguriert werden. Durch das remote Backend, wird der Terraform State direkt im entsprechenden Gitlab Repository gespeichert und kann somit von überall verwendet werden. Die Initialisierung von Terraform sieht in etwa so aus:

terraform init \        
    -backend-config="address=https://gitlabhost/api/v4/projects/projectid/terraform/state/projectname" \
    -backend-config="lock_address=https://gitlabhost/api/v4/projects/projectid/terraform/state/projectname/lock" \
    -backend-config="unlock_address=https://gitlabhost/api/v4/projects/projectid/terraform/state/projectname/lock" \
    -backend-config="username=apiuser" \
    -backend-config="password=apitoken" \
    -backend-config="lock_method=POST" \
    -backend-config="unlock_method=DELETE" \
    -backend-config="retry_wait_min=5

In einer Gitlab CI/CD Pipeline wird dann unseren Infrastructure as Code angewendet. Unser .gitlab-ci.ymldazu sieht wie folgt aus:

image:
  name: hashicorp/terraform:light
  entrypoint:
    - '/usr/bin/env'
    - 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'


variables:
  GITLAB_TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
  TF_ROOT: ${CI_PROJECT_DIR}
  PLAN: plan.tfplan
  PLAN_JSON: tfplan.json

cache:
  paths:
    - .terraform

before_script:
  - apk --no-cache add jq
  - alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
  - cd ${TF_ROOT}
  - terraform --version
  - terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=${GITLAB_USER_LOGIN}" -backend-config="password=${GITLAB_TF_PASSWORD}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"

stages:
  - validate
  - build
  - test
  - deploy

validate:
  stage: validate
  tags:
    - docker_cloudscale
  script:
    - terraform validate

plan:
  stage: build
  tags:
    - docker_cloudscale
  script:
    - terraform plan -out=$PLAN
    - terraform show --json $PLAN | convert_report > $PLAN_JSON
  artifacts:
    name: plan
    paths:
      - $PLAN
    reports:
      terraform: $PLAN_JSON


apply:
  stage: deploy
  tags:
    - docker_cloudscale
  environment:
    name: k8s-prod-cluster
  script:
    - terraform apply -auto-approve
  dependencies:
    - plan
  when: manual
  only:
    - master

Nach Erstellen eines Merge Request in Gitlab, wird automatisch ein terraform validate und terraform plan ausgeführt. Der Report daraus kann Gitlab direkt im Merge Request auswerten und anzeigen. Das sieht dann in etwa so aus:

Das komplette Log wird als Artifact dem Merge Request hinterlegt und kann natürlich auch geöffnet werden. Sobald der Merge Request gemerged wird, läuft automatisch ein terraform apply um die entsprechende Änderung auch anzuwenden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.