一、镜像是什么?
作为最为火爆的容器技术,docker快速占据市场的原因之一就是docker镜像。那么docker镜像是什么呢?docker镜像可以简单的理解为环境和应用的集合,是一种封装方式,类似于java的jar包,centos的rpm包。这一打包格式使得通过docker封装的应用可以快速的推广,并且在任何环境下运行此应用。所以说,镜像的规范是docker的最大的贡献,那么这一规范是什么呢?
我们先从dockerhub上拉取一个镜像
[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
这是一个从dockerhub上拉取的tomcat镜像,可以看到,在拉取过程中镜像分成了10层,下面可以查看一下镜像的详情
[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" },
...
而镜像的数据就在这些目录之内,而这也就是基于UnionS实现的镜像的分层规范。同时除了最底层镜像,每一层都有一个指向底层的索引,便于找到这一层镜像的父层,而这就意味着,父层是可以进行复用的,通过这一共用父层的方式,可以降低储存的使用,提高构建的速率。
二、定制属于你的镜像
1、构建镜像的方法
既然镜像有这么多的优势,那么应该如何制作镜像呢?第一种是通过容器构建镜像,即容器镜像,通过docker commit 的方式进行构建,该方法本菜鸟并不推荐,原因有如下几点:1)镜像为分层结构,容器则为镜像顶层加了一个可写层,这一方式构建的镜像极容易镜像过大。2)容器镜像无法确定这一层可写层内的内容,对于安全性上,存在问题,而且也不易维护。第二种镜像构建的方法则为dockerfile,通过dockerfile来控制镜像的构建,这个也是官方推荐的方案。
2、深入理解dockerfile
那么dockerfile是什么样子的呢?
FROM centos:7 FROM centos:7
ENV VERSION=8.5.43 ENV VERSION=8.5.43
RUN yum install java-1.8.0-openjdk wget curl unzip iproute net-tools -y && \ RUN yum install java-1.8.0-openjdk wget curl unzip iproute net-tools -y && \
yum clean all && \ yum clean all && \
rm -rf /var/cache/yum/* rm -rf /var/cache/yum/*
COPY apache-tomcat-${VERSION}.tar.gz / COPY apache-tomcat-${VERSION}.tar.gz /
RUN tar zxf apache-tomcat-${VERSION}.tar.gz && \ RUN tar zxf apache-tomcat-${VERSION}.tar.gz && \
mv apache-tomcat-${VERSION} /usr/local/tomcat && \ mv apache-tomcat-${VERSION} /usr/local/tomcat && \
rm -rf apache-tomcat-${VERSION}.tar.gz /usr/local/tomcat/webapps/* && \ rm -rf apache-tomcat-${VERSION}.tar.gz /usr/local/tomcat/webapps/* && \
mkdir /usr/local/tomcat/webapps/test && \ mkdir /usr/local/tomcat/webapps/test && \
echo "ok" > /usr/local/tomcat/webapps/test/status.html && \ 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 && \ 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 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV PATH $PATH:/usr/local/tomcat/bin ENV PATH $PATH:/usr/local/tomcat/bin
WORKDIR /usr/local/tomcat WORKDIR /usr/local/tomcat
EXPOSE 8080 EXPOSE 8080
CMD ["catalina.sh", "run"] CMD ["catalina.sh", "run"]
这是一个tomcat的dockerfile,里面涉及到了dockerfile的专用语法,包括FROM、RUN、ENV、WORKDIR等等,那么都有什么作用呢?
2.1、FROM
FROM <img>:<tag>
镜像是分层的,通过FROM可以引用一个基础镜像,这个是必须的模块,而镜像是以此为基础,没执行一条命令,就在此基础上增加一层镜像,所以基础镜像需要尽可能的精简,同时要保证安全性。
2.2、WORKDIR
WORKDIR /path/to/workdir
这一模块用于指定工作目录,如果指定后,通过exec进入容器后,便在WORKDIR中,同时需要注意,通过相对路径执行命令时,一定要正确设置WORKDIR,避免出现因路径导致的失败。
2.3、COPY 和AND
COPY 源文件 目标路径 COPY 源文件 目标路径
ADD 源文件 目标路径 ADD 源文件 目标路径
这两个模块用处相同,都是将文件拷贝到镜像内,但是还是存在一些差异:
a、ADD 可以通过网络源下载文件封入镜像内。
b、ADD可以对tar、gzip、bzip2等文件进行解压。
2.4、RUN
RUN command1 ... commandN
RUN模块引导的是构建镜像时执行的命令,没执行一次RUN,会生成一层镜像,所以建议在使用中,通过&& 链接来减少镜像层数,比如构建基础镜像时,通过一条RUN 将java和tomcat封装在同一层内做基础镜像,那么业务镜像仅需要在基础镜像外再加一层,这样便可以保障镜像的精简。
2.5、ENV
ENV key=value
ENV模块将环境变量注入到镜像内。
2.6、EXPOSE
EXPOSE port XXXX
此模块用于声名暴露的端口。
2.7、USER
USER app USER app
USER app:app USER app:app
USER 500 USER 500
USER 500:500 USER 500:500
USER app:500 USER app:500
USER 500:app USER 500:app
USER模块用于限定用户,可以用来实现降权运行容器内的应用,提高安全性。再使用这个模块时,需要使用useradd创建用户。
2.8、ENTRYPOINT
ENTRYPOINT ["command","param"] ENTRYPOINT ["command","param"]
ENTRYPOINT command param ENTRYPOINT command param
ENTRYPOINT模块用于设置容器启动时的命令,设置一个不退出的前台进程,同时,ENTRYPOINT设置的命令不会被docker run所覆盖。
2.9、CMD
CMD ["command","param"] CMD command param CMD ["param1","param2"]
CMD模块和ENTRYPOINT 使用方法一致,但是CMD模块可以被docker run所覆盖,同时CMD 可以和ENTRYPOINT 配合使用,作为ENTRYPOINT 的参数出现。
3、构建属于你的镜像
构建镜像是通过docker build来实现的,下面我们来构建一个tomcat的镜像,先看一下dockerfile
FROM centos:7 FROM centos:7
COPY apache-tomcat-8.5.56.tar.gz /mnt COPY apache-tomcat-8.5.56.tar.gz /mnt
COPY jdk-8u261-linux-x64.tar.gz /mnt COPY jdk-8u261-linux-x64.tar.gz /mnt
RUN cd /mnt && \ RUN cd /mnt && \
tar zxf jdk-8u261-linux-x64.tar.gz && \ tar zxf jdk-8u261-linux-x64.tar.gz && \
mv jdk1.8.0_261 /usr/local/java && \ mv jdk1.8.0_261 /usr/local/java && \
tar zxf apache-tomcat-8.5.56.tar.gz && \ tar zxf apache-tomcat-8.5.56.tar.gz && \
mv apache-tomcat-8.5.56 /usr/local/tomcat && \ mv apache-tomcat-8.5.56 /usr/local/tomcat && \
rm -f apache-tomcat-8.5.56.tar.gz jdk-8u261-linux-x64.tar.gz && \ rm -f apache-tomcat-8.5.56.tar.gz jdk-8u261-linux-x64.tar.gz && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV JAVA_HOME=/usr/local/java ENV JAVA_HOME=/usr/local/java
ENV JRE_HOME=/usr/local/java/jre ENV JRE_HOME=/usr/local/java/jre
ENV PATH $PATH:/usr/local/java/bin:/usr/local/tomcat/bin ENV PATH $PATH:/usr/local/java/bin:/usr/local/tomcat/bin
WORKDIR /usr/local/tomcat WORKDIR /usr/local/tomcat
EXPOSE 8080 EXPOSE 8080
CMD ["catalina.sh", "run"] CMD ["catalina.sh", "run"]
开始构建镜像
[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
这里有一个很有意思的事情,当构建构成中,执行docker ps命令可以看到一个构件中的容器
[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
而这个容器在做的事情就是构建这个镜像,而当镜像构建成功后,这个容器也会被删掉。
下面我们验证一下这个镜像
[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
容器启动成功,那么进入容器内验证一下环境变量
[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]#
至此,一个基于java8 和 tomcat8的 centos7基础镜像构建完毕
三、镜像管理
1、清理dangling镜像
在日常工作中,常常会出现一个现象,就是针对一个部署包进行多次修改,每次修改都创建一个新的镜像,但是tag和image都为修改,这样每一次构建后,image和tag都会指向最新的镜像,而之前的镜像就会变成:的样式,也就是dangling镜像
[root@k8s03 hgfs]# docker images -f "dangling=true"
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> d4aa089b72f7 16 minutes ago 734MB
<none> <none> e0d2d9814c3f 17 minutes ago 357MB
这一镜像是需要频繁清理以实现节约空间的目的
2、清理业务镜像
业务镜像理论上应谨慎清理,可定时清理超期镜像,但是为了实现版本控制,制品库内的镜像进行归档封存,不建议彻底清理。