开发者如何快速精简容器云镜像?| 技术头条
戳藍字“CSDN云計算”關(guān)注我們哦!
作者:阿木
接觸過容器云或者用過容器的同學一般都會遇到容器鏡像占用空間很大的問題,遇到此類問題的時候大部分同學可能更加習慣于為容器的鏡像倉庫增加磁盤空間,當然這種方式無可厚非,畢竟這種方式可以幫助我們快速的解決掉手里的問題。
?
除了上面擴磁盤的解決方式,其實我們還可以采用縮減容器鏡像的方式,此種方式不但可以幫助我們節(jié)省添加新磁盤的開支還可減少我們制作鏡像和傳輸鏡像的時間。優(yōu)化的比較好的鏡像占用的空間基本和應用的文件占用的空間相當,基本不會占用太多的額外存儲空間。下面我們會具體看一下。?
?
?
接觸過Docker 的同學都知道Dockerfile 是一些指令的組成,且Dockerfile?文件中的每條指令對應著Linux?操作系統(tǒng)中的一條命令,當我們構(gòu)建鏡像時Docker 程序會將這些Dockerfile指令翻譯成Linux可執(zhí)行的命令。
?
Dockerfile 中每一條指令都會創(chuàng)建一個鏡像層,隨著指令的執(zhí)行繼而會增加鏡像整體的大小。
?
Dockerfile?文件有自己的書寫格式和支持的命令,常用Dockerfile 指令如下:
FROM ?指定基鏡像。
?
MAINTAINER ?設(shè)置鏡像的作者信息,如作者姓名、郵箱等。
?
COPY ?將文件從本地復制到鏡像,拷貝前需要保證本地源文件存在。
?
ADD ?與 COPY 類似,復制文件到鏡像。不同的是,如果文件是歸檔文件(tar, zip, tgz, xz 等),會被自動解壓。
?
ENV ?設(shè)置環(huán)境變量,格式: ENV key=value或ENV key value,運行容器后,可直接在容器中使用。
?
EXPOSE ?暴露容器中指定的端口,只是一個聲明,主要用戶了解應用監(jiān)聽的端口。
?
VOLUME ?掛載卷到容器,需要注意的是,保存鏡像時不會保存卷中的數(shù)據(jù)。
?
WORKDIR ?設(shè)置當前工作目錄,后續(xù)各層的當前目錄都被指定。
?
RUN ?在容器中運行指定的命令。
?
CMD ?容器啟動時運行的命令。Dockerfile 中可以有多個 CMD 指令,但只有最后一個生效。CMD 可以被 docker run 之后的參數(shù)替換。
?
ENTRYPOINT ?設(shè)置容器啟動時運行的命令。Dockerfile 中可以有多個 ENTRYPOINT 指令,但只有最后一個生效。CMD 或 docker run 之后的參數(shù)會被當做參數(shù)傳遞給 ENTRYPOINT,這個是與CMD的區(qū)別。
?
精簡鏡像的好處不言而喻,即可以節(jié)省存儲存儲空間也可,也可減少鏡像傳輸時間,減少帶寬的消耗,加快傳輸。
?
在開始制作鏡像之前,我們先了解一下容器鏡像的基本基本理論知識。
?
容器鏡像中最重要的概念就是layers,即鏡像層。
?
?
鏡像層依賴于一系列的底層技術(shù),比如文件系統(tǒng)(filesystems)、寫時復制(copy-on-write)、聯(lián)合掛載(union mounts)等技術(shù),這些技術(shù)的細節(jié)在此我們不再贅述,感興趣的同學可以直接直接查看Docker 官方文檔(https://docs.docker.com/storage/storagedriver/)進行學習。
?
總的來說,精簡鏡像我們最需要記住的一句話即:
?
“在Dockerfile中,每條指令都會創(chuàng)建一個鏡像層,繼而會增加鏡像整體的大小。”
?
下面我們以一個示例來說明一下:
?
我們pull 一個鏡像,以busybox為例:
?
[root@work ~]# docker pull hub.c.163.com/library/busybox:latest
Trying to pull repository hub.c.163.com/library/busybox ...
latest: Pulling from hub.c.163.com/library/busybox
aab39f0bc16d: Pull compl`ete
Digest: sha256:662af1d642674367b721645652de96f9c147417c2efb708eee4e9b7212697762
Status: Downloaded newer image for hub.c.163.com/library/busybox:latest
?
#?看下鏡像大小
[root@work ~]# docker images | grep busybox
hub.c.163.com/library/busybox ?????????????????????????latest ??????????????????????????????????????????d20ae45477cb ???????18 months ago ??????1.129 MB
?
從上面結(jié)果看我們pull下來的鏡像大小只有1.129MB。
?
下面我們編寫一個Dockerfile文件,文件中我們新建一個目錄,目錄中新建一個100MB的文件,最后我們刪掉新建的文件。
?
Dockerfile?內(nèi)容如下:
?
#基鏡像
FROM hub.c.163.com/library/busybox:latest
#新建目錄
RUN mkdir /tmp/dir1
#新建一個100MB的文件
RUN dd if=/dev/zero of=/tmp/dir1/file1 bs=1M count=100
#刪除文件
RUN rm /tmp/dir1/file1
?
從Dockerfile 內(nèi)容看,其實我們基本什么都沒干。
?
然后我們用這個Dockerfile構(gòu)建新建的鏡像,并查看新鏡像的大小:
?
[root@work ~]# docker build -t busybox:v1 .
Sending build context to Docker daemon 1.307 GB
Step 1 : FROM hub.c.163.com/library/busybox:latest
?---> d20ae45477cb
Step 2 : RUN mkdir /tmp/dir1
?---> Running in 63fa5f27c779
?---> da95ea8ae5ee
Removing intermediate container 63fa5f27c779
Step 3 : RUN dd if=/dev/zero of=/tmp/dir1/file1 bs=1M count=100
?---> Running in d3e8bbb4f151
100+0 records in
100+0 records out
104857600 bytes (100.0MB) copied, 0.247500 seconds, 404.0MB/s
?---> 42b721238144
Removing intermediate container d3e8bbb4f151
Step 4 : RUN rm -rf /tmp/dir1/file1
?---> Running in 6b51b633fb21
?---> 04096cc5d718
Removing intermediate container 6b51b633fb21
Successfully built 04096cc5d718
?
#?查看鏡像信息
[root@work ~]# docker images | grep busybox
busybox ???????????????????????????????????????????????v1 ??????????????????????????????????????????????04096cc5d718 ???????58 seconds ago ?????106 MB
hub.c.163.com/library/busybox ?????????????????????????latest ??????????????????????????????????????????d20ae45477cb ???????18 months ago ??????1.129 MB
?
從上面的結(jié)果可以看出,雖然在Dockerfile中我們將新建的100MB的文件刪除了,但新鏡像的大小仍大于100MB。
?
多出了100多MB,這是為何?其實這點和git類似,Docker鏡像和git都用到了寫時復制技術(shù),git每次提交時都會保存一個文件的版本,Dockerfile每行指令都會增加整體鏡像的大小,即使我們什么都沒做。
?
下面我們開始說下本文的重點:鏡像精簡。
?
我們以最常見的nosql數(shù)據(jù)庫Redis為例,一步步來介紹如何制作更精簡的Docker 鏡像。
?
首先我們編寫下構(gòu)建Redis鏡像的Dockerfile文件,具體內(nèi)容如下:
FROM hub.c.163.com/library/ubuntu:trusty
#redis 版本
ENV VER ????3.0.0
ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz
RUN apt-get update
#安裝依賴的工具
RUN apt-get install -y ?curl make gcc
#下載redis源碼包并解壓
RUN curl -L $TARBALL | tar zxv
#進入解壓后的目錄
WORKDIR ?redis-$VER
#編譯redis源碼
RUN make
#安裝redis
RUN make install
WORKDIR /
#清理前面安裝的依賴工具
RUN apt-get remove -y --auto-remove curl make gcc
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* ?/redis-$VER
#運行redis
CMD ["redis-server"]
?
利用上面的Dockerfile 構(gòu)建鏡像:
[root@work ~]# docker build -t redis:3.0.0 .
……
……
Removing intermediate container b55656487022
Successfully built 7df9c7899ae3
????
查看構(gòu)建出的鏡像大小:
[root@work ~]# docker images | grep redis
redis ?????????????????????????????????????????????????3.0.0 ???????????????????????????????????????????7df9c7899ae3 ???????10 hours ago ???????359.7 MB
從結(jié)果看構(gòu)建出優(yōu)化前的鏡像約為360MB。
?
下面我們逐步優(yōu)化。?
?
1.?選用更小的基鏡像
?
常用的linux系統(tǒng)一般有CentOS、Debian、Ubuntu,三者中Debian更輕量,且Debian系統(tǒng)鏡像中提供的功能一般也是夠用的,三個系統(tǒng)鏡像尺寸對比如下:
????????
鏡像名稱????????????標簽?????????鏡像?ID ???????鏡像大小
-------- ?????????????------ ???????------------ ?????????--------
centos ?????????????7 ?????????214a4932132a ????215.7 MB
centos ?????????????6 ?????????f6808a3e4d9e ????202.6 MB
ubuntu ?????????????trusty ????d0955f21bf24 ????188.3 MB
ubuntu ?????????????precise ???9c5e4be642b7 ????131.9 MB
debian ?????????????jessie ????65688f7c61c4 ????122.8 MB
debian ?????????????wheezy ????1265e16d0c28 ????84.96 MB
?
在此我們以上面最小的鏡像debian:wheezy?作為即鏡像,重新進行構(gòu)建:
?
Dockerfile內(nèi)容:
FROM hub.c.163.com/library/debian:wheezy
#redis 版本
ENV VER ????3.0.0
ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz
RUN apt-get update
#安裝依賴的工具
RUN apt-get install -y ?curl make gcc
#下載redis源碼包并解壓
RUN curl -L $TARBALL | tar zxv
#進入解壓后的目錄
WORKDIR ?redis-$VER
#編譯redis源碼
RUN make
#安裝redis
RUN make install
WORKDIR /
#清理前面安裝的依賴工具
RUN apt-get remove -y --auto-remove curl make gcc
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* ?/redis-$VER
#運行redis
CMD ["redis-server"]
?
構(gòu)建新鏡像:
[root@work ~]# docker build -t redis:3.0.0-v2 .
……
……
Removing intermediate container 3498689792ce
Successfully built 4faa1aa0936d
?
對比兩個鏡像大小:
[root@work ~]# docker images | grep redis
redis ?????????????????????????????????????????????????3.0.0-v2 ????????????????????????????????????????4faa1aa0936d ???????38 seconds ago ?????228.5 MB
redis ?????????????????????????????????????????????????3.0.0 ???????????????????????????????????????????7df9c7899ae3 ???????11 hours ago
?
從結(jié)果看更換基鏡像后的新鏡像減少了37%,精簡效果還算可以,但精簡效果并未達到我們的目標。
?
如果仔細看的話我們會發(fā)現(xiàn)只有85MB的debian基鏡像,在構(gòu)建后增加到了228MB可見此處還有很大的優(yōu)化空間,后續(xù)的優(yōu)化就需要用到我們在上文中說到的鏡像層相關(guān)的知識了。
??????
?
2.?合并Dockerfile中指令
?
Dockerfile?中指令的合并一般是指RUN指令的合并。
?
我們可以通過&&符號和/?將Dockerfile?中的多個RUN指令合并成一條RUN 指令,此種方式一般精簡效果較好。
?
優(yōu)化后的Dockerfile 內(nèi)容如下:
FROM hub.c.163.com/library/debian:wheezy
#redis 版本
ENV VER ????3.0.0
ENV TARBALL http://download.redis.io/releases/redis-$VER.tar.gz
RUN apt-get update && \
apt-get install -y ?curl make gcc &&\
curl -L $TARBALL | tar zxv ?&& \
cd ?redis-$VER ?&& \
make ?&& \
make install && \
cd / ?&& \
apt-get remove -y --auto-remove curl make gcc && \
apt-get clean ?&& \
rm -rf /var/lib/apt/lists/* ?/redis-$VER
#運行redis
CMD ["redis-server"]
?
構(gòu)建新鏡像:
?
[root@work ~]# docker build -t redis:3.0.0-v3 .
……
……
Removing intermediate container 9e5cffcd8bdb
Successfully built dafd91993dfb
?
查看鏡像大小:
[root@work ~]# docker images | grep redis
redis ?????????????????????????????????????????????????3.0.0-v3 ????????????????????????????????????????dafd91993dfb ???????33 seconds ago ?????102.3 MB
redis ?????????????????????????????????????????????????3.0.0-v2 ????????????????????????????????????????4faa1aa0936d ???????15 minutes ago ?????228.5 MB
redis ?????????????????????????????????????????????????3.0.0 ???????????????????????????????????????????7df9c7899ae3 ???????11 hours ago ???????359.7 MB
?
從結(jié)果看鏡像大小約縮減72%,可見合并Dockerfile指令的方式精簡效果較明顯,新鏡像只比基鏡像增加約10MB。
合并Dockerfilec指令精簡鏡像這種方式是最常用的精簡鏡像尺寸的方式。
?
3.?使用最精簡的基鏡像
?
上文中第1步中,我們使用的基鏡像為Debian鏡像,約89MB,但如果我們只是安裝Redis 服務(wù)的話不一定非得使用這么大的系統(tǒng)鏡像,我們可以借助一些更小的鏡像,如scratch、busybox、alpine等,這些鏡像大小往往小于5MB,因此我們可以直接以此作為基鏡像來構(gòu)建新的Redis鏡像。
?
此處我們以scratch作為基鏡像構(gòu)建Redis。scratch鏡像往往只有1~5MB大小。
?
Scrach?是一個空鏡像,只能用于構(gòu)建鏡像。在構(gòu)建一些基礎(chǔ)鏡像,如debian、busybox時非常有用。Scrach也常用于構(gòu)建超小的鏡像,如構(gòu)建一個只包含所有庫的二進制文件。
?
但使用最精簡的基鏡像我們還需要做些額外的工作,具體過程見下文。
?
4.?提取.so庫
?
了解過Redis源碼的話大家會知道Redis?開發(fā)語言為C語言,會依賴一些.so庫,因此我們需要先準備好編譯Redis 所需的.so文件。
?
我們通過前面構(gòu)建好的redis:3.0.0-v3鏡像運行容器,然后進入容器中獲取下redis依賴的.so文件。
?
# 后臺運行容器:
[root@work ~]# docker run --name redisv3 -d ?redis:3.0.0-v3
ab361e7fc2e70b5b45fa1545917ee92158bb859e833c3f7fcfb80e43bb69cb0c
?
# 查看容器運行狀態(tài)
[root@work ~]# docker ps
CONTAINER ID ???????IMAGE ??????????????COMMAND ????????????CREATED ????????????STATUS ?????????????PORTS ??????????????NAMES
ab361e7fc2e7 ???????redis:3.0.0-v3 ?????"redis-server" ?????3 seconds ago ??????Up 3 seconds ???????????????????????????redisv3
?
#?進入容器
[root@work ~]# docker exec -ti redisv3 /bin/bash
root@ab361e7fc2e7:/#
?
#?查看redis-server?二進制文件位置
root@ab361e7fc2e7:/# which redis-server
/usr/local/bin/redis-server
?
#?查看redis-server依賴的.so文件
root@ab361e7fc2e7:/# ldd /usr/local/bin/redis-server
linux-vdso.so.1 => ?(0x00007ffedfd01000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0de7a5e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0de785a000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0de763d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0de72b0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0de7ce4000)
?
將編譯Redis 需要的所有依賴打包:
root@ab361e7fc2e7:/# mkdir so
root@ab361e7fc2e7:/# cp usr/local/bin/redis-server so/
root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libm.so.6 so/
root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libpthread.so.0 so/
root@ab361e7fc2e7:/# cp lib/x86_64-linux-gnu/libc.so.6 so/ ?????
root@ab361e7fc2e7:/# cp lib64/ld-linux-x86-64.so.2 so/ ?
root@ab361e7fc2e7:/# cd so
root@ab361e7fc2e7:/# tar zcvf so.tar.gz ./*
so/
so/redis-server
so/libm.so.6
so/libpthread.so.0
so/libc.so.6
so/ld-linux-x86-64.so.2
?
#?將打包好的文件從容器拷貝出來
[root@work ~]# docker cp redisv3:/so/so.tar.gz .
?
編寫Dockerfile 文件,具體內(nèi)容如下:
?
FROM scratch
# 添加依賴的庫文件
ADD ?so.tar.gz ?/
# redis 配置文件,需要自己準備一份
COPY redis.conf ????/etc/redis/redis.conf
# 暴露的端口
EXPOSE 6379
CMD ["redis-server"]
?
構(gòu)建新鏡像:
?
[root@work ~]# docker build -t redis:3.0.0-v4 .
Sending build context to Docker daemon 1.316 GB
Step 1 : FROM scratch
?--->
Step 2 : ADD so.tar.gz /
?---> Using cache
?---> 82b2b6def214
Step 3 : COPY redis.conf /etc/redis/redis.conf
?---> 3f382da261be
Removing intermediate container 60af6a5ab042
Step 4 : EXPOSE 6379
?---> Running in 78c541686668
?---> 043ed6cf87e0
Removing intermediate container 78c541686668
Step 5 : CMD redis-server
?---> Running in 2c8b9fb0547d
?---> 75d828ebf3aa
Removing intermediate container 2c8b9fb0547d
Successfully built 75d828ebf3aa
?
對比鏡像大小:
[root@work ~]# docker images | grep redis
redis ?????????????????????????????????????????????????3.0.0-v4 ????????????????????????????????????????75d828ebf3aa ???????30 seconds ago ?????6.938 MB
redis ?????????????????????????????????????????????????3.0.0-v3 ????????????????????????????????????????dafd91993dfb ???????About an hour ago ??102.3 MB
redis ?????????????????????????????????????????????????3.0.0-v2 ????????????????????????????????????????4faa1aa0936d ???????About an hour ago ??228.5 MB
redis ?????????????????????????????????????????????????3.0.0 ???????????????????????????????????????????7df9c7899ae3 ???????12 hours ago ???????359.7 MB
?
從結(jié)果看精簡效果顯著提高,基于scratch構(gòu)建的新鏡像大小只有6.9MB,相比之前的359MB、228MB、102MB,新鏡像空間占用已經(jīng)很少。
?
以上即是本文精簡Docker 鏡像的整個過程。
?
除了上面我們介紹的精簡方法之外還有一些常見的精簡方式,如使用鏡像壓縮工具docker-squash,但此種方式壓縮效果并不明顯,因此在此我們并未介紹,感興趣的同學可以自己嘗試下。
福利
掃描添加小編微信,備注“姓名+公司職位”,加入【云計算學習交流群】,和志同道合的朋友們共同打卡學習!
推薦閱讀:
趣挨踢 | 用大數(shù)據(jù)扒一扒蔡徐坤的真假流量粉
姚期智提出的"百萬富翁"難題被破解? 多方安全計算MPC到底是個什么鬼?
全民 AI !教育部宣布 35 所高校新增 AI 本科專業(yè)
深度 | 人工智能究竟能否實現(xiàn)?
程序媛報告:調(diào)查了 12,000 名女性開發(fā)者發(fā)現(xiàn),女性比男性更懂 Java!
程序員怒了!你敢削減專利獎金,我敢拒絕提交代碼!
喜歡就點擊“在看”吧
總結(jié)
以上是生活随笔為你收集整理的开发者如何快速精简容器云镜像?| 技术头条的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: boost::contract模块实现u
- 下一篇: 为什么部队用智慧中小学app