• 【docker系列】逐行解析Nginx镜像Dockerfile(学习经典)


    文章目录

    我一直觉得学习程序最快的方式就是: 首先自学一些基本概念,然后去看看大牛是怎么写代码的,通过模仿大牛的写法然后领悟,是学习编程最快的方法。 可能有的朋友会说:哪有那么多大牛的代码给你看啊,请去github上面有很多的开源组织官方代码。我们学习Dockerfile也不例外,尽量使用官方资源去学习,看看人家是怎么写的,此文就和大家一起逐行解析nginx官方的镜像构建文件Dockerfile。本文中涉及的脚本都可以在 github:docker-nginx官方渠道获取。

    一、什么是Dockerfile

    Dockerfile是自动构建docker镜像的配置文件,将镜像构建过程通过指令的方式定义在Dockerfile中。配合docker build命令行可以实现自动化的Docker镜像的构建。

    FROM debian:bullseye-slim
    
    LABEL maintainer="NGINX Docker Maintainers "
    
    ENV NGINX_VERSION   1.20.2
    ENV NJS_VERSION     0.7.0
    ENV PKG_RELEASE     1~bullseye
    
    RUN set -x   #节省篇幅,主要是学习Dcokerfile语法,这里省略了若干行linux的shell脚本
    
    COPY docker-entrypoint.sh /
    COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
    COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
    COPY 30-tune-worker-processes.sh /docker-entrypoint.d
    ENTRYPOINT ["/docker-entrypoint.sh"]
    
    EXPOSE 80
    
    STOPSIGNAL SIGQUIT
    
    CMD ["nginx", "-g", "daemon off;"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上图是nginx:1.20.2版本docker镜像的Dockerfile,下面我们来逐行解析。

    二、解析nginx的Dockerfile语法

    我们在学习一门语言或文档语法的时候,最快的学习方式就是看别人是怎么写的。这里这个“别人”是谁就很重要,跟着臭棋篓子下棋越下越臭。所以学习Dockerfile语法,我们有必要找一个模范:大家可以去Dockerhub看一下那些开源软件官方提供的镜像,都可以找到对应的Dockerfile,看看别人是怎么写的。
    我们就以上文中的nginx:1.20.2版本docker镜像的Dockerfile( 官方提供的),我们来逐行解析它的语法及构建过程。

    FROM

    一般我们构建镜像的都需要一个基础的linux操作系统的发行版镜像,并且在此基础上我们构建自己的镜像。

    FROM debian:bullseye-slim
    
    • 1

    所以FROM指令的作用就是指定基础镜像,nginx这里使用的基础linux镜像是debian:bullseye-slim。其中debian:bullseye是debian的linux发行本操作系统的一个版本,版本名称叫做bullseye。slim通常是指这个镜像是该发行版本中的最小安装版本,因为我们构建完成的镜像是要在后续的持续集成过程中,以及仓库和docker服务器之间网络传播的,所以尽可能让镜像的构建结果size最小化。基础镜像的选择要着重考虑size的大小,满足linux基本功能及你的程序运行的前提下越小越好

    LABEL

    LABEL用于给当前镜像添加一些描述、解释性信息,如:当前镜像的维护人及联系方式等信息。用键值对的方式自定义,一行可以定义多个。

    LABEL = = = ...
    
    • 1

    也可以定义多行,如maintainer维护人信息,description镜像描述信息。如果描述信息一行写不下,可以用“”换行。Dockerfile语法中有一个指令叫做MAINTAINER,专门用于描述该镜像的维护人信息,但是现在已经不建议使用了,统一使用LABEL

    LABEL maintainer="NGINX Docker Maintainers "
    LABEL description="This is a Docker image 
    
    • 1
    • 2

    for nginx 1.20.2. "

    ENV

    ENV的作用是设置环境变量,该环境变量设置之后,可以在构建过程及容器运行时的shell脚本中使用该变量,使用方法如:${NGINX_VERSION}。学过JAVA的同学想想你的JAVA_HOME环境变量怎么设置的以及怎么使用的?ENV是同样的道理。只不过放到docker这里语法发生了变化而已,语法格式:ENV 环境变量KEY 环境变量Value

    ENV NGINX_VERSION 1.20.2
    ENV NJS_VERSION 0.7.0
    ENV PKG_RELEASE 1~bullseye
    
    • 1
    • 2
    • 3

    RUN

    RUN指令的作用就是执行linux的shell脚本,通过下图可以看到在shell脚本中可以使用通过ENV定义的环境变量。

    对于nginx镜像而言,RUN指令的作用就是执行一系列shell命令行(脚本)来完成nginx的安装。所以说要想掌握RUN指令的重点不在于RUN指令本身,关键在于:

    • 你会不会手动安装nginx?
    • 你会不会linux的shell脚本语法?
    • 你能不能把nginx的安装过程写成shell脚本?

    如果上面三个问题的答案都是肯定的,用RUN指令即可执行shell脚本完成软件的安装。这也是Dockerfile编写内容的核心所在,linux shell并不是本文要为大家说明的内容。

    COPY

    COPY指令的作用是将本地文件(执行镜像构建所在的服务器),拷贝到镜像文件中。语法是:COPY <本地文件路径>:<镜像文件路径>,镜像文件路径同时也是容器运行时文件系统的路径。

    COPY docker-entrypoint.sh /
    COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
    COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
    COPY 30-tune-worker-processes.sh /docker-entrypoint.d
    
    • 1
    • 2
    • 3
    • 4

    如果本地文件路径只有文件名,就是表示文件和Dockerfile在同一个目录下(相对路径语法)。即:下面的这些文件在同一个目录(这些文件在我上文给出的nginx官方Dockerfile连接中都可以看到)。

    另外可以使用WORKDIR 指令为 Dockerfile 中跟随它的任何 RUN、CMD、ENTRYPOINT、COPY、ADD 指令设置本地工作目录。这样上文提到的相对路径,就是相对WORKDIR指定路径的相对路径。但是通常情况下,不建议使用WORKDIR,因为很难保证执行构建的开发者工作主机的工作路径和Dockerfile编写者的工作路径都是一致的。

    WORKDIR  /root
    
    • 1

    ENTRYPOINT

    一个Dockerfile中如果定义多个ENTRYPOINT,只有最后一条ENTRYPOINT生效,并且每次启动docker容器,都会执行ENTRYPOINT指定的脚本。对于nginx:1.20.2而言,"/docker-entrypoint.sh"脚本中定义了nginx配置检查及nginx服务的启动指令。所以通常情况下ENTRYPOINT指定的脚本通常都是镜像内核心服务的启动脚本

    ENTRYPOINT ["/docker-entrypoint.sh"]
    
    • 1

    这个脚本中最后执行了nginx服务启动,需要配合CMD命令完成,参考下文的CMD命令。

    EXPOSE

    Docker 容器在运行时暴漏指定的网络端口,可用于容器端口映射,默认协议是 TCP。格式如下:

    EXPOSE <端口号>
    EXPOSE <端口号>/<协议>
    
    • 1
    • 2

    将容器端口暴露出去后,可以与宿主机的端口建立映射关系,这样可以通过访问宿主机的端口来访问容器内部的服务。比如同时在 TCP、UDP 上暴露容器的80端口。

    EXPOSE 80/tcp
    EXPOSE 80/udp
    
    • 1
    • 2

    STOPSIGNAL

    这个指令笔者也并不常用,我查询了docker-nginx在github上的issues里面给出的答案是,之所以加上STOPSIGNAL信号的目的是:避免docker容器停止后,nginx服务不能正确终止造成僵尸进程的存在。

    STOPSIGNAL SIGQUIT
    
    • 1

    CMD

    CMD指令也是用来执行linux命令或脚本,这一点和RUN指令是一致的。二者的区别在于

    • CMD指令是在执行docker run指令时被执行,也就是创建容器时执行;而RUN指令实在镜像构建的时候执行,即docker bulid时候执行。
    • 也正因为指令的执行期不同,RUN命名执行的写入操作被写入到镜像层。CMD指令执行结果包含写入操作,被写入到容器层。(可以参考我之前文章《镜像分层原理》学习理解)。
    • 所以CMD和ENTRYPOINT 指令有点相似,都是在创建容器时运行。需要注意的是:一旦Dockerfile中包含ENTRYPOINT指令,CMD指令就作为ENTRYPOINT指定的脚本的参数存在。参考下文格式语法。

    CMD包含三种格式:

    • 第一种为ENTRYPOINT指定的脚本传参,上文中的ENTRYPOINT脚本最后一行的exec "$@"就是在执行CMD传递的命令及参数。来完成nginx服务的启动。这种用法是各官方Dockerfile常用的做法(如:nginx、redis),就是在ENTRYPOINT指定的脚本中做一些配置准备工作,然后在ENTRYPOINT脚本的最后一行通过exec "$@"调用CMD命令进行容器服务的启动。

      CMD [“nginx”, “-g”, “daemon off;”]

    • 第二种 执行一个命令或shell脚本,可以传参,注意是双引号。与第一种格式语法是一样的,只是没有ENTRYPOINT指定脚本,所以不作为ENTRYPOINT指定脚本的参数存在。

      CMD [“executable”,“param1”,“param2”]

    • 第三种为普通的执行shell脚本的语法,如执行echo "This is a test." | wc -cshell命令行。笔者说:在Dockerfile中这种方法不要用,没必要知道为什么

      CMD echo “This is a test.” | wc -c

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    Vue自定义指令
    LeetCode刷题笔记【24】:贪心算法专题-2(买卖股票的最佳时机II、跳跃游戏、跳跃游戏II)
    05 数据操纵之插入数据 | OushuDB 数据库使用入门
    Java编程技巧:if-else优化实践总结归纳
    牛客刷题<19>使用3-8译码器实现逻辑函数
    Flink 程序剖析
    解决TP6使用PHPExcel导出excel文件时报错
    1.什么是闭包
    ElasticSearch介绍和基本用法(一)
    基于Spring Boot的健身房管理系统设计与实现
  • 原文地址:https://blog.csdn.net/m0_67401270/article/details/126080558