前端也該知道的 Dockerfile 最佳實(shí)踐
Dockerfile 最佳實(shí)踐已經(jīng)出現(xiàn)在官方文檔中,地址在 Best practices for writing Dockerfiles[1]。如果再寫一份最佳實(shí)踐,倒有點(diǎn)關(guān)公門前耍大刀之意。因此本篇文章是對官方文檔的翻譯,理解,擴(kuò)展與示例補(bǔ)充
容器應(yīng)該是短暫的
通過 Dockerfile 構(gòu)建的鏡像所啟動的容器應(yīng)該盡可能短暫 (ephemeral)。短暫意味著可以很快地啟動并且終止
使用 .dockerignore 排除構(gòu)建無關(guān)文件
.dockerignore 語法與 .gitignore 語法一致。使用它排除構(gòu)建無關(guān)的文件及目錄,如 node_modules
使用多階段構(gòu)建
多階段構(gòu)建可以有效減小鏡像體積,特別是對于需編譯語言而言,一個應(yīng)用的構(gòu)建過程往往如下
安裝編譯工具 安裝第三方庫依賴 編譯構(gòu)建應(yīng)用
而在前兩步會有大量的鏡像體積冗余,使用多階段構(gòu)建可以避免這一問題
這是構(gòu)建 Go 應(yīng)用的一個示例
FROM golang:1.11-alpine AS build
# Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep
# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only
# Copy the entire project and build it
# This layer is rebuilt when a file changes in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project
# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
這是構(gòu)建前端應(yīng)用的一個示例,可以參考 如何使用 docker 高效部署前端應(yīng)用[2]
FROM node:10-alpine as builder
ENV PROJECT_ENV production
ENV NODE_ENV production
# http-server 不變動也可以利用緩存
WORKDIR /code
ADD package.json /code
RUN npm install --production
ADD . /code
RUN npm run build
# 選擇更小體積的基礎(chǔ)鏡像
FROM nginx:10-alpine
COPY --from=builder /code/public /usr/share/nginx/html
避免安裝不必要的包
減小體積,減少構(gòu)建時間。如前端應(yīng)用使用 npm install --production 只裝生產(chǎn)環(huán)境所依賴的包。
一個容器只做一件事
如一個 web 應(yīng)用將會包含三個部分,web 服務(wù),數(shù)據(jù)庫與緩存。把他們解耦到多個容器中,方便橫向擴(kuò)展。如果你需要網(wǎng)絡(luò)通信,則可以將他們至于一個網(wǎng)絡(luò)下。
如在我的個人服務(wù)器中,我使用 traefik 做負(fù)載均衡與服務(wù)發(fā)現(xiàn),所有應(yīng)用及數(shù)據(jù)庫都在 traefik_default 網(wǎng)絡(luò)下,詳情參考 使用 traefik 做負(fù)載均衡與服務(wù)發(fā)現(xiàn)
version: '3'
services:
# 該鏡像會暴露出自身的 `header` 信息
whoami:
image: containous/whoami
restart: always
labels:
# 設(shè)置Host 為 whoami.docker.localhost 進(jìn)行域名訪問
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
# 使用已存在的 traefik 的 network
networks:
default:
external:
name: traefik_default
減少鏡像層數(shù)
只有 RUN,COPY,ADD會創(chuàng)建層數(shù), 其它指令不會增加鏡像的體積盡可能使用多階段構(gòu)建
使用以下方法安裝依賴
RUN yum install -y node python go
錯誤的方法安裝依賴,這將增加鏡像層數(shù)
RUN yum install -y node
RUN yum install -y python
RUN yum install -y go
將多行參數(shù)排序
便于可讀性以及不小心地重復(fù)裝包
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
充分利用構(gòu)建緩存
在鏡像的構(gòu)建過程中 docker 會遍歷 Dockerfile 文件中的所有指令,順序執(zhí)行。對于每一條指令,docker 都會在緩存中查找是否已存在可重用的鏡像,否則會創(chuàng)建一個新的鏡像
我們可以使用 docker build --no-cache 跳過緩存
ADD和COPY將會計算文件的checksum是否改變來決定是否利用緩存RUN僅僅查看命令字符串是否命中緩存,如RUN apt-get -y update可能會有問題
如一個 node 應(yīng)用,可以先拷貝 package.json 進(jìn)行依賴安裝,然后再添加整個目錄,可以做到充分利用緩存的目的。
FROM node:10-alpine as builder
WORKDIR /code
ADD package.json /code
# 此步將可以充分利用 node_modules 的緩存
RUN npm install --production
ADD . /code
RUN npm run build
參考資料
Best practices for writing Dockerfiles: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
[2]如何使用 docker 高效部署前端應(yīng)用: https://github.com/shfshanyue/op-note/blob/master/deploy-fe-with-docker.md
