Tài khoảng Docker Hub:dùng để lưu trữ và chia sẻ Docker images.
Kiến thức cơ bản về Docker: cần phải có để thao tác được với docker và viết docker file cho dự án.
Linux Server: để triển khai ứng dụng.
2. Viết Docker FIle và Push Image lên Docker Hub
Trong phần này, chúng ta sử dụng Dockerfile để build ứng dụng thành image và đẩy lên Docker Hub.
2.1 Viết Dockerfile
Về phần Dockerfile mình sẽ sử dụng file example của NextJS.
FROM node:18-alpine AS base# Install dependencies only when neededFROM base AS deps# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.RUN apk add --no-cache libc6-compatWORKDIR /app# Install dependencies based on the preferred package managerCOPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi# Rebuild the source code only when neededFROM base AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .# Next.js collects completely anonymous telemetry data about general usage.# Learn more here: https://nextjs.org/telemetry# Uncomment the following line in case you want to disable telemetry during the build.# ENV NEXT_TELEMETRY_DISABLED=1RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi# Production image, copy all the files and run nextFROM base AS runnerWORKDIR /appENV NODE_ENV=production# Uncomment the following line in case you want to disable telemetry during runtime.# ENV NEXT_TELEMETRY_DISABLED=1RUN addgroup --system --gid 1001 nodejsRUN adduser --system --uid 1001 nextjsCOPY --from=builder /app/public ./public# Set the correct permission for prerender cacheRUN mkdir .nextRUN chown nextjs:nodejs .next# Automatically leverage output traces to reduce image size# https://nextjs.org/docs/advanced-features/output-file-tracingCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/staticUSER nextjsEXPOSE 3000ENV PORT=3000# server.js is created by next build from the standalone output# https://nextjs.org/docs/pages/api-reference/next-config-js/outputENV HOSTNAME="0.0.0.0"CMD ["node", "server.js"]
2.2 Cập nhật Next config
Để run được Dockerfile, các bạn cần cập nhật lại một chút ở file next.config.js. Thêm Key output vào Object nextConfig.
Việc này sẽ tạo thư mục .next/standalone khi build và sau đó chúng ta có thể deploy ứng dụng của mình mà không cần cài đặt node_modules.
2.3 Build ứng dụng thành Image
Ở bước này, chúng ta sẽ build ứng dụng thành image với tên là nexjts bằng cách chạy câu lệnh bên dưới.
docker build -t nextjs .
Chạy câu lệnh sau để kiểm tra image vừa build được.
docker image ls | grep nextjs
2.4 Push image vừa build lên Docker Hub để lưu trữ
Đầu tiên, các bạn cần phải đăng nhập vào Docker Hub.
docker login
Build lại image với username docker hub của các bạn, các bạn nhớ thay bằng docker hub username của các bạn nhé (của mình là duykhanh2401). Không push bằng username của mình được đâu nhé.
Như vậy, chúng ta đã có sẵn image trên Docker Hub và nắm được logic cơ bản về việc build và deploy một ứng dụng NextJS. Tiếp theo, chúng ta sẽ thiết lập server và cài đặt các môi trường cần thiết để có thể chạy ứng dụng trên server.
3. Cài đặt server
Trong phần này bạn hãy sử dụng 1 server linux bất kỳ. Và chuẩn bị SSH Key để truy cập vào server
3.1 Cài đặt docker
Chạy các câu lệnh bên dưới để cài đặt Docker trên server.
Ở bước này, bạn đã cài đặt Docker trên server, nhưng khi chạy lệnh docker ps, bạn sẽ gặp vấn đề về quyền truy cập. Để chạy lệnh mà không cần dùng sudo, bạn có thể sử dụng đoạn script dưới đây.
sudo usermod -aG docker $(whoami)
Reboot lại server của bạn
sudo reboot
3.2 Khởi tạo Docker Conainer
Đầu tiên, chúng ta sẽ pull image mà ta đã lưu trữ trên Docker Hub về lại server.
docker pull duykhanh2401/nextjs
Kiểm tra image đã có trên server chưa.
Kiểm tra image đã có trên server chưa.
Chạy container với image vừa được pull về.
docker run -it -d --name next -p 3000:3000 duykhanh2401/nextjs
Kiểm tra container có running thành công hay chưa.
docker ps
Bây giờ các bạn có thể truy cập trong web của mình bằng [IP_server]:[PORT]. Bạn cần mở port 3000 vì đây là cổng mà ứng dụng Next.js thường sử dụng để chạy.
Vậy là chúng ta đã có thể deploy ứng dụng NextJS lên server thành công. Nhưng hiện tại thì chúng ta đang phải thao tác tay quá nhiều, bây giờ chúng ta sẽ bắt đầu tự động hoá quá trình build và deploy ứng dụng thông qua Github Action.
4. Thiết lập quy trình CI/CD bằng Github Actions
Ở phần này thì chúng ta cũng chỉ sẽ thực hành build CI/CD cơ bản sử dụng Github Actions.
4.1 Push code lên Github repository
Vì chúng ta sẽ sử dụng Github Actions để thiết lập quy trình CI/CD, nên bước đầu tiên là phải đảm bảo source code đã được đẩy lên Github.
4.2 Build và Push image lên Docker Hub
Trong thư mục gốc (root) của dự án, chúng ta cần tạo một thư mục mới có tên .github/workflows. Đây là nơi lưu trữ các workflow mà Github Actions sẽ sử dụng để tự động hóa quá trình build và deploy.
Tiếp theo, bạn tạo một file có tên deploy.yml bên trong thư mục vừa tạo. File này sẽ chứa các bước cấu hình để build image của ứng dụng và đẩy lên Docker Hub, đảm bảo quá trình triển khai diễn ra liên tục mỗi khi có thay đổi trong repository.
name: 'Build and deploy to server'on: push: branches: - mainjobs: deploy: name: Deploy to server runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Login to docker hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Tags docker image id: meta uses: docker/metadata-action@v5 with: images: ${{ secrets.DOCKER_USERNAME }}/blog # Change this to your docker image name - name: Create .env file # Tạo File .env run: | echo "JINA=${{ secrets.JINA }}" >> .env echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> .env - name: Build and push to docker hub uses: docker/build-push-action@v6 with: context: . push: true tags: taodo2000/blog:latest secrets: | "DATABASE_URL=${{ secrets.DATABASE_URL }}" "JINA=${{ secrets.JINA }}" - name: Deploy to server uses: appleboy/[email protected] with: host: ${{ secrets.HOST }} # Địa chỉ của server username: ${{ secrets.USERNAME }} # Username để login vào server key: ${{ secrets.SSH_KEY }} # Private key để login vào server script: | docker pull taodo2000/blog:latest docker stop blog || true docker rm blog || true docker run -d --name blog -p 127.0.0.1:3000:3000 taodo2000/blog:latest
Lưu ý rằng trong file deploy.yml, chúng ta sẽ cần sử dụng các thông tin nhạy cảm như DOCKER_USERNAME và DOCKER_PASSWORD. Các bạn tuyệt đối không nên viết trực tiếp những thông tin này vào file, vì việc này có thể làm lộ tài khoản và gây rủi ro bảo mật. Thay vào đó, chúng ta sẽ sử dụng tính năng secrets của Github để bảo vệ và quản lý các thông tin nhạy cảm này.
Truy cập lại vào repository trên Github, chọn Setting -> Secrets and variables -> Actions.
Chọn New repository secret => điền thông tin Key và Secret (value) vào đây, chúng ta có bao nhiêu Secret thì chúng ta tạo bấy nhiêu. Lưu ý, với DOCKER_PASSWORD chúng ta sẽ dùng chuỗi secret của docker hub thay cho mật khẩu.
Như các bạn đã thấy thì mình đã tạo được 2 key chứa thông tin username và password cho tài khoản Docker Hub của mình.
4.3 Thực thi Github Actions đã thiết lập
Chuyển sang tab Actions, bạn sẽ thấy Github Actions của chúng ta tự động bắt đầu chạy. Đây là bước giúp kích hoạt quy trình CI/CD mà chúng ta đã thiết lập, đảm bảo việc build và deploy diễn ra ngay sau khi phiên bản mới được phát hành.
Bạn có thể theo dõi tiến trình của từng job trong quy trình CI/CD. Tại đây, bạn sẽ thấy chi tiết từng bước mà chúng ta đã cấu hình, từ build image đến push lên Docker Hub.
Như vậy là chúng ta đã tự động hoá thành công bước build và push image của chúng ta lên Docker Hub thành công. Bây giờ chúng ta sẽ tự động hoá quá trình deploy lên server và khởi chạy với image mà chúng ta vừa build đó.
5. Kết luận
Trong bài viết này, mình đã hướng dẫn các bạn cách xây dựng một luồng CI/CD cơ bản để tự động triển khai ứng dụng NextJS lên server. Qua đó, chúng ta đã thấy cách tự động hóa quá trình build và deploy giúp tiết kiệm thời gian và giảm bớt các thao tác thủ công.