Deploy Your First App using Gappynator and Argo CD
This guide walks you through deploying your first application to GAP using Gappynator and Argo CD. By the end, you will have a GitHub Actions workflow that builds and pushes a container image, updates your Kubernetes manifests, and triggers a GitOps-based deployment via Argo CD.
Prepare Your Dockerfile
Your GitHub repository must contain a Dockerfile that builds your application image.
Set Up Repo Access to ACR
In order to push images to ACR, you will need to add your repo to terraform-github-repo-credentials. Create a PR that adds your repo to your team's tf file team_<your-team>.tf located at repos-container-registry-push.
Once merged and applied by the Platform team, the secret AZURE_CONTAINER_REGISTRY_CLIENT_ID will be provisioned in your repo and can be used to push images using GitHub Actions.
GitHub Actions Workflow for Building and Pushing Images to ACR
We will use our custom action docker-build-scan-push-action to build and push to a container registry.
name: "Build and Push to ACR"
on:
push:
branches:
- "main"
env:
TEAM_NAME: "my-team-name" # Change this
permissions:
id-token: write
contents: read
security-events: write
attestations: write
jobs:
build:
runs-on: "ubuntu-latest"
environment: "azure" # Must match one of the environments added in "terraform-github-repo-credentials"
steps:
- name: "Git checkout"
uses: "actions/checkout@v2"
# Depending on your Dockerfile, you might have to build your application before proceeding
- name: "Build, Scan and Push Docker image"
id: "build-scan-push"
uses: "gjensidige/docker-build-scan-push-action@main"
with:
team-name: ${{ env.TEAM_NAME }}
tag: ${{ github.sha }}
azure-client-id: ${{ secrets.AZURE_CONTAINER_REGISTRY_CLIENT_ID }}
# Use container image output to proceed performing GitOps actions
# Image output is on the format: "gjensidige.azurecr.io/your-team-name/github-repo-name:tag"
- name: "Example using container image output"
run: echo ${{ steps.build-scan-push.outputs.image }}
For more details view guide for pushing container images.
Repo GitHub Token
Create another PR in terraform-github-repo-credentials that adds your repo under repos-github-token - an example PR can be found here.
After merging, the secret <TEAM_NAME>_KUBERNETES_MANIFESTS_TOKEN will be added to your repo secrets.
For details you can check this README.
Gappynator Manifest
We will be using Gappynator, a custom operator that makes it easier to deploy services in GAP, ensures best practices and automates many steps.
Gappynator documentation is coming soon — a link will be added here when available.
In this guide we will use jsonnet for the Gappynator manifest.
You can learn more about jsonnet in our guide.
Add the manifest to your team's Kubernetes manifests repo, which follows the naming convention <your-team>-kubernetes-manifests.
local params = import 'params.json';
local environment = 'test'; // value set to 'test' or 'prod'
local name = <app_name>;
local namespace = <your_namespace>;
local image = params.container_image;
local github_labels = params.github;
local labels = {
['github.gap.io/' + key]: std.get(github_labels, key, null)
for key in std.objectFields(std.get({ github_labels: github_labels }, 'github_labels', {}))
} + {
'gap.io/owner': <owner>,
'gap.io/cost-center': <cost-center>,
'gap.io/service-code': <service-code>,
environment: environment,
};
local appPort = <application_port>;
local app = {
apiVersion: 'gap.io/v1',
kind: 'Application',
metadata: {
labels: labels,
name: name,
annotations: {},
namespace:: namespace,
},
spec: {
image: image,
port: appPort,
},
};
{
apiVersion: 'v1',
kind: 'List',
items: [app],
}
Each environment will have a subfolder with manifests and a params.json file:
{
"container_image": "",
"container_image_tag": "",
"github": {
"workflow_actor_username": "",
"repo_name": "",
"repo_commit_sha": "",
"manifest_repo_name": "",
"manifest_repo_commit_sha": ""
}
}
The values in params.json will be automatically patched by the deploy workflow after each successful build, as described in the next step.
Update Your Workflow to Trigger Deployment
Now that your manifests are in place, extend the workflow from the earlier step with an additional GAP Workflow Dispatch step. This step patches params.json with the correct container image and metadata after each build, which Argo CD then picks up to sync the cluster to the desired state.
name: "Build and Push to ACR"
on:
push:
branches:
- "main"
env:
TEAM_NAME: "my-team-name" # Change this
permissions:
id-token: write
contents: read
security-events: write
attestations: write
jobs:
build:
runs-on: "ubuntu-latest"
environment: "azure" # Must match one of the environments added in "terraform-github-repo-credentials"
steps:
- name: "Git checkout"
uses: "actions/checkout@v2"
# Depending on your Dockerfile, you might have to build your application before proceeding
- name: "Build, Scan and Push Docker image"
id: "build-scan-push"
uses: "gjensidige/docker-build-scan-push-action@main"
with:
team-name: ${{ env.TEAM_NAME }}
tag: ${{ github.sha }}
azure-client-id: ${{ secrets.AZURE_CONTAINER_REGISTRY_CLIENT_ID }}
# Use container image output to proceed performing GitOps actions
# Image output is on the format: "gjensidige.azurecr.io/your-team-name/github-repo-name:tag"
- name: "Example using container image output"
run: echo ${{ steps.build-scan-push.outputs.image }}
# Dispatch GAP workflow
- name: "GAP Workflow Dispatch"
uses: gjensidige/gap-workflow-dispatch-action@main
with:
token: ${{ secrets.KUBERNETES_MANIFESTS_TOKEN }}
repository: <manifest-repo-name> # replace with manifest repo name
environment: <environment> # replace with your environment
deployment-type: backend
container-image: ${{ steps.build-scan-push.outputs.image }}
Argo CD Manifest
Now in order for Argo CD to watch and sync the changes applied to our manifests, we need to create an Argo CD "Application" manifest.
local app = import '../../../../templates/app.libsonnet';
app {
// replace with your own values
name:: '<your-app-name>',
namespace:: '<your-namespace>',
team:: '<your-team>',
project:: '<your-project>',
environment:: '<environment>',
type:: 'infrastructure',
sources:: [
{
repoURL: '<manifests-repo-link>', // replace with your Kubernetes manifests repo URL
targetRevision: 'main',
path: 'apps/<app-name>/<env-subdirectory>', // replace with the path of your deployment manifests
directory: {
// Must be excluded as it is not a valid manifest
exclude: 'params.json',
jsonnet: {
libs: [
'vendor',
],
},
},
},
],
auto_sync+:: {
enabled: true,
},
}
For more details, follow our guide on Argo CD here.
Verify Application is Running
Once the Argo CD Application manifest has been applied, log in to Argo CD for your target environment and confirm your application shows a health status of Healthy and a sync status of Synced.