Below is the model we will deploy
Build Stage:
Deploy Stage:
Go to Docker Hub Container Image Library | App Containerization and create a new account.
After creating an account, you will see the main interface
If you logged in using Google or GitHub and haven’t created a password yet, go to My profile
Select Edit profile
And choose reset password, you will receive an email to change your password, click the link to change your password
After obtaining a password, log in to Docker on the Build Instance using your username and password
docker login
Your password will be stored at /home/gitlab-runner/.docker/config.json
We have successfully logged in on the Build Instance. Now let’s move to the Deploy Instance
Logged in successfully!
Previously in /root/ecommerce-fullstack-netcore-react, we had the frontend and backend projects
We will test the Dockerfile to start both the frontend and backend projects
First, we will write the Dockerfile in the backend project
cd /root/projects/backend && vi Dockerfile
Enter the following content
FROM mcr.microsoft.com/dotnet/sdk:6.0
WORKDIR /app
COPY . .
RUN dotnet restore
CMD ["dotnet", "run", "--urls", "http://0.0.0.0:5214"]
Before running the Docker file, check if port 5214 is running.
netstat -tlpun
We see that port 5214 is running with PID 70345
Stop the process by running
kill -9 70345
Please replace 70345 with your backend’s PID
We run the backend container project using the following command
Docker will build the Dockerfile into a Docker image with the tag ecommerce-backend:v1
docker build -t ecommerce-backend:v1 .
We have successfully built the Docker image
Next, we will run Docker with the name backend, with the option to run in the background with internal and external ports set to 5214 from the ecommerce-backend:v1 image
docker run --name backend -dp 5214:5214 ecommerce-backend:v1
Successfully ran
We can also use logs to check what exactly is running in the backend
docker logs -f id_container
First, we will write the Dockerfile in the frontend project
cd /root/projects/frontend && vi Dockerfile
Enter the following content
## build ##
FROM node:18-alpine as build
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
## run ##
FROM nginx:alpine
COPY --from=build /app/build/ /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Similar to the backend, before running the Docker file, check if port 3000 is running.
netstat -tlpun
We see that port 3000 is running with PID 263764
Stop the process by stopping pm2 because we had run CI/CD in previous lessons and started pm2. If you use kill -9 PID as usual, pm2 will still restart the project unless you use the following command.
pm2 stop all
We run the backend container project using the following command
Docker will build the Dockerfile into a Docker image with the tag ecommerce-frontend:v1
docker build -t ecommerce-frontend:v1 .
We have successfully built the Docker image
Next, we will run Docker with the name frontend with the option to run in the background with internal and external ports set to 3000 from the ecommerce-frontend:v1 image
docker run --name frontend -dp 3000:80 ecommerce-frontend:v1
The execution was successful
To save time, I will simulate it on the deploy Instance because it already has the source code. To push an image to the registry, you will push the image in the following format:
domain/project/repo:tag
But since we are pushing to Docker Hub, the format will be:
user_name/repo:tag
Let’s tag the frontend image from an existing image first.
Enter the following command:
docker tag ecommerce-frontend:v1 tunhatphuong/ecommerce-frontend:v1
Image created successfully
Let’s push this image to Docker Hub:
docker push tunhatphuong/ecommerce-frontend:v1
In the Docker Hub repository, we can see a new repo that we just pushed.
We will stop the frontend container to test pulling the image from Docker Hub:
docker ps
docker stop container_id
docker rm container_id
docker images
docker rmi your_name/ecommerce-frontend:v1
docker rmi ecommerce-frontend:v1
We have removed all containers and images of the frontend.
Let’s start the frontend project, even though there is no image.
docker run --name frontend -dp 3000:80 tunhatphuong/ecommerce-frontend:v1
When Docker cannot find the image locally, it will pull it from Docker Hub (assuming you are logged in with docker login
).
Before proceeding, we will grant permissions to the gitlab-runner on the Build Instance:
visudo
Add a new line:
gitlab-runner ALL=(ALL) NOPASSWD:ALL
Go to the frontend project → Build → Pipelines Editor
Access the editor in the main branch.
Enter the following lines:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
IMAGE_VERSION: "${CI_REGISTRY_USER}/${CI_PROJECT_NAME}-${USER_PROJECT}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
stages:
- build
- push_container
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
build:
stage: build
variables:
GIT_STRATEGY: clone
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker build -t $IMAGE_VERSION .
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
only:
- tags
push_container:
stage: push_container
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker push $IMAGE_VERSION
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker pull $IMAGE_VERSION
- sudo su ${USER_PROJECT} -c "
container_exists=\$(sudo docker ps -a -q -f name=${CI_PROJECT_NAME});
if [ ! -z \"\$container_exists\" ]; then
sudo docker rm -f ${CI_PROJECT_NAME};
fi;
sudo docker run --name ${CI_PROJECT_NAME} -dp ${FRONTEND_PORT}:80 ${IMAGE_VERSION}"
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner
only:
- tags
Variables
"ecommerce"
. This is a custom variable to reference the project name.USER_PROJECT
with the project name from GitLab CI variables to create a directory path for the project.USER/PROJECT-USER:COMMIT_REF_NAME_COMMIT_SHORT_SHA
. Example: tunhatphuong/frontend-ecommerce:frontend_v1.0.1_abcd123.Stages
Jobs
build
build
GIT_STRATEGY: clone
ensures the repository will be fully cloned for each build.IMAGE_VERSION
tag.group-ecommerce-shell-runner-build
runner to execute this job.push_container
push_container
GIT_STRATEGY: none
does not require cloning the repository for this stage.IMAGE_VERSION
tag to the Docker registry.group-ecommerce-shell-runner-build
.deploy
deploy
GIT_STRATEGY: none
does not require cloning the repository for this stage.IMAGE_VERSION
tag from the registry.CI_PROJECT_NAME
already exists. If so, it removes that container.CI_PROJECT_NAME
, mapping port FRONTEND_PORT
to port 80 of the container, and using the Docker image just pulled.group-ecommerce-shell-runner
.🡇 Content 🡇
System Variables:
CI_PROJECT_NAME
: The name of the GitLab project.CI_COMMIT_REF_NAME
: The name of the current branch or tag.CI_COMMIT_SHORT_SHA
: The short hash of the current commit.Custom Variables:
CI_REGISTRY_USER
: The username or organization in Docker registry.CI_REGISTRY_PWD
: The password for Docker registry (this is also a system variable but might need configuration through GitLab).FRONTEND_PORT
: The port for running the frontend.🡅 Content 🡅
Perform commit changes
With the custom variables, we will add them in the ecommerce group.
Navigate to group ecommerce → Settings → CI/CD
Select Expand → Add Variable
Add the following variables:
CI_REGISTRY_USER
: Docker Hub usernameCI_REGISTRY_PWD
: Docker Hub passwordFRONTEND_PORT
: 3000Final result
Add Dockerfile to the main branch
Add Dockerfile and commit
Create Tags in frontend
Check the results, all jobs passed
Check Docker Hub, a repo tunhatphuong/frontend-ecommerce with tag ecommerce-fe-v1.0.2_dd6e2d25 has been pushed from the Build Instance
At the Deploy Instance, when checking, there is a running container with the name frontend and image tunhatphuong/frontend-ecommerce:ecommerce-fe-v1.0.2_dd6e2d25
Don’t worry about the second pipeline run; even though port 3000 is already in use, the script checks, finds, and removes a container named frontend
if it is running!
Navigate to the backend project → Build → Pipelines Editor
Go to the editor in the main branch
Enter the following:
variables:
USER_PROJECT: "ecommerce"
PATH_PROJECT: "/home/${USER_PROJECT}/${CI_PROJECT_NAME}"
IMAGE_VERSION: "${CI_REGISTRY_USER}/${CI_PROJECT_NAME}-${USER_PROJECT}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
stages:
- build
- push_container
- deploy
before_script:
- sudo mkdir -p $PATH_PROJECT
build:
stage: build
variables:
GIT_STRATEGY: clone
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker build -t $IMAGE_VERSION .
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
only:
- tags
push_container:
stage: push_container
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker push $IMAGE_VERSION
after_script:
- sudo docker logout
tags:
- group-ecommerce-shell-runner-build
only:
- tags
deploy:
stage: deploy
variables:
GIT_STRATEGY: none
before_script:
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PWD
script:
- sudo docker pull $IMAGE_VERSION
- sudo su ${USER_PROJECT} -c "
container_exists=\$(sudo docker ps -a -q -f name=${CI_PROJECT_NAME});
if [ ! -z \"\$container_exists\" ]; then
sudo docker rm -f ${CI_PROJECT_NAME};
fi;
sudo docker run --name ${CI_PROJECT_NAME} -dp ${BACKEND_PORT}:${BACKEND_PORT} ${IMAGE_VERSION}"
after_script:
- sudo docker logout
tags:
- ecommerce-deploy-runner
only:
- tags
Perform commit changes
With the custom variables, add them in the ecommerce group.
Add the variable BACKEND_PORT
with the value 3000
Add Dockerfile to the main branch and commit
Create Tags in backend, check if all jobs passed
Check Docker Hub, a repo tunhatphuong/backend-ecommerce with tag ecommerce-be-v1.0.2_934f8c8d has been pushed from the Build Instance
At the Deploy Instance, when checking, there is a running container with the name backend and image tunhatphuong/backend-ecommerce:ecommerce-be-v1.0.2_934f8c8d