Docker-04-镜像与制作
手动制作
Docker镜像有没有内核?
从镜像大小上面来说,一个比较小的镜像只有十几MB,而内核文件需要一百多兆,因此镜像里面是没有内核的,镜像在被启动为容器后将直接使用宿主机的内核,而镜像本身则只提供相应的rootfs,即系统正常运行所必须的用户空间的文件系统,比如/dev/ , /proc,/bin,/etc等目录,所以容器当中基本是没有/boot目录的,而/boot当中保存的就是与内核相关的文件和目录。
为什么没有内核?
由于容器启动和运行过程中是直接使用了宿主机的内核,所以没有直接调用过物理硬件,所以也不会涉及到硬件驱动,因此也用不上内核和驱动,另外有内核的那是虚拟机。
手动制作yum版nginx镜像
Docker制作类似于虚拟机的模板制作,即按照公司的实际业务务求将需要安装的软件、相关配置等基础环境配置完成,然后将虚拟机再提交为模板,最后再批量从模板批量创建新的虚拟机,这样可以极大的简化业务中相同环境的虚拟机运行环境的部署工作,Docker的镜像制作分为手动制作和自动制作(基于DockerFile),企业通常都是基于Dockerfile制作镜像,其中手动制作镜像步骤具体如下:
下载镜像并初始化系统
基于某个基础镜像之上重新制作,因此需要先有一个基础镜像,本次使用官方提供的centos镜像为基础:
1 | [root@docker01 ~]$ docker pull centos |
yum安装并配置nginx
1 | [root@d08b9898e6b3 yum.repos.d]# dnf install nginx -y #安装nginx |
关闭nginx后台运行
1 | [root@d08b9898e6b3 /]# vim /etc/nginx/nginx.conf |
提交为镜像
不要退出容器,另起一个宿主机终端操作
1 | [root@docker01 ~]$ docker commit --help |
1 | [root@docker01 ~]$ docker commit -a "kinmfer kinmfer@foxmail.com" -c "EXPOSE 80" -m "Nginx yum v1" d08b9898e centos-nginx-v1:1.16.1 |
验证镜像
1 | [root@docker01 ~]$ docker run -it -p 8001:80 centos-nginx-v1:1.16.1 nginx |
如果在配置nginx时未加入daemon off
,则应以如下命令启动
1 | [root@docker01 ~]$ docker run -it -p 8001:80 centos-nginx-v1:1.16.1 nginx "-g daemon off;" |
手动制作编译版本nginx镜像
过程为在centos基础镜像之上手动编译安装nginx,然后再提交为镜像
下载镜像并初始化系统
1 | [root@docker01 ~]$ docker pull centos |
编译安装nginx
1 | [root@84ac00d2d9d2 src]# wget http://nginx.org/download/nginx-1.18.0.tar.gz |
这里将nginx命令设置了软链接,这样就可以直接以nginx
命令启动容器,否则在启动容器时需要写成/app/nginx/sbin/nginx
,有类似情况需要注意
提交为镜像
1 | [root@docker01 ~]$ docker commit -a "kinmfer kinmfer@foxmail.com" -m "Nginx make v1" 84ac00d2d9d2 centos-nginx:v1 |
验证镜像
由于没有在nginx配置文件里加入daemon off
,所以启动容器的时候要加入参数nginx "-g daemon off;"
1 | [root@docker01 ~]$ docker run -it -p 8002:80 centos-nginx:v1 nginx "-g daemon off;" |
Docker不建议再通过这种方式构建镜像;具体原因如下
- 手工创建,容易出错,效率低并且可重复性弱。
- 无法对镜像进行审计,存在安全隐患。
Dockfile制作
基础介绍
DockerFile可以说是一种可以被Docker程序解释的脚本,DockerFile是由一条条的命令组成的,每条命令对应Linux下面的一条命令,Docker程序将这些DockerFile指令再翻译成真正的Linux命令,其有自己的书写方式和支持的命令,Docker程序读取DockerFile并根据指令生成Docker镜像,相比手动制作镜像的方式,DockerFile更能直观的展示镜像是怎么产生的,有了写好的各种各样DockerFile文件,当后期某个镜像有额外的需求时,只要在之前的DockerFile添加或者修改相应的操作即可重新生成新的Docker镜像,避免了重复手动制作镜像的麻烦,具体如下:
https://docs.docker.com/engine/reference/builder/
注意:Dockerfile里涉及到的Linux命令需要FROM的基础镜像中有
1 | FROM # 基础镜像,一切从这里开始构建 |
FROM
- FROM指令是最重的一个且必须为Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境
- 实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件。如果找不到指定的镜像文件,docker build会返回一个错误信息
- Syntax
FROM<repository>[:<tag>]
或FROM<resository>@<digest>
- reposotiry :指定作为base inage的名称;
- <tag>: base inage的标签,为可选项,省咯时默认为latest;
MAINTAINER(现以 LABEL +kv对来实现)
用于让Dockerfile制作者提供本人的详细信息
Dockerfile并不限制MAINTAINER指令可在出现的位置,但推荐将其放置于FROM指令之后
Syntax
MAINTAINER <authtor's detail>
MAINTAINER "kinmfer<kinmfer@foxmail.com>"
<author’s detail>可是任何文本信息,但约定俗成地使用作者名称及邮件地址
现被
LABEL maintainer="kinimfer <kinmfer@foxmail.com>"
代替
LABEL
用来在镜像中添加元数据,一个镜像可以有多个LABEL,可以在一行定义
一个LABEL标签是一个kv对
要在标签值中包含空格,请像在命令行分析中那样使用引号和反斜杠。
Syntax:
1
LABEL<key>=<value> <key>=<value> <key>=<value>...
COPY
- 用于从Docker主机复制文件至创建的新映像文件
- Syntax
COPY <src> ...<dest>
或COPY ["<src>",..."<dest>"]
- <src>:要复制的源文件或目录,支持使用通配符
- <dest>:目标路径,即正在创建的image的文件系统路径;建议为<dest>使用绝对路径,否则,COPY指定则以WORKDIR为其起始路径;
- 注意:在路径中有空白字符时,通常使用第二种格式
- 文件复制准则
- <src>必须是build上下文中的路径,不能是其父目录中的文件
- 如果<src>是目录,则其内部文件或子目录会被递归复制,但<src>目录自身不会被复制
- 如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以/结尾
- 如果<dest>事先不存在,它将会被自动创建,这包括其父目录路径
ADD
- ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径
- Syntax
ADD<src> ..<dest>
或ADD["<src>",... "<dest>"]
- 操作准则
- 同COPY指令
- 如果<src>为URL且<dest>不以/结尾,则<src>指定的文件将被下载并直接被创建为<dest>;如果<dest>以/结尾,则文件名URL指定的文件将被直接下载并保存为<dest>/<filename>
- 如果<src>是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于“tar -x”命令;然而,通过URL获取到的tar文件将不会自动展开;
- 如果<rc>有多个,或其间接或直接使用了通配符,则<dest>必须是一个以/结尾的目录路径;如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>;
WORKDIR
- 用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指定设定工作目录
- Syntax
- WORKDIR<dirpath>
- 在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个WORKDIR指令指定的路径
- 另外,WORKDIR也可调用由ENV指定定义的变量
- 例如
- WORKDIR /var/log
- WORKD1R $STATEPATH
- WORKDIR<dirpath>
VOLUMN
- 用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其它容器上的卷
- Syntax
VOLUME <mountpoint≥
或VOLUME["<mountpoint>"]
- 如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中
EXPOSE
- 用于为容器打开指定要监听的端口以实现与外部通信,只有在运行容器时加
-P
选项才会真正暴露端口 - Syntax
EXPOSE<port>[<protocol>][[]...]
- <protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
- EXPOSE指令可一次指定多个端口,例如
- EXPOSE 11211/udp 11211/tcp
ENV
- 用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)调用
- 调用格式为$variable_name或${variable_name}
- Syntax
ENV <key> <value>
或ENV <key>=<value> ...
- 第一种格式中,<key>之后的所有内容均会被视作其<value>的组成部分,因此,一次只能设置一个变量;
- 第二种格式可用一次设置多个变量,每个变量为一个”<key>=<value>”的键值对,如果<value>中包含空格,可以以反斜线(N)进行转义,也可通过对<value>加引号进行标识;另外,反斜线也可用于续行;
- 定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能
RUN
- 用于指定docker build过程中运行的程序,其可以是任何命令
- Syntax
RUN <command>
或RUN ["<executable>", "<param1>", "<param2>"]
,注意:要使用双引号 - 第一种格式中,<command>通常是一个shell命令,且以”/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号;
- 第二种语法格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命令不会以”/bin/sh-c”来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
RUN["/bin/bash", "-c","<executable>", "<param1>"]
CMD
- 类似于RUN指令,CMD指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同
- RUN指令运行于映像文件构建过程中,而CMD指令运行于基于Dockerfile构建出的新镜像文件启动一个容器时
- CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD指定的命令其可以被docker run的命令行选项所覆盖
- 在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效
- Syntax
CMD <command>
或CMD["<executable>","<param1>","<param2>"]
或CMD["<param1>","<param2>"]
- 前两种语法格式的意义同RUN
- 第三种则用于为ENTRYPOINT指今提供默认参数
ENTRYPOINT
- 类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序
- 与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序
- 不过,docker run命令的–entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
- Syntax
ENTRYPOINT<command>
ENTRYPOINT["<executable>", "<param1>", "<param2>"]
- docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后做为其参数使用
- Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效
USER
- 用于指定运行image时的或运行Dockerfile中任何RUN、CMD或ENTRYPOINT指令指定的程序时的用户名或UID
- 默认情况下,container的运行身份为root用户
- Syntax
USER <UID>|<UserName>
- 需要注意的是,<UID>可以为任意数字,但实践中其必须为/etc/passwd中某用户的有效UID,否则,docker run命令将运行失败
ONBUILD
- 用于在Dockerfile中定义一个触发器
- Dockerfile用于build映像文件,此映像文件亦可作为base image被另一个Dockerfile用作FROM指令的参数,并以之构建新的映像文件
- 在后面的这个Dockerfile中的FROM指令在build过程中被执行时,将会”触发”创建其base image的Dockerfile文件中的ONBUILD指令定义的触发器
- Syntax
ONBUILD <INSTRUCTION>
- 尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令
- 使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如ruby:2.0-onbuild
- 在ONBUILD指令中使用ADD或COPY指令应该格外小心,因为新构建过程的上下文在缺少指定的源文件时会失败
DockerFile制作编译版nginx1.18.0镜像
下载镜像并初始化系统
1 | [root@docker01 ~]$ docker pull centos |
进入到指定目录
1 | [root@docker01 dockfile]$ cd web/nginx/ |
编写Dockfile文件
1 | [root@docker01 nginx]$ vim ./Dockerfile |
生成的镜像的时候会在执行命令的当前目录查找Dockerfile文件,所以名称不可写错,而且D必须大写
1 | # My Dockerfile |
如果在从该镜像启动容器的时候也指定了命令,那么该指定的命令会覆盖Dockfile构建的镜像里的CMD命令,即指定的命令优先级更高
准备源码包和配置文件
执行镜像构建
1 | [root@docker01 nginx]$ docker build -t nginx-1.18.0:v1 . |
构建完成
启动容器
访问web界面
自定义Tomcat镜像
基于官方提供的centos、debain、ubuntu、alpine等基础镜像构建JDK(Java环境),然后再基于自定义的JDK镜像构建出业务需要的tomcat镜像。
构建JDK镜像
先基于官方提供的基础镜像,制作出安装了常用命令的自定义基础镜像,然后在基础镜像的基础之上,再制作JDK镜像、Tomcat镜像等。
自定义CentOS基础镜像
1 | [root@docker01 ~]$ docker pull centos |
执行构建JDK镜像
1 | # CentOS 8.2 jdk-8u261 |
上传JDK压缩包和profile文件
执行构建自定义JDK基础镜像
1 | [root@docker01 jdk-8u261]$ vim docker-command.sh |
验证
构建Tomcat9镜像
基于自定义的JDK基础镜像,构建出通用的自定义Tomcat基础镜像,此镜像后期会被多个业务的多个服务共同引用(相同的JDK版本和Tomcat版本)。
编写Dockfile
1 | # CentOS 8.2 tomcat9.0.37 |
上传Tomcat9源码包
执行构建自定义JDK基础镜像
1 | [root@docker01 jdk-8u261]$ vim docker-command.sh |
验证
构建业务镜像1
创建app1和app2两个目录,表示基于tomcat自定义基础镜像构建出不同业务的tomcat app镜像。
准备Dockerfile1
1 | #Tomcat Web Image |
准备自定义app1界面
1 | [root@docker01 tomcat-app1]$ mkdir app1 |
准备容器启动执行脚本
1 | [root@docker01 tomcat-app1]$ cat run_tomcat.sh |
执行构建
1 | [root@docker01 tomcat-app1]$ cat build_command.sh |
验证
构建业务镜像2
有了之前的app1,则app2可以很快的部署
1 | [root@docker01 tomcat]$ cp -rf tomcat-app1/ tomcat-app2 |
接下来将app1和app2同时开启验证
基于官方alpine基础镜像制作自定义镜像
1 | FROM alpine |
基于官方Ubuntu基础镜像制作自定义镜像
1 | # Docker image for nginx |
构建HAProxy镜像
构建出haproxy镜像,将haproxy通过容器的方式运行。
准备Dockfile
1 | FROM centos-base:v1 |
准备HAProxy源码和配置文件
1 | [root@docker01 haproxy-centos]$ cat haproxy.cfg |
启动容器
192.168.242.100(HAProxy+Tomcat)
192.168.242.101(Tomcat)
验证
会在两个页面之间切换
本地镜像上传到官方docker仓库
将自制的镜像上传至docker仓库;https://hub.docker.com/,实现镜像跨主机备份。
注册账户并配置相关信息
略
在虚拟机上用自己的账号登陆
1 | [root@docker01 ~]$ docker login docker.io |
设置tag并上传
本地镜像上传到阿里云
将镜像推送到Registry
将本地镜像上传至阿里云,实现镜像备份与统一分发的功能。https://cr.console.aliyun.com
注册账户、创建namespace、创建仓库、修改镜像tag及上传镜像