初识容器
首先在机器上安装好docker,并且通过命令行确认状态。
尝试从官方镜像仓库拉取镜像
Docker 会默认从 docker.io 官方镜像仓库中搜索。
:latest指的是获取最新的版本,也可以手动指定版本号
1 | docker pull lyzhang1999/hello-world-flask:latest |
使用命令列出所有可用的镜像(images)
1 | docker images |
运行镜像
1 | docker run -d -p 8000:5000 lyzhang1999/hello-world-flask:latest |
-d 代表“在后台运行容器”,同时它会输出容器 ID,这是运行容器的唯一标识。
-p 代表“将容器内的 5000 端口暴露到宿主机(本地的 8000 端口)”,这可以方便我们在本地进行访问。
看到下面的输出说明我们成功启动了 hello-world-flask 镜像。
打开浏览器访问 localhost:8000,可以看到:
连接容器
首先查看运行中的容器列表,找到CONTAINER ID
1 | docker ps |
执行命令进入容器内部:
1 | docker exec -it c3f7297de428 bash |
-it 的含义是“保持 STDIN 打开状态,并且分配一个虚拟的终端(Terminal)”。可以简单理解为,我们通过 SSH 登录到了容器内部,在当前终端下运行的所有命令都是基于容器内的
返回的结果如下
尝试编辑容器内部的文件app.py,保存并退出
再次访问 http://localhost:8000 可以看到页面已经发生了变化
输入exit退出容器
1 | root@c3f7297de428:/app# exit |
最后可以输入以下命令停止容器
1 | docker stop [容器id] |
例如
关于容器和镜像
通俗地说,镜像是一个同时包含业务应用和运行环境的“系统安装包”,它需要运行起来之后才能提供服务,运行后镜像的“实例化”称为容器(Container)。你可以对同一个镜像实例化多次,产生多个独立的容器,这些容器拥有不同的容器 ID,不同的容器之间是相互隔离的。
进一步理解,你可以把容器比喻为虚拟机,虚拟机也是,彼此之间的数据和状态都是隔离的。
构建镜像
使用 Python 编写的 Flask Web 应用作为例子
复制以下代码,并保存为app.py
1 | from flask import Flask |
这段代码的含义非常简单,启动一个 Web 服务器,当接收到 HTTP 请求时,返回 “Hello, my first docker images!” 以及 HOSTNAME 环境变量。
接下来,创建 Python 的依赖文件 requirements.txt ,用它来安装依赖的 Flask 框架。你可以执行下面的命令来创建 requirements.txt 文件并将 Flask==2.2.2 内容写入该文件。
1 | echo "Flask==2.3.3" >> requirements.txt |
有了这两个文件,我们已经可以在本地启动这个 Python Web 应用了。
接下来,将这段最简单的 Python 业务代码制作成镜像。
我们需要一个文件来描述镜像是如何被构建的,这个文件叫做 Dockerfile。
将以下内容保存为 Dockerfile 文件。
1 | # syntax=docker/dockerfile:1 |
解释一下 Dockerfile 文件里的这几个命令
第一行以 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 命令所需要的参数。
准备构建第一个镜像
确保当前目录下有所需的文件
在当前目录下执行命令
1 | docker build -t hello-docker-flask . |
-t 代表的是镜像名。这里隐含了镜像版本,Docker 会默认用 latest 作为版本号,也就是说,hello-docker-flask 与 hello-docker-flask:latest 的写法是等价的。
此外,还需要注意最后面有一个 “.” ,这代表了构建镜像的上下文。
执行这条命令时,Docker 会帮我们从官方镜像仓库拉取 python:3.8-slim-buster 镜像,并启动该镜像。接下来,该容器会依次执行我们在 Dockerfile 中书写的命令,例如 WORKDIR、COPY、RUN 等等。
如果遇到fail to authorize的异常,需要指定用户和密码,并重启docker
查看本地镜像,并运行
1 | docker images |
使用命令查看容器启动情况
1 | docker logs [容器ID] |
可以看到容器启动正常
打开浏览器访问 localhost:8000,可以看到正常输出
共享镜像
为了能够在团队中共享镜像,需要先注册一个 Docker HUB 的账号,并且使用 docker login 登录,这和我们使用的 Git 工具类似。
接下来,使用 docke tag 重命名我们之前在本地构建的镜像。
1 | docker tag hello-world-flask my_dockerhub_name/hello-world-flask |
这里需要把 my_dockerhub_name 替换为你实际的 Docker Hub 账户名,也称为镜像仓库的名字
然后,我们就可以使用 docker push 把本地的镜像上传到 Docker Hub 了。
1 | docker push my_dockerhub_name/hello-world-flask |
成功上传后,其他人可以通过 docker pull 命令来拉取我们上传的镜像。
例如