Kể từ khi Bitnami release container Docker đầu tiên vào năm 2015, các kỹ thuật viết Dockerfiles đã phát triển đáng kể.
Trong hướng dẫn này, tôi chia sẻ những bài học kinh nghiệm, mô tả một số thực tiễn tốt nhất và những vấn đề phổ biến mà bạn có thể gặp phải khi phát triển Dockerfiles, bằng cách áp dụng chúng vào các ví dụ thực tế. Đầu tiên, tôi sẽ giải thích ngắn gọn một số khái niệm cơ bản mà bạn cần làm mới trước khi kiểm tra các trường hợp cụ thể. Sau đó, tôi sẽ hướng dẫn bạn qua một số ví dụ thực tế, để cải thiện thời gian build, kích thước và bảo mật của Docker Image. Để làm điều đó, Bạn có thể tham khảo kho lưu trữ GitHub chứa tất cả các tệp bạn cần, để làm theo các mẹo và thủ thuật được hiển thị trong bài đăng này.
Nhắc lại khái niệm cơ bản về Docker Image và Dockerfiles
Hướng dẫn này giả định rằng bạn quen thuộc với Docker và môi trường Build của nó. Hãy xem lại một số khái niệm cơ bản trước khi bạn bắt đầu áp dụng chúng vào thực tế.
Docker Image là gì?
Docker Image là một Template cho phép bạn tạo ra các container. Nó được biểu diễn dưới dạng một danh sách các hướng dẫn (được gọi là các Layer) trong một hệ thống tập tin.
Dockerfile là gì?
Dockerfile chỉ là một bản thiết kế có chứa các hướng dẫn để build Image Docker. Hiện tại, hơn một triệu Dockerfiles đang có trên GitHub.
“Docker build” là gì?
Quá trình build Image Docker từ Dockerfile được gọi là Docker build.
Tìm thông tin chi tiết trong tài liệu tham khảo Dockerfile .
Docker Layer là gì?
Mỗi layer trong Docker context đại diện cho một hướng dẫn có trong Dockerfile của Docker Image. Các layer cũng có thể được gọi là “build step”.
Docker build cache là gì?
Mỗi khi bạn Build một Docker Image, mỗi bước Build được lưu trữ. Sử dụng lại các Layer được lưu trong bộ nhớ cache không thay đổi trong quá trình Build lại Image để cải thiện thời gian Build.
Những lo ngại khi Build Image
Đây là phầnchính mà tôi sẽ đề cập trong hướng dẫn này:
- Tính nhất quán: Nếu bạn nhất quán thiết kế Image của mình, chúng sẽ dễ bảo trì hơn và bạn sẽ giảm thời gian dành cho việc phát triển Image mới.
- Thời gian Build: Đặc biệt là khi các bản dựng của bạn được tích hợp trong một Continuous Integration pipeline (CI), việc giảm thời gian Build có thể giảm đáng kể chi phí phát triển ứng dụng của bạn.
- Kích thước Image: Giảm kích thước Image của bạn để cải thiện tính bảo mật, hiệu suất, hiệu quả và khả năng bảo trì của các Container của bạn.
- Bảo mật: Quan trọng đối với môi trường sản xuất, bảo mật các Container của bạn là rất quan trọng để bảo vệ các ứng dụng của bạn khỏi các mối đe dọa và tấn công bên ngoài.
Điều kiện tiên quyết
Có hai công cụ sẽ giúp bạn phát triển Dockerfiles. Trước khi bắt đầu hướng dẫn, tôi khuyên bạn nên cài đặt:
- Kích hoạt BuildKit
- Cài đặt Linter cho Dockerfiles trên trình chỉnh sửa của bạn
Kích hoạt BuildKit
Buildkit là bộ công cụ là một phần của dự án Moby giúp cải thiện hiệu suất khi Build Image Docker. Nó có thể được kích hoạt theo hai cách khác nhau:
- Tạo biến môi trường
DOCKER_BUILDKIT
:
export DOCKER_BUILDKIT=1
TIP: Thêm hướng dẫn này vào tệp ~/.bashrc của bạn
- Hoặc cấu hình Docker Daemon để thêm tính năng Buildkit :
{ "features": { "buildkit": true } }
Cài đặt Linter cho Dockerfiles trên trình chỉnh sửa của bạn
Linter giúp bạn phát hiện lỗi cú pháp trên Dockerfiles của bạn và cung cấp cho bạn một số gợi ý dựa trên thông lệ chung.
Có các plugin cung cấp các chức năng này cho hầu hết mọi Môi trường phát triển tích hợp (IDE). Đây là một vài gợi ý:
- Atom: linter-docker
- Eclipse: Docker Editor
- Visual Studio: Docker Linter
Ví dụ: cải thiện Image Docker của ứng dụng Node.js
Để giúp bạn làm theo các ví dụ dưới đây, Tham khảo kho lưu trữ GitHub chứa tất cả các tệp bạn cần trong mỗi bước của hướng dẫn.
Các ví dụ dựa trên việc Build Image Docker của ứng dụng Node.js rất đơn giản bằng cách sử dụng các tệp bên dưới:
- Một Dockerfile có chứa các định nghĩa Image.
- Một LICENSE .
- Một package.json mô tả ứng dụng và các phụ thuộc của nó (về cơ bản là mô-đun NPM Express).
- Một server.js xác định ứng dụng web bằng framework Express.
- Một README.md với một số hướng dẫn.
Dockerfile khá đơn giản:
FROM debian # Copy application files COPY . /app # Install required system packages RUN apt-get update RUN apt-get -y install imagemagick curl software-properties-common gnupg vim ssh RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - RUN apt-get -y install nodejs # Install NPM dependencies RUN npm install --prefix /app EXPOSE 80 CMD ["npm", "start", "--prefix", "app"]
Đây là cách các dòng trên có thể được đọc:
Sử dụng debian
làm Image Base, nó cài đặt nodejs và npm trong hệ thống bằng lệnh apt-get
. Để chạy ứng dụng, cần phải cài đặt một số gói hệ thống bổ sung để tập lệnh thiết lập Node.js hoạt động, chẳng hạn như curl , fantemagick, software-properties-common hoặc gnupg . Hơn nữa, ta cài đặt thêm các gói vim và ssh cho mục đích gỡ lỗi.
Khi Image có tất cả những gì nó cần để Build ứng dụng, nó sẽ cài đặt các phụ thuộc của ứng dụng và sử dụng lệnh npm start
để khởi động ứng dụng. Cổng 80
được expose cho ứng dụng sử dụng nó và nó được chỉ định với tham số EXPOSE
. Để Build Image Docker cho ứng dụng này, hãy sử dụng lệnh dưới đây:
docker build . -t express-image:0.0.1
LƯU Ý: Bạn có thể chỉ định tag Image bằng định dạng:
IMAGE_NAME:TAG
Phải mất 127,8 giây để Build Image và nó là 554 MB . Hãy cải thiện kết quả này bằng cách làm theo một số thực hành khác !!
Tận dụng Build cache
Build cache dựa trên các bước phía trước nó đã chạy. Bạn nên luôn luôn ghi nhớ và giảm thời gian Build bằng cách sử dụng lại các Layer hiện có.
Hãy thử mô phỏng quá trình Build lại Image của ứng dụng của bạn để giới thiệu một thay đổi mới trong code, để bạn có thể hiểu cách cache hoạt động. Để làm như vậy, chỉnh sửa message được sử dụng trong console.log
lúc server.js và Build lại Image bằng cách sử dụng lệnh dưới đây:
$ docker build . -t express-image:0.0.2
Phải mất 114,8 giây để Build Image.
Sử dụng phương pháp hiện tại, bạn không thể sử dụng lại bộ đệm Build để tránh cài đặt các gói hệ thống nếu một bit thay đổi trong code của ứng dụng. Tuy nhiên, nếu bạn chuyển đổi thứ tự của các Layer, bạn sẽ có thể tránh cài đặt lại các gói hệ thống:
FROM debian - # Copy application files - COPY . /app # Install required system packages RUN apt-get update ... RUN apt-get -y install nodejs + # Copy application files + COPY . /app # Install NPM dependencies ...
Build lại Image bằng cách sử dụng cùng một lệnh, nhưng tránh cài đặt các gói hệ thống. Đây là kết quả: chỉ mất 5,8 giây để Build !! Sự cải thiện là rất lớn !!
Nhưng điều gì sẽ xảy ra nếu một ký tự đơn thay đổi trong tệp README.md (hoặc trong bất kỳ tệp nào khác có trong kho lưu trữ nhưng không liên quan đến ứng dụng)? Hiện tại bạn sẽ sao chép toàn bộ thư mục vào Image và do đó, bạn sẽ lại ném bộ nhớ cache !!
Bạn nên cụ thể hơn về các tệp bạn sao chép để đảm bảo rằng bạn không làm mất hiệu lực cache với các thay đổi không ảnh hưởng đến ứng dụng.
... # Copy application files - COPY . /app + COPY package.json server.js /app # Install NPM dependencies ...
LƯU Ý: Sử dụng “COPY” thay vì “ADD” khi có thể. Cả hai lệnh về cơ bản đều làm cùng một việc, nhưng “add” phức tạp hơn nhiều (nó có các tính năng bổ sung như trích xuất tệp hoặc sao chép chúng từ các nguồn từ xa).
Tránh cài package mà bạn không cần
Khi Build các container để chạy trong production, mọi package không sử dụng, hoặc các package được bao gồm cho mục đích gỡ lỗi, cần được loại bỏ.
Dockerfile hiện tại bao gồm package hệ thống ssh . Tuy nhiên, bạn có thể truy cập vào các Container của mình bằng lệnh docker exec
thay vì ssh vào chúng. Ngoài ra, nó cũng bao gồm vim cho mục đích gỡ lỗi, có thể được cài đặt khi cần, thay vì đóng gói theo mặc định. Cả hai gói đều có thể xóa bỏ khỏi Image.
Ngoài ra, bạn có thể chỉnh cấu hình trình quản lý package để tránh cài đặt các package mà bạn không cần. Để làm như vậy, sử dụng --no-install-recommends
trên các apt-get
của bạn :
... RUN apt-get update - RUN apt-get -y install imagemagick curl software-properties-common gnupg vim ssh + RUN apt-get -y install --no-install-recommends imagemagick curl software-properties-common gnupg RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - - RUN apt-get -y install nodejs + RUN apt-get -y install --no-install-recommends nodejs # Install NPM dependencies ...
Mặt khác, không nên sử dụng các bước Build khác nhau để cập nhật / cài đặt các package hệ thống, vì bạn có thể cài đặt các package lỗi thời khi Build lại Image. Hãy hợp nhất chúng trên một Layer duy nhất:
... - RUN apt-get update - RUN apt-get install -y --no-install-recommends imagemagick curl software-properties-common gnupg + RUN apt-get update && apt-get -y install --no-install-recommends imagemagick curl software-properties-common gnupg - RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - - RUN apt-get -y install --no-install-recommends nodejs + RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get -y install --no-install-recommends nodejs # Install NPM dependencies ...
Cuối cùng, xóa bộ đệm của trình quản lý package để giảm kích thước Image:
... RUN apt-get update && apt-get -y install --no-install-recommends imagemagick curl software-properties-common gnupg - RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get -y install --no-install-recommends nodejs + RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get -y install --no-install-recommends nodejs && rm -rf /var/lib/apt/lists/* # Install NPM dependencies ...
Nếu bạn Build lại Image một lần nữa
$ docker build . -t express-image:0.0.3
Image đã giảm xuống còn 340 MB !! Đó là gần một nửa kích thước ban đầu của nó.
Sử dụng minideb
Minideb là một Image dựa trên Debian tối giản được Build để sử dụng làm Base Image cho các Container. Để giảm đáng kể kích thước Image, sử dụng nó làm Image Base.
- FROM debian + FROM bitnami/minideb # Install required system packages ...
Minideb bao gồm một lệnh được gọi là install_packages
:
- Cài đặt các package được đặt tên, bỏ qua lời nhắc, vv
- Dọn dẹp siêu dữ liệu apt sau đó để giữ Image nhỏ.
- Tự động thử lại nếu
apt-get
không thành công.
Thay thế các apt-get
bằng lệnh như sau:
... # Install required system packages - RUN apt-get update && apt-get -y install --no-install-recommends imagemagick curl software-properties-common gnupg + RUN install_packages imagemagick curl software-properties-common gnupg - RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && apt-get -y install --no-install-recommends nodejs && rm -rf /var/lib/apt/lists/* + RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && install_packages nodejs # Copy application files ...
Build lại Image:
$ docker build . -t express-image:0.0.4
Như bạn thấy, bạn đã tiết kiệm thêm 63 MB . Kích thước Image bây giờ là 277 MB !!
Tái sử dụng Image có thể
Sử dụng Image được maintain bởi Bitnami mang lại cho bạn một số lợi ích:
- Giảm kích thước bằng cách chia sẻ các Layer giữa các Image.
- Theo dõi lỗ hổng Image Base trên Quay bằng cách tham khảo kết quả quét.
- Đảm bảo tất cả các thành phần được đóng gói với các bản vá có sẵn mới nhất kể từ khi chúng được Build lại mỗi ngày.
Thay vì cài đặt các gói hệ thống bạn cần chạy ứng dụng ( Node.js trong trường hợp này), hãy sử dụng bitnami/node
Image:
- FROM bitnami/minideb + FROM bitnami/node - # Install required system packages - RUN install_packages imagemagick curl software-properties-common gnupg - RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && install_packages nodejs # Copy application files ...
Cụ thể hơn về Tag Image Base
Image được duy trì thường có các tag khác nhau, được sử dụng để chỉ định các điểm khác nhau của chúng. Chẳng hạn, bitnami/node
Image được Build cho các phiên bản Node.js khác nhau và nó có prod
bao gồm các gói cần thiết tối thiểu để chạy ứng dụng Node (xem Thẻ được hỗ trợ ).
Theo ví dụ này, hãy tưởng tượng rằng ứng dụng đang yêu cầu node >= 10
trong package.json . Do đó, bạn nên sử dụng tag 10-prod
để đảm bảo rằng bạn đang sử dụng Node.js 10 với các package tối thiểu:
- FROM bitnami/node + FROM bitnami/node:10-prod # Copy application files ...
Khi bạn thêm tag đó, hãy Build lại Image:
$ docker build . -t express-image:0.0.5
Đây là các kết quả: 48 MB đã được giảm do kích thước Image hiện là 229 MB . Nhờ sự thay đổi tinh tế nhưng quan trọng đó, bạn sẽ không phải lo lắng về các package hệ thống nữa!
Sử dụng multi-stage để phân tách các môi trường Build và thời gian chạy
Nhìn vào Dockerfile hiện tại (sau khi áp dụng các cải tiến ở trên) để thấy những điều sau đây:
FROM bitnami/node:10-prod # Copy application files COPY package.json server.js /app # Install NPM dependencies RUN npm install --prefix /app EXPOSE 80 CMD ["npm", "start", "--prefix", "/app"]
Trạng thái hiện tại của Dockerfile Template hiển thị hai loại bước Build có thể nhận ra:
- Build ứng dụng từ mã nguồn và cài đặt các phụ thuộc của nó.
- Chạy ứng dụng.
Để tiếp tục cải thiện hiệu quả và kích thước của Image, hãy chia quá trình Build thành các giai đoạn khác nhau. Bằng cách đó, Image cuối cùng sẽ đơn giản nhất có thể.
Sử dụng multi-stage là cách tốt để chỉ sao chép các phần cần thiết trong Image cuối cùng. Hãy xem cách làm trong ví dụ này:
FROM bitnami/node:10 AS builder COPY package.json server.js /app RUN npm install --prefix /app FROM bitnami/node:10-prod COPY --from=builder /app/package.json /app/server.js /app COPY --from=builder /app/node_modules /app/node_modules EXPOSE 80 CMD ["node", "/app/server.js"]
Đây là một bản tóm tắt ngắn về những gì tôi đã làm:
Sử dụng bitnami/node:10
để Build ứng dụng của chúng ta, tôi đã thêm vào AS builder
để đặt tên cho giai đoạn đầu tiên là “builder” . Sau đó, tôi sử dụng COPY --from=builder
để sao chép các tập tin từ giai đoạn đó. Bằng cách đó, các tạo tác được sao chép chỉ là những thứ cần thiết để chạy Image tối thiểu bitnami/node:10-prod
.
Cách tiếp cận này cực kỳ hiệu quả khi Build Image cho các ứng dụng được biên dịch. Trong ví dụ dưới đây, tôi đã thực hiện một số điều chỉnh để giảm đáng kể kích thước Image. Image Template là Image Build Kubeapps Tiller proxy, một trong những thành phần cốt lõi của Kubeapps :
ARG VERSION FROM bitnami/minideb:stretch AS builder RUN install_packages ca-certificates curl git RUN curl https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz | tar -xzf - -C /usr/local ENV PATH="/usr/local/go/bin:$PATH" CGO_ENABLED=0 RUN go get -u github.com/golang/glog && go get -u github.com/kubeapps/kubeapps/cmd/tiller-proxy RUN go build -a -installsuffix cgo -ldflags "-X main.version=$VERSION" github.com/kubeapps/kubeapps/cmd/tiller-proxy FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /tiller-proxy /proxy EXPOSE 80 CMD ["/proxy"]
Image cuối cùng sử dụng scratch
(chỉ ra rằng lệnh tiếp theo trong Dockerfile là Layer hệ thống tập tin đầu tiên trong Image) và nó chỉ chứa những gì chúng ta cần : file binảy và chứng chỉ SSL.
LƯU Ý: sử dụng
ARG
và--build-arg K=V
sửa đổi các bản build của bạn từ dòng lệnh.
Build Image bằng cách sử dụng lệnh:
docker build . -t tiller-proxy-example --build-arg VERSION=1.0.0
Kích thước Image cuối cùng chỉ là 37,7 MB !! Nếu bạn bao gồm cả hướng dẫn Build và chạy trong cùng một Image, kích thước Image sẽ > 800MB .
Mẹo chuyên nghiệp hơn: Sử dụng các bản build multi-stage để tạo platform-specific Image
Tái sử dụng những thứ được có được trên builder
để tạo ra Image platform-specific . Chẳng hạn, theo ví dụ Kubeapps Tiller Proxy, sử dụng Dockerfile để tạo các Image khác nhau cho các nền tảng khác nhau. Trong Dockerfile bên dưới, Debian Stretch và Oracle Linux 7 là các nền tảng được chỉ định cho bản build:
... FROM oraclelinux:7-slim AS target-oraclelinux COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /tiller-proxy /proxy EXPOSE 80 CMD ["/proxy"] FROM bitnami/minideb:stretch AS target-debian COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /tiller-proxy /proxy EXPOSE 80 CMD ["/proxy"]
Trong các lệnh Build, chỉ cần thêm --target X
để cho biết platform nào bạn muốn Build Image cho:
docker build . -t tiller-proxy-example:debian --target target-debian --build-arg VERSION=1.0.0 docker build . -t tiller-proxy-example:oracle --target target-oraclelinux --build-arg VERSION=1.0.0
Sử dụng một Dockerfile duy nhất, bạn đã Build Image cho platform khác nhau, trong khi vẫn giữ quá trình Build rất đơn giản.
Sử dụng phương pháp không root để thực thi bảo mật container
Chạy các container như không root là một trong những cách tốt nhất để bảo mật.
Cách tiếp cận này ngăn chặn mã độc để có được quyền trong máy chủ chứa. Nó cũng cho phép chạy các container trên các bản phân phối Kubernetes không cho phép chạy các container dưới quyền root, chẳng hạn như OpenShift . Để biết thêm thông tin về lý do sử dụng bộ chứa không root , hãy kiểm tra các bài đăng trên blog này:
- Tại sao các container không root lại quan trọng đối với bảo mật .
- Chạy các container không root trên Openshift .
Để chuyển đổi Image Docker thành một Container không root , hãy thay đổi user mặc định từ root
thành nonroot
:
... EXPOSE 80 + useradd -r -u 1001 -g nonroot root + USER nonroot CMD ["node", "/app/server.js"] ...
TIP: Thêm user
nonroot
vào nhómroot
Hãy xem xét các chi tiết này khi chuyển một container sang không root :
- Quyền truy cập tập tin. Những thư mục nên được ghi bởi ứng dụng? Điều chỉnh chúng bằng cách cấp quyền viết cho người dùng không root. Kiểm tra Linux Wiki để biết thêm thông tin về việc thay đổi quyền.
- Bạn không thể sử dụng các port đặc quyền (1-1023) nữa.
- Bạn không thể thực hiện bất kỳ hành động nào yêu cầu quyền đặc quyền cho mục đích gỡ lỗi.
Ứng dụng mẫu của chúng ta sử dụng cổng 80
để lắng nghe các kết nối. Điều chỉnh nó để sử dụng một cổng thay thế, chẳng hạn như 8080
:
Dockerfile :
... COPY --from=builder /tiller-proxy /proxy - EXPOSE 80 + EXPOSE 8080 RUN useradd -r -u 1001 -g root nonroot ...
server.js :
... const serverHost = '127.0.0.1'; - const serverPort = 80; + const serverPort = 8080; ...
Mặt khác, ứng dụng ghi log của nó vào tệp /var/log/app.log . Cấp quyền cho nonroot
trên thư mục đó:
... RUN useradd -r -u 1001 -g root nonroot EXPOSE 80 + RUN chmod -R g+rwX /var/log USER nonroot ...
Kiểm tra nó:
$ docker build . -t express-image:0.0.7 $ docker run --rm -p 8080:8080 -d express-image:0.0.7 $ curl http://127.0.0.1:8080 Hello world $ docker exec express-app whoami nonroot $ docker stop express-app
Như bạn có thể thấy, mọi thứ đều hoạt động như mong đợi và bây giờ container của bạn không còn hoạt động dưới quyền root
nữa.
Đặt lệnh WORKDIR
Giá trị mặc định cho thư mục làm việc là /
. Tuy nhiên, trừ khi bạn sử dụng FROM scratch
Image, có khả năng Image Base bạn đang sử dụng đã đặt nó. Đó là một việc tốt để thiết lập WORKDIR
để thích ứng với đặc điểm ứng dụng của bạn.
Code ứng dụng của chúng ta nằm dưới thư mục /app
. Do đó, điều hợp lý là điều chỉnh thư mục làm việc với nó:
... USER nonroot + WORKDIR /app - CMD ["node", "/app/server.js"] + CMD ["node", "server.js"] ...
LƯU Ý: Nên sử dụng đường dẫn tuyệt đối trong hướng dẫn này.
Gắn cấu hình ứng dụng và sử dụng lệnh volume
Khi chạy container của bạn trên Kubernetes, rất có thể bạn muốn nhập cấu hình configMaps
hoặc secrets
tài nguyên của mình. Để sử dụng các loại tài nguyên này, gắn kết chúng dưới dạng tệp cấu hình trong hệ thống tệp chứa. Sau đó, điều chỉnh ứng dụng của bạn để nó đọc các cài đặt từ các tệp cấu hình đó.
Sử dụng VOLUME
để tạo điểm gắn kết là việc được khuyến khích. Docker đánh dấu các điểm gắn kết này là “holding externally mounted volumes”, vì vậy máy chủ hoặc các Container khác biết dữ liệu nào được hiển thị.
Hãy sửa đổi ứng dụng của chúng ta để tên máy chủ và cổng được lấy từ tệp cấu hình. Thực hiện theo các bước sau:
- Trong tệp server.js , thực hiện các thay đổi sau:
... // Constants - const serverHost = '127.0.0.1'; - const serverPort = 8080; + const settings = require('/settings/settings.json'); + const serverHost = settings.host; + const serverPort = settings.port; ...
Tạo tệp settings.json như hiển thị bên dưới:
$ mkdir settings && cat > settings/settings.json<<'EOF' { "host": "127.0.0.1", "port": "8080" } EOF
Thêm mount để trỏ đến Dockerfile:
... EXPOSE 8080 + VOLUME /settings RUN useradd -r -u 1001 -g root nonroot ...
Tại thời điểm này, Build lại Image và mount config cấu hình của nó như hiển thị bên dưới:
$ docker build . -t express-image:0.0.8 $ docker run -v $(pwd)/settings:/settings --rm -p 8080:8080 -d --name express-app express-image:0.0.8
Chuyển hướng log ứng dụng đến luồng stdout / stderr
Các ứng dụng nên chuyển hướng logs của chúng đến stdout / stderr để máy chủ có thể thu thập chúng.
Trên các bản phân phối như Kubernetes, rất phổ biến khi có một hệ thống ghi logs (chẳng hạn như ELK ) thu thập các bản ghi từ mọi container để chúng có sẵn cho các SYSAdmin. Làm cho logs có sẵn cho máy chủ để thu thập là bắt buộc đối với các loại giải pháp này.
Ứng dụng của chúng ta ghi logs của nó vào tệp /var/log/app.log . Chuyển hướng logs đến stdout bằng cách sử dụng cách giải quyết bên dưới:
... VOLUME /settings + RUN ln -sf /dev/stdout /var/log/app.log RUN useradd -r -u 1001 -g root nonroot ...
Với thay đổi đó, hãy thực hiện các lệnh sau để kiểm tra xem Docker đã lấy chính xác logs:
$ docker build . -t express-image:0.0.9 $ docker run -v $(pwd)/settings:/settings --rm -p 8080:8080 -d --name express-app express-image:0.0.9 $ docker logs express-app Running on http://127.0.0.1:8080
Xác định entrypoint
Để làm cho container linh hoạt hơn, hãy đặt entrypoint vào hoạt động như lệnh chính của Image. Sau đó, sử dụng CMD
để chỉ định các Tham số / tùy chọn của lệnh:
... - CMD ["node", "server.js"] + ENTRYPOINT ["node"] + CMD ["server.js"]
Bằng cách này, bạn có thể sửa đổi hành vi của container tùy thuộc vào các đối số được sử dụng để chạy nó. Chẳng hạn, sử dụng lệnh dưới đây để duy trì hành vi ban đầu:
$ docker build . -t express-image:0.0.10 $ docker run -v $(pwd)/settings:/settings --rm -p 8080:8080 -d --name express-app express-image:0.0.10
Hoặc sử dụng lệnh dưới đây để kiểm tra cú pháp mã:
$ docker run --rm express-image:0.0.10 --check server.js
Bạn luôn có thể override lại entrypoint vào bằng --entrypoint
. Ví dụ: để kiểm tra các tệp có sẵn tại /app
, hãy chạy:
$ docker run --rm --entrypoint "/bin/ls" express-image:0.0.10 -l /app total 12 drwxr-xr-x 51 root root 4096 Jan 24 12:45 node_modules -rw-r--r-- 1 root root 301 Jan 24 10:11 package.json -rw-r--r-- 1 root root 542 Jan 24 12:43 server.js
Khi một ứng dụng yêu cầu khởi tạo, hãy sử dụng script làm entrypoint vào của bạn. Tìm một ví dụ về một cái được sử dụng trên bitnami/redis
Image ở đây .
Kết thúc: Image container sẵn sàng cho production
Mục đích của bài đăng trên blog này là chỉ cho bạn cách cải thiện Dockerfile để Build các container theo cách hiệu quả hơn và nhanh hơn.
Để giải thích cách thực hiện một số thay đổi trên Dockerfile cụ thể, tôi đã sử dụng một ví dụ với một số lỗi sẽ được sửa bằng cách áp dụng các cách tối tốt này. Dockerfile ban đầu có các vấn đề sau:
- Nó không sử dụng tốt Build Cache.
- Đó là quá nhiều thành phần không cần thiết.
- Nó đòi hỏi quá nhiều bảo trì do sự phức tạp của nó.
- Nó không an toàn (chạy bằng root).
- Nó không xuất bất kỳ logs nào đến máy chủ lưu trữ, vì vậy các hệ thống không thể phân tích chúng.
Sau khi thực hiện các tinh chỉnh nhỏ này, Dockerfile kết quả đã sẵn sàng để được sử dụng để Build các Container cho môi trường Production.
Nhưng, đây có phải là tất cả những gì bạn có thể làm để viết Dockerfiles tốt nhất cho các Container trên môi trường production của mình không? Các bước tiếp theo là gì? Dưới đây là danh sách các mẹo để trở thành “pro” trong việc Build các container:
- Tests, tests, và more tests. Bất cứ khi nào một container được Build lại, bạn nên chạy các test để xác nhận, chức năng và tích hợp cho nó. Bạn càng có nhiều bài test, càng tốt.
- Build lại các Container của bạn thường xuyên nhất có thể (tốt nhất là hàng ngày) để đảm bảo bạn không đóng gói các thành phần cũ trong chúng.
- Thực hiện một pipeline CI / CD tự động hóa việc Build, testing và release Image container.
- Phân tích các Image và tìm hiểu CVE.