Tối ưu docker image
tl;dr
- tối ưu
.dockerignore. - dùng minimal base image, ví dụ
alpine,slim,distroless. - giảm số lượng command / layer.
- install production dependency thôi.
- dùng remote build system, ví dụ
nxhayturborepo, để cache lại build. - sử dụng multistage build.
mục đích và ngữ cảnh
- mục đích: tối ưu dung lượng và thời gian build docker image để tăng hiệu suất triển khai.
- ngữ cảnh: build một full-stack JavaScript web app dùng React và Nest.js.
gốc
FROM node:18
EXPOSE 8081
WORKDIR /web
# copy source files
COPY package.json ./
COPY package-lock.json ./
COPY turbo.json ./
COPY packages ./packages
COPY apps/client ./apps/client
COPY apps/server ./apps/server
# install dependencies
RUN npm ci
# build
RUN npm run build
# run
CMD ["npm", "run", "serve", "-w", "server"]tối ưu .dockerignore
- ignore hết mấy file không cần thiết khỏi docker image như
README, ci, git, logs, env, dist, …
.env
logs
*.log
npm-debug.log*
docs
*.md
coverage
node_modules
.npm
dist
.git
.DS_Storedùng minimal base image
- dùng image nhẹ làm base image. image này được lược bỏ tối đa tính năng không cần thiết để tối ưu dung lượng.
- một vài minimal base image tiêu biểu là
alpine,slimvàdistroless. - trong 3 base images trên, thì
alpinelà image thông dụng nhất, còn nhẹ nhất thì làdistroless. - vì bị lược bỏ tối đa tính năng nên bạn phải tự cài tay những thư việc bạn muốn. ví dụ, sử dụng
distrolessimage, phải tự cài thêmnpmnếu muốn cài thêm thư viện. - vào việc. ở bước này mình sẽ dùng
node:alpine(alpinecài thêmnode.js).
FROM node:18-alpine- một lưu ý nhỏ là
Alpinekhông phảiUbuntu, nên các câu lệnh có thể khác biệt. điển hình là câu lệnh quản lý thư viện, trongAlpinelàapkcòn trongUbuntulàapt-get.
giảm số lượng câu lệnh / layer
- hiểu đơn giản thế này, docker image được tạo từ nhiều layer khác nhau. mỗi câu lệnh (
RUN,COPY,FROM,ADD, …) tạo thêm một layer cho docker image, càng nhiều câu lệnh thì docker image càng nặng. - do đó, nên gộp chung các câu lệnh để giảm số lượng layer.
- vào việc. ở bước này mình sẽ gộp các cậu lệnh
COPYở trên thành 2 câu lệnh.
COPY package*.json turbo.json ./
COPY packages apps ./install production dependency thôi
- thay vì install tất cả dependency, bạn chỉ nên install các dependency giúp app build và chạy.
- vào việc. ở bước này mình thêm
omit=devvào câu lệnhnpm ci.
# install dependencies
RUN npm ci --omit=devdùng remote build system
- cache lại output sau mỗi lần build, nếu ở lần build tiếp theo code không đổi thì lấy từ cache ra thay vì phải build lại.
- vào việc. ở bước này mình dùng
turborepo, bạn có thể dùngnxcái gì khác thay thế. lưu ý, bạn cần setup file settingturbo.jsontrước.
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"dev": {
"cache": false,
"persistent": true
},
"build": {
"cache": true,
"persistent": false,
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}{
"scripts": {
"build": "turbo run build --remote-only"
}
}ENV TURBO_TEAM=xxx
ARG TURBO_TOKEN
ENV TURBO_TOKEN=$TURBO_TOKENsử dụng multistage build
- tách stage build và stage run riêng. mục đích là để loại bỏ những layer chỉ cần thiết ở stage build mà không cần thiết ở stage run để giảm dung lượng image.
- vào việc. ở bước này mình tách ra thành 2 stage run và build. stage run chỉ copy thư mục
disttừ stage build. tiện tay, mình cũng dùng imagedistrolessthay chonode:18-alpine.
# BUILD
FROM node:20 AS build
WORKDIR /app
...
# build
RUN npm run build
# RUN
FROM gcr.io/distroless/nodejs20-debian11
EXPOSE 8081
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app /app
CMD ["apps/server/index.js"]nếu bạn vẫn rảnh
bạn có thể dùng depot.dev để giảm thời gian build. bên này quảng cáo giảm ~ 20 lần thời gian build trong một số trường hợp. nếu rảnh bạn có thể dùng thử và chia sẻ trải nghiệm giúp mình, chứ mình chưa dùng 🙈.
trong trường hợp mà bạn vẫn muốn tối ưu docker image lên một tầm cao mới (hoặc bạn quá rảnh), bạn có thể cân nhắc dùng một vài tool để audit docker image, ví dụ Dive, Docker Slim hay Docker Squash. mấy tool này mình cũng chưa dùng đâu, ai dùng rồi thì lại chia sẻ trải nghiệm nhé.
kết luận
- trên đây là một vài kinh nghiệm để tối ưu docker image mình tự đúc rút (cũng như copy trên mạng). lý thuyết vẫn chỉ là lý thuyết, bạn cần áp dụng chúng một cách hợp lý cho dự án của mình để đạt kết quả tốt.
