Giới thiệu về Docker Multi-Stage Builds
Bài đăng này đã không được cập nhật trong 3 năm
Giới thiệu
Ở bài viết trước của mình (https://viblo.asia/p/lam-the-nao-de-giam-dung-luong-cua-docker-image-63vKjeRN52R) có đề cập tới một thứ gọi là Docker Multi-Stage Builds, và sau khi tìm hiểu về nó thì mình xin được giới thiệu với các bạn về nó.
Đa số các ví dụ về Dockerfile đều là những step giống hệt nhau, tuy nhiên không phải Dockerfile nào cũng giống nhau. Có một cách tối ưu để viết nhằm tạo ra một Docker image mà bạn cần và muốn cho sản phẩm của mình. Nếu bạn để ý thì một Docker image bao gồm những lớp filesystem có liên quan tới những build step riêng lẻ góp phần tạo nên image.
Để tạo ra một Docker image tối ưu và hiệu quả, bạn cần phải loại bỏ bớt những lớp filesystem này khỏi phần output cuối cùng. Một cách tiếp cận để giữ cho dung lượng của Docker image càng nhỏ càng tốt đó chính là sử dụng multi-stage builds.
Multi-stage builds cho phép bạn sử dụng nhiều image để tạo ra một sản phẩm cuối cùng. Bạn chỉ có/cần một Dockerfile, nhưng có thể sử dụng nhiều image ở trong đó để giúp cho việc tạo image của chính bạn.
Docker Multi-Stage Builds
Trước khi đi vào phần chi tiết thì mình xin giới thiệu về lý do vì sao bạn lại cần nó. Chắc bạn đã quen thuộc với khởi điểm của một container image, đó chính là Dockerfile. Dockerfile là một file text chứa những command cần thiết để xây dựng container image, và những command này nên gộp lại chung với nhau nếu có thể, nhằm tối ưu hóa image.
Khi sử dụng Docker thì đôi khi mình tự hỏi sẽ có ảnh hưởng gì nếu bạn không tối ưu file image? Nó có ảnh hưởng nhiều tới application của mình hay không? Thường thì bạn sẽ build ra những image có dung lượng khá là to, và điều này cần tránh bởi vì nó tăng khả năng tạo ra những lỗ hổng bảo mật cũng như những phương pháp mà có thể tấn công application của bạn. Bạn cần phải đảm bảo là image của bạn chỉ có những gì cần thiết để chạy ở môi trường production.
Làm thế nào để sử dụng Multi-Stage Builds
Bạn có thể làm điều tương tự bằng cách sử dụng hai Dockerfile khác nhau từ phiên bản Docker 17.06 CE trở về trước. Tuy nhiên nó khá là phiền phức bởi vì bạn cần phải quản lý hai Dockerfile khác nhau, chưa kể đến việc cần chạy nhiều lệnh Docker khác nhau,
Với Multi-Stage Builds thì bạn có thể làm được chỉ trong một Dockerfile. Bằng cách sử dụng nhiều lệnh FROM
, mỗi lệnh FROM
là một build stage mà bạn có thể COPY
artifact từ stage trước để sử dụng. Với phương pháp này, bạn sẽ loại bỏ được những bước trung gian như tải source code, cài dependencies,... Những thứ trên đều tạo ra những layer, và bạn cần loại bỏ điều này ra khỏi final image.
FROM node:12.13.0-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/html
Ví dụ ở trên có 2 lệnh FROM
, mỗi lệnh là một build stage khác nhau. Ở build stage thứ 2, lệnh COPY --from
được sử dụng để chỉ copy những phần cần thiết của application từ build stage trước để sử dụng.
Vấn đề thường gặp
Tùy vào cách thiết kế của Dockerfile, tốc độ build có thể là một vấn đề bạn sẽ gặp phải trong quá trình build. Docker cache sẽ không chứa những image của từng build stage khác nhau, dẫn đến việc mỗi lần build lại thì sẽ khá là tốn thời gian. Để có thể tận dụng cache, bạn có thể tag và đẩy image của build stage lên Dockerhub, và sau đó pull về, tránh việc phải build lại stage đó.
FROM node:12.13.0-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM lukondefmwila/react:build-stage as build
FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/html
Tổng kết
Tạo ra một Dockerfile đúng và tối ưu không phải là một công việc dễ dàng, nhưng sản phẩm cuối cùng sẽ giúp bạn về mặt bảo mật của application của mình.
All rights reserved