心蓝的博客 心蓝的博客
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档

心蓝

人生苦短,我用python
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • pycharm激活码
  • docker基础
  • Dockerfile
    • 1. 镜像与容器的关系
    • 2. 什么是Dockerfile
    • 3. 构建一个web服务器
      • 3.1 web服务
      • 3.2 编写Dockerfile
      • 3.3 构建镜像
    • 4. Dockerfile指令详解
      • FROM
      • RUN
      • CMD
      • ENTRYPOINT
      • EXPOSE
      • ENV
      • ARG
      • ADD
      • COPY
      • VOLUME
      • WORKDIR
  • docker-compose
  • nginx介绍
  • nginx初学者指南
  • nginx如何处理一个请求
  • nginx服务名称详解
  • 使用nginx作为http负载均衡器
  • 配置HTTPS服务器
  • 技术
心蓝
2021-10-04
目录

Dockerfile

# Dockerfile

# 1. 镜像与容器的关系

image-20211001212226953

docker中镜像是层级结构的,我们可以通过命令docker history <id/name>查看镜像中的每一层的大小和内容。镜像是readonly的。容器从镜像启动时,docker会在镜像的最上层创建一个可写层,镜像本身保持不变。删除容器只是删除容器创建的可写层,因此创建和删除容器都很快。

所以我们在创建应用时会利用Dockerfile将只读文件提前构建在镜像中来提高容器的效率。

# 2. 什么是Dockerfile

Dockerfile 是一个文本文档,其中包含组装 Docker 映像的指令。当我们通过执行docker build 命令告诉 Docker 构建我们的镜像时,Docker 会读取这些指令,执行它们,并因此创建一个 Docker 镜像。

# 3. 构建一个web服务器

我们通过构建一个python版本的web服务器来学习如何构建一个镜像。

# 3.1 web服务

使用 Flask 框架创建一个简单的 Python 应用程序。在本地创建一个名为python-docker的目录,然后按照下面的步骤创建一个简单的web服务器。

er cd /path/to/python-docker
 pip3 install Flask
 pip3 freeze > requirements.txt
 touch app.py
1
2
3
4

然后在app.py文件中输入以下代码。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, Docker!'
1
2
3
4
5
6

# 3.2 编写Dockerfile

在项目的根目录中(上个步骤中创建的python-docker目录),创建一个名为的文件Dockerfile并在文本编辑器中打开该文件。

Dockerfile 的名称是什么?

用于 Dockerfile 的默认文件名是Dockerfile(没有文件扩展名)。使用默认名称允许您运行docker build命令而无需指定其他命令标志。

某些项目可能需要不同的 Dockerfiles 用于特定目的。一个常见的约定是将这些命名为Dockerfile.<something>或<something>.Dockerfile。然后可以通过 docker build命令上的--file(或-f速记)选项使用此类 Dockerfile。

一般情况建议使用默认文件名Dockerfile。

接下来,在Dockerfile 中添加一行,告诉 Docker 我们希望为我们的应用程序使用什么基础镜像。

FROM python:3.8-slim-buster
1

Docker 镜像可以从其他镜像继承。因此,我们将使用官方 Python 镜像,而不是创建我们自己的基础镜像,该镜像已经拥有运行 Python 应用程序所需的所有工具和包。

为了在运行其余命令时更容易,创建一个工作目录。这会指示 Docker 使用此路径作为所有后续命令的默认位置。通过这样做,我们不必输入完整的文件路径,而是可以使用基于工作目录的相对路径。

WORKDIR /app
1

通常,下载了用 Python 编写的项目后要做的第一件事就是安装第三方包。这可确保您的应用程序已安装其所有依赖项。

我们需要将requirements.txt文件放入镜像中。使用COPY命令来执行此操作。COPY命令有两个参数,第一个参数告诉 Docker 您想要复制到镜像中的文件,第二个参数告诉 Docker 您希望将该文件复制到何处。我们要把requirements.txt文件复制到我们的工作目录/app中。

COPY requirements.txt requirements.txt
1

一旦在镜像中拥有requirements.txt文件,我们就可以使用RUN命令来执行命令pip3 install。这与我们在本地机器上运行pip3 install完全相同,但这次模块安装到镜像中。

RUN pip3 install -r requirements.txt
1

此时,我们有一个基于 Python 3.8 版的镜像,并且我们已经安装了我们的依赖项。下一步是将我们的源代码添加到镜像中。 我们将使用COPY命令,就像上面对requirements.txt文件所做的那样。

COPY . .
1

COPY命令获取当前目录中的所有文件并将它们复制到映像中。现在,我们要做的就是告诉Docker,当我们的映像在容器中执行时,我们想要运行什么命令。我们使用CMD命令来做这个。注意,我们需要通过指定——host=0.0.0.0使应用程序在外部可见(即在容器外部可见)。

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
1

下面是完整的Dockerfile。

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
1
2
3
4
5
6
7
8
9
10
11
12

回顾一下,我们在本地机器上创建了一个名为python-docker的目录,并使用 Flask 框架创建了一个简单的 Python 应用程序。我们还使用requirements.txt文件来收集我们的需求,并创建了一个 Dockerfile,其中包含构建镜像的命令。Python 应用程序目录结构现在看起来像:

python-docker
|____ app.py
|____ requirements.txt
|____ Dockerfile
1
2
3
4

# 3.3 构建镜像

我们已经创建了我们的 Dockerfile,现在使用docker build命令来构建我们的镜像。docker build命令从Dockerfile和上下文来构建Docker镜像。构建的上下文是指定的PATH或URL中的一组文件。Dokcer构建过程可以访问位于此上下文中的任何文件。

docker build命令可接受一个--tag参数,用于设置镜像的名称和格式为name:tag

现在我们已经创建了我们的 Dockerfile,让我们构建我们的镜像。为此,我们使用docker build命令。该docker build命令从 Dockerfile 和“上下文”构建 Docker 镜像。构建的上下文是位于指定 PATH 或 URL 中的一组文件。Docker 构建过程可以访问位于此上下文中的任何文件。

标签用于设置图像的名称和格式为name:tag的可选标签。

build 命令可选地接受一个--tag标志。标签用于设置图像的名称和格式中的可选标签name:tag。简单起见我们暂时不使用可选项tag。如果您不传递tag,Docker 将使用“latest”作为其默认标签。

让我们构建我们的第一个 Docker 镜像。

 docker build --tag python-docker .
 [internal] load build definition from Dockerfile
 => transferring dockerfile: 203B
 [internal] load .dockerignore
 => transferring context: 2B
 [internal] load metadata for docker.io/library/python:3.8-slim-buster
 [1/6] FROM docker.io/library/python:3.8-slim-buster
 [internal] load build context
 => transferring context: 953B
 CACHED [2/6] WORKDIR /app
 [3/6] COPY requirements.txt requirements.txt
 [4/6] RUN pip3 install -r requirements.txt
 [5/6] COPY . .
 [6/6] CMD [ "python3", "-m", "flask", "run", "--host=0.0.0.0"]
 exporting to image
 => exporting layers
 => writing image sha256:8cae92a8fbd6d091ce687b71b31252056944b09760438905b726625831564c4c
 => naming to docker.io/library/python-docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

运行docker images命令查看我们当前本地的所有镜像。

$ docker images
REPOSITORY      TAG               IMAGE ID       CREATED         SIZE
python-docker   latest            8cae92a8fbd6   3 minutes ago   123MB
python          3.8-slim-buster   be5d294735c6   9 days ago      113MB
1
2
3
4

您应该看到至少列出了两个镜像。一个是base镜像3.8-slim-buster,另一个是我们刚刚构建的镜像python-docker:latest。

接下来通过这个镜像我们启动一个容器

$ docker run -d -p 5000:5000 python-docker:latest
2810011fd21029b3cd9817229e0b6cd16733a6d50855b9e6e0c04c1397e4afe9
1
2

现在通过http://127.0.0.1:5000就可以访问这个服务器了 。

# 4. Dockerfile指令详解

Dockerfile的指令不区分大小写。但是,约定是将它们大写,以便更容易地将它们与参数区分开来。

# FROM

Docker按顺序运行Dockerfile指令。一个Dockerfile 必须以FROM指令开始。FROM指令指定我们从哪个父镜像开始构建。例如:

FROM python:3.8-slim-buster
1

表示构建需要的基础镜像是python:3.8-slim-buster,后续的操作都是基于它。

# RUN

RUN用于执行命令行命令,有以下两种格式:

shell命令行格式

RUN <command>
# <command>等价于直接在终端执行shell命令
1
2

exec格式

RUN ["executable", "param1", "param2"]
# 例如
# RUN ["python3", "app.py"] 等价于 RUN python3 app.py
1
2
3

注意exec格式会以json数组的形式解析,所以必须使用双引号。

两种格式的主要区别是shell命令行格式默认调用命令shell,所以原生的shell命令最好使用这种格式。当有外部可执行文件时,使用exec格式。

# CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build 时运行

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

CMD命令有三种格式:

  • CMD ["executable","param1","param2"] (exec 格式,推荐使用)
  • CMD ["param1","param2"] (该写法是为 ENTRYPOINT 指令指定的程序提供默认参数)
  • CMD command param1 param2 (shell 格式)

# ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
1

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例:

假设已通过 Dockerfile 构建了 python-docker:test 镜像:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY . .
ENTRYPOINT ["python3", "-m" , "flask", "run"] # 定参
CMD ["--host=0.0.0.0", "--port=5000"] # 变参
1
2
3
4
5
6
7
8
9
10
11
12

1、不传参运行

docker run python-docker:test
1

容器内会默认运行以下命令,启动主进程。

python3 -m flask run --host=0.0.0.0 --port=5000
1

2、传参运行

docker run python-docker:test --host=0.0.0.0 --port=5001
1

容器内部会运行一下命令,启动主进程。

python3 -m flask run --host=0.0.0.0 --port=5001
1

注意:一个Dockerfile中至少要要有一个CMD或ENTRYPOINT命令。

# EXPOSE

仅仅只是声明端口。

作用:

  • 帮助镜像使用者理解这个镜像服务的监听端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

EXPOSE <端口1> [<端口2>...]
1

# ENV

设置环境变量,定义了环境变量,那么在后续构建的指令中,就可以使用这个环境变量。

格式:

ENV <key1>=<value1> <key2>=<value2>...
1

注意ENV设置的环境变量会一致存在于容器中,因此在某些情况下它会产生副作业,所以如果只是在构建过程中使用的环境变量可以使用ARG。

# ARG

ARG也可以设置环境变量,不过与ENV的作用于不同,ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

格式:

ARG <参数名>[=<默认值>]
1

# ADD

ADD指令从上下文目录中拷贝文件,目录到镜像中。格式如下:

ADD [--chown=<user>:<group>] <源路径>... <目标路径>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
1
2

ADD遵守如下规则:

  1. 源路径如果不是URL则必须在构建上下文目录下
  2. 如果源路径是可识别的压缩格式(identity、gzip、bzip2或xz)的本地tar归档文件,则会自动将其解压缩为一个目录。
  3. 如果目标路径以/结尾,目标路径被识别为目录,源路径内容会被拷贝到其中
  4. 当源路径有多个时,目标路径必须以/结尾
  5. 目标路径不以/结尾,它会被识别为普通文件
  6. 目标路径不存在时,它会自动创建

# COPY

COPY指令与ADD命令格式完全一致,最主要的区别是:

  1. ADD 中的源路径可以是url(制定一个远程的文件或文件夹)这在Dockerfile是从标准输入中接收的时候非常有用。

  2. COPY中的源路径必须是在构建上下文路径中。

  3. COPY不会自动解压压缩文件

虽然ADD和COPY在功能上相似,但一般来说,COPY是首选。这是因为它比ADD更透明。 COPY仅支持将本地文件基本复制到容器中,而ADD有一些特性(如仅本地的tar提取和远程URL支持)不是很明显。因此,ADD的最佳用途是将本地tar文件自动提取到映像中,如ADD rootfs.tar.xz /。

如果您有多个Dockerfile步骤,它们使用来自您的上下文中不同的文件,请分别复制它们,而不是一次复制所有文件。这确保了只有当特定要求的文件发生更改时,每个步骤的构建缓存才会失效(强制重新运行该步骤)。

# VOLUME

创建挂着点。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失。
  • 避免容器不断变大。

格式:

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

在启动容器 docker run 的时候,我们可以通过 -v 参数指定卷。

# WORKDIR

WORKDIR指令为Dockerfile中跟随它的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令设置工作目录。如果WORKDIR不存在,即使没有在任何后续Dockerfile指令中使用它,也会创建它。WORKDIR指令可以在Dockerfile中多次使用。如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径。For example:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
1
2
3
4

这个Dockerfile中最后一个pwd命令的输出是/a/b/c。

更多命令见官方文档 (opens new window)

本文完,感谢你的耐心阅读,如有需要可加我微信,备注「博客」并说明原因,我们一起进步,下次见。

#docker
上次更新: 2023/04/16, 21:34:38
docker基础
docker-compose

← docker基础 docker-compose→

最近更新
01
requests让接口测试如此简单 原创
03-31
02
最简明的python正则教程
03-30
03
pycharm激活码
12-30
更多文章>
Theme by Vdoing | Copyright © 2019-2025 心蓝
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式