Photo par Bernd Dittrich

Préparez votre Azure Container Registry pour votre Azure Container Apps avec Terraform

Store your Docker Images

Créé par Damien Aicheh le 03/05/2023 · 15 mins

Dans ce tutoriel, vous allez configurer un Azure Container Registry pour stocker les images Docker. Vous créerez également les images Docker que vous utiliserez dans l’environnement Azure Container Apps.

Ce tutoriel fait partie d’une série complète de tutoriels sur l’utilisation d’Azure Container Apps avec Terraform.

Structuration du projet

Créons un projet Terraform dédié appelé par exemple fruits-on-azure-container-apps avec cette structure à l’intérieur :

| - fruits-on-azure-container-apps
   | - .gitignore

Le .gitignore contiendra tous les fichiers terraform que nous voulons ignorer. Il ressemblera à ceci:

.terraform
*.tfstate
**/*.lock.*
**/*.tfplan
terraform.tfplan
terraform.tfstate.backup
plan.out

Initialiser terraform

Pour pouvoir utiliser Terraform avec Azure, vous devez installer Azure CLI et vous connecter à votre compte Azure. Vous pouvez également utiliser un Service Principal Name (SPN) pour vous authentifier, tous les détails peuvent être trouvés dans la documentation officielle.

Pour pouvoir stocker le state de Terraform dans Azure, vous devez créer un Azure Storage Account et un container.

Ensuite, connectez-vous à votre abonnement et créez un groupe de ressources dédié aux states Terraform, par exemple rg-dev-terraform-states :

# Connect to Azure
az login
# Select the subscription
az account set --subscription <your-subscription-id>
# Create the resource group with the location of your choice
az group create --name rg-dev-terraform-states --location westeurope

Ensuite, exécutez les commandes suivantes pour créer un Azure Storage Account et le container pour stocker le state de Terraform. Assurez-vous de définir votre propre nom de stockage account qui doit être unique :

# Create the storage account
az storage account create -n <your-storage-account-name> \
                          -g rg-dev-terraform-states \
                          -l westeurope \
                          --sku Standard_LRS

Sur la base de la ligne de commande ci-dessous, pour créer le container appelé tfstates pour les fichiers de states terraform, vous devez récupérer l’une des clés d’accès de votre Storage Account. Vous pouvez l’obtenir dans le portail Azure dans la section Access Keys de votre Storage Account :

Storage account access keys

# Then create the tfstates container inside it

az storage container create -n tfstates \
                            --account-name <your-storage-account-name> \
                            --account-key <your-storage-account-key>

Créons un fichier provider.tf et déclarons-y le provider azurerm :

provider "azurerm" {
  features {
    log_analytics_workspace {
      permanently_delete_on_destroy = true
    }
  }
  skip_provider_registration = false
}

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.51.0"
    }
  }
  required_version = ">=1.4.5"

  backend "azurerm" {}
}

Nous pouvons maintenant exécuter la commande suivante pour initialiser Terraform :

terraform init \
        -reconfigure \
        -input=false \
        -backend-config="resource_group_name=rg-dev-terraform-states" \
        -backend-config="storage_account_name=<your-storage-account-name>" \
        -backend-config="container_name=tfstates" \
        -backend-config="key=fruits.aca.terraform.tfstate"

Si tout va bien vous devriez voir ce genre de message dans votre terminal :

Terraform init

Définir les conventions de nommage

Vous utiliserez la convention de nommage officielle pour les ressources que vous créerez sur Azure. Toutes les abréviations se trouvent dans la documentation.

Définissons cette convention de nommage :

<!--If the resource prefix has a dash: -->
<service-prefix>-<environment>-<region>-<application-name>-<instance>
<!--If the resource does not autorize any special caracters: -->
<service-prefix><environment><region><application-name><instance>

Créons donc un fichier variables.tf et définissons toutes ces valeurs en tant que variables :

variable "environment" {
  description = "The environment deployed"
  type        = string
  default     = "dev"
  validation {
    condition     = can(regex("(dev|stag|prod)", var.environment))
    error_message = "The environment value must be valid."
  }
}

variable "region" {
  description = "Azure deployment region"
  type        = string
  default     = "we"
}

variable "application" {
  description = "The name of the application"
  type        = string
  default     = "frt"
}

variable "location" {
  description = "Azure deployment location"
  type        = string
  default     = "westeurope"
}

variable "resource_group_name_suffix" {
  type        = string
  default     = "01"
  description = "The resource group name suffix"
  validation {
    condition     = can(regex("[0-9]{2}", var.resource_group_name_suffix))
    error_message = "The resource group name suffix value is not a valid number."
  }
}

variable "repository" {
  description = "The repository name"
  type        = string
  default     = "fruits-on-azure-container-apps"
}

variable "creator" {
  description = "The name of the project's creator"
  type        = string
  default     = "terraform"
}

variable "tags" {
  type        = map(any)
  description = "The custom tags for all resources"
  default     = {}
}

Définissons un suffixe de ressource et les tags pour vos ressources dans un fichier locals.tf :

locals {
  resource_lowercase_array  = [lower(var.environment), lower(var.region), lower(var.application), var.resource_group_name_suffix]
  resource_suffix_kebabcase = join("-", local.resource_lowercase_array)
  resource_suffix_lowercase = join("", local.resource_lowercase_array)

  tags = merge(
    var.tags,
    tomap(
      {
        "Creator"     = var.creator,
        "Environment" = var.environment,
        "Region"      = var.region,
        "Repository"  = var.repository,
        "Application" = var.application,
      }
    )
  )
}

Cela servira à définir le suffixe de toutes les ressources que nous allons créer.

Créer un groupe de ressources

La première chose que vous devez faire est de créer un groupe de ressources pour stocker toutes les ressources que nous allons créer. Pour ce faire, créez un fichier appelé rg.tf et ajoutez ce code :

resource "azurerm_resource_group" "this" {
  name     = format("rg-%s", local.resource_suffix_kebabcase)
  location = var.location
  tags     = local.tags
}

Créer le registre d’images Docker

Pour stocker les images Docker vous avez 2 options :

Dans ce tutoriel, nous utiliserons le service Azure Container Registry. Pour ce faire, créez un fichier appelé acr.tf et ajoutez ce code :

resource "azurerm_container_registry" "this" {
  name                = format("cr%s", local.resource_suffix_lowercase)
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location
  sku                 = "Basic"
  admin_enabled       = true
}

Comme vous pouvez le constater, vous devez activer le mode administrateur pour pouvoir utiliser les informations d’identification dans Azure Container Apps. Vous pouvez également spécifier le SKU que vous souhaitez utiliser. Dans ce tutoriel, nous utiliserons le SKU Basic.

Lancez Terraform

Exécutons la commande plan de Terraform et appliquons-la :

terraform plan --out=plan.out

Then:

terraform apply plan.out

Créer les images Docker

Maintenant que nous avons notre registre, nous pouvons créer nos images Docker. Vous avez 2 images à construire, une pour le frontend et une pour l’API backend.

L’image frontend

Clonons le repository qui contient l’application frontend et exécutons la commande docker build dedans :

docker build -t <your-container-registry-name>.azurecr.io/fruits-frontend:1.0.0 .

Vous pouvez bien sûr préciser le nom et le tag de l’image de votre choix. Dans ce tutorial, nous utiliserons le nom et le tag suivants : <your-container-registry-name>.azurecr.io/fruits-frontend:1.0.0.

L’image backend

Clonons le repository qui contient l’API backend et exécutons la commande docker build :

docker build -t <your-container-registry-name>.azurecr.io/fruits-backend:1.0.0 .

Le nom et le tag de cette image seront <your-container-registry-name>.azurecr.io/fruits-backend:1.0.0.

Poussez les images vers le registre

Maintenant que nous avons nos images, nous pouvons les pousser dans le registre. Connectez-vous à Azure Container Registry :

# Connect to the registry
az acr login --name <your-container-registry-name>

Assurez-vous d’avoir le préfixe <your-container-registry-name>.azurecr.io/ pour pouvoir pousser vos images Docker.

Poussez les images :

# Push the frontend image
docker push crdevwefrt01.azurecr.io/fruits-frontend:1.0.0
# Push the backend image
docker push crdevwefrt01.azurecr.io/fruits-backend:1.0.0

Vérifier les images localement avec Docker

Cette partie est facultative mais elle peut être utile pour vérifier que les images sont correctement construites avant de les déployer sur Azure Container Apps. Pour ce faire, vous devrez exécuter un conteneur mongodb localement.

Tout d’abord, créez un réseau local à Docker appelé fruits-network :

docker network create fruits-network

Cela permettra aux conteneurs frontend, backend et mongodb de communiquer ensemble.

Instanciez le conteneur mongodb, choisissez le nom d’utilisateur et les mots de passe que vous souhaitez, vous en aurez besoin pour vous connecter à la base de données depuis l’image backend :

docker run --name mongodb \
           --network=fruits-network \
           -e MONGODB_USERNAME=ratingsuser \
           -e MONGODB_PASSWORD=ratingspassword \
           -e MONGODB_DATABASE=ratingsdb \
           -e MONGODB_ROOT_USER=root \
           -e MONGODB_ROOT_PASSWORD=ratingspassword \
           -it bitnami/mongodb:latest

Comme vous pouvez le voir ci-dessus, le conteneur s’exécute à l’intérieur du réseau fruits-network.

Ensuite, exécutez l’image backend et transmettez la variable d’environnement MONGODB_URI. Le MONGODB_URI doit être au format mongodb://[username]:[password]@[endpoint]:27017/ratingsdb

docker run --name api \
           --network=fruits-network \
           -p 3000:3000 \
           -e MONGODB_URI=mongodb://ratingsuser:ratingspassword@mongodb:27017/ratingsdb \
           -it --rm <your-container-registry-name>.azurecr.io/fruits-backend:1.0.0

Si vous allez sur http://localhost:3000/api/items vous devriez voir la liste des fruits dans la base de données.

Enfin, exécutez l’image frontale et passez la variable d’environnement API :

docker run --name web \
           --network=fruits-network \
           -p 8080:8080 \
           -e API=http://api:3000 \
           -it --rm <your-container-registry-name>.azurecr.io/fruits-frontend:1.0.0

Si tout va bien, vous devriez pouvoir accéder à l’application sur http://localhost:8080 et voir quelque chose comme ceci :

Front web

Vous pouvez voter pour votre fruit préféré et voir les résultats dans le classement.

Touche finale

Votre Azure Container Registry et vos images Docker sont prêts à être utilisés dans l’environnement Azure Container Apps. Vous trouverez le code source complet dans ce répertoire Github.

Et après ?

Dans le tutoriel suivant de cette série, nous nous concentrerons sur la création de l’environnement Azure Container Apps.

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !