编写Dockerfile

Dockerfile编写指南

上期给大家讲了Docker入门,今天我们继续讲Dockerfile如何编写。

简介

Dockerfile是一个文本文件,它包含了一系列的指令,用来描述如何从一个基础镜像构建出一个新的镜像。每一条指令都会在基础镜像上创建一个新的层,并且对其进行一些修改,比如安装软件包、设置环境变量、复制文件、运行命令等。最终,所有的层会被合并成一个单一的镜像,这就是我们定制的目标镜像。

使用Dockerfile的好处有很多,比如:

  • 可以实现镜像的自动化构建和版本控制,方便重复使用和共享。
  • 可以减少镜像的大小和复杂度,提高镜像的可读性和可维护性。
  • 可以避免手动操作镜像的错误和不一致,保证镜像的质量和安全性。

语法和结构

Dockerfile的基本语法和结构如下:

  • 每一行代表一条指令,指令的格式为 INSTRUCTION arguments,其中 INSTRUCTION 是不区分大小写的关键字,arguments 是指令的参数,可以有多个,用空格分隔。
  • 每一条指令都会在上一条指令的结果上创建一个新的层,并且对其进行修改,最终形成一个新的镜像。
  • 指令可以有多种形式,比如:
    • # 开头的行表示注释,会被忽略。
    • FROM 指定基础镜像,必须是第一条指令,可以有多个,表示多阶段构建。
    • LABEL 添加元数据,比如作者、版本、描述等,可以有多个,每个 LABEL 是一个键值对,用等号连接。
    • RUN 执行命令,可以有多个,每个 RUN 可以执行多个命令,用 && 连接,或者用反斜杠换行。
    • CMD 指定容器启动时的默认命令,可以有多个,但只有最后一个会生效,可以被 docker run 的参数覆盖。
    • ENTRYPOINT 指定容器启动时的固定命令,可以有多个,但只有最后一个会生效,不会被 docker run 的参数覆盖,而是会追加在后面。
    • EXPOSE 声明容器内部的端口,可以有多个,不会自动映射到宿主机,需要用 -p-P 参数指定。
    • ENV 设置环境变量,可以有多个,每个 ENV 是一个键值对,用空格分隔,或者用等号连接。
    • ADD 复制文件或目录到容器内部,可以有多个,可以是本地路径,也可以是URL或压缩包,如果是压缩包,会自动解压。
    • COPY 复制文件或目录到容器内部,可以有多个,只能是本地路径,不会自动解压。
    • WORKDIR 设置工作目录,可以有多个,后面的指令会在该目录下执行,如果目录不存在,会自动创建。
    • VOLUME 创建数据卷,可以有多个,用来持久化容器内部的数据,或者与宿主机或其他容器共享数据。
    • USER 设置用户,可以有多个,后面的指令会以该用户的身份执行,如果用户不存在,会报错。
    • ARG 定义构建参数,可以有多个,可以在 docker build 时用 --build-arg 参数传递,也可以有默认值。
    • ONBUILD 定义触发指令,可以有多个,当该镜像被用作其他镜像的基础镜像时,会执行该指令。
    • STOPSIGNAL 设置停止信号,只能有一个,用来终止容器,可以是信号名,比如 SIGKILL,也可以是信号值,比如 9
    • HEALTHCHECK 设置健康检查,只能有一个,用来检测容器是否正常运行,可以是 CMDCMD-SHELL,也可以是 NONE 表示不检查。
    • SHELL 设置默认的shell,只能有一个,用来执行后面的 RUNCMDENTRYPOINT 等指令,可以是任何可执行的程序,比如 /bin/bash/bin/sh/bin/powershell 等。
  • 指令的参数可以有多种形式,比如:
    • Shell形式,比如 RUN apt-get update && apt-get install -y curl,会在默认的shell中执行,可以使用环境变量和通配符等特性,但是也会有一些潜在的问题,比如空格、引号、转义符等。
    • Exec形式,比如 RUN ["apt-get", "update"],会直接执行指定的程序,不会经过shell的解析,可以避免一些潜在的问题,但是也会失去一些shell的特性,比如环境变量和通配符等。
    • JSON数组形式,比如 CMD ["echo", "Hello World"],会被解析为JSON数组,每个元素都是一个字符串,不会被转义或引用,一般用于 CMDENTRYPOINTRUN 等指令。
    • 键值对形式,比如 LABEL version="1.0",会被解析为一个键值对,键和值都是字符串,可以用引号包围,也可以不用,一般用于 LABELENVARG 等指令。

问题

编写Dockerfile时,有一些最佳实践和常见问题需要注意,比如:

  • 尽量使用官方的或者可信的基础镜像,避免使用不明来源或者过时的镜像,以保证镜像的质量和安全性。
  • 尽量使用精简的基础镜像,比如 alpinedebian-slim 等,以减少镜像的大小和复杂度,提高镜像的启动速度和运行效率。
  • 尽量合并多个 RUN 指令,使用 && 连接或者反斜杠换行,以减少镜像的层数和大小,避免产生无用的中间层和缓存。
  • 尽量删除多余的文件和缓存,比如 apt-get cleanrm -rf /var/lib/apt/lists/*

实践

以下作者给大家2个实例,帮助大家快速上手。

  1. Python

情景

  • 你想构建一个Python的Web应用,使用Flask框架和Gunicorn服务器。
  • 你的应用代码在 app 目录下,主文件是 app.py,依赖文件是 requirements.txt
  • 你的应用需要监听 8000 端口,并且使用 4 个工作进程。

编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用官方的Python3.9镜像作为基础镜像
FROM python:3.9

# 设置工作目录为/app
WORKDIR /app

# 复制当前目录下的所有文件到/app目录
COPY . /app

# 安装依赖包
RUN pip install -r requirements.txt

# 声明容器内部的端口
EXPOSE 8000

# 设置环境变量,指定Gunicorn的配置
ENV GUNICORN_CMD_ARGS="--bind=0.0.0.0:8000 --workers=4"

# 设置容器启动时的默认命令,运行Gunicorn服务器,加载app.py文件
CMD ["gunicorn", "app:app"]
  1. PHP-81

情景

  • 你想构建一个PHP-81的Web应用,使用example框架,这是一个在线文件管理系统。
  • 你的应用代码在GitHub上,需要下载并解压到/app目录,然后赋予所有文件可读写执行的权限。
  • 你的应用使用sqlite数据库,不需要指定端口。

编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用官方的PHP-81镜像作为基础镜像
FROM php:8.1

# 安装sqlite扩展
RUN docker-php-ext-install pdo_sqlite

# 设置工作目录为/app
WORKDIR /app

# 下载并解压Kodbox框架到/app目录
RUN wget https://github.com/someone/where/example/main.zip \
&& unzip main.zip \
&& mv kodbox-main/* . \
&& rm -rf main.zip kodbox-main

# 赋予所有文件可读写执行的权限
RUN chmod -Rf 777 ./*

# 声明容器内部的端口
EXPOSE 80

# 设置容器启动时的默认命令,运行PHP内置服务器,加载index.php文件
CMD ["php", "-S", "0.0.0.0:80", "-t", "/app", "index.php"]

总结

以上便是Dockerfile的简单入门,学习一门语言不会过于简单,还是要多下功夫。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023 xiaoguigui
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信