:::color3 问题:容器和镜像之间的关系是什么?如何将业务代码构建为容器镜像?容器镜像又是怎么存储和使用的呢?

:::


初识容器镜像


拉取镜像

  1. $ docker pull lyzhang1999/hello-world-flask:latest

查看本地镜像列表

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. lyzhang1999/hello-world-flask latest e2b1a18ed1c1 1 minutes ago 116MB

运行镜像

  1. $ docker run -d -p 8000:5000 lyzhang1999/hello-world-flask:latest
  2. c370825640b6b3669cae20f14e2684ec82b20e4980b329c02b47e47771c931fd
看到上面的输出说明我们成功启动了 hello-world-flask 镜像。
  • -d 代表“在后台运行容器”,同时它会输出容器 ID,这是运行容器的唯一标识。
  • -p 代表“将容器内的 5000 端口暴露到宿主机(本地的 8000 端口)”,这可以方便我们在本地进行访问。
现在,我们打开浏览器访问 localhost:8000。

进入容器内部

使用 docker ps 命令来查看当前运行中的容器列表,输出结果的 “c370825640b6” 即为容器 ID:
  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. c370825640b6 lyzhang1999/hello-world-flask:latest "python3 -m flask ru…" 1 hours ago Up 1 hours 0.0.0.0:8000->5000/tcp xenodochial_black
使用 docker exec [容器 ID] 进入容器内部:
  1. $ docker exec -it c370825640b6 bash
  2. root@c370825640b6:/app#
  • -it 的含义是“保持 STDIN 打开状态,并且分配一个虚拟的终端(Terminal)”。
你可以简单理解为,我们通过 SSH 登录到了容器内部,在当前终端下运行的所有命令都是基于容器内的。 例如,你可以在当前终端执行 ls 查看容器内的文件:
  1. $ root@c370825640b6:/app# ls
  2. Dockerfile __pycache__ app.py requirements.txt
列出和编辑文件的操作,事实上都是对容器的操作。

:::color2 容器和镜像的区别:

通俗地说,镜像是一个同时包含业务应用和运行环境的“系统安装包”,它需要运行起来之后才能提供服务,运行后镜像的“实例化”称为容器(Container)。你可以对同一个镜像实例化多次,产生多个独立的容器,这些容器拥有不同的容器 ID,不同的容器之间是相互隔离的。

:::

最后,要想停止运行中的容器呢,只需要使用 docker stop [容器 ID] 命令就可以了。
  1. $ docker stop c370825640b6

镜像是怎么构建出来的?

Dockerfile 文件,描述如何构建镜像文件

  1. # syntax=docker/dockerfile:1
  2. FROM python:3.8-slim-buster
  3. RUN apt-get update && apt-get install -y procps vim apache2-utils && rm -rf /var/lib/apt/lists/*
  4. WORKDIR /app
  5. COPY requirements.txt requirements.txt
  6. RUN pip3 install -r requirements.txt
  7. COPY . .
  8. CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
这个 Dockerfile 只有几行,看起来非常简单,但他代表了一种非常典型的镜像构建的命令。例如 FROM、COPY、RUN、CMD 等命令,它们是从上到下按顺序执行的。当然, Dockefile 还有很多其他命令,你只需要了解最常用的这几个命令就够了。
  • 第一行以 syntax 开头的是解析器注释,它与 Docker 构建镜像的工具 buildkit 相关,在一般情况,我都建议你使用 docker/dockerfile:1,它代表始终指向最新的语法版本。
  • FROM 命令,表示使用官方仓库的 python:3.8-slim-buster 镜像作为基础镜像。在我们熟悉的编程方法中,你可以理解为从该镜像继承。这个镜像已经安装了 Python3 和 Pip3 等所有的 Python 相关的工具和包,我们可以直接使用。
  • RUN 的含义是在镜像内运行指定的命令,这里我们为镜像安装了一些必要的工具
  • WORKDIR 的含义是镜像的工作目录,你可以理解为后续所有的命令都将以此为基准路径。这样,我们就可以在后续的命令中使用相对路径而不是完整路径了。
  • COPY 的含义是将本地的文件或目录复制到镜像内指定的位置。 - 第一个参数代表本地文件或目录,第二个参数代表要复制到镜像内的位置。 - 例如,第七行 COPY 表示,将本地当前目录下的 requirements.txt 文件复制到镜像工作目录 /app 中,文件命名同样为 requirements.txt。
  • 第十行 RUN 的含义是在镜像里运行 pip3 安装 Python 依赖。请注意,这些依赖将会被安装在镜像里而不是本地。
  • 接下来,第十行又出现了一个 COPY 命令,它的含义是将当前目录所有的源代码复制到镜像的工作目录 /app 下,复制目录的语法和我们之前提到的复制文件是类似的。
  • 最后一行 CMD 的含义是镜像的启动命令。在一个 Dockerfile 中,只能有一个 CMD 命令,如果有多个,那么只有最后一个 CMD 命令会起作用。
    • 例如,我们希望在镜像被运行时启动 Python Flask Web 服务器,并监听在特定主机上。CMD 的第一个参数 python3 是我们希望运行的可执行命令,后面的参数表示运行 python3 命令所需要的参数。
开始构建镜像: 在本地电脑的当前目录执行 ls 命令,确认我们刚才保存的 app.py、requirements.txt 以及 Dockefile 是否存在。
  1. $ ls
  2. Dockerfile app.py requirements.txt
在本机的当前目录下执行 docker build 命令,这样就可以开始制作镜像了。
  1. $ docker build -t hello-world-flask .
  • -t 代表的是我们的镜像名。还记得我们之前提到的镜像版本的概念吗?这里隐含了镜像版本,Docker 会默认用 latest 作为版本号,也就是说,hello-world-flask 与 hello-world-flask:latest 的写法是等价的。
  • 此外,你还需要注意最后面有一个 “.” ,这代表了构建镜像的上下文。现阶段你只需要知道这代表本地源码与执行 docker build 命令的相对位置即可。
执行这条命令时,Docker 会帮我们从官方镜像仓库拉取 python:3.8-slim-buster 镜像,并启动该镜像。接下来,该容器会依次执行我们在 Dockerfile 中书写的命令,例如 WORKDIR、COPY、RUN 等等。 构建好镜像之后,我们可以使用 docker images 命令来查看本地镜像。
  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. hello-world-flask latest 3b0803ab8c9c 1 hours ago 121MB
接下来我们使用 docker run 启动镜像,验证是不是已经成功把业务代码打包为容器镜像了。
  1. $ docker run -d -p 8000:5000 hello-world-flask:latest

构建容器镜像的基本套路

  1. 使用 FROM 命令指定一个已经安装了特定编程语言编译工具的基础镜像。 1. 你可以在官方镜像仓库找到你所需的任何基础镜像。例如,对于 Java 而言,你可以使用 eclipse-temurin:17-jdk-jammy,对于 Golang 而言,你可以使用 golang:1.16-alpine。
  2. 你可以在官方镜像仓库找到你所需的任何基础镜像。例如,对于 Java 而言,你可以使用 eclipse-temurin:17-jdk-jammy,对于 Golang 而言,你可以使用 golang:1.16-alpine。
  3. 使用 WORKDIR 命令配置一个镜像的工作目录,如 WORKDIR /app。
  4. 使用 COPY 命令将本地目录的源码复制到镜像的工作目录下,例如 COPY 。
  5. 使用 RUN 命令下载业务依赖,例如 pip3 install。如果是静态语言,那么要进一步编译源码生成可执行文件。
  6. 最后,使用 CMD 命令配置镜像的启动命令,也就是将你的业务代码启动起来。

共享镜像

使用 docke tag 重命名我们之前在本地构建的镜像
  1. $ docker tag hello-world-flask my_dockerhub_name/hello-world-flask
使用 docker push 把本地的镜像上传到 Docker Hub
  1. $ docker push my_dockerhub_name/hello-world-flask
成功上传后,其他人可以通过 docker pull 命令来拉取我们上传的镜像。