[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

Thay đổi Package Name của Android Studio dể dàng với plugin APR

Nếu bạn đang gặp khó khăn hoặc bế tắc trong việc thay đổi package name trong And

Lỗi không Update Meta_Value Khi thay thế hình ảnh cũ bằng hình ảnh mới trong WordPress

Mã dưới đây hoạt động tốt có 1 lỗi không update được postmeta ” meta_key=

Bài 1 – React Native DevOps các khái niệm và các cài đặt căn bản

Hướng dẫn setup jenkins agent để bắt đầu build mobile bằng jenkins cho devloper an t

Chuyển đổi từ monolith sang microservices qua ví dụ

1. Why microservices? Microservices là kiến trúc hệ thống phần mềm hướng dịch vụ,