Continuous Integration and GitOps
We have successfully bootstrapped Flux on EKS cluster and deployed the application. To demonstrate how to make changes in the source code an application, build a new container images and leverage GitOps to deploy a new image to a cluster we introduce Continuous Integration pipeline. We will leverage AWS Developer Tools and DevOps principles to build multi-architecture container images for Amazon ECR.
We created Continuous Integration Pipeline during the prepare environment step and now we need to make it up and running.
First, clone CodeCommit repository for the application sources:
Next, populate the CodeCommit repository with the sources from the public repository of the Sample application:
We use AWS CodeBuild and define buildspec.yml
to build new x86_64
and arm64
images in parallel.
version: 0.2
phases:
install:
commands:
- echo Build started on `date`
pre_build:
commands:
- echo Logging in to Amazon ECR in $AWS_REGION
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-8)
- IMAGE_TAG_I=i$(date +%Y%m%d%H%M%S)-${COMMIT_HASH:=latest}
- echo ECR_URI=$ECR_URI
- echo IMAGE_TAG=$IMAGE_TAG
- echo IMAGE_TAG_I=$IMAGE_TAG_I
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_URI
build:
commands:
- echo Building a container image ...
- component=ui
- component_dir="./src/$component"
- cd $component_dir
- docker build -t $ECR_URI:$IMAGE_TAG .
- docker tag $ECR_URI:$IMAGE_TAG $ECR_URI:$IMAGE_TAG_I
- docker images
post_build:
commands:
- docker push $ECR_URI:$IMAGE_TAG_I
- docker push $ECR_URI:$IMAGE_TAG
- echo Build completed on `date`
We use AWS CodeBuild also to build Image Index
for multi-architecture image
using buildspec-manifest.yml
version: 0.2
phases:
install:
commands:
- echo Build started on `date`
pre_build:
commands:
- echo Logging in to Amazon ECR in $AWS_REGION
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-8)
- IMAGE_TAG=i$(date +%Y%m%d%H%M%S)-${COMMIT_HASH:=latest}
- echo ECR_URI=$ECR_URI
- echo COMMIT_HASH=$COMMIT_HASH
- echo IMAGE_TAG=$IMAGE_TAG
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_URI
build:
commands:
- echo Building the Docker manifest...
# Based on the Docker documentation, must include the DOCKER_CLI_EXPERIMENTAL environment variable
# https://docs.docker.com/engine/reference/commandline/manifest/
- export DOCKER_CLI_EXPERIMENTAL=enabled
- docker manifest create $ECR_URI:$IMAGE_TAG $ECR_URI:latest-arm64 $ECR_URI:latest-amd64
- docker manifest create $ECR_URI:latest $ECR_URI:latest-arm64 $ECR_URI:latest-amd64
- docker manifest annotate --arch arm64 $ECR_URI:$IMAGE_TAG $ECR_URI:latest-arm64
- docker manifest annotate --arch arm64 $ECR_URI:latest $ECR_URI:latest-arm64
- docker manifest annotate --arch amd64 $ECR_URI:$IMAGE_TAG $ECR_URI:latest-amd64
- docker manifest annotate --arch amd64 $ECR_URI:latest $ECR_URI:latest-amd64
post_build:
commands:
- echo Pushing the Docker image...
- docker manifest push $ECR_URI:$IMAGE_TAG
- docker manifest push $ECR_URI:latest
- docker manifest inspect $ECR_URI:$IMAGE_TAG
- docker manifest inspect $ECR_URI:latest
- echo Build completed on `date`
Now we are ready to push our changes to CodeCommit and start the CodePipeline
You can navigate to CodePipeline
in AWS Console and explore eks-workshop-retail-store-sample
pipeline:

It should look something like this:
As a result of a CodePipeline run with CodeBuild you will have a new image in ECR
The suffix z7llv2
in the name retail-store-sample-ui-z7llv2
is random and will be different in your case.
While we are waiting for pipeline to create new images (5-10 minutes), let's automate image updates to Git using Flux Image Automation Controller which we installed during the initial Flux bootstrap process.
Next, edit file deployment.yaml
and add placeholder for new container image URL:
image: "public.ecr.aws/aws-containers/retail-store-sample-ui:0.4.0" # {"$imagepolicy": "flux-system:ui"}
This will change the image in the pod specification to:
image: "public.ecr.aws/aws-containers/retail-store-sample-ui:0.4.0" `# {"$imagepolicy": "flux-system:ui"}`
Commit these changes to deployment:
We need to deploy custom resource definitions (ImageRepository, ImagePolicy, ImageUpdateAutomation) for Flux to enable monitoring of new container images in ECR and automated deployment using GitOps.
An ImageRepository
:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: ui
namespace: flux-system
spec:
provider: aws
interval: 1m
image: ${IMAGE_URI_UI}
accessFrom:
namespaceSelectors:
- matchLabels:
kubernetes.io/metadata.name: flux-system
An ImagePolicy
:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: ui
namespace: flux-system
spec:
imageRepositoryRef:
name: ui
filterTags:
pattern: "^i[a-fA-F0-9]"
policy:
alphabetical:
order: asc
An ImageUpdateAutomation
:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: ui
namespace: flux-system
spec:
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcdbot@users.noreply.github.com
name: fluxcdbot
messageTemplate: "{{range .Updated.Images}}{{println .}}{{end}}"
push:
branch: main
interval: 1m0s
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
update:
path: ./apps
strategy: Setters
Add these files to the application repository and apply them:
We created the following architecture:
Now, lets reconcile the changes.
We can check that image:
in the deployment
has been updated to a new tag.
To access UI
using a browser we need to expose it by creating an Ingress resource with the following manifest:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ui
namespace: ui
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/liveness
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ui
port:
number: 80
This will cause the AWS Load Balancer Controller to provision an Application Load Balancer and configure it to route traffic to the Pods for the ui
application.
Let's inspect the Ingress object created:
NAME CLASS HOSTS ADDRESS PORTS AGE
ui alb * k8s-ui-ui-1268651632.us-west-2.elb.amazonaws.com 80 15s
We wait 2-5 minutes until Application Load Balancer will be provisioned and check the UI page using URL of the ingress.
Let's introduce changes to the source code of the Sample Application.
Edit the file:
Change line 24
<a class="navbar-brand" href="/home">Retail Store Sample</a>
to <a class="navbar-brand" href="/home">Retail Store Sample New</a>
Commit changes.
Lets wait until the CodePipeline execution has finished:
Then we can trigger Flux to make sure it reconciles the new image:
If we pull the Git repository we can see the changes made in the log:
commit f03661ddb83f8251036e2cc3c8ca70fe32f2df6c (HEAD -> main, origin/main, origin/HEAD)
Author: fluxcdbot <fluxcdbot@users.noreply.github.com>
Date: Fri Nov 3 17:18:08 2023 +0000
1234567890.dkr.ecr.us-west-2.amazonaws.com/retail-store-sample-ui-c5nmqe:i20231103171720-ac8730e8
[...]
Similarly the CodeCommit commits view will show activity:

We can also check the pods to see the image has been update:
After successful build and deployment (5-10 minutes) we will have the new version of UI application up and running.