Continuous Delivery mit Azure Web App for Containers und mehreren Container
Azure Web Apps for Containers bieten einen einfachen Weg, Anwendungen aus mehreren Containern zu hosten (aktuell noch in Preview). Die Container konfiguriert man mit Hilfe einer docker-compose-Datei, in der Services mit den entsprechenden Images enthalten sind (zu beachten ist, dass es einige Einschränkungen hinsichtlich der Einstellungen gibt, die verwendet werden können):
Auch für Multi-Container-Apps kann der Schalter “Continuous Deployment” genutzt werden, um die Images erneut zu ziehen, wenn eine aktualisierte Version verfügbar wird. Wie in unserem letzten Blog-Eintrag zum Continuous Deployment von Single-Container Apps beschrieben, ist das ein pragmatischer Weg, um das Image mit dem latest-Tag zu aktualisieren. Dieser besitzt jedoch einige Nachteile, z.B. wenn man einen früheren Stand des Images deployen will oder mehrere Umgebungen geordnet nacheinander aktualisiert werden sollen.
Voraussetzungen
Auch in diesem Beispiel gehen wir davon aus, dass die verwendeten Images jeweils in einer Azure DevOps Pipeline erzeugt und in eine Registry gepushed werden. Hierbei werden sie es mit einigen Tags versehen, u.a. latest, aber auch der Build-Nummer. Diese dient als Aufhänger, um die passende Version zu erhalten.
Azure DevOps Release Pipelines
Beim Aufsetzen der Release-Pipeline gehen wir folgendermaßen vor: im Gegensatz zum Beispiel mit einem Container fügen wir die Artefakte aller Build-Pipelines hinzu. Hierbei haben wir es primär auf die Build-Nummer abgesehen. Außerdem ist es hilfreich, eine Vorlage für die docker-compose-Konfiguration als Artefakt verfügbar zu haben. Dies kann entweder in den Build-Artefakten enthalten sein, oder man greift direkt auf die Datei im Repository zurück. Wichtig ist, dass diese sich nicht allzu häufig ändert, um Inkonsistenzen zwischen den verschiedenen Build-Ständen zu vermeiden. Aufgrund der Einschränkungen hinsichtlich der docker-compose-Datei dient diese jedoch primär der Konfiguration der verwendeten Images und der Service-Namen.
Das Deployment auf einer Umgebung erfolgt in mehreren Schritten:
Anpassung der docker-compose-Vorlage
Die Vorlage der docker-compose-Datei wird so angepasst, dass die aktuellen Build-Nummern der Images eingesetzt werden. Hierfür kann beispielsweise der Config Transformation Task von Magic Chunks genutzt werden. Dieser unterstützt das Ersetzen von Werten in YAML-Dateien und kann so die Build-Nummern der Images in die docker-compose-Datei einsetzen:
Aktualisierung der Web App-Konfiguration
Im Gegensatz zum Deployment eines einzelnen Containers setzen wir hierbei nicht auf den Azure App Service deploy Task, sondern auf den Azure CLI Task. Dieser wird genutzt, um die neue Konfiguration für die Web App zu setzen:
az webapp config container set \
--resource-group $(ResourceGroup) \
--name $(TestAppService) \
--multicontainer-config-type compose \
--multicontainer-config-file $(System.DefaultWorkingDirectory)/repos/docker-compose.yml
In obigem Beispiel werden Variablen der Release Pipeline genutzt, um die Resource-Group ($(ResourceGroup)
) und den App-Service ($(TestAppService)
) anzugeben. Durch diesen Azure CLI-Befehl wird die Konfiguration des App Service so geändert, dass die angepasste docker-compose.yml verwendet wird.
Für weitere Umgebungen fügen wir weitere Stages und mit den entsprechenden Tasks hinzu. Durch Approvals können wir so absichern, so dass das Deployment auf diese Umgebungen erst nach einem OK gestartet wird.
Nach dem Deployment ist die Web App for Containers so konfiguriert, dass sie die Images in der passenden Version übernimmt. Wollen wir später zu einem anderen Stand zurückkehren, ist dies einfach möglich, indem wir ein Release für einen früheren Build erstellen.
Fazit
Auch beim Hosten mehrerer Container in einer Azure Web App for Containers ist eine CI/CD-Pipeline eine gute Unterstützung, die Deployments deutlich vereinfacht. Durch die Verwendung einer docker-compose-Vorlage und Anpassung der App-Service-Konfiguration kann man so sicherstellen, dass die Images zu einem bestimmten Tag geladen werden. Damit erhält man deutlich mehr Kontrolle als beim Anstoßen über den Webhook.
YAML: Transformation der docker-compose.yml
steps:
- task: sergeyzwezdin.magic-chunks.magic.chunks.MagicChunks@2
displayName: "Config transform - $(System.DefaultWorkingDirectory)/repos/docker-compose.yml"
inputs:
sourcePath: "$(System.DefaultWorkingDirectory)/repos/docker-compose.yml"
fileType: Yaml
transformations: |
{
"services/api/image": "***.azurecr.io/***/api:$(Release.Artifacts.***-api.BuildNumber)",
"services/app/image": "***.azurecr.io/***/app:$(Release.Artifacts.***-spa.BuildNumber)"
}
YAML: Azure CLI für die App-Service-Konfiguration
variables:
ResourceGroup: "***"
TestAppService: "***"
steps:
- task: AzureCLI@2
displayName: "Azure CLI "
inputs:
azureSubscription: "***"
scriptType: ps
scriptLocation: inlineScript
inlineScript: "az webapp config container set --resource-group $(ResourceGroup) --name $(TestAppService) --multicontainer-config-type compose --multicontainer-config-file $(System.DefaultWorkingDirectory)/repos/docker-compose.yml"