使用更精簡的鏡像
常用的Linux系統鏡像一般有 Debian、Ubuntu、CentOS和Alpine,其中Alpine是面向安全的輕量級Linux發行版本。Docker的Alpine鏡像僅有不到5M的大小,非常適合作為基礎鏡像。
Alpine使用ash這個輕量級的shell,而上述其他Linux發行版默認使用bash作為其shell。另外,Aline使用apk作為其包管理工具,軟件安裝包的名字可能與其他發行版不同,可以在https://pkgs.alpinelinux.org/packages搜索確定安裝包的名字。
壓縮RUN語句&清理不必要的文件和程序
壓縮RUN語句
Docker鏡像是分層的,Dockerfile中的每一條RUN語句都會增加一層鏡像,導致鏡像非常臃腫。多個RUN命令應盡量用一條RUN命令完成,用“&&”和“\”串聯每一條命令。
比如下面的兩條RUN命令
RUN apt-get update RUN apt-get install -y git
可以壓縮成一條
RUN apt-get update && \ apt-get install -y git
清理不必要的程序和文件
另外,在用apt安裝軟件包時,我們可以使用--no-install-recommends參數來避免安裝非必須的文件,從而減小鏡像的體積;安裝完成之后rm -rf /var/lib/apt/lists/* ,清理apt緩存,進一步縮小鏡像。
RUN apt-get update && \ apt-get install -y --no-install-recommends git && \ rm -rf /var/lib/apt/lists/*
使用apk安裝軟件包時,我們可以使用--no-cache參數達到同樣的目的,或者在安裝完軟件包后使用rm -rf /var/cache/apk/*。
RUN apk -U --no-cache add git 或者 RUN apk -U add git && \ rm -rf /var/cache/apk/*
要注意的是,安裝軟件包和清理緩存需要在同一條RUN語句中執行,因為每一條RUN語句都會增加一層,這樣把apt-get和rm -rf /var/lib/apt/lists/*分開的話,就不能清理apt-get產生的緩存;apk也是同理。
# 正確 RUN apt-get update && \ apt-get install -y --no-install-recommends git && \ rm -rf /var/lib/apt/lists/* # 錯誤 RUN apt-get update && \ apt-get install -y --no-install-recommends git && \ RUN rm -rf /var/lib/apt/lists/*
在構建鏡像的時候,我們在編譯階段可能會下載一些依賴的頭文件和用於編譯的程序,或者其他相關程序(比如git),這些在程序運行時完全不需要,可以刪除掉。
RUN apt-get update && \ apt-get install -y git make gcc libssl-dev && \ …… # 編譯完成后,清理編譯環境和跟程序運行無關的軟件 apt-get purge -y git make gcc libssl-dev ……
多段構建
從Docker 17.05開始,一個Dockerfile文件可以使用多條FROM語句,每條FROM語句可以使用不同的鏡像。這樣我們可以把Docker的構建階段分層多個階段,以兩個FROM語句為例,我們可以使用一個鏡像編譯我們的程序;另一個鏡像使用更精簡的鏡像,拷貝上一階段的編譯的結果。
在使用FROM語句時,我們可以用AS為不同的鏡像起別名,方便后續操作。用COPY命令從其他鏡像拷貝文件時,我們可以用--from=alias src dst從別的階段復制文件;如果沒有為鏡像起別名,第一個鏡像的ID為0,第二個為1,我們可以用ID從別的階段拷貝文件,--from=0 src dst
FROM golang:1.9-alpine as builder RUN apk --no-cache add git WORKDIR /go/src/github.com/go/helloworld/ RUN go get -d -v github.com/go-sql-driver/mysql COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest as prod RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /go/src/github.com/go/helloworld/app . CMD ["./app"]
壓縮鏡像
使用docker export和docker import
docker export是用來保存一個容器的,所以我們需要有一個正在運行的容器才能使用此命令。
docker export <CONTAINER ID> > export.tar
docker import用來加載保存的容器,但是不能恢復成一個容器,而是變成一個鏡像
docker import export.tar <IMAGE NAME>:[TAG]
可以用一條命令實現
docker export <容器ID> | docker import - <鏡像名>[:標簽]
使用export和import后得到的鏡像不會保存鏡像的歷史,所以鏡像會變小。
docker save是用來保存一個鏡像的,docker save <IMAGE ID> > save.tar;然后可以用docker load加載我們保存的鏡像,docker load < save.tar。使用docker save和load恢復后的鏡像依然會保存鏡像的歷史。
test鏡像未經過壓縮的,test/import鏡像是經過壓縮的鏡像,可以看到已經變小了一些
使用docker-squash
github地址:https://github.com/jwilder/docker-squash
docker save <image id> | sudo docker-squash -t newtag | docker load
參考文章
https://www.cnblogs.com/kuku0223/p/8421964.html
https://zhuanlan.zhihu.com/p/42815689
https://stackoverflow.com/questions/49118579/alpine-dockerfile-advantages-of-no-cache-vs-rm-var-cache-apk
https://www.cnblogs.com/zhangmingcheng/p/7122386.html
https://www.jianshu.com/p/1d889800cdfe
https://tuhrig.de/flatten-a-docker-container-or-image/