[Nodejs thực chiến] Dockerize, Containerize nodejs app thật chuẩn

1. Đặt vấn đề: Container là một phương pháp đóng gói ứng dụng để ứng dụng có thể chạy với các phụ thuộc của mình (gồm source code và library, runtime, framework…) một cách độc lập, tách biệt với các chương trình khác. Tool đầu tiên, phổ biến nhất để container ứng dụng là Docker.

1. Đặt vấn đề:

  • Container là một phương pháp đóng gói ứng dụng để ứng dụng có thể chạy với các phụ thuộc của mình (gồm source code và library, runtime, framework…) một cách độc lập, tách biệt với các chương trình khác.
  • Tool đầu tiên, phổ biến nhất để container ứng dụng là Docker. Ngoài ra còn nhiều nhiều tools khác.
  • Có rất nhiều blog hướng dẫn containerize nodejs nhưng mình thấy rằng khá sơ khai. Với blog này hy vọng giúp mọi ngừoi containerize nodejs app một cách chuẩn hơn.

2. Bước đầu tiên

  • Source code ví dụ mình fork của freecodecamp về repo của mình. source.
  • Để containerize mình tạo file Dockerfile:
#1 Từ base imageFROM nodeWORKDIR /app#2 Coppy toàn bộ sourceCOPY . .#3 install dependenceRUN npm i# build codeRUN npm run build# run appCMD [ "npm", "run", "start" ]
  • Run build docker bằng docker-compose: docker compose up --build
  • Check health: curl http://localhost:3333/users Server trả về: {"id":0,"name":"Test containerize"}
  • Check size image: docker image inspect node-containerize_server --format='{{.Size}}', Kết quả: 1755772592 = 1,76G
  • Các bước tiếp theo mình sẽ thực hiện để giảm image zize và tăng tính hợp lý cho dockerfile.

3. Tạo dockerignore file

  • File dockerignore sẽ giúp docker
  1. bỏ qua mọt số file không cần thiết cho quá trình build image.
  2. Đảm bảo môi trường container không có liên hệ với các config, các dependence của môi trường local. Ví dụ môi trường local dùng node:14, docker dùng node:16, nếu coppy folder node_modules từ môi trường sẽ có thể dẫn đến bug.
  • touch .dockerignore
# Ignore file IDE setting
.vscode/
# Ignore folder node_modules
node_modules/

# Ignore file build
dist/

# Ignore các file folder khác không cần sửa dung
npm-debug.log
test/
  • Image size không thay đổi nhiều. > 1,7G
  • Thời gian build tăng lên do không còn node_modules nên npm i lâu hơn

4. Chọn base image:

  • Ban đầu: FROM node

  • Thành: FROM node:18.13.0-alpine3.16@sha256:3eb81689b639f6a7308c04003653daa94122bfcdbba9945e897b12cfe10bb034 as node

  • Giải thích:

    • node -> imgae có node
    • 18.13.0 version của node, nên chọn các version chẳn 14, 16 18 là các version LTS
    • alpine một hệ điều hành nân linux nhẹ hơn ubuntu
    • Phần @sha256:3....DIGEST Là định danh độc lập và bất biến của image
  • Kết quả khi build image size: 978871571 = 98M = 0.97G Bé hơn nhiều so với 1,72G. Chủ yếu là do alpine có size nhỏ hơn.

  • Thời gian build nhỏ hơn một tý do thừoi gian tải image nhanh hơn.

5. Install dependences.

  • Ban đầu
RUN npm iRUN npm run buildRUN ["npm", "run", "start"]
  • Thành
RUN npm ciRUN npm run buildRUN npm prune --productionRUN ["node", "dist/main"]
  • Giải thích:

    • npm ci chỉ install từ file package-log.json
    • npm prune --production remove devDependences vốn không cần thiết khi chạy trên production
    • npm run start bản chất là nest start chạy file source chưa build đổi thành node dist/main chạy file code đã build
  • Kết quả image size: 931057119 = 93M bé hơn một chút so với 98M. Nhưng thực tế khi mình làm việc thì source dự án thường lớn hơn source đang dùng để test nhiều lần nên với cánh này size sẽ bé hơn khá nhiều.

5. Multi stage

  • Thêm:
FROM node as serverENV NODE_ENV=productionWORKDIR /app# COPY --from=server-builder /app/ /app/COPY--from=server-builder /app/node_modules /app/node_modulesCOPY--from=server-builder /app/.env /app/COPY--from=server-builder /app/dist /app/dist
  • COPY --from=server-builder /app/.env /app/ Nếu config thông qua configMap hay secretKeys thì không cần coppy file này.
  • Kết quả size giảm còn 414639502 = 41M Chưa bằng 1/2 của 93M.
  • Nguyên nhân do khi install và build tạo rất nhiều file cache, file log. Tạo image mới chỉ coppy những folder cần thiết giúp giảm size file đi rất nhiều.
  • Xem thêm tại document của docker Mutil stage

6. Kết luận:

  • Image size giảm từ 1,7G -> 0.41G, ứng dụng vẫn chạy bình thường.
  • Dockerfile:
#1 Từ base imageFROM node:18.13.0-alpine3.16@sha256:3eb81689b639f6a7308c04003653daa94122bfcdbba9945e897b12cfe10bb034 as nodeFROM node as server-builderWORKDIR /app#2 Coppy toàn bộ sourceCOPY . .#3 install dependenceRUN npm ci#4 build codeRUN npm run build#4 remove dev dependenceRUN npm prune --production#5 Coppy qua image mớiFROM node as serverENV NODE_ENV=productionWORKDIR /appCOPY--from=server-builder /app/node_modules /app/node_modulesCOPY--from=server-builder /app/.env /app/COPY--from=server-builder /app/dist /app/dist#6 run appCMD [ "node", "dist/main.js" ]
  • Full source/ branch finalhere

  • Mọi người có thể xem thêm các bài viết của mình trong seri Node thực chiến

  • Cảm ơn mọi người đã đọc.

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ