docker镜像介绍
一、镜像的分层结构
1、共享宿主机的kernel
2、base镜像提供的是最小的Linux发行版
3、同一docker主机支持运行多种Linux发行版
4、采用分层结构的最大好处是:共享资源
5、Copy-on-Write 容器层可写
6、容器层以下所有镜像层都是只读的
7、docker从上往下依次查找文件
8、容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
9、一个镜像最多127层
二、镜像的构建
1、docker commit 构建
用docker commit 构建镜像分为三步:
(1)运行容器
(2)修改容器
(3)将容器保存为新的镜像
-it
:交互方式,Ctrl+D
:退出容器,Ctrl+p+q
:退出时让容器在后台运行
docker load -i busybox.tar ##导入镜像
docker run -it --name test busybox:latest ##以交互方式运行容器
echo helloworld > testfile ##在容器中执行命令
docker commit test test:v1 ##容器保存为新的镜像
docker images ##查看镜像
2、Dockerfile 构建
(1)创建一个Dockerfile
mkdir docker
vim docker/Dockerfile
FROM busybox
RUN echo westos > testfile
(2)构建镜像
docker build -t test:v2 /root/docker/
docker images
3、查看镜像的分层结构
- 镜像都为三层结构,用
docker commit
创建的镜像容器层不会显示具体的操作命令,用Dockerfile
创建的镜像容器会显示具体的操作命令 - 删除镜像实质是删除了容器层,再次创建只是添加了容器层
docker history test:v2
docker history test:v2
docker rmi test:v2
docker history busybox:latest
三、Dockerfile详解
1、Dockerfile常用指令
- FROM
指定base镜像,如果本地不存在会从远程仓库下载 - MAINTAINER
设置镜像的作者,比如用户邮箱等 - COPY
把文件从建立Dockerfile
的目录复制到镜像
支持两种形式:COPY src dest
和COPY ["src", "dest"]
src
必须指定建立Dockerfile
的目录中的文件或目录
echo hello linux > index.html
vim Dockerfile
FROM busybox
RUN echo westos > testfile ##RUN后面可以跟shell语句
RUN echo hello word > testfile1
COPY index.html / ##COPY复制当前目录的index.html到容器的根目录下
docker build -t test:v3 /root/docker/ ##构建容器
docker run -it --name test test:v3 ##运行容器
- ADD
用法与COPY类似,不同的是src
可以是归档压缩文件,文件会被自动解压到dest
,也可以自动下载URL并拷贝到镜像:
ADD html.tar /var/www
ADD http://ip/html.tar /var/www
vim Dockerfile
FROM busybox
RUN echo westos > testfile
RUN echo hello word > testfile1
COPY index.html /
ADD nginx-1.20.1.tar.gz /
docker build -t test:v4 /root/docker/
docker run -it --rm test:v4 ##`--rm`表示运行完容器后,不会保存容器
使用-rm
后,查看docker ps -a
时不会看到新建的容器
- ENV
设置环境变量,变量可以被后续的指令使用:
ENV HOSTNAME sevrer1.example.com
- EXPOSE
如果容器中运行应用服务,可以把服务端口暴露出去:
EXPOSE 80
- VOLUME
申明数据卷,通常指定的是应用的数据挂载点:
VOLUME ["/var/www/html"]
vim Dokerfile
FROM busybox
RUN echo westos > testfile
RUN echo hello word > testfile1
COPY index.html /
ADD nginx-1.20.1.tar.gz /
ENV HOSTNAME server1 ##重新定义变量HOSTNAME为server1
EXPOSE 80 ##设定端口为80
VOLUME ["/data"] ##设定挂载目录为/data
docker build -t test:v5 /root/docker/
docker run -it --name demo test:v5
查看data是否挂载成功:
docker inspect demo ##查看demo容器的详细信息
进入上图的路径查看:
cd /var/lib/docker/volumes/85d2dc62ee308813d1840e2b1807e2f7f2a1f13370d986c6efb93e28e5011dc4/_data
再进入容器查看:
docker start demo ##启动容器
docker attach demo ##进入容器
- WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建 - RUN
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim
- CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run
后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run
后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。
CMD的使用:
vim Dockerfile
FROM busybox
RUN echo westos > testfile
RUN echo hello word > testfile1
COPY index.html /
ADD nginx-1.20.1.tar.gz /
ENV HOSTNAME server1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /data ##设定工作目录为/data
CMD echo "hello $HOSTNAME" ##运行命令输出 hello world
docker build -t test:v6 /root/docker/
docker run -it --rm test:v6
要使用Exec格式时,需修改CMD为以下形式:
vim Dockerfile
CMD ["/bin/sh", "-c", "echo hello $HOSTNAME"]
docker build -t test:v7 /root/docker
docker run -it --rm test:v7
ENTRYPOINT的使用:
Exec格式时,ENTRYPOINT可以通过CMD提供额外参数,CMD的额外参数可以在容器启动时动态替换
在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数
vim Dockerfile
FROM busybox
RUN echo westos > testfile
RUN echo hello word > testfile1
COPY index.html /
ADD nginx-1.20.1.tar.gz /
ENV HOSTNAME server1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /data ##设定工作目录为/data
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]
docker build -t test:v8 /root/docker
docker run -it --rm test:v8
docker run -it --rm test:v8 linux
2、快捷删除镜像
docker rmi `docker images | grep ^test |awk '{print $3}'`
四、构建nginx镜像
- 提前准备好
rhel7.tar
和nginx-1.20.1.tar.gz
1、导入rhel7的镜像,发现大小为140M
docker load -i rhel7.tar
2、编写软件仓库文件
vim dvd.repo
[dvd]
name=dvd
baseurl=http://172.25.36.250/rhel7.6
gpgcheck=0
3、编写Dockerfile
vim /root/docker/Dockerfile
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"] ##挂载目录
COPY dvd.repo /etc/yum.repos.d/ ##复制软件仓库文件
ADD nginx-1.20.1.tar.gz /mnt ##解压到指定目录
RUN rpmdb --rebuilddb ##仓库记录清理
RUN yum install -y gcc pcre-devel zlib-devel make
WORKDIR /mnt/nginx-1.20.1
RUN ./configure &> /dev/null
RUN make &> /dev/null
RUN make install &> /dev/null
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
4、创建镜像,运行容器
docker build -t rhel7:v1 /root/docker/
docker run -d --name demo rhel7:v1
docker inspect demo ##查看新建容器信息
挂载的数据目录:
查看到ip地址:
5、访问ip地址
(1)访问报错是因为真实路径下没有数据
curl 172.17.0.2
(2)添加数据再访问
cd /var/lib/docker/volumes/91387133be81617cc32aabd3d1cd146a102a821644a3cba0d367da655e5893c8/_data
echo www.westos.org > index.html
curl 172.17.0.2
五、优化镜像
1、查看新建镜像大小,发现是rhel7的两倍还多,所以就需要进行优化镜像大小
docker images
2、优化镜像大小的方法
- 选择最精简的基础镜像
- 减少镜像的层数
- 清理镜像构建的中间产物
- 注意优化网络请求
- 尽量去用构建缓存
- 使用多阶段构建镜像
3、针对上面建立的nginx镜像进行优化
(1)把RUN命令放到一起,中间用&&
连接起来,因为RUN命令运行一次就会增加一层镜像层,同时关闭nginx编译debug模块,清楚缓存及nginx编译目录
vim Dockerfile
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.20.1.tar.gz /mnt
WORKDIR /mnt/nginx-1.20.1
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -rf /mnt/nginx-1.20.1 /var/cache/*
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
docker build -t rhel7:v2 /root/docker/
docker run -d --name demo1 rhel7:v2
docker images
镜像大小有所变化,但变化较小,需要继续优化
(2)使用多模块(用基础模块生成nginx,再复制基础模块中的/usr/local/nginx
目录即可)
vim Dockerfile
FROM rhel7 as build
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/
ADD nginx-1.20.1.tar.gz /mnt
WORKDIR /mnt/nginx-1.20.1
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure &> /dev/null && make &> /dev/null && make install &> /dev/null && rm -rf /mnt/nginx-1.20.1 /var/cache/*
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
docker build -t rhel7:v3 /root/docker/
docker run -d --name demo2 rhel7:v3
docker images
rhel7:v3
镜像的大小明显变小,且创建时速度明显变快,但还是可以进一步优化
(3)rhel7镜像中很多模块并没有被使用,消耗大量空间,所以更换轻量级源镜像,同时安装nginx需要的函数库
需要提前下载
base-debian10.tar
(轻量级源镜像)
docker load -i base-debian10.tar ##先导入轻量级源镜像
vim Dockerfile
FROM nginx:latest as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE
RUN mkdir -p /opt/var/cache/nginx && \
cp -a --parents /usr/lib/nginx /opt && \
cp -a --parents /usr/share/nginx /opt && \
cp -a --parents /var/log/nginx /opt && \
cp -aL --parents /var/run /opt && \
cp -a --parents /etc/nginx /opt && \
cp -a --parents /etc/passwd /opt && \
cp -a --parents /etc/group /opt && \
cp -a --parents /usr/sbin/nginx /opt && \
cp -a --parents /usr/sbin/nginx-debug /opt && \
cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime
FROM gcr.io/distroless/base-debian10
COPY --from=base /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx", "-g", "daemon off;"]
docker build -t rhel7:v4 /root/docker/
docker run -d --name demo3 rhel7:v4
docker images
查看镜像大小仅为31.9M,达到优化效果