接上篇:https://blog.csdn.net/shenyuanhaojie/article/details/121501028
UnionFS(联合文件系统)是一种分层、轻量级并且高性能的文件系统,它支持将对文件系统的修改作为一次提交操作来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。AUFS、OverlayFS 及 Devicemapper
都是一种 UnionFS。
UnionFS 是 Docker 镜像的基础。overlay2 是目前 Docker 默认的存储驱动,以前则是 aufs。镜像可以通过分层来进行集成,基于基础镜像(基础镜像没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统还会包含所有底层的文件和目录。
我们下载的时候看到的一层层的就是联合文件系统。
Docker 支持的联合文件系统包括 OverlayFS,AUFS,Btrfs,VFS,ZFS,Device Mapper
overlay 存储引擎结构(区别于镜像分层):
#overlayfs 在 linux 主机上只有两层,一个目录在下层,用来保存镜像(docker),另外一个目录在上层,用来存储容器信息
1、rootfs 基础镜像
2、lower 下层信息(为镜像层,可读)
3、upper 上层目录(容器信息,可写)
4、worker 运行的工作目录(copy-on-write写时复制-》准备容器环境)
5、merged "视图层"(容器视图)
docker 镜像层次结构:
1、base image:基础镜像
2、image:固化了一个标准运行环境,镜像本身的功能-封装一组功能性的文件,通过统一的方式,文件格式提供出来(只读)
3、container:容器层(读写)
4、docker-server 端
5、呈现给 docker-client(视图)
Docker 核心技术-联合文件系统
bootfs
rootfs
Docker 的轻量化的原因:
因为对于精简的 OS,rootfs 可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的 kernel,自己只需要提供 rootfs 就可以了,由此可见对于不同的 linux 发行版,bootfs 基本是一致的,rootfs 会有差别,因此不同的发行版可以公用 bootfs。
镜像不是一个单一的文件,而是有多层构成。容器其实是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker 使用存储驱动管理镜像每层内容及可读写层的容器层。
docker history
来查看一个镜像由哪些层组成> docker 镜像分层(基于 AUFS 构建):
Docker 镜像位于 bootfs 之上
每一层镜像的下一层成为父镜像
第一层镜像成为 base image(操作系统环境镜像)
容器层(可读可写),在最顶层(writeout-able)
容器层以下都是 readonly
PS:
LXC 是内核中的容器技术,早期 docker 在没有将资源容器化的功能时,就是靠内核中的 LXC 来完成容器虚拟化的,现在 docker 拥有了自己的 docker libcontainer 库文件,可以做到将资源容器化的操作所以对 LXC 的依赖性大大降低。
Dockerfile 文件详解
如何编写最佳的 Dockerfile
指令 | 含义 |
---|---|
FROM 镜像 | 指定新镜像所基于的镜像,第一条指令必须为 FROM 指令,每创建一个镜像就需要一条 FROM 指令 |
MAINTAINER 名字 | 说明新镜像的维护人信息 |
RUN 命令 | 在所基于的镜像上执行命令,并提交到新的镜像中 |
ENTRYPOINT [“要运行的程序”,“参数1”,“参数2”] | 设定容器启动时第一个运行的命令参数 |
CMD[“要运行的程序”,“参数1”,“参数2”] | 指定启动容器时要运行的命令或脚本,Dockerfile 只能有一条 CMD 命令,如果指定多条则只能最后一条被执行 |
EXPOSE 端口号 | 指定新镜像加载到 Docker 时要开启的端口 |
ENV 环境变量 变量值 | 设置一个环境变量的值,会被后面的 RUN 使用 |
ADD 源文件/目录 目标文件/目录 | 将源文件复制到目标文件,源文件要与 Dockerfile 位于相同目录中,或者是一个 URL |
COPY 源文件/目录 目标文件/目录 | 将本地主机上的文件/目录复制到目标地点,源文件/目录要与 Dockerfile 在相同的目录中 |
VOLUME[“目录”] | 在容器中创建一个挂载点 |
USER 用户名/UID | 指定运行容器时的用户 |
WORKDIR 路径 | 为后续的 RUN、CMD、ENTRYPOINT 指定工作目录 |
ONBUILD 命令 | 指定所生成的镜像作为一个基础镜像时所要运行的命令 |
HEALTHCHECK | 健康检查 |
构建镜像命令(可在构建镜像时指定资源限制)示例:
docker build -t nginx:test .
-t
:tag 打标签-f
:指定 dockerfile 目录.
:指构建镜像时使用的环境(当前)目录,构建镜像时使用的上下文环境
在编写 Dockerfile 时,有严格的格式需要遵循:
- 第一行必须使用 FROM 指定新镜像所基于的镜像名称
- 之后使用 MAINTAINER 指令说明维护该镜像的用户信息
- 然后是镜像操作相关指令,如 RUN 指令。每运行一条指令,都会给基础镜像添加新的一层
- 最后使用 CMD 指令指定启动容器时要运行的命令操作
Docker 执行 Dockerfile 流程:
- docker 从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- docker 再基于刚提交的镜像运行一个新容器
- 执行 dockerfile 中的下一条指令直到所有指令都执行完成
构建镜像命令举例:
docker build -t image_name . (不要忽视这个点)
使用镜像命令举例:
docker run -d -P image_name
最后使用 docker ps -a 查看容器运行状态,如果是 up 状态就可以镜像测试验证了
COPY 只能用于复制,ADD 复制的同时,如果复制的对象是压缩包,ADD 还可以解压。但是 COPY 比 ADD 节省资源。
CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换(覆盖)。如果 docker run 没有指定任何的执行命令或者 dockerfile 里面也没有 entrypoint,那么就会使用 cmd 指定的默认的执行命令执行。CMD 命令只有最后一个会生效。
ENTRYPOINT 指定容器启动时要执行的命令,可以追加命令。ENTRYPOINT 指定的命令需要与 docker run 启动容器进行搭配,将 docker run 指令后面跟的内容当做参数作为 ENTRYPOINT 指令指定的运行命令的参数,ENTRYPOINT 指定的 linux 命令一般是不会被覆盖的。ENTRYPOINT 指令并不是必须的,因为它会增加复杂度。
总结:
CMD 是容器环境启动时默认加载的命令。
ENTRYPOINT 是容器环境启动时第一个加载的命令程序/脚本程序 init(init pid 1)如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略。
如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的内容被追加为 ENTRYPOINT 指定命令的参数。
如果 ENTRYPOINT 使用了 exec 模式,CMD 也应该使用 exec 模式。
ENTRYPOINT ["sh","-c","echo $HOME"]
Dockerfile 镜像案例
[root@c7-1 ~]#mkdir /opt/apache
[root@c7-1 ~]#cd /opt/apache/
[root@c7-1 /opt/apache]#vim Dockerfile
#基于 centos:7 的基础镜像
FROM centos:7
#定义镜像的用户信息
MAINTAINER this is apache image <test>
#镜像操作指令安装 apache 软件
RUN yum -y update;yum -y install httpd
#开启 80 端口
EXPOSE 80
#复制网站首页文件
ADD index.html /var/www/index.html
##前台启动方法一(需准备执行脚本)
#将执行脚本复制到镜像中
ADD run.sh /run.sh
RUN chmod 755 /run.sh
#启动容器时执行脚本
CMD ["/run.sh"]
##前台启动方法二(无需其他脚本)
ENTRYPOINT ["/usr/sbin/apachectl"]
#前台启动 apache,CMD 为 ENTRYPOINT 传递参数
CMD ["-D","FOREGROUND"]
[root@c7-1 /opt/apache]#vim run.sh
#!/bin/bash
rm -rf /run/httpd/*
#清理httpd的缓存
/usr/sbin/apachectl -D FOREGROUND
#指定为前台运行
Docker 容器仅在它的 1 号进程(PID 为 1)时会保持运行。如果 1 号进程退出了,Docker 容器也会退出。
[root@c7-1 /opt/apache]#echo "this is test web" > index.html
[root@c7-1 /opt/apache]#ls
Dockerfile index.html run.sh
先确保自己这有 centos:7 镜像,这里使用第一种 dockerfile
#速度和网络、主机性能等有关
[root@c7-1 /opt/apache]#docker build -t http:test1 .
......
Successfully built fba8c0058eaf
Successfully tagged http:test1
[root@c7-1 /opt/apache]#docker images #可以看到已经构建完成,但是相比于官网下载的镜像很大,那是因为没有优化
REPOSITORY TAG IMAGE ID CREATED SIZE
http test1 fba8c0058eaf 52 seconds ago 512MB
centos 7 eeb6ee3f44bd 2 months ago 204MB
[root@c7-1 /opt/apache]#docker run -itd --name http_test -p 11111:80 http:test1
38a79b3a18411a3c4e79046a5d576a7ce63d47422f22e19353b06817e26e733b
[root@c7-1 /opt/apache]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38a79b3a1841 http:test1 "/run.sh" 4 seconds ago Up 3 seconds 0.0.0.0:11111->80/tcp, :::11111->80/tcp http_test
[root@c7-1 /opt/apache]#curl http://192.168.10.20:11111
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Apache HTTP Server Test Page powered by CentOS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
......
如果有网络报错提示:WARNING: IPv4 forwarding is disabled. Networking will not work.
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p
systemctl restart network && systemctl restart docker
编写 Dockerfile
FROM centos:7
MAINTAINER GONGBOYI
RUN yum -y update && \
yum -y install pcre-devel zlib-devel gcc gcc-c++ make && \
useradd -M -s /sbin/nologin nginx
ADD nginx-1.15.9.tar.gz /usr/local/src
WORKDIR /usr/local/src/nginx-1.15.9
RUN ./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_stub_status_module && make && make install
ENV PATH /usr/local/nginx/sbin:$PATH
VOLUME ["/usr/local/nginx/html"]
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
#RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
#CMD nginx
运行测试
docker run -itd --name nginx-v1 -P nginx:1.12.0 #我们写的镜像有自己的环境,运行不要指定 bash
#dockerfile 里面以守护进程的方式启动了 nginx
curl IP:PORT
-------------------------------------------------------
[root@c7-1 ~]#docker build -t nginx:1.15.9 .
......
Successfully built 944d66226a64
Successfully tagged nginx:1.15.9
[root@c7-1 ~]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.15.9 944d66226a64 2 minutes ago 574MB
centos 7 eeb6ee3f44bd 2 months ago 204MB
[root@c7-1 ~]#docker run -itd --name nginx-test -P nginx:1.15.9
849a45b6835fa39f59b3e0da4b9d3758f1aef901f8da82eedba91d6acbfc7ce7
[root@c7-1 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
849a45b6835f nginx:1.15.9 "nginx -g 'daemon of…" 17 seconds ago Up 16 seconds 0.0.0.0:49163->80/tcp, :::49163->80/tcp nginx-test
[root@c7-1 ~]#curl 192.168.10.20:49163
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
镜像构建过程中:
① 每一层镜像会临时产生一层新的镜像层,并且会运行为临时容器
② 每一层临时容器会基于前一层镜像的缓存层(容器)进行运行
③ 如果该镜像层的临时容器在运行时报错,会exited(非0值)退出,并且在docker ps -a 中会保存下来,同时docker build构建过程会中止
④ 在基于同一个dockerfile修改时,修改的指令对应的image镜像缓存会失效,但该镜像层之前的image层的缓存会保留
FROM centos:7
MAINTAINER this is ssh image <test>
RUN yum -y update;\
yum install -y openssh* net-tools lsof telnet passwd;\
echo '123456' | passwd --stdin root;\
sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config;\
#不使用PAM认证
sed -ri '/^session\s+required\s+pam_loginuid.so/ s/^/#/' /etc/pam.d/sshd;\
#取消pam限制
ssh-keygen -t rsa -A;\
#生成密钥认证文件
mkdir -p /root/.ssh;\
chown root.root /root;\
chmod 700 /root/.ssh
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
#/usr/sbin/sshd -D 用于前台启动 sshd 服务
------------------------------------
docker build -t sshd:test .
docker images
#启动容器并修改 root 密码
docker run -itd --name sshd -P sshd:test
docker ps -a
ssh localhost -p 49154
echo '654321' | passwd --stdin root #容器中
FROM sshd:test
MAINTAINER this is systemctl iamge <test>
ENV container docker
#除了systemd-tmpfiles-setup.service,删除其他所有文件
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done);\
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /lib/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*;\
rm -f /lib/systemd/system/sockets.target.wants/*udev*;\
rm -f /lib/systemd/system/sockets.target.wants/*initctl*;\
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD [ "/usr/sbin/init" ]
----------------------------------------------------
#启动容器,并挂载宿主机目录挂载到容器中并进行初始化
#--privileged:使container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限。
docker run -d --privileged --name systemctl -P -v /sys/fs/cgroup:/sys/fs/cgroup:ro systemctl:test
docker exec -it systemctl bash
systemctl status sshd
FROM centos:7
MAINTAINER this is tomcat image <test>
ADD jdk-8u91-linux-x64.tar.gz /usr/local/
WORKDIR /usr/local/
RUN mv jdk1.8.0_91 /usr/local/java
ENV JAVA_HOME /usr/local/java
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH $JAVA_HOME/bin:$PATH
ADD apache-tomcat-8.5.16.tar.gz /usr/local/
WORKDIR /usr/local/
RUN mv apache-tomcat-8.5.16 /usr/local/tomcat
EXPOSE 8080
ENTRYPOINT [ "/usr/local/tomcat/bin/catalina.sh","run" ]
-----------------------------------------------------------
docker build -t tomcat:test .
docker run -d --name tomcat -P tomcat:test
docker ps -a
curl IP:PORT
emsp;我们在自己构建镜像的时候会发现官网上下的镜像比我们自己构建的镜像小很多,这是为什么呢?官网上下的镜像都经过了优化,常用的优化方式有以下几种:
不需要输出的指令丢入 /dev/null
FROM centos:7
RUN yum install -y gcc pcre pcre-devel devel zlib-devel make &> /dev/null && yum clean all
ADD nginx-1.12.2.tar.gz /mnt
WORKDIR /mnt/nginx-1.12.2
#关闭debug日志
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx &> /dev/null
RUN make &> /dev/null
RUN make install &> /dev/null
RUN rm -rf /mnt/nginx-1.12.2
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx""-g","daemon off;"]
减少 RUN 构建
FROM centos:7
ADD nginx-1.12.2.tar.gz /mnt
WORKDIR /mnt/nginx-1.12.2
RUN yum install -y gcc pcre pcre-devel devel zlib-devel make &> /dev/null && \
yum clean all && \
sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && \
./configure --prefix=/usr/local/nginx &> /dev/null && \
make &> /dev/null && make install &> /dev/null &&\
rm -rf /mnt/nginx-1.12.2
EXPOSE 80
VOLUME ["/usr/local/nginx/html"] ##挂载;若是不指定挂载点,默认是/var/lib/docker/volumes/容器id/_data
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
多阶段构建
FROM centos:7 as build
ADD nginx-1.12.2.tar.gz /mnt
WORKDIR /mnt/nginx-1.12.2
RUN yum install -y gcc pcre pcre-devel devel zlib-devel make &> /dev/null && \
yum clean all && \
sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && \
./configure --prefix=/usr/local/nginx &> /dev/null && \
make &>/dev/null && \
make install &>/dev/null && \
rm -rf /mnt/nginx-1.12.2
FROM centos:7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY --from=build /usr/local/nginx /usr/local/nginx
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
---------------------------------------------------------------
docker build -t nginx:test .