作者:DevOps旭
來自:DevOps探路者
一、鏡像是什麼?
作為最為火爆的容器技術,docker快速佔據市場的原因之一就是docker鏡像。那麼docker鏡像是什麼呢?docker鏡像可以簡單的理解為環境和應用的集合,是一種封裝方式,類似於java的jar包,centos的rpm包。這一打包格式使得通過docker封裝的應用可以快速的推廣,並且在任何環境下運行此應用。所以說,鏡像的規範是docker的最大的貢獻,那麼這一規範是什麼呢?
我們先從dockerhub上拉取一個鏡像
<code>[root@k8s03 ~]# docker pull tomcat Using default tag: latest latest: Pulling from library/tomcat d6ff36c9ec48: Pull complete c958d65b3090: Pull complete edaf0a6b092f: Pull complete 80931cf68816: Pull complete bf04b6bbed0c: Pull complete 8bf847804f9e: Pull complete 6bf89641a7f2: Pull complete 3e97fae54404: Pull complete 10dee6830d45: Pull complete 680b26b7a444: Pull complete Digest: sha256:cbdcddc4ca9b47e42c7d0c2db78cbc0f7a8b4bbe1fa395f4a53c5a236db29146 Status: Downloaded newer image for tomcat:latest docker.io/library/tomcat:latest/<code>
這是一個從dockerhub上拉取的tomcat鏡像,可以看到,在拉取過程中鏡像分成了10層,下面可以查看一下鏡像的詳情
<code>[root@k8s03 ~]# docker inspect d5eef28cf41d ... "Data": { "LowerDir": "/mnt/docker-lib/overlay2/ad0fdd6e7fadd5b77a7383d47553f77a727fa85c70da136be40ea44c6944ab11/diff:/mnt/docker-lib/overlay2/7b7f957f86be54f7497129f4a5cf664fc1a6879a5a83d5f37d720decbb229d78/diff:/mnt/docker-lib/overlay2/bc72109526d4f1a45ebab8b1abe9a36ceb5841ec37bad3d5b69873b9a41bb687/diff:/mnt/docker-lib/overlay2/eea0db10d31a06a305dd9d980d5c18e42f04f9026e2367f273066bd0e53ef2a5/diff:/mnt/docker-lib/overlay2/67512ff785e66fe5401dd44f2791decd1111026fd39f0b0dd8017e5a2a3d98bc/diff:/mnt/docker-lib/overlay2/1f7e4902a79ef2038947523535192aeadd097ee16020a0c95a695311534b70d3/diff:/mnt/docker-lib/overlay2/a46448ddb1c30de01b7e8539a958203b202bca669ad62707a9aca818c7273548/diff:/mnt/docker-lib/overlay2/62dc4b28677571e3abef933b1f199a85e62105cbe62752b840cc7e535300755d/diff:/mnt/docker-lib/overlay2/7a1f7a7de4e054c05ff4513a30bf087385f74caf5fce4462fc14bfb177d47239/diff", "MergedDir": "/mnt/docker-lib/overlay2/b6f983ae3751c3ea17d91c964060dd44d39430c75fc7821caac7eeb8871dfc77/merged", "UpperDir": "/mnt/docker-lib/overlay2/b6f983ae3751c3ea17d91c964060dd44d39430c75fc7821caac7eeb8871dfc77/diff", "WorkDir": "/mnt/docker-lib/overlay2/b6f983ae3751c3ea17d91c964060dd44d39430c75fc7821caac7eeb8871dfc77/work" }, 。。。/<code>
而鏡像的數據就在這些目錄之內,而這也就是基於UnionS實現的鏡像的分層規範。同時除了最底層鏡像,每一層都有一個指向底層的索引,便於找到這一層鏡像的父層,而這就意味著,父層是可以進行復用的,通過這一共用父層的方式,可以降低儲存的使用,提高構建的速率。
二、定製屬於你的鏡像
1、構建鏡像的方法
既然鏡像有這麼多的優勢,那麼應該如何製作鏡像呢?第一種是通過容器構建鏡像,即容器鏡像,通過docker commit 的方式進行構建,該方法本菜鳥並不推薦,原因有如下幾點:1)鏡像為分層結構,容器則為鏡像頂層加了一個可寫層,這一方式構建的鏡像極容易鏡像過大。2)容器鏡像無法確定這一層可寫層內的內容,對於安全性上,存在問題,而且也不易維護。第二種鏡像構建的方法則為dockerfile,通過dockerfile來控制鏡像的構建,這個也是官方推薦的方案。
2、深入理解dockerfile
那麼dockerfile是什麼樣子的呢?
<code>FROM centos:7 ENV VERSION=8.5.43 RUN yum install java-1.8.0-openjdk wget curl unzip iproute net-tools -y && \ yum clean all && \ rm -rf /var/cache/yum/* COPY apache-tomcat-${VERSION}.tar.gz / RUN tar zxf apache-tomcat-${VERSION}.tar.gz && \ mv apache-tomcat-${VERSION} /usr/local/tomcat && \ rm -rf apache-tomcat-${VERSION}.tar.gz /usr/local/tomcat/webapps/* && \ mkdir /usr/local/tomcat/webapps/test && \ echo "ok" > /usr/local/tomcat/webapps/test/status.html && \ sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ENV PATH $PATH:/usr/local/tomcat/bin WORKDIR /usr/local/tomcat EXPOSE 8080 CMD ["catalina.sh", "run"]/<code>
這是一個tomcat的dockerfile,裡面涉及到了dockerfile的專用語法,包括FROM、RUN、ENV、WORKDIR等等,那麼都有什麼作用呢?
2.1、FROM
<code>FROM /<code>
鏡像是分層的,通過FROM可以引用一個基礎鏡像,這個是必須的模塊,而鏡像是以此為基礎,每執行一條命令,就在此基礎上增加一層鏡像,所以基礎鏡像需要儘可能的精簡,同時要保證安全性。
2.2、WORKDIR
<code>WORKDIR /path/to/workdir/<code>
這一模塊用於指定工作目錄,如果指定後,通過exec進入容器後,便在WORKDIR中,同時需要注意,通過相對路徑執行命令時,一定要正確設置WORKDIR,避免出現因路徑導致的失敗。
2.3、COPY 和AND
<code>COPY 源文件 目標路徑 ADD 源文件 目標路徑/<code>
這兩個模塊用處相同,都是將文件拷貝到鏡像內,但是還是存在一些差異:
a、ADD 可以通過網絡源下載文件封入鏡像內。
b、ADD可以對tar、gzip、bzip2等文件進行解壓。
2.4、RUN
<code>RUN command1 ... commandN/<code>
RUN模塊引導的是構建鏡像時執行的命令,每執行一次RUN,會生成一層鏡像,所以建議在使用中,通過&& 鏈接來減少鏡像層數,比如構建基礎鏡像時,通過一條RUN 將java和tomcat封裝在同一層內做基礎鏡像,那麼業務鏡像僅需要在基礎鏡像外再加一層,這樣便可以保障鏡像的精簡。
2.5、ENV
<code>ENV key=value/<code>
ENV模塊將環境變量注入到鏡像內。
2.6、EXPOSE
<code>EXPOSE port XXXX/<code>
此模塊用於聲名暴露的端口。
2.7、USER
<code>USER app USER app:app USER 500 USER 500:500 USER app:500 USER 500:app/<code>
USER模塊用於限定用戶,可以用來實現降權運行容器內的應用,提高安全性。在使用這個模塊時,需要使用useradd創建用戶。
2.8、ENTRYPOINT
<code>ENTRYPOINT ["command","param"] ENTRYPOINT command param/<code>
ENTRYPOINT模塊用於設置容器啟動時的命令,設置一個不退出的前臺進程,同時,ENTRYPOINT設置的命令不會被docker run所覆蓋。
2.9、CMD
<code>CMD ["command","param"] CMD command param CMD ["param1","param2"]/<code>
CMD模塊和ENTRYPOINT 使用方法一致,但是CMD模塊可以被docker run所覆蓋,同時CMD 可以和ENTRYPOINT 配合使用,作為ENTRYPOINT 的參數出現。
3、構建屬於你的鏡像
構建鏡像是通過docker build來實現的,下面我們來構建一個tomcat的鏡像,先看一下dockerfile
<code>FROM centos:7 COPY apache-tomcat-8.5.56.tar.gz /mnt COPY jdk-8u261-linux-x64.tar.gz /mnt RUN cd /mnt && \ tar zxf jdk-8u261-linux-x64.tar.gz && \ mv jdk1.8.0_261 /usr/local/java && \ tar zxf apache-tomcat-8.5.56.tar.gz && \ mv apache-tomcat-8.5.56 /usr/local/tomcat && \ rm -f apache-tomcat-8.5.56.tar.gz jdk-8u261-linux-x64.tar.gz && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ENV JAVA_HOME=/usr/local/java ENV JRE_HOME=/usr/local/java/jre ENV PATH $PATH:/usr/local/java/bin:/usr/local/tomcat/bin WORKDIR /usr/local/tomcat EXPOSE 8080 CMD ["catalina.sh", "run"] /<code>
開始構建鏡像
<code>[root@k8s03 hgfs]# docker build -t tomcat8:base . Sending build context to Docker daemon 521.8MB Step 1/10 : FROM centos:7 ---> 7e6257c9f8d8 Step 2/10 : COPY apache-tomcat-8.5.56.tar.gz /mnt ---> 154214a58ae1 Step 3/10 : COPY jdk-8u261-linux-x64.tar.gz /mnt ---> 94af330c2ab1 Step 4/10 : RUN cd /mnt && tar zxf jdk-8u261-linux-x64.tar.gz && mv jdk1.8.0_261 /usr/local/java && tar zxf apache-tomcat-8.5.56.tar.gz && mv apache-tomcat-8.5.56 /usr/local/tomcat && rm -f apache-tomcat-8.5.56.tar.gz jdk-8u261-linux-x64.tar.gz && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ---> Running in a53326701fb1 Removing intermediate container a53326701fb1 ---> 926a7bd6dade Step 5/10 : ENV JAVA_HOME=/usr/local/java ---> Running in 3608b84318e5 Removing intermediate container 3608b84318e5 ---> c41f3ee011fb Step 6/10 : ENV JRE_HOME=/usr/local/java/jre ---> Running in 4ed9b3a8fe13 Removing intermediate container 4ed9b3a8fe13 ---> c577b0979b9c Step 7/10 : ENV PATH $PATH:/usr/local/java/bin:/usr/local/tomcat/bin ---> Running in a718feca40fb Removing intermediate container a718feca40fb ---> 18f66356c84e Step 8/10 : WORKDIR /usr/local/tomcat ---> Running in 5bd80d012f28 Removing intermediate container 5bd80d012f28 ---> db5a93e1adb0 Step 9/10 : EXPOSE 8080 ---> Running in 488067edba1d Removing intermediate container 488067edba1d ---> 96a08d9468c9 Step 10/10 : CMD ["catalina.sh", "run"] ---> Running in 703caa5216f1 Removing intermediate container 703caa5216f1 ---> e7d08a6fe4b6 Successfully built e7d08a6fe4b6 Successfully tagged tomcat8:base/<code>
這裡有一個很有意思的事情,當構建構成中,執行docker ps命令可以看到一個構件中的容器
<code>[root@k8s03 hgfs]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d4621ccda168 df2785b6788e "/bin/sh -c 'cd /mnt…" 4 seconds ago Up 3 seconds interesting_yalow/<code>
而這個容器在做的事情就是構建這個鏡像,而當鏡像構建成功後,這個容器也會被刪掉。
下面我們驗證一下這個鏡像
<code>[root@k8s03 hgfs]# docker run -d tomcat8:base ed0a14b3a86e6e96237ef84aed0229cad51ce3baa6125f28e16757178d67c01c [root@k8s03 hgfs]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ed0a14b3a86e tomcat8:base "catalina.sh run" 5 seconds ago Up 5 seconds 8080/tcp /<code>
容器啟動成功,那麼進入容器內驗證一下環境變量
<code>[root@k8s03 hgfs]# docker exec -it ed0a14b3a86e bash [root@ed0a14b3a86e tomcat]# echo $JAVA_HOME /usr/local/java [root@ed0a14b3a86e tomcat]# echo $JRE_HOME /usr/local/java/jre [root@ed0a14b3a86e tomcat]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/java/bin:/usr/local/tomcat/bin [root@ed0a14b3a86e tomcat]#/<code>
至此,一個基於java8 和 tomcat8的 centos7基礎鏡像構建完畢
三、鏡像管理
1、清理dangling鏡像
在日常工作中,常常會出現一個現象,就是針對一個部署包進行多次修改,每次修改都創建一個新的鏡像,但是tag和image都未修改,這樣每一次構建後,image和tag都會指向最新的鏡像,而之前的鏡像就會變成:的樣式,也就是dangling鏡像
<code>[root@k8s03 hgfs]# docker images -f "dangling=true" REPOSITORY TAG IMAGE ID CREATED SIZE d4aa089b72f7 16 minutes ago 734MB e0d2d9814c3f 17 minutes ago 357MB/<code>
這一鏡像是需要頻繁清理以實現節約空間的目的
2、清理業務鏡像
業務鏡像理論上應謹慎清理,可定時清理超期鏡像,但是為了實現版本控制,製品庫內的鏡像進行歸檔封存,不建議徹底清理。