和我一起學docker(三)鏡像的那些事兒

和我一起學docker(三)鏡像的那些事兒

docker


作者: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實現的鏡像的分層規範。同時除了最底層鏡像,每一層都有一個指向底層的索引,便於找到這一層鏡像的父層,而這就意味著,父層是可以進行復用的,通過這一共用父層的方式,可以降低儲存的使用,提高構建的速率。


和我一起學docker(三)鏡像的那些事兒


二、定製屬於你的鏡像

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、清理業務鏡像

業務鏡像理論上應謹慎清理,可定時清理超期鏡像,但是為了實現版本控制,製品庫內的鏡像進行歸檔封存,不建議徹底清理。


和我一起學docker(三)鏡像的那些事兒


分享到:


相關文章: