1.Dockerfile简介
Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令。
Docker程序将读取Dockerfile,根据指令生成定制的image。相比image这种黑盒子,Dockerfile这种显而易见的脚本更容易被使用者接受,它明确的表明image是怎么产生的。有了Dockerfile,当我们需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻烦。
Dockerfile的指令根据作用可以分为两种,构建指令和设置指令。构建指令用于构建image,其指定的操作不会在运行image的容器上执行;设置指令用于设置image的属性,其指定的操作将在运行image的容器中执行。
1.FROM:
用法:FROM
说明:第一个指令必须是FROM了,其指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签,可以出现多次,如果需要在一个Dockerfile中构建多个镜像。
2.MAINTAINER
用法:MAINTAINER
说明:描述镜像的创建者,名称和邮箱
3.RUN
用法:RUN “command” “param1” “param2”
说明:RUN命令是一个常用的命令,执行完成之后会成为一个新的镜像,这里也是指镜像的分层构建。一句RUN就是一层,也相当于一个版本。这就是之前说的缓存的原理。我们知道docker是镜像层是只读的,所以你如果第一句安装了软件,用完在后面一句删除是不可能的。所以这种情况要在一句RUN命令中完成,可以通过&符号连接多个RUN语句。RUN后面的必须是双引号不能是单引号(没引号貌似也不要紧),command是不会调用shell的,所以也不会继承相应变量,要查看输入RUN “sh” “-c” “echo” “HOME",而不是RUN"echo""
HOME",而不是RUN"echo""HOME”。
4.EXPOSE
告诉Docker服务器容器对外映射的容器端口号,在docker run -p的时候生效。
5.ENV
设置环境变量,变量可以被后续的指令使用
6.ADD
说明:复制本机文件或目录或远程文件,添加到指定的容器目录,支持GO的正则模糊匹配。路径是绝对路径,不存在会自动创建。如果源是一个目录,只会复制目录下的内容,目录本身不会复制。ADD命令会将复制的压缩文件夹自动解压,这也是与COPY命令最大的不同。
7.COPY
说明:COPY除了不能自动解压,也不能复制网络文件。其它功能和ADD相同。
8.VOLUME
说明:在主机上创建一个挂载,挂载到容器的指定路径。docker run -v命令也能完成这个操作,而且更强大。这个命令不能指定主机的需要挂载到容器的文件夹路径。但docker run -v可以,而且其还可以挂载数据容器
9.WORKDIR
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工
作目录,如果目录不存在会自动创建。
10.CMD 与 ENTRYPOINT
这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT指令当作参数。
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效。
1.在容器中执行相应的命令
1.清理实验环境,删除创建的容器及其相应的镜像
[root@server1 ~]# docker rm -f vm2
[root@server1 ~]# docker rm -f vm1
[root@server1 ~]# docker rmi ubuntu:v1
[root@server1 ~]# docker rmi ubuntu:v2
2.下载rhel7的镜像并且将镜像导入本地
[root@server1 ~]# ls
anaconda-ks.cfg docker game2048.tar rhel7.tar ubuntu.tar
[root@server1 ~]# docker load -i rhel7.tar
3.以该镜像建立容器并且指定其工作环境为bash,进入容器之后是读bash下的文件的,但是可以写入文件
编写repo文件需要在文件中添加yum源:
4.下载htppd时会出现下面报错,是因为rpm数据库损坏需要重建
因此需要在 “yum install …” 前使用 “rpm –rebuilddb” 重建数据库,再次安装如下:
会显示httpd已经安装,但是实际在安装htppd的时候出现了问题,在容器内是由root用户的,如果命令执行成功则会返回给用户0,则证明该条命令的返回数值为0
发现在容器中执行相应的命令后无法成功安装httpd,因此就是要构建新的镜像里面添加新的应用。
补充知识:
Linux user namespace 为正在运行的进程提供安全相关的隔离(其中包括 uid 和 gid),限制它们对系统资源的访问,而这些进程却感觉不到这些限制的存在。
对于容器而言,阻止权限提升攻击(privilege-escalation attacks)的最好方法就是使用普通用户权限运行容器的应用程序。
然而有些应用必须在容器中以 root 用户来运行,这就是我们使用 user namespace 的最佳场景。我们通过 user namespace 技术,把宿主机中的一个普通用户(只有普通权限的用户)映射到容器中的 root 用户。在容器中,该用户在自己的 user namespace 中认为自己就是 root,也具有 root 的各种权限,但是对于宿主机上的资源,它只有很有限的访问权限(普通用户)。
2.Dockfile构建新的镜像
1.在/tmp/docker中编写Dockerfile文件
[root@server1 ~]# mkdir -p /tmp/docker
[root@server1 ~]# cd /tmp/docker/
[root@server1 docker]# vim Dockerfile
文件编辑内容如下:
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb && yum install -y httpd
CMD ["/usr/sbin/httpd","-D","FOREGROUND"]
RUN指令做的是在镜像里安装一些软件,或者做一些需要的操作。Docker RUN之后执行了Shell指令,它对镜像里面的内容做了一些改动,最后再执行Docker COMMIT,把当前容器里面的改动持久化到镜像里面。RUN命令可以执行多次。但是通常来说建议把需要的指令都写在一条RUN命令里面,用&&符号连接起来,好处是镜像只增加了一层,可以加快镜像构建的速度,减少镜像的层次。
2.创建构建新镜像时所要用到的yum源文件
[root@server1 docker]# pwd
/tmp/docker
[root@server1 docker]# vim dvd.repo
文件编辑内容如下:
[rhel7.3]
name=rhel7.3
baseurl=http://172.25.254.77/file
gpgcheck=0
3.根据给定的Dockerfile和上下文以构建Docker镜像,注意后面要加点,代表是在当前目录下面
会出现执行过程,并且最后显示安装完成
4.以新建的镜像运行容器要添加端口映射并且查看端口
[root@server1 docker]# docker run -d --name apache -p 80:80 rhel7:v1
浏览器上访问查看:
//在dockerfile中添加访问默认测试页:
[root@server1 docker]# pwd
/tmp/docker
[root@server1 docker]# vim Dockerfile
文件编辑内容如下:
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb && yum install -y httpd
COPY index.html /var/www/html/index.html
CMD ["/usr/sbin/httpd","-D","FOREGROUND"]
//在该目录下编辑默认访问测试页的文件:
[root@server1 docker]# vim index.html
文件编辑内容如下:
www.westos.org
//再次以该dockerfile生成新的镜像
[root@server1 docker]# docker build -t rhel7:v2 .
//使用该镜像新建容器
[root@server1 docker]# docker run -d --name apache -p 80:80 rhel7:v2
在浏览器上再次查看,为添加默认访问页面下的内容
6.创建容器时对其指定挂载点
//运行容器指定挂载点,注意指定之后会在该目录下自动生成website挂载点
[root@server1 docker]# docker run -d --name apache -p 80:80 -v /tmp/docker/website:/var/www/html rhel7:v2
//编辑挂载点下的测试文件
[root@server1 docker]# ls
Dockerfile dvd.repo index.html website
[root@server1 docker]# cd website/
[root@server1 website]# ls
[root@server1 website]# vim index.html
文件编辑内容如下:
zll.westos.org zll.westos.org
在浏览器再次测试查看:
1.下载nginx的镜像并且导入到本地
[root@server1 ~]# ls
anaconda-ks.cfg docker nginx-1.15.8.tar.gz rhel7.tar
busybox.tar game2048.tar nginx.tar ubuntu.tar
[root@server1 ~]# docker load -i nginx.tar
2.下载nignx的安装包到本地,因为实验中需要用到,并且编写dockfile文件
//在/tmp/docker中添加已经下载的nginx的安装包
[root@server1 ~]# mv nginx-1.15.8.tar.gz /tmp/docker/
[root@server1 ~]# cd /tmp/docker/
[root@server1 docker]# ls
Dockerfile dvd.repo index.html nginx-1.15.8.tar.gz website
//编写Dockfile文件
[root@server1 docker]# vim Dockerfile
文件编辑内容如下:
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
//以dockerfile生成新的镜像
[root@server1 docker]# ls
Dockerfile dvd.repo(yum源文件之前已经写好) index.html nginx-1.15.8.tar.gz(nginx安装包)
[root@server1 docker]# docker build -t nginx:v1 .
//查看生成的镜像
//使用新生成的镜像运行容器
[root@server1 docker]# docker run -d --name nginx -p 80:80 nginx:v1 -d表示打入后台,-f表示添加端口映射
在浏览器查看:
3.重新编写dockerfile文件添加端口及其挂载点
//编写dockerfile:
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
EXPOSE 80 端口
VOLUME ["/usr/local/nginx/html"] 挂载点
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
//删除原来生成的镜像并建立新的镜像
//运行新的镜像:
//查看运行的容器的详细信息会发现挂载点
//在挂载点上添加要发布的内容,挂载点下默认有测试页及其相关内容
//在浏览器上查看
//查看挂载点并且清空挂载点
镜像的优化:
1.减少镜像层数
//编辑dockerfile
[root@server1 ~]# cd /tmp/docker/
[root@server1 docker]# vim Dockerfile
文件编写的内容如下:
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx && make && make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
//运行新的镜像v2并且查看大小,发现比v1少了20MB
[root@server1 docker]# docker build -t nginx:v2 .
[root@server1 docker]# docker images nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 57c313c825dd 22 seconds ago 250MB
nginx v1 0f28d8eb67fe 20 minutes ago 276MB
2.清理镜像构建的中间产物并且使用多阶段构建镜像
//编辑dockerfile
[root@server1 docker]# pwd
/tmp/docker
[root@server1 docker]# vim Dockerfile
文件
FROM rhel7 as build
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel && yum clean all && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx && make && make install &&rm -rf /mnt/nginx-1.15.8
FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
//重新以dockerfile创建新的镜像并且查看大小发现变为144MB
3.终极优化从底层优化
//下载distroless镜像并且进行本地导入
[root@server1 docker]# docker load -i distroless.tar
//编写dockerfile
[root@server1 docker]# pwd
/tmp/docker
[root@server1 docker]# vim Dockerfile
文件编辑内容如下:
FROM nginx as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG Asia/Shanghai
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 /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.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt.so.* /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
COPY --from=base /opt /
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
//生成新的镜像
[root@server1 docker]# docker build -t busybox:v5 .
//使用新的镜像运行容器看是否能够使用
输入http://172.25.254.1/ ,发现访问成功