Dockerfile 学习文档

Dockerfile

在 CentOS 7 上安装 Docker 步骤:来源

  1. 设置存储库

    1
    2
    sudo yum install -y yum-utils
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    • 在这里出现了意外【Could not fetch/save url https://download.docker.com/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#7 - “Failed to connect to 2a03:2880:f10c:83:face:b00c:0:25de: 网络不可达”】
    • 配置 v2ray 并设置环境变量 export http_proxy="http://127.0.0.1:10809"export https_proxy="http://127.0.0.1:10809" 使得所有的 curl / yum 都通过 HTTP/HTTPS 代理访问
  2. 安装 Docker 引擎

    1
    sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
  3. 启动 Docker

    1
    sudo systemctl start docker
  4. 测试 Docker(或者说是 Docker 仓库能否正常使用)

    1
    $ docker run hello-world
    • 测试官方镜像仓库并不可用

      1
      2
      3
      4
      5
      6
      # docker run hello-world
      Unable to find image 'hello-world:latest' locally
      latest: Pulling from library/hello-world
      c1ec31eb5944: Retrying in 1 second
      docker: error pulling image configuration: download failed after attempts=6: dial tcp 103.246.246.144:443: connect: connection refused.
      See 'docker run --help'.
    • 创建一个为 Docker 准备的系统配置目录,而后创建一个 http 代理的配置文件,把 HTTP 和 HTTPS 代理配置填进去,然后重新加载系统配置文件并重启 Docker(为 Docker daemon 配置代理)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # mkdir -p /etc/systemd/system/docker.service.d
      # vim /etc/systemd/system/docker.service.d/http-proxy.conf

      [Service]
      Environment="HTTP_PROXY=http://127.0.0.1:10809"
      Environment="HTTPS_PROXY=http://127.0.0.1:10809"

      # systemctl daemon-reload
      # systemctl restart docker
    • 然后再测试,成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      $ docker run hello-world
      Unable to find image 'hello-world:latest' locally
      latest: Pulling from library/hello-world
      c1ec31eb5944: Pull complete
      Digest: sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6
      Status: Downloaded newer image for hello-world:latest

      Hello from Docker!
      This message shows that your installation appears to be working correctly.

      To generate this message, Docker took the following steps:
      1. The Docker client contacted the Docker daemon.
      2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
      (amd64)
      3. The Docker daemon created a new container from that image which runs the
      executable that produces the output you are currently reading.
      4. The Docker daemon streamed that output to the Docker client, which sent it
      to your terminal.

      To try something more ambitious, you can run an Ubuntu container with:
      $ docker run -it ubuntu bash

      Share images, automate workflows, and more with a free Docker ID:
      https://hub.docker.com/

      For more examples and ideas, visit:
      https://docs.docker.com/get-started/

一、 什么是 Dockerfile

​ Dockerfile 是一个用来构建 Docker 镜像的文本文件,它包含了一系列的指令和参数。每一条指令都会创建一个新的镜像层,并对镜像进行提交。这些指令描述了镜像的构建过程,包括基础映像、软件包安装、文件拷贝、环境变量设置等。

​ 通过编写 Dockerfile,你可以将应用程序、环境和依赖项打包成一个独立的容器镜像,使其可以在不同的环境和平台上运行,实现应用程序的可移植性和可扩展性。

​ Dockerfile 的基本结构包括以下几个部分:

  • FROM:使用 FROM 指令指定基础映像,作为构建镜像的起点。
  • RUN:在构建过程中在镜像中执行命令。
  • CMD:指定容器创建时的默认命令。
  • COPY:将文件或目录复制到镜像中。
  • ADD:将文件、目录或远程URL复制到镜像中。
  • ENV:在容器内部设置环境变量。
  • EXPOSE:声明容器运行时监听的特定网络端口。
  • WORKDIR:设置后续指令的工作目录。
  • USER:指定后续指令的用户上下文。
  • VOLUME:为容器创建挂载点或声明卷。

​ 总的来说,Dockerfile 是定义和构建 Docker 镜像的文本文件,通过编写指令和参数来描述镜像的构建过程和配置,以实现应用程序的打包和部署。它是使用 Docker 进行容器化开发和部署的重要工具。

二、 Dockerfile 的作用

​ Dockerfile 的主要作用是自动化构建 Docker 镜像。以下是 Dockerfile 的一些关键作用:

  1. 自动化镜像构建:Dockerfile 包含了创建 Docker 镜像所需的所有命令,这些命令在执行 docker build 命令时按顺序运行。

  2. 版本控制和共享:Dockerfile 可以被视为应用程序环境的文本表示,可以使用版本控制工具进行管理,并可以与团队成员共享。

  3. 可重复性:只要 Dockerfile 保持不变,无论在何处运行,都将创建相同的 Docker 镜像。

  4. 配置管理:Dockerfile 可以用来设置运行应用程序所需的环境变量,安装必要的软件包,运行初始化脚本等。

  5. 基础设施即代码:Dockerfile 是一种 “基础设施即代码” 的实现,它允许开发者将基础设施的配置信息编码到文本文件中,从而实现基础设施的版本控制、审计和重用。

​ 总的来说,Dockerfile 的作用是自动化 Docker 镜像的构建过程,使得应用程序的部署更加快速、一致和可重复。它是 Docker 的核心组件,对于容器化应用程序的开发和部署至关重要。

三、 常用命令

  1. FROM:用于指定基础镜像

    1
    2
    FROM [基于的镜像名称]:[版本(tag)]
    *版本(tag)可不写,默认latest
  2. 注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大,例如:

    1
    2
    3
    4
    FROM centos
    RUN yum -y install wget
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
    RUN tar -xvf redis.tar.gz

    以上执行会创建 3 层镜像。可简化为以下格式:

    1
    2
    3
    4
    FROM centos
    RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

    如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

  3. WORKDIR:指定接下来的 shell 语句运行在哪个目录下(可自动创建)

    1
    WORKDIR /opt/dockerfile
  4. COPY:将我们当前机器的文件 copy 到 docker 镜像中去

    1
    2
    COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
    COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
    • **<源路径>**:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

      1
      2
      3
      4
      5
      # 将所有以 hom 开头的文件复制到镜像的 /mydir/ 目录下
      COPY hom* /mydir/

      # 将所有名字为 hom 后跟 一个任意字符 并以 .txt 结尾的文件复制到镜像的 /mydir/ 目录下。这里的 ? 是一个通配符,它只匹配一个字符
      COPY hom?.txt /mydir/
    • **<目标路径>**:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

  5. ADD:和 COPY 相似(且相同需求下官方更推荐 RUN),不同之处如下

    • ADD 优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>
    • ADD 缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定
    • 总结:不能直接复制 .tar 的文件,但是可以在 .tar.gz 的文件解压的时候自动复制里面的 .tar 文件
  6. RUN:执行后面跟着的命令行命令

    • shell 格式:

      1
      2
      RUN <命令行命令>
      # <命令行命令> 等同于,在终端操作的 shell 命令。
    • exec 格式:

      1
      2
      3
      RUN ["可执行文件", "参数1", "参数2"]
      # 例如:
      # RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
  7. CMD:和 RUN 相似(但是 RUN 是在构建的时候就会运行,而 CMD 只会在容器真正运行的时候才会运行的脚本),但是 CMD 是为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 RUN 中指定要运行的程序所覆盖

    • 注:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效
    1
    2
    3
    CMD <shell 命令>
    CMD ["<可执行文件或命令>","<param1>","<param2>",...]
    CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

    ​ 推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh

  8. ENTRYPOINT:和 CMD 类似

  9. ENV:设置环境变量(如果定义了环境变量,那么在后续的指令中就可以使用这个环境变量)

    1
    2
    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...

    eg. 设置 NODE_VERSION = 7.2.0,在后续的指令中可以通过 $NODE_VERSION 引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 设置环境变量 NODE_VERSION 并赋值为 7.2.0 
    ENV NODE_VERSION 7.2.0

    # 使用 curl 通过 Nodejs 官方那里下载 NODE_VERSION 版的 Node.js 二进制包和对应的 SHA256 校验和文件
    # -S 表示只有在错误的时候显示信息
    # -L 表示自动追随新链接并进行下载
    # -O 表示保存到本地
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
  10. ARG:和 ENV 作用一致,但是作用域不一样
    ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量
    构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖

    1
    ARG <参数名>[=<默认值>]
  11. VOLUME:指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据

    1
    2
    VOLUME ["<路径1>", "<路径2>"...]
    VOLUME <路径>

    eg.

    1
    VOLUME /data
    • 这里的 /data 目录就会在容器运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化

    在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点:

    1
    $ docker run -d -v mydata:/data xxxx
    • 在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置
  12. EXPOSE:声明容器运行时提供服务的端口,但仅仅只是声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。
    在 Dockerfile 中写入这样的声明有两个好处:

    • 一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
    • 另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    1
    EXPOSE <端口1> [<端口2>...]
    • 注意:要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
  13. USER:指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)

    1
    USER <用户名>[:<用户组>]
    • 注:如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。官方文档建议使用 gosu
  14. HEALTHCHECK:用于指定某个程序或者指令来监控 docker 容器服务的运行状态

    1
    2
    3
    4
    5
    HEALTHCHECK [选项] CMD <命令> # 设置检查容器健康状况的命令

    HEALTHCHECK NONE # 如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

    HEALTHCHECK [选项] CMD <命令> # 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

    当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy

    • HEALTHCHECK 支持下列选项:

      • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;

      • --timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;

      • --retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

    • 注意:和 CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

  15. ONBUILD:用于延迟构建命令的执行(官方文档:为他人做嫁衣裳)

    • 简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令
    1
    ONBUILD <其它指令>

    eg. 我们要制作 Node.js 所写的应用的镜像。我们都知道 Node.js 使用 npm 进行包管理,所有依赖、配置、启动信息等会放到 package.json 文件里。在拿到程序代码后,需要先进行 npm install 才可以获得所有需要的依赖。然后就可以通过 npm start 来启动应用。因此,一般来说会这样写 Dockerfile

    1
    2
    3
    4
    5
    6
    7
    FROM node:slim
    RUN mkdir /app
    WORKDIR /app
    COPY ./package.json /app
    RUN [ "npm", "install" ]
    COPY . /app/
    CMD [ "npm", "start" ]

    然后我们再在这个上面添加点东西,然后想要让别人使用我修改后的版本(我创建的基础镜像)
    假设这个基础镜像的名字为 my-node 的话,各个项目内的自己的 Dockerfile 就变为:

    1
    2
    3
    4
    5
    6
    7
    FROM node:slim
    RUN mkdir /app
    WORKDIR /app
    ONBUILD COPY ./package.json /app
    ONBUILD RUN [ "npm", "install" ]
    ONBUILD COPY . /app/
    CMD [ "npm", "start" ]

    就这样小小的把原来 COPY RUN COPY 三行的前面添加上 ONBUILD ,就能够实现“别人使用的软件是加了我所维护的功能的版本”

  16. LABEL:给镜像添加一些元数据(metadata),以键值对的形式

    1
    LABEL <key>=<value> <key>=<value> <key>=<value> ...

    我们还可以用一些标签来申明镜像的作者、文档地址等:

    1
    2
    3
    LABEL org.opencontainers.image.authors="AnitsuriW"

    LABEL org.opencontainers.image.documentation="https://docker.anitsuri.top"

Dockerfile 学习文档
https://moka.anitsuri.top/2024/07/31/Dockerfile/
作者
アニつり
发布于
2024年7月31日
许可协议