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
- bỏ qua mọt số file không cần thiết cho quá trình build image.
- Đả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ênnpm 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ó node18.13.0
version của node, nên chọn các version chẳn 14, 16 18 là các version LTSalpine
một hệ điều hành nân linux nhẹ hơn ubuntu- Phần
@sha256:3....
Là 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ừ filepackage-log.json
npm prune --production
remove devDependences vốn không cần thiết khi chạy trên productionnpm run start
bản chất lànest start
chạy file source chưa build đổi thànhnode 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 /app/node_modules /app/node_modulesCOPY /app/.env /app/COPY /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 /app/node_modules /app/node_modulesCOPY /app/.env /app/COPY /app/dist /app/dist#6 run appCMD [ "node", "dist/main.js" ]
-
Full source/ branch
final
here -
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