Feb 09, 2025
CICD với CircleCI và NextJS sử dụng docker
Phineas
@Phineas

CICD (Continuous Integration, Continuous Deployment) là bộ óc của một quy trình phát triển phần mềm chất lượng. Trong blog này, mình sẽ hướng dẫn các bạn thiết lập CICD cho một dự án Next.js, sử dụng CircleCI để tự động build, test và deploy code lên Docker Hub. Bài viết sẽ không đi quá sâu vì đã có GPT giải thích mà chỉ hướng dẫn đơn giản nhất.
Yêu cầu ^^
- Các bạn phải có 1 chút kiến thức về Docker, VPS, đã biết cấu hình source chạy trên VPS bằng Docker…
- Chuẩn bị cho mình 1 VPS (Hiện tại mình đang dùng contabo)
Source mẫu cho Reactjs: https://github.com/phineasdev-one/circle-ci
Source mẫu cho Nextjs: https://github.com/phineasdev-one/blog-fe
1. Tại sao lại chọn CircleCI và Docker Hub?
- CircleCI: Dễ sử dụng, tích hợp tốt với nhiều công cụ và đem lại hiệu quả cao cho quy trình CICD.
- Docker Hub: Là nơi chứa các Docker image, giúp chúng ta dễ dàng chia sẻ và triển khai.
2. Bắt đầu với Next.js và Docker
Tạo một dự án Next.js
- Nextjs Installation with Jest
npx create-next-app@latest --example with-jest with-jest-app
Lệnh trên cài đặt app với jest
Jest là một framework dùng để kiểm thử JavaScript, phổ biến cho các ứng dụng như React, Node.js và Next.js. Được phát triển bởi Facebook, Jest hỗ trợ việc viết và chạy các bài kiểm thử (test) với giao diện đơn giản và nhiều tính năng mạnh mẽ.
Tạo docker file
# Sử dụng Node.js 18 làm image cơ sở
FROM node:18-alpine AS base
# Thiết lập thư mục làm việc trong container
WORKDIR /app
# Bước 1: Tạo layer chứa dependencies để tối ưu build
FROM base AS dependencies
# Copy file package.json và package-lock.json vào container
COPY package.json package-lock.json* ./
# Cài đặt dependencies
RUN npm install --frozen-lockfile
# Bước 2: Build ứng dụng Next.js
FROM dependencies AS builder
# Copy toàn bộ mã nguồn vào container
COPY . .
# Build ứng dụng Next.js
RUN npm run build
# Bước 3: Chuẩn bị container chạy ứng dụng
FROM base AS runner
# Đặt biến môi trường NODE_ENV là production
ENV NODE_ENV=production
# Copy files cần thiết từ bước build
COPY /app/public ./public
COPY /app/.next ./.next
COPY /app/package.json ./package.json
COPY /app/next.config.mjs ./
# Cài đặt production dependencies
RUN npm install --only=production --frozen-lockfile
# Expose cổng để container nhận request
EXPOSE 3000
ENV PORT=3000
# Command mặc định để khởi chạy ứng dụng
CMD ["npm", "start"]
Tiếp theo, các hãy build và test ứng dụng xem đã chạy được chưa bằng lệnh
#Build và chạy image Docker
docker build -t my-personal-blog .
docker run -p 3000:3000 my-personal-blog
Truy cập http://localhost:3000 để xem kết quả.
3. Kết nối với Docker Hub
Đăng nhập Docker Hub từ CLI
docker login
Đẩy image lên Docker Hub thử
Gắn tag và push:
docker tag my-personal-blog <your-dockerhub-username>/my-personal-blog
docker push <your-dockerhub-username>/my-personal-blog
Sau đó hãy kiểm tra xem đã push được image lên Docker Hub chưa?

Sau đó có thể thêm 1 file docker compose
version: '3.8'
services:
client:
image: phianhdev/my-personal-blog:latest
container_name: Blog-FE
build: .
environment:
- TZ=Asia/Ho_Chi_Minh
restart: unless-stopped
# Cau hinh nginx neu muon
# nginx:
# container_name: Blog-Nginx-Client
# build: ./nginx
# ports:
# - 3001:80
# environment:
# - TZ=Asia/Ho_Chi_Minh
# restart: unless-stopped
# labels:
# io.portainer.accesscontrol.users: admin
# depends_on:
# - client
Tạo 1 file tên là deploy.sh ở source của bạn và cấu hình như sau
#!/bin/bash
# Dừng tất cả các container đang chạy
docker compose stop
# Xóa các container đã dừng (không giữ lại bất kỳ container nào cũ)
docker compose rm -f
# Tải các image mới nhất từ Docker registry
docker compose pull
# Tạo và khởi chạy các container mới trong chế độ chạy nền (detached mode)
docker compose up -d
4. Cài đặt CircleCI
Đầu tiên hãy đăng nhập vào CircleCI bằng cách chọn Login với Github. Chọn github để nó sync dữ liệu với Github, đỡ phải config nhiều thứ hơn.

- Sau khi đăng nhập xong, chọn phần PROJECTS, sẽ thấy các project có trong Github của các bạn

- Chọn set up project mà các bạn muốn và chọn setup config file ở nhánh mà bạn muốn

- Sau đó pull code mới về từ nhánh develop các bạn sẽ thấy thư mục circleci và file config.yml
- Cấu hình cho config.yml
version: 2.1
# Import các orbs tái sử dụng
orbs:
node: circleci/node@3.0.0 # Orb cho Node.js (cài đặt môi trường Node.js)
docker: circleci/docker@1.4.0 # Orb hỗ trợ làm việc với Docker (build, push image)
jobs:
# Job build và push Docker image
build-and-push:
docker:
- image: cimg/node:18.20.5 # Chọn image Docker để chạy môi trường Node.js
environment:
DOCKER_IMAGE: phianhdev/my-personal-blog # Tên Docker image sẽ build
DOCKER_TAG: latest # Tag của Docker image
executor: docker/docker # Dùng executor Docker từ orb
steps:
- setup_remote_docker # Thiết lập môi trường Docker từ xa để build image
- checkout # Clone source code của bạn từ repository
- node/install-packages # Cài đặt các package cần thiết từ `package.json`
- run:
name: Run tests # Chạy test để kiểm tra ứng dụng
command: |
npm run test # Lệnh jest test trong package.json
- docker/check: # Kiểm tra xem Docker đã sẵn sàng chưa
docker-username: DOCKER_USER # Username Docker (Có thể cấu hình env trong circle setting env)
docker-password: DOCKER_PASSWORD # Password Docker
- docker/build: # Build Docker image
image: $DOCKER_IMAGE
tag: $DOCKER_TAG
- docker/push: # Push Docker image lên Docker Hub
digest-path: /tmp/digest.txt # Lưu digest của image vào file tạm
image: $DOCKER_IMAGE
tag: $DOCKER_TAG
- run:
command: |
echo "Digest is: $(</tmp/digest.txt)" # In ra digest của image
# Job deploy ứng dụng lên VPS
deploy:
executor: docker/docker # Dùng executor Docker từ orb
steps:
- add_ssh_keys: # Thêm SSH key để kết nối tới server
fingerprints:
- FINGER_PRINT # Dùng fingerprint của SSH key để nhận diện
# Mình sẽ hướng dẫn ở bước sau
- run:
name: Debug Environment Variables # Debug các biến môi trường
command: |
echo "USERNAME: $USERNAME"
echo "IP_ADDRESS: $IP_ADDRESS"
- run:
name: Deploy to VPS # Deploy ứng dụng lên VPS
command: ssh -oStrictHostKeyChecking=no $USERNAME@$IP_ADDRESS "cd projects/blog-fe && bash -x deploy.sh"
#Chạy lệnh ssh với oStrictHostKeyCheck và trỏ đến project của bạn trong VPS, run file deploy.sh
# Định nghĩa workflows để chạy các jobs
workflows:
my-pipeline: # Tên workflows
jobs:
- build-and-push: # Chạy job build-and-push
filters: # Lọc nhánh để chỉ chạy trên nhánh develop
branches:
only:
- develop
- deploy: # Chạy job deploy
requires: # Chỉ chạy sau khi job build-and-push thành công
- build-and-push
OK. Sau khi cấu hình xong, các bạn hãy push source lên VPS và cấu hình source chạy
5. Cấu hình kết nối Github với CircleCI
Để thiết lập SSH key với GitHub trong CircleCI, bạn cần làm theo các bước sau:
Bước 1: Tạo SSH Key (nếu chưa có)
Nếu bạn chưa có SSH key, bạn có thể tạo một key mới trên máy tính của bạn:
- Mở terminal và chạy lệnh sau để tạo SSH key mới:
- Sau khi tạo key, SSH key sẽ được lưu trong thư mục
~/.ssh/
. Bạn cần lấy public key (thường có đuôi.pub
). - Kiểm tra xem SSH key đã được tạo chưa:
- Mở public key (
id_rsa.pub
hoặc tên file mà bạn đã chọn) và sao chép nội dung. - Truy cập vào GitHub, vào trang Settings của bạn.
- Trong phần SSH and GPG keys, nhấn New SSH key.
- Dán public key vào ô "Key" và nhập tên cho key trong ô "Title".
- Nhấn Add SSH key.
Bước 3: Thêm SSH Key vào CircleCI
- Truy cập vào trang CircleCI của bạn và vào project bạn muốn cấu hình.
- Vào phần Project Settings (Settings của project, ở góc dưới bên trái).
- Chọn mục SSH Keys từ menu bên trái.
- Nhấn Add SSH Key:
- Nhấn Add SSH Key để lưu.

Sau khi thiết lập xong, hãy thử commit 1 cái gì đó rồi kiểm tra pipeline

Cảm ơn các bạn đã đọc đến đây, chúc thành công!