Uptime Kuma ist ein self-hosted Monitoring System, das es erlaubt, die Verfügbarkeit von unterschiedlichen Services zu überwachen. Es bietet eine Vielzahl von Monitoring-Typen an, darunter für verschiedene Arten von HTTP-Endpunkte wie Websites oder REST-APIs.

Da es auch als fertiges Docker Image zur Verfügung steht, kann man es einfach als Azure App Service deployen. Für ein Ausrollen mittels Terraform genügt eine einfache Konfiguration.

Der Terraform-Code zu diesem Post findet sich auf Github: https://github.com/davull/uptime-kuma-azure-app-service

Azure App Service

Um einen Docker Container in Azure als Web App for Container zu betreiben, benötigen wir einen App Service, welcher in einem App Service Plan beheimatet ist. Alternativ kann man Container auch in Azure Container App oder Azure Kubernetes Service betreiben, eine Übersicht bietet Microsoft hier.

Damit die von Uptime Kuma gesammelten Daten (eine SQLite Datenbank) nicht bei jedem Neustart des Containers verloren gehen, persistieren wir sie in einem Storage Account.

Resource Group

Um die Ressourcen in Azure zu organisieren, legen wir eine neue Resource Group an. Dazu suchen wir im Azure Marketplace nach Recource Group und klicken Create.

Create Resource Group

Wir vergeben einen Namen, wählen eine Region und klicken Review + Create. Anschließend können wir unsere Services innerhalb der Resource Group anlegen.

Storage Account

Beim Anlegen des Storage Accounts wählen wir die zuvor erstellte Resource Group aus, legen Namen und Region fest und das Redundanz-Level. Für nicht-kritische Anwendungen genügt die günstigste Variante Local-redundant storage (LRS). Die übrigen Einstellungen können beibehalten werden.

Create Storage Account

Sobald unser Storage Account provisioniert ist, können wir ein File share anlegen. Als Tier wählen wir Hot, die Backup-Option können wir nach Bedarf an- oder abschalten.

Create File Share

App Service Plan und Web App for Container

Um einen App Service Plan anzulegen, gehen wir analog vor. Als Operation System wählen wir Linux, der kleinste nicht kostenlose Pricing Plan Basic B1 genügt. Dieser schlägt mit ca. 13 € / Monat zu Buche. Er bietet 1.75 GB Arbeitsspeicher und 1 vCPU. Je nach Anzahl der zu überwachenden Services kann es sinnvoll sein, einen größeren Plan zu wählen. Ein Scale-Up ist aber auch später jederzeit möglich.

Create App Service Plan

Nachdem nun ein App Service Plan und Storage zur Verfügung stehen, können den eigentlichen App Service anlegen. Um den Wizard zu starten, suchen wir im Azure Portal nach Web App for Containers oder Web App. Im Marketplace werden diese als unterschiedliche Einträge geführt, es handelt sich aber um den gleichen Service.

Als Publish-Methode wählen wir Docker und als Operating System Linux. Der hier gewählte Name ist auch zugleich die Subdomain, unter der der Service später verfügbar ist (<name>.azurewebsites.net).

Create Web App

Im nächsten Reiter Docker wechseln wir die Image Source auf Docker Hub und tragen das gewünschte Image inkl. Tag ein, z.B. louislam/uptime-kuma:latest.

Create Web App

Nachdem wir den Wizard abgeschlossen haben, ruft Azure das Docker Image aus dem Hub ab und starten unseren Container. Beim ersten Start von Uptime Kuma dauert es einige Zeit, bis der Service läuft und die Webseite verfügbar ist. Unter dem Eintrag Deployment Center im Reiter Logs kann man den Prozess verfolgen.

Azure File Share mounten

Damit die Datenbank von Uptime Kuma nicht bei jedem Neustart des Containers verloren geht, mounten wir das zuvor angelegte File Share in den Container. Dazu wählen wir den Punkt Configuration und wechseln in den Reiter Path mappings. Dort fügen wir einen neuen Eintrag hinzu. Hier wählen wir den zuvor angelegten Storage Account und den für das File Share erstellten Storage Container aus. Als Storage Type wählen wir Azure File. Der Pfad, unter dem das File Share im Container gemountet wird, muss /app/data lauten. Hier legt Uptime Kuma seine Anwendungsdaten ab. Nach dem Speichern wird der Container neu gestartet die Daten landen nun in unserem Storage Container.

Add Path Mapping

Rufen wir nun die Website der Web App auf, begrüßt uns Uptime Kuma mit dem Setup-Dialog, in dem wir den Admin-Nutzer anlegen können.

Terraform

Als Alternative zum Azure Portal bietet es sich an, die Konfiguration unserer Anwendung in bester Infrastructure as Code-Manier mittels Terraform zu verwalten. Hierzu bemühen wir den azurerm Provider. Die verschiedenen Arten zur Authentifizierung sind in der Dokumentation beschrieben. Wir legen die gleichen Ressourcen an, wie wir es im Portal getan haben.

Die gesamte Konfigurationsdatei sieht wie folgt aus:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.79"
    }
  }
}

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "rg_kuma" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_storage_account" "sa_kuma" {
  name                     = var.storage_account_name
  resource_group_name      = azurerm_resource_group.rg_kuma.name
  location                 = azurerm_resource_group.rg_kuma.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  min_tls_version          = "TLS1_2"
}

resource "azurerm_storage_share" "share_kuma" {
  name                 = "kumashare"
  storage_account_name = azurerm_storage_account.sa_kuma.name
  quota                = var.storage_quota
  access_tier          = "Hot"
}

resource "azurerm_service_plan" "service_plan_kuma" {
  name                = "service-plan-kuma"
  resource_group_name = azurerm_resource_group.rg_kuma.name
  location            = azurerm_resource_group.rg_kuma.location
  os_type             = "Linux"
  sku_name            = var.service_plan_sku
}

resource "azurerm_linux_web_app" "web_app_kuma" {
  name                = var.web_app_name
  resource_group_name = azurerm_resource_group.rg_kuma.name
  location            = azurerm_resource_group.rg_kuma.location
  service_plan_id     = azurerm_service_plan.service_plan_kuma.id
  https_only          = true

  site_config {
    http2_enabled       = true
    minimum_tls_version = "1.2"

    application_stack {
      docker_image_name   = var.docker_image
      docker_registry_url = "https://index.docker.io"
    }
  }

  app_settings = {
    "DOCKER_ENABLE_CI" = "true"
  }

  storage_account {
    access_key   = azurerm_storage_account.sa_kuma.primary_access_key
    account_name = azurerm_storage_account.sa_kuma.name
    name         = "webappstorage"
    type         = "AzureFiles"
    share_name   = azurerm_storage_share.share_kuma.name
    mount_path   = "/app/data"
  }
}

Die Variablen werden in einer separaten Datei definiert.

variable "resource_group_name" {
  type = string
}

variable "storage_account_name" {
  type = string
}

variable "web_app_name" {
  type = string
}

variable "location" {
  type    = string
  default = "westeurope"
}

variable "storage_quota" {
  type    = number
  default = 50
}

variable "service_plan_sku" {
  type    = string
  default = "B1"
}

variable "docker_image" {
  type    = string
  default = "louislam/uptime-kuma:latest"
}

In einer .tfvar-Datei werde die Werte für die Variablen gesetzt.

resource_group_name  = "rg-kuma"
storage_account_name = "<your-storage-account-name>"
web_app_name         = "<your-web-app-name>"

Nun können wir mit einem einzigen Befehl die gesamte Umgebung erzeugen und auch wieder löschen.

terraform apply -var-file="terraform.tfvar"
terraform destroy -var-file="terraform.tfvar"