Based on devops-qr-code
This app generates QR codes based on input URLs. It is designed to be flexible, working seamlessly in both internal environments like Kubernetes and external deployments on platforms like Vercel and Render by using NextJS routing features along with environment variables to switch between the two modes
- Prerequisites
- Running the app using docker-compose
- Deploying an EKS cluster using Terraform
- Setting up Jenkins
- YAML files
- CI/CD Pipeline
- Deploying the app using render and vercel
- Results
- Front-End built with NextJs
- Api built with python FastApi
- Data Base created with postgres
The user enters a url and clicks the button to send the url to the api ,the api checks if the url is present in the data-base or not :
- if yes it retrieves the corresponding qr-code and sends it back to the user
- if not it generates a qr-code sends it back to the user and save it in the data-base
- Docker
- Docker-compose
- AWS account (Free Tier)
- Jenkins (as a container)
- AWS-CLI
- Kubectl
- Terraform
-
make sure to set up the right enviroment variables in the .env file for the internal docker compose environmnet
NEXT_PUBLIC_USE_INTERNAL_ROUTE=true NEXT_PUBLIC_INTERNAL_API=http://api ("api" is the service name for the api in the docker-compose.yml)
-
the external network can be used but it's not practicle for this case
NEXT_PUBLIC_USE_INTERNAL_ROUTE=false NEXT_PUBLIC_EXTERNAL_API=http://localhost:3001 (the api service must be forwarded for this to work)
-
run the app using the Docker files in build/context folders view file here :
docker compose up --build
-
run the app using the docker images from DockerHub view file here :
docker compose up -f ./docker-compose-images.yml up
-
here is a quick setup guide on how to deploy an minimal EKS cluster using terraform :
-
after creating the cluter make sure to add an access entry for the IAM user to access the cluster
-
go to the eks console
EKS>Cluster>"eks-cluster-name">IAM access entries
and add the user to the cluster with the necessary permissions : -
The Terraform configuration is divided into three files:
- eks.tf: Contains the main configuration for the EKS cluster.
- network.tf: Defines the network settings for the EKS cluster.
- provider.tf: Specifies the provider configuration for AWS.
-
follow this guide to set up jenkins as a container Docker in Jenkins
-
make sure docker is setup in jenkins (more details here)
-
it's preferable to use a multibranch pipeline cause of the usefull plugins and features it provides
-
add "Multibranch Scan Webhook Trigger" plugin in jenkins
-
configure a webhook in the git repo webhook settings :
<jenkins-url>/multibranch-webhook-trigger/invoke?token=<token>
-
more details about multibranch webhooks here
-
install the aws cli in the jenkins container :
docker exec -it jenkins bash
apt install unzip wget wget "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" unzip awscli-exe-linux-x86_64.zip sudo ./aws/install
-
configure the aws cli with the credentails of the IAM user used to cretae the EKS cluster or add a new user for jenkins with right permissions :
aws configure
-
verify the configuration :
aws sts get-caller-identity
-
install kubectl in the jenkins container :
K8S_VERSION=1.32 apt-get update apt-get install --quiet --yes apt-transport-https ca-certificates curl curl -fsSL https://pkgs.k8s.io/core:/stable:/v${K8S_VERSION}/deb/Release.key | \ gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v${K8S_VERSION}/deb/ /" | \ tee /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install --quiet --yes kubectl
-
update the kubeconfig file :
aws eks --region <region> update-kubeconfig --name <cluster-name>
- set up a deployment with its corresponding service for each component of the app
- utilized two Function Templates to build and push the images
- hamdiz0/qr-front:latest
- hamdiz0/qr-api:latest
- postgres:15-alpine (default)
- utilized a script to change the demployment image version change_version.sh
- in the
update version
stage, Jenkins runs the script to change both the front-end and api image versionsscript { sh """ cd k8s-manifests chmod +x change_version.sh ./change_version.sh -v $VERSION """ }
- set up jenkins to update the git repository with new version
- set up a git
user.name
anduser.eamil
for Jenkins :docker exec -it jenkins bash git config --global user.name "jenkins" git config user.email "jenkins@jenkins.com"
- ustilized a function that adds and pushs changes to a git repository
- view the function here
- the function uses a git access token wish must be configured and added to jenkins as a
Username and Password
credential - make sure to add the
Ignore Commiter Strategy
plugin and ignore jenkins pushes to avoid infinite build loops : - example of using the function :
gs.git_push( 'github.com/hamdiz0/qr-code-generator', // url without "https://" 'github-api-token', // credentialsId "updated to version ${VERSION}", // commit message 'main' // branch )
- after the
updated version
stage thedeploy
stage is triggered - the
deploy
stage runs the deploy.sh script wich applies the changes to theEKS
cluster :script { sh """ export KUBECONFIG=$KUBECONFIG // eks kubeconfig's absolute path in the jenkins container cd k8s-manifests // change directory to the yaml files directory chmod +x deploy.sh // make the script executable ./deploy.sh // run the script """ }
- the yaml files must be in the same directory as the script
-
create a new project on vercel and link the gitub repo hosting the front end
-
choose the right directory containing the front end files
-
make sure to select the NextJs framework
-
add the necessary environment variables for the external routing mode in
Settings/Environment Varaibles
NEXT_PUBLIC_USE_INTERNAL_ROUTE=true NEXT_PUBLIC_EXTERNAL_API=an external api url "https://api.onrender.com"
- create a new webservice
- make sure to select the nearest region
- add all the needed information
- create a new webservice
- make sure to select the nearest region
- select python as the language
- add the Start command :
uvicorn main:app --host 0.0.0.0 --port 80
- add the environment variables along with their values from the postgres service
CORS_ORIGIN // "*" to allow all traffic or set a specific url POSTGRES_HOST // Hostname value from info/connections POSTGRES_DB // Database value from info/connections POSTGRES_USER // Username value from info/connections POSTGRES_PASSWORD // Password value from info/connections
- the required values are present in the connections part of the postgres db info tab