Dockerfile 使用 - 最佳实践和技巧

silverwq
2024-03-15 / 0 评论 / 73 阅读 / 正在检测是否收录...

概述

Docker 为我们提供的一个用于自定义构建镜像的一个配置文件:描述如何构建一个对象。

利用Docker提供的build命令,指定Dockerfile文件,就可以按照配置的内容将镜像构建出来。

构建镜像

commit

基于现有的容器,来构建一个新的镜像,我们可以修改nginx默认容器里的某些内容,比如改写配置,然后重新打包成镜像,这样的话新生成的镜像,下次运行容器的话,就是我们改好的那个,这个只要了解下即可,工作中用的少。

# -a 镜像作者 -m 镜像描述  nginx_commit是要打包成镜像的容器名称 打包成的镜像名称
docker commit -a "xiaoliu" -m "my nginx container" nginx_commit mynginx:latest

ltsfihhm.png

build

dockerfile的规则

# -t 表示基于dokcerfile来构建 .表示从当前目录找dockerfile
docker build -t myImageName:1.0.0 .

常用指令

FROM

指定以什么镜像作为基础镜像,在改进项的基础之上构建新的镜像,如果不想以任何镜像作为基础:FROM scratch,语法:

FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>

以上为三种写法,后两者为指定具体版本,第一种则使用latest也就是最新版

MAINTAINER(可选)

维护者,述这个镜像的作者,以及联系方式(可选),一般开源的软件才需要

MAINTAINER wuqiyin<silverwq@qq.com>

LABEL(可选)

为镜像设置标签,一个Dockerfile中可以配置多个LABEL,语法:

LABEL <key>=<value>

例如:

LABEL "example.label"="Example Label"
LABEL label-value=LAWEL
LABEL version="1.0.0"
LABEL description="可以写成多行,使用\
符号可以拼接多行的value"

ENV

设置容器的环境变量,可以设置多个,语法:

ENV <key> <value>
ENV <key>=<values <key>=<value> ...

两种语法的区别为:

  1. 第一种一次只能设置一个环境变量
  2. 第二种可ockerfile以一次设置多个

RUN

构建镜像的过程中要执行的命令,语法:

RUN <command>
RUN["executable","param1","param2","paramN"]

第一种写法就是直接写Shell脚本即可
第二种写法类似函数调用,第一个参数为可执行文件,后面的都是参数

ADD

复制命令,把主机中src的文件复制到镜像的dest位置,语法:

# ADD *.jar /app.jar 
# 可以相对目录,模糊匹配
ADD <src> <dest>
ADD ["<src>", "<dest>]

需要注意的是,如果add的是压缩包,则会自动解压

# 当前目录的解压到容器的/usr/local/src
ADD nginx-1.21.tar.gz  /usr/local/src

WORKDIR

设置容器中的工作目录,如果该目录不存在,那么会自已创建,然后进入该目录

VOLUME

设置挂载目录,可以将主机中的指定目录挂载到容器中,语法:

VOLUMET ["<dir>"]
VOLUME <dir>
VOLUME <dir> <dir>

例如

VOLUME /data

这里的挂载的/data目录就会在运行时自动挂载为匿名卷,任何向容器的 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。这个匿名卷不同于启动命令行里指定的匿名卷,容器被删除的时候,匿名卷也不会被删除

EXPOSE

该镜像运行容器后,需要暴露给外部的端口,但仅仅表示该容器内暴露了该端口,并不会与主机端口有映射关系,如果想将容器暴露的端口与主机映射则需要使用-p或-P参数来映射,可以暴露多个端口

语法:

EXPOSE <port>/<tcp/udp>

这个最好都要暴露端口,不然的话使用-P参数的时候,不会自动绑定

# -P参数不会自动绑定
docker run -d -P 自定义镜像名称

CMD

该镜像启动容器时默认执行的命令或参数

语法:

# 例如:CMD ["sh","-c","ping 127.0.0.1"]
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
# 例如:CMD ping 127.0.0.1
CMD <command> <param1> <param2>

以上为该命令的三种写法,第三种与普通Shel命令类似,第一、二两种都是可执行文件+参数的形式,另外数组内的参数必须使用双引号。

如果dockerfile里有多个cmd,只会最后一个生效,并不会报错

ENTRYPOINT

运行容器时的启动命令,感觉与CMD命令会很像,实际上还是有很大区别,简单对比一下:

相同点:

  1. 在整个Dockerfile中只能设置一次,如果写了多次则只有最后一次生效

不同点:

  1. ENTRYPOINT不会被运行容器时指定的命令所覆盖,而CMD会被覆盖,例如 docker run xxx /bin/bash,这里的/bin/bash会把cmd里的命令给覆盖了,但是entrypoint里的不会被覆盖
  2. 如果同时设置了这两个指令,且CMD仅仅是选项而不是参数,CMD中的内容会作为ENTRYPOINT的参数(一般不这么做)
  3. 如果两个都是完整命令,那么只会执行最后一条

语法:

ENTRYPOINT ["executable","param1","param2"]
# 例如:ENTRYPOINT ping 127.0.0.1
ENTRYPOINT <command> <param1> <param2>

ARG

设置变量,在镜像中定义一个变量,当使用docker build命令构建镜像时,带上 --build-arg <name>=<value> 来指定参数值,如果该变量名在Dockerile中不存在则会抛出一个警告

语法:

ARG <name>[=<default value>]

USER

设置容器的用户,可以是用户名或UID,如果容器设置了以daemon用户去运行,那么RUN、CMD和ENTRYPOINT都会以这个用户去运行,一定要先确定容器中有这个用户,并且拥有对应的操作权限。

语法:

USER <username>
USER <PID>

ONBUILD

表示在构建镜像时做某操作,不过不对当前Dockerfile的镜像生效,而是对以当前Dockerfile镜像作为基础镜像的子镜像生效,主要是为子镜像做一些操作

语法:

ONBUILD [INSTRUCTION]

例:
当前镜像为A,设置了如下指令 ONBUILD RUN ls -al
镜像B:FROM 镜像A

STOPSINNAL

STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。此信号可以是与内核的系统调用表中的位置匹配的有效无符号数,例如9,或SIGNAME格式的信号名,例如SIGKILL。

构建镜像默认的stop-signal是SIGTERM,在docker stop的时候会给容器内PID为1的进程发送这个signal,通过--stop-signal可以设置自己需要的signal,主要的目的是为了让容器内的应用程序在接收到signal之后可以先做一些事情,实现容器的平滑退出,如果不做任何处理,容器将在一段时间之后强制退出,会造成业务的强制中断,这个时间默认是10%。

STOPSIGNAL <signal>

HELATHCHECK

容器健康状况检查,可以指定周期检查容器当前的健康状况,该命令只能出现一次,如果有多次则只有最后一次生效。

语法:

THEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE

第一种:在容器内部按照指定周期运行指定命令来检测容器健康状况
第二种:取消在基础镜像

OPTIONS选项:

  1. --interval=DURATION 两次检查的间隔时间,默认30S
  2. --timeout=DURATION 命令执行的超时时间,默认30s
  3. --retries=N 当连续失败指定次数,容器会被认定为不健康,默认为3次

返回参数:

  1. 0表示success,监控状态
  2. 1表示failed,不健康状态
  3. 2表示reserved,保留值

dokerfile 样例

普通样例

# 1. 先指定当前镜像的基础镜像
ARG jdkversion=8 # docker built -t --build-arg jdkversion=11
FROM openjdk:$jdkversion

# 要提前新增用户
RUN user add wolfcode
#拓展:表示后续容器中所有执行命令的操作都是以wolfcode这个用户操作的
USER wolfcode


# 2.描述这个镜像的作者,以及联系方式(可选)
MAINTAINER wuqiyin<silverwq@qq.com>

# 3.镜像的标签信息(可选)
LABEL version="1.0"
LABEL description="这是我的第一个Dockerfile"

# 4. 环境变量
ENV JAVA_ENV=dev
ENV APP_NAME test-dockerfile

# 5. 在构建镜像的时候,需要执行的 shell 命令
RUN ls -al
RUN mkdir /www/dockerfile/test

# 6.将主机中的指定文件复制到容器的目标位置,可以简单理解为 CP 命令
ADD /usr/local/www /www

# 7.设置容器中的工作目录,如果该目录不存在,那么会自已创建,然后进入该目录
WORKDIR /app
# 在设置完工作目录后,执行pwd 命令,打印的目录就是/app
RUN pwd

# 8.镜像数据卷绑定,将主机中的指定目录挂载到容器中,也就说容器内部也会有个一模一样的目录,他们自己是相通的
VOLUME ["/usr/local/www"]

# 9.设置容器启动后要暴露的端口
EXPOSE 8080/tcp

# CMD和ENTRYPOINT选择其一即可,作用是描述镜像构建完成后,启动容器时默认执行的脚本
# CMD ping 127.0.0.1
CMD ["sh","-c","ping 127.0.0.1"]

# 健康检查,每隔10s,检查容器是否正常,每次检查的操作不能超过3s,并且如果失败了,最多不能超过3次
HEAlTHCHECK --interval=10 --timeout=3 --retries=5 CMD ps -ef | grep java | exit 1

nginx 源码构建

#基准镜像
FROM centos:7
#作者信息
LABEL version="nginx v1"
LABEL "emill"="243254384@qq.com"
#调整系统时间差
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
#工作目录
WORKDIR /usr/local/src/ 
#定义环境变量
ENV NG_VERSION nginx-1.21.0 
#安装epel仓库
RUN yum -y install epel-release 
#安装wget
RUN yum -y install wget 
#下载nginx文件并解压
RUN wget http://nginx.org/download/$NG_VERSION.tar.gz && tar xzvf $NG_VERSION.tar.gz 
#安装编译依赖包
RUN yum install -y gcc gcc-c++ glibc make autoconf openssl openssl-devel && yum install -y pcre-devel libxslt-devel gd-devel GeoIP GeoIP-devel GeoIP-data
#清理仓库
RUN yum clean all 
#创建nginx用户
RUN useradd -M -s /sbin/nologin nginx 
#切换工作目录
WORKDIR /usr/local/src/$NG_VERSION 
#编译安装nginx
RUN ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module && make && make install
#复制测试页面到容器中
# 本地需要有index.html文件
ADD index.html /usr/local/nginx/html 
#设置容器中要挂在到宿主机的目录
VOLUME /usr/local/nginx/html 
#设置sbin环境变量
ENV PATH /usr/local/nginx/sbin:$PATH 
#暴露80端口
EXPOSE 80/tcp 
ENTRYPOINT ["nginx"]
CMD ["-g","daemon off;"] # 不要守护进程方式运行,不然docker会没有进程占用,就会退出容器
#当ENTRYPOINT和CMD连用时,CMD的命令是ENTRYPOINT命令的参数,两者连用相当于nginx -g "daemon off;"而当一起连用的时候命令格式最好一致(这里选择的都是json格式的是成功的,如果都是sh模式可以试一下)

将上述dockerfile保存,然后执行

docker build -t wuqiyin_nginx:1.0.0 .
0

评论 (0)

取消