Docker运维教程(3)镜像制作、更新与Dockerfile说明

TangLu 系统运维 2024-03-26 7338 0

一、为什么需要自己制作 Docker 镜像

当互联网上的Docker镜像无法满足业务需求时,也可以自行创建镜像并打包发布。Docker提供了docker save 、docker commit 以及Dockerfile构建文件这3种方式来制作镜像


二、使用Docker save保存本地镜像

该方式可以直接将本地已经做好的镜像保存好,然后在另外的主机上载入

docker image save  tanglu/centos:v1.1 tanglu/nginx:v14  -o myimages.gz  #保存了2个镜像,名为myimages.gz
docker image lode  -i myimages.gz    #在另外的机器上载入即可,-i=input


三、使用docker commit命令制作\更新镜像

docker commit命令是创建镜像最直观的方法,也可以说是在一个基础镜像上更新一个新的镜像。整个过程就是运行已有镜像、修改容器、使用commit来保存容器为新镜像。下面是一个操作示例,详细记录了在一个全新Centos镜像中安装一个VIM,并且将安装好VIM的容器保存为一个新镜像的过程

1、运行一个基础镜像

docker run -it centos


2、在该容器中安装vim

yum install vim -y


3、使用commit提交镜像

# commit后面的第一个参数是被打包或者更新的镜像名,第二个参数则是生成的新的镜像名,-p可以在打包时先暂停容器,避免数据不一致
docker commit centos centos_with_yum  #在centos这个容器基础上生成名为centos_ip的新镜像


4、查看镜像,发现生成新镜像,大小有了显著的变化,如图

docker_commit.png


四、使用Dockerfile制作镜像

由于docker commit是一种纯手工的制作方式,容易出错,效率也不够高,且无法得知别人镜像中包含了什么东西,有安全隐患,所以官方推荐使用Dockerfile来构建镜像。当使用Dockerfile来构建镜像时,只需要按照制作镜像(Dockerfile) --> 构建镜像(Build) --> 启动镜像(Run)的顺序来进行就可以了。而制作镜像时编写的 Dockerfile 是一个纯文本文件(默认文件名就为 Dockerfile,首字母必须大写),里面包含了各类Docker所支持的指令,支持读取系统环境变量等。Docker运行时会自上而下去执行Dockerfile中的文件,所以只需要在Dockerfile文件中堆叠好相关指令即可。


1、Dockerfile常用指令

· FROM:该指令必须是Dockerfile中第一个非注释行,用于指定基准镜像,后续的指令都是根据基准镜像所运行。在进行镜像选择的时候官方镜像优先考虑,tag版本尽量固定而不要依赖latest,尽量选择体积小的镜像(推荐使用各个组件的apline镜像,它是一个只有5M不到的完整发行系统)

· MAINTAINER:可选项,用于提供制作者的相关信息。该选项可以使用LABLE代替

· COPY:将Docker宿主中的本地文件复制到镜像中,如果目录不存在则自动创建。这些文件必须和Dockerfile保持同级或是其子目录;如果源文件是目录则会自动复制目录内的文件,而不包含目录本身;目录必须以/结尾

· ADD:作用类似COPY指令,但是支持对压缩包的自动解压和URL路径

· WORKDIR:相当于cd命令,可以指定镜像的工作目录,尽量用绝对路径,如果目录不存在会自动创建

· EXPOSE:为容器打开指定要监听的端口以实现端口映射(docker -p选项的作用),默认为tcp协议,可一次暴露多个端口,只不过宿主机中的端口只能动态绑定。

· VOLUME:在容器启动时自动挂载一个卷实现数据持久化(代替了docker run -v选项),这个卷在宿主机中的存储路径是随机生成、无法指定的,通过docker inspect中的mounts信息可以看到路径

· RUN:在docker build过程中运行一个基于基础镜像的命令(该命令需要在基础镜像中存在)。该指令可以使用N个,但是由于每一个RUN都会生成一个临时容器,为了减少镜像体积可以把多条命令放在一个RUN指令中,然后通过&&进行结合。(如果是特别长的 RUN 指令会比较不美观,也可以把这些 指令集中到一个脚本文件里,用 COPY 命令拷贝进去再用 RUN 来执行

COPY setup.sh  /tmp/                # 拷贝脚本到/tmp目录
RUN cd /tmp && chmod +x setup.sh && ./setup.sh

· ARG\ENV:上面提到的 RUN 指令本质就是运行了一个 Shell 命令,既然是 Shell 就可以通过变量实现参数化运行。在 Dockerfile 中要使用变量的话需要使用 ARG 或者 ENV指令。区别在于 ARG 创建的变量是在镜像构建过程中使用,而 ENV 创建的变量不仅能够在构建镜像的过程中使用,在容器运行时也能够以环境变量的形式被应用程序或者ADD、COPY等命令调用

vi Dockerfile
ARG IMAGE_BASE="nginx"
ARG IMAGE_TAG="1.21-alpine"
FROM ${IMAGE_BASE}:${IMAGE_TAG}      #使用了ARG变量
ENV PATH=$PATH:/tmp
ENV DEBUG=OFF
COPY ./default.conf /etc/nginx/conf.d/
RUN cd /usr/share/nginx/html && echo "hello nginx" > a.txt
EXPOSE 8081 8082 8083

# docker build -t ngx-app .

· CMD运行docker run启动镜像时,如果没有指定命令则会使用CMD里配置的命令,如果显式指定了命令则会覆盖CMD的配置,同时设置多个CMD的话只有最后一个会生效

· ENTRYPOINT:类似于CMD指令用于指定容器默认运行程序,但是该指令无法被docker run的命令所覆盖,而是将docker run的命令作为ENTRYPOINT的参数,除非加了--entrypoint选项。所以一个命令需要接受参数的话就用ENTRYPOINT,该命令通常写为EXEC格式的,如果执行的命令包含变量则要显示使用sh来执行,如["sh","-c","echo $NAME"]

· USER:运行镜像时任何指令所使用的用户名和UID,默认的root用户有一定风险


2、Dockerfile优化建议

尽可能的选择体积小的初始镜像,如alpine;尽可能合并RUN指令;记得清理yum缓存和源码包;把变化的内容尽可能放在结尾


3、Dockerfile示例

vim /data/dockerfile1/Dockerfile

# Description: test image
FROM centos:lastest
RUN yum install nginx -y
ENV DOC_ROOT=/data/web/html
COPY index.html $DOC_ROOT            #引用变量
COPY yum.repos.d /etc/yum.repos.d/   #确保源文件在工作目录中,并且目录目录必须以/结尾
ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src #如果是本地tar包会解压到容器的目录中,URL的话则不会}
WORKDIR /usr/local/soft/             #使用WORKDIR指定目录
ADD nginx-1.15.2.tar.gz ./           #这里就使用了上面的WORKDIR
VOLUME /data/web                     #将容器中的/data/web持久化到宿主机
EXPOSE  80/tcp 80/udp                #将容器的80端口暴露给宿主机
USER  501                            #UID可以是任意数字,但必须在/etc/passwd中存在
CMD ["nginx","-g","daemon off;"]


4、运行docker image build命令创建镜像,指定Dockerfile文件路径时可以使用绝对路径或者相对路径,但只用指定到上一级目录就可以了,它会自动到目录中去寻找Dockerfile文件,这也是为什么上面提到文件名一定要正确的原因

docker build -t test:v1 -f /data/dockerfile .  
# -t指定镜像名,不指定的话默认为latest
# -f指定dockerfile路径,默认为当前目录中的Dockerfile
# .代表基于宿主本地的哪个目录进行构建,dockerfile里依赖的文件要存在于该目录中,如果该目录有大量不需要构造的文件,需要使用.dockerignore文件忽略指定文件


5、查看并运行新的镜像

docker images
docker run --name test --rm test:v1 ls /etc/yum.repos.d/


评论