skip to content
A covered up pug in the woodsBlog

Deploy Kotlin API to Azure Container Apps

/ 6 min read

En este tutorial veremos como deployar una API en Kotlin dockerizada a Azure Container Apps

repositorio

Contenido

  • Crear un repositorio para nuestra API
  • Crear una api en kotlin y dockerizarla
  • Crear un Azure Container Registry para guardar la imagen de nuestra api
  • Buildear y pushear la imagen de nuestra API
  • Crear una Azure Container App para deployar la imagen de nuestra api
  • Automarizar deploys con GitHub Actions

Crear un repositorio para nuestra API

Lo primero es crear el repositorio de GitHub para luego poder automatizar los deploys con github actions.

Para eso ejecutaremos los siguiente comandos:

ldamore@Desktop:~ $ mkdir azure-container-apps
ldamore@Desktop:~ $ cd azure-container-apps
ldamore@Desktop/azure-container-apps:~ $ echo "# azure-container-apps" >> README.md
ldamore@Desktop/azure-container-apps:~ $ git init
ldamore@Desktop/azure-container-apps:~ $ git add README.md
ldamore@Desktop/azure-container-apps:~ $ git commit -m "first commit"
ldamore@Desktop/azure-container-apps:~ $ git branch -M main
ldamore@Desktop/azure-container-apps:~ $ git remote add origin [your-origin]
ldamore@Desktop/azure-container-apps:~ $ git push -u origin main

Crear una api en kotlin y dockerizarla

El codigo de la API pueden sacarlo de este repositorio. O pueden codear su propia api. En la de este ejemplo es un simple aplicacion que levanta un http server y escucha peticiones en el puerto que pongas como variable de entorno. Y al enviar un GET a / resibiremos un texto con la frase “Hello World!”

Una vez descargado el codigo (o habiendo creado su propia API) debemos dockerizarla.

Para eso crearemos un archivo .Dockerfile en la raiz del proyecto

En .Dockefile:

FROM gradle:7.2.0-jdk16 as builder

USER root
ARG COMMIT_SHA

ADD . .

RUN printf %s ".${COMMIT_SHA}" >> VERSION

RUN gradle --parallel --build-cache -Dorg.gradle.console=plain -Dorg.gradle.daemon=false :shadowJar

###########################################################################
FROM openjdk:16-alpine

ENV PORT 80

WORKDIR /app

COPY --from=builder /home/gradle/build/libs/backend_api.jar backend_api.jar

EXPOSE $PORT

CMD java -Xms256m -Xmx256m -Xss512k -jar backend_api.jar

Para probar que nuestra API esta funcionando correctamente dentro de un container, debemos buildearla y correrla.

ldamore@Desktop/azure-container-apps:~ $ docker image build -t kotlin-api .
ldamore@Desktop/azure-container-apps:~ $ docker container run -p 80:80 kotlin-api

Si vamos a http://localhost:80 deberiamos ver lo que nos responde nuestra API.

Crear un Azure Container Registry para guardar la imagen de nuestra api

Debemos entrar a nuestra cuenta en https://portal.azure.com/ y crear un nuevo recurso. El recurso que crearemos sera “Container Registry”.

Luego en Container Registry debemos crear un nuevo resource grupo (en mi caso container-app-example). Y debemos ponerle un nombre a nuestro registry (en mi caso kotlinapi)

registry

Le damos al boton “Next” hasta crearlo.

Y si vamos a Container Registry devuelta, podemos ver que ya esta creado:

cr

Dentro de nuestro registry debemos ir a update:

crd

Y activar el Admin user

up

Buildear y pushear la imagen de nuestra API

Cuando usamos herramientas de CI/CD como github actions para deployar recursos a Azure, necesitamos crear un “service principal” que este autorizado a hacer eso.

Para eso debemos descargar el CLI de Azure y logearnos.

ldamore@Desktop/azure-container-apps:~ $ az login

Una vez logeados, lo primero es obtener el id del resource group donde crearemos un “service principal” y luego crearemos el “service principal”.

Para obtener el resource group id:

ldamore@Desktop/azure-container-apps:~ $ az group show --name <your-resource-group-name>

Para crear el “service principal”:

ldamore@Desktop/azure-container-apps:~ $ az ad sp create-for-rbac --scope $resourceGroupId --role Contributor --sdk-auth

Obtendremos un resultado similar a este:

{
  "clientId": "xxxx6ddc-xxxx-xxxx-xxx-ef78a99dxxxx",
  "clientSecret": "xxxx79dc-xxxx-xxxx-xxxx-aaaaaec5xxxx",
  "subscriptionId": "xxxx251c-xxxx-xxxx-xxxx-bf99a306xxxx",
  "tenantId": "xxxx88bf-xxxx-xxxx-xxxx-2d7cd011xxxx",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

Debemos guardar este JSON porque nos servira mas tarde.

El siguiente paso es actualizar el “service principal” para que tenga permisos de pushear y pullear una imagen de nuestro container registry.

Para eso debemos tener el id de nuestro container registry:

ldamore@Desktop/azure-container-apps:~ $ az acr show --name <registry-name> --resource-group <resource-group-name>

Y actualizarlo con el id de nuestro registry y el ClientId del JSON del paso anterior.

ldamore@Desktop/azure-container-apps:~ $ az role assignment create --assignee <ClientId> --scope $registryId --role AcrPush

Una vez hecho esto debemos guardar nuestras credenciales en nuestro Repo:

  1. Entra a tu repo y navega a SettingsSecretsActions.
  2. Selecciona New repository secret y agrega los siguientes secretos AZURE_CREDENTIALS: el json del paso anterior REGISTRY_USERNAME: el clientId del json del paso anterior REGISTRY_PASSWORD: el clientSecret del json del paso anterior

Por ultimo debemos agregar nuestro script de GitHub Actions que buildeara la imagen y pusheara al registry. En la raiz de nuestro proyecto crearemos una carpeta .github y dentro de ella una carpeta llamada workflows dentro de workflows crearemos nuestro script llamado api.yml

Dentro de .github/workflows/api.yml:

name: Deploy api

on:
  push:
    branches:
    - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout to the branch
        uses: actions/checkout@v2
        with:
          path: main

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Log in to container registry
        uses: docker/login-action@v1
        with:
          registry: kotlinapi.azurecr.io
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Build and push container image to registry
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: kotlinapi.azurecr.io/kotlinapi:${{ github.sha }}
          file: ./Dockerfile

Pushear y comittear

Luego en nuestro repositorio podemos ver que el action se habra activado

bw

Y si vemos nuestros repositories en Azure veremos que hay una nueva imagen pusheada.

imgre

Crear una Azure Container App para deployar la imagen de nuestra api

Una vez que ya tenemos el mecanismo para buildear y pushear la imagen a nuestro registry, podemos crear un Container App que tome la imagen de nuestro registry.

Para eso debemos crear un nuevo recurso llamado “Container App”

Luego en Container App debemos seleccionar nuestro resource group y un Container Apps Environment, si no tenemos uno debemos crearlo. En mi caso lo cree con el nombre “staging”

cra1

En la seccion de App settings debemos desmarcar la opcion “Use quickstart image”. Luego debemos seleccionar nuestro registry, la imagen y el tag.

cra2

En mi caso agregue las environment variables que necesita mi API. Y tambien active para que sea accesible por la internet publica.

cra1

Una vez creado si volvemos a Container Apps podemos ver que nuestro Container App esta creado y si lo seleccionamos nos saldra la URL.

cra1

Automarizar deploys con GitHub Actions

Una vez ya creado nuestro Container App, podemos automatizar los deploys mediante GitHub Actions. A nuestro workflow ya creado (que buildea la imagen y pushea a nuestro registry), le debemos agregar ahora que update el container con la nueva version de la imagen buildeada.

En .github/workflows/api.yml:

name: Deploy api

on:
  push:
    branches:
    - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout to the branch
        uses: actions/checkout@v2
        with:
          path: main

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Log in to container registry
        uses: docker/login-action@v1
        with:
          registry: kotlinapi.azurecr.io
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Build and push container image to registry
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: kotlinapi.azurecr.io/kotlinapi:${{ github.sha }}
          file: ./Dockerfile
  deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Deploy to containerapp
        uses: azure/CLI@v1
        with:
          inlineScript: |
            echo "Installing containerapp extension"
            az extension add --name containerapp --yes
            echo "Starting Deploying"
            az containerapp update -n <container-app-name> -g <resource-group-name> -i kotlinapi.azurecr.io/kotlinapi:${{ github.sha }} --set-env-vars "PORT=80" --debug

Recuerde cambiar los nombres en “container-app-name” y “resource-group-name”