Back to blog

Feb 09, 2025

CICD với CircleCI và NextJS sử dụng docker

PH

Phineas

@Phineas

cover

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

bash
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

docker
# 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 --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /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

bash
#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

bash
docker login

Đẩy image lên Docker Hub thử

Gắn tag và push:

bash
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

docker
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

bash
#!/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
bash
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:

  1. Mở terminal và chạy lệnh sau để tạo SSH key mới:
  2. 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).
  3. Kiểm tra xem SSH key đã được tạo chưa:
  1. Mở public key (id_rsa.pub hoặc tên file mà bạn đã chọn) và sao chép nội dung.
  2. Truy cập vào GitHub, vào trang Settings của bạn.
  3. Trong phần SSH and GPG keys, nhấn New SSH key.
  4. Dán public key vào ô "Key" và nhập tên cho key trong ô "Title".
  5. Nhấn Add SSH key.

Bước 3: Thêm SSH Key vào CircleCI

  1. Truy cập vào trang CircleCI của bạn và vào project bạn muốn cấu hình.
  2. Vào phần Project Settings (Settings của project, ở góc dưới bên trái).
  3. Chọn mục SSH Keys từ menu bên trái.
  4. Nhấn Add SSH Key:
  5. 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!