Containerization of a website with Docker and NGINX + Build a Jenkins pipeline + Deploying App on HEROKU Cloud Platform
For the installation of the Jenkins Server on your local machine, you can check my repo :
When you deploy a web server on HEROKU, you don't have the permission to bind the default port 80.
HEROKU automatically assigns a binding port for your web server and renew it with each new deployment.
To bypass this rule you must create a dynamic environment variable which will retrieve the value that HEROKU returns to you, and insert into your NGINX config file when you launch your App.
This is what we are going to put in place.
To deploy your HTML web site in a Docker container, we are going to follow these steps :
- Create a Dockerfile with an Ubuntu image
- Install NGINX server
- Edit the NGINX config file to add an environnement variable
- Replace the default NGINX config file
- Retrieve the value of the environnement variable "PORT" return by HEROKU and insert into your config file
Here is the dockerfile with which we create our container embedding the NGINX server and the website files :
FROM ubuntu
LABEL org.opencontainers.image.authors="Tony DJA"
RUN apt-get update
RUN apt install -y nginx
ENV PORT=80
RUN rm -R /etc/nginx/sites-available/*
RUN rm -R /var/www/html/*
COPY ./website/ /var/www/html/
COPY default /etc/nginx/sites-available/
CMD sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/sites-available/default && nginx -g 'daemon off;'
-
Docker image
FROM ubuntu LABEL org.opencontainers.image.authors="Tony DJA" -
NGINX installation
RUN apt-get update RUN apt install -y nginx -
Create the "PORT" environnement variable and assign a default value
ENV PORT=80 -
Delete the NGINX default configuration file
RUN rm -R /etc/nginx/sites-available/* -
Delete all default files in the root folder of the web server
RUN rm -R /var/www/html/* -
Add you HTML files into the root folder of the web server
COPY ./website/ /var/www/html/ -
Add your new NGINX default configuration file including the "PORT" environnement variable
COPY default /etc/nginx/sites-available/ -
Launch your Web server => Inserting the value of "PORT" that HEROKU returns and insert it into your NGINX configuration file
CMD sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/sites-available/default && nginx -g 'daemon off;'
Now create a new Jenkins pipeline :
To trigger the launch of our pipeline from an action carried out on GitHub we will create a WebHook.
For this you need to configure 2 things :
- Enter the URL of your Github repository
- Enable reception of github hook trigger
Install Jenkins plugins :
We adding 4 plugins to run the pipeline and allow it to perform these actions :
- Build Docker Image and run containers
- Make a http request to test your image
- Use a Docker agent
- Installing the HEROKU CLI and push to the CLOUD
The 4 plugins you have to install :
- Docker
- Docker build step
- Docker pipeline
- Http request
Your local machine does not have a public IP address.
To link your Github repository and your Jenkins pipeline we need to create a relay with a public IP address and which will redirect Github notifications directly to our local Jenkins server in order to trigger the pipeline.
To make this way, you have to install "NGROK".
This tool will allow us to have a public IP map directly with our local machine
1 - First, create your account on ngrok.com :
2 - Create a Tunnel Authtokens :
3 - Install NGROK agent :
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | \
sudo tee /etc/apt/sources.list.d/ngrok.list && \
sudo apt update && sudo apt install ngrok
4 - Connect the agent with your NGROK account :
ngrok config add-authtoken <TOKEN>
5 - Connect your local machine to NGROK :
ngrok http http://localhost:8080
Your public address is the https "Forwarding" address.
You can check that everything works into your browser. You must arrive on your jenkins server.
1 - Create a WebHook
Use the forwarding address from ngrok
2 - Check if the WebHook is connected with your Jenkins server
Now, for each "Push action" on your GitHub repo, the trigger will start automically the execution of the jenkins pipeline.
To be able to deploy our web server on the Cloud, we have to create an account on HEROKU :
After creating your account, you have to recover and copy you API key, which will allow us to authenticate with the heroku CLI from the Jenkis pipeline.
Connect your HEROKU account with your Jenkins server
Now, go to your Jenkins server, and manage credentials.
Create new credentials with the ID "HEROKU_API_KEY".
All the pipeline is configured in the Jenkins file.
We make 6 stages :
- Image Build
- Run docker container based on build image
- Test build image
- Clean containers before deployment
- Deploy on HEROKU Staging
- Deploy on HEROKU Production
pipeline {
environment {
IMAGE_NAME = "tonydja/static-website"
IMAGE_TAG = "latest"
CONTAINER = "website"
CONTAINER_DYNOS_HEROKU = "web"
STAGING = "tonydja-staging"
PRODCUTION = "tonydja-production"
LOCALHOST_DOCKER_NETWORK = "192.168.208.3"
}
agent none
stages {
stage('Build image') {
agent any
steps {
script {
sh 'docker build -t $IMAGE_NAME:$IMAGE_TAG .'
}
}
}
stage('Run docker container based on build image') {
agent any
steps {
script {
sh '''
docker run -d -p 3000:80 --name $CONTAINER ${IMAGE_NAME}:${IMAGE_TAG}
sleep 5
'''
}
}
}
/* Just Replace the IP address of ENV var "LOCALHOST_DOCKER_NETWORK" with your own local Docker container IP
stage('Test image') {
agent any
steps {
script {
sh 'curl http://$LOCALHOST_DOCKER_NETWORK:3000 | grep -q "Enjoy"'
}
}
}
*/
stage('Clean container') {
agent any
steps {
script {
sh '''
docker container stop $CONTAINER
docker container rm $CONTAINER
'''
}
}
}
stage('Push image in STAGING and Deploy') {
when {
expression { GIT_BRANCH == 'origin/main'}
}
agent {
docker {
image 'franela/dind'
args '-u root:root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
environment {
HEROKU_API_KEY = credentials('HEROKU_API_KEY')
}
steps {
script {
sh '''
apk --no-cache add npm
npm install -g heroku
ls -l /var/run/docker.sock
docker ps
heroku container:login
heroku create $STAGING || echo "project already exist"
heroku container:push -a $STAGING $CONTAINER_DYNOS_HEROKU
heroku container:release -a $STAGING $CONTAINER_DYNOS_HEROKU
'''
}
}
}
stage('Push image in PRODUCTION and Deploy') {
when {
expression { GIT_BRANCH == 'origin/main'}
}
agent {
docker {
image 'franela/dind'
args '-u root:root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
environment {
HEROKU_API_KEY = credentials('HEROKU_API_KEY')
}
steps {
script {
sh '''
apk --no-cache add npm
npm install -g heroku
ls -l /var/run/docker.sock
docker ps
heroku container:login
heroku create $PRODCUTION || echo "project already exist"
heroku container:push -a $PRODCUTION $CONTAINER_DYNOS_HEROKU
heroku container:release -a $PRODCUTION $CONTAINER_DYNOS_HEROKU
'''
}
}
}
}
}
Local Docker network IP
Before testing your image with a http request you have to put your own local Docker network IP into the environnement variable "LOCAL_DOCKER_NETWORK".
To retrieve this address on your local machine, you just have to inspect your docker container :
docker ps
docker inspect <name of container where the Docker daemon is running>"
After doing this action, you can uncomment the "Test image" stage.
Heroku dynos
Never change the value "web" of the variable "CONTAINER_DYNOS_HEROKU"
The dynos is in fact a special name for naming containers on HEROKU Cloud platform. And "web" is a type of container that launching a web process. We need it for making NGINX working correctly.
Other environnement variables
You can change the other environnement variables as you wish.
STAGING and PRODUCTION deployment
Just an important step. For deploying the App in these 2 stages, we use a Docker agent with a franela/dind image.
We must run the stages into a Docker container to be able to install the HEROKU CLI, log in and push.
To deploy the container you must log in to root user and mouting a volume to map the Docker socket on the host machine to the container
args '-u root:root -v /var/run/docker.sock:/var/run/docker.sock'
Installing HEROKU CLI
apk --no-cache add npm
npm install -g heroku
Add traces to log to check the Docker socket is connected
ls -l /var/run/docker.sock
docker ps
Login to HEROKU and Deploy App
heroku container:login
heroku create $STAGING || echo "project already exist"
heroku container:push -a $STAGING $CONTAINER_DYNOS_HEROKU
heroku container:release -a $STAGING $CONTAINER_DYNOS_HEROKU
Make a push on GitHub repository or run the pipeline manually into Jenkins.
You can check the Stage view into Jenkins
When the pipeline is finished, go to your HEROKU account and check that your 2 applications STAGING and PRODUCTION are well deployed.
Click on it and check that your web Dyno is ON.
Click on the "open App" button.
Enjoy ! your website is on the Cloud...