Docker制作自己的镜像

需求:

  • 基于docker创建在python 3.10.13上运行我们开发的Flask网站。

流程:

  1. 基于docker软件

  2. 在基础镜像基础上构建自定义镜像[ python 3.10.13+代码]

  3. 基于镜像创建容器+运行

安装 Docker

首先,确保你已经在你的系统上安装了Docker。你可以访问Docker的官方网站,按照适用于你的操作系统的指南来安装。

编写Dockerfile

在你的Flask项目目录中创建一个名为Dockerfile的文件,导出pip freeze > requirements.txt依赖,并添加以下内容:

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
# 使用官方Python 3.10.13镜像作为基础镜像  
FROM python:3.10.13

# 维护者信息
LABEL maintainer="jerry <jerry@qq.com>"

# 设置工作目录
WORKDIR /app

# 将当前目录内容复制到容器的/app内
COPY . /app

# 更新pip
RUN pip install --upgrade pip
# 设置为阿里云的源
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 安装任何需要的包
RUN pip install --no-cache-dir -r requirements.txt

# 对外暴露的端口号 不写也没关系
EXPOSE 5000

# 定义环境变量 举例
ENV NAME World

# 容器启动时运行的命令
CMD ["python", "app.py"]

Dockerfile文档

当你使用官方的Python镜像作为基础镜像时,你就不需要再指定一个基础的系统镜像了,因为Python镜像本身就是基于一个特定的操作系统镜像(比如Debian、Ubuntu或其他)构建的,并且已经包含了特定版本的Python解释器和其他常用的工具。

在这个简化的Dockerfile中,我们直接使用了官方的Python 3.10.13镜像作为基础,然后设置了工作目录,拷贝了应用文件,并安装了Flask。这样可以确保我们使用的是特定版本的Python,而无需担心如何在基础操作系统上安装它。

使用官方Python镜像的好处是它们经过了官方的维护和测试,确保了Python环境的稳定性和一致性。此外,这些镜像通常也比较小,因为它们针对Python环境进行了优化,不包含不必要的系统和软件包。这样可以使你的Docker镜像更小、更快构建,并且更容易维护。

构建镜像

在包含Dockerfileapp.py的目录中执行以下命令来构建自定义镜像:

1
docker build -t jerry/web1:1.0 .

这里的-t参数用于指定镜像的名称和标签。jerry/flask-app是镜像的名称,v1.0是标签,用于标识镜像的版本。.表示Dockerfile所在的当前目录。

注意:

  • 不需要指定-f Dockerfile,因为Docker默认会查找当前目录下的Dockerfile
1
docker build -t jerry/web1:1.0 . -f Dockerfil

这里是一个简化的过程:

  1. 你编写一个包含 FROM python:3.10.13 的 Dockerfile。
  2. 你运行 docker build -t your-image-name .(注意最后的点 . 表示 Dockerfile 所在的上下文路径)。
  3. Docker 开始构建你的镜像。
  4. Docker 检查本地是否有 python:3.10.13 这个基础镜像。
  5. 如果本地没有这个镜像,Docker 会自动从 Docker Hub 拉取 python:3.10.13
  6. 一旦基础镜像拉取完成,Docker 会继续按照 Dockerfile 中的指令构建你的镜像。

基于镜像创建容器+运行

使用以下命令来创建并运行容器:

1
2
3
4
5
6
7
8
# 创建并运行第一个容器,映射宿主机的80端口到容器的8000端口  
docker run -d -p 80:5000 --name jerry-flask1 jerry/web1:1.0

# 创建并运行第二个容器,映射宿主机的81端口到容器的8000端口 --no-cache不要用之前已构建的缓存
docker run -d -p 81:5000 --name jerry-flask2 jerry/web1:1.1 --no-cache

# 创建并运行第三个容器,映射宿主机的82端口到容器的8000端口 [python3 v2.py]自己指定的会覆盖Dockerfile的CMD
docker run -d -p 82:5000 --name jerry-flask3 jerry/web1:1.0 python3 v2.py

这样,我们就创建了三个基于同一个镜像的容器,每个容器都运行着我们的Flask应用,并通过不同的端口对外提供服务。

这里的参数解释如下:

  • -d:在后台运行容器。
  • -p 80:5000:将宿主机的80端口映射到容器的5000端口。这样,你就可以通过访问宿主机的80端口来访问你的Flask应用了。
  • --name my-flask-container:给容器指定一个名称,方便后续管理。
  • jerry/flask-app:v1.0:要运行的镜像名称和标签。

访问你的Flask应用

现在,你的Flask应用应该已经在Docker容器中运行了。你可以通过访问宿主机的IP地址和映射的端口(在这个例子中是http://<host_ip>:80)来查看你的Flask网站。

注意事项和拓展

  • 确保你的Flask应用的入口文件是app.py,并且它配置为在5000端口上运行。如果不是这样,你需要在Dockerfile中相应地调整CMD指令。
  • 如果你的Flask应用需要数据库或其他服务,你可以在Dockerfile中添加相应的安装和配置步骤,或者使用Docker Compose来管理多个容器和服务。
  • 你可以根据需要添加更多的环境变量、配置或优化步骤到Dockerfile中,以满足你的特定需求。
  • 使用Docker可以方便地扩展你的Flask应用。你可以通过运行多个容器实例来提高应用的可用性和性能。每个容器实例都是独立的,并且可以轻松地进行扩展或缩减。
  1. 容器中必须有前台进程,否则创建立即销毁。

Docker 容器在启动时需要一个前台进程来保持容器的运行状态。当容器内没有前台进程时,它会立即退出。这是因为 Docker 容器是基于进程的,它不会管理后台服务或守护进程。

关于你提到的 systemctl 命令,通常它用于管理系统的服务,而在 Docker 容器中并不适用,因为容器通常没有完整的系统服务管理能力。相反,你应该直接运行你的应用程序或服务的可执行文件,并确保它以前台模式运行。

对于 Nginx:

1
2
3
4
5
# 比如
systemctl start nginx # 创建即销毁
nginx -C /etc/nginx/myblog.conf -g ' daemon off; ' # 必须前台运行

nginx -g 'daemon off;'

这将使 Nginx 以非守护进程模式运行,作为前台进程保持容器的运行状态。

  1. docker run 的正确用法

    docker run 命令用于从指定的镜像创建并启动一个新的容器。它通常用于部署应用程序或服务。正确的用法包括指定镜像名称或ID,以及任何必要的运行参数和命令。

    例如:

  • docker run -ti --rm <image_name> bash

这个命令会做以下事情:

  • -t:分配一个伪终端(pseudo-TTY),以便可以交互式地运行容器。
  • -i:保持容器的标准输入(STDIN)打开,这样你可以与容器进行交互。
  • --rm:在容器退出后自动删除容器。
  • image_name:这是你要基于的镜像名称。
  • bash:这是在容器内部要运行的命令。在这个例子中,我们启动一个 bash shell。

这个命令用于从指定的 Docker 镜像启动一个新的容器实例,并在容器内部运行一个命令。

  1. docker exec 的正确用法

docker exec 命令用于在正在运行的容器内部执行一个命令。它允许你与正在运行的容器进行交互,例如运行额外的命令或脚本。

首先,你需要知道正在运行的容器的 ID 或名称。你可以使用 docker ps 命令来获取这个信息。

然后,你可以使用 docker exec 在该容器内执行命令。例如:

  • docker exec -ti <container_id_or_name> bash

这个命令用于在已经运行的 Docker 容器中执行一个命令。具体来说,docker exec 命令允许你在一个正在运行的容器内部启动一个额外的进程。

  • -t--tty:同样用于分配一个伪终端。
  • -i--interactive:保持 STDIN 开放。
  • docker exec 是用来在已经运行的容器中执行命令的。
  • docker run 是用来从指定的镜像创建并启动新的容器实例的。
1
2
3
4
5
6
7
# 进入正在运行的容器的bash环境  
docker exec -ti <container_id_or_name> bash

# 从指定的镜像进入bash环境
[root@wang demo]# docker run -ti <image_name> bash
root@e24fbb63e2a6:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
  1. 如果是Django呢?
1
2
RUN pip install --no-cache-dir -r requirements.txt
CMD ['python启动器路径','manage.py','runserver','0.0.0.0:9000']
  1. 切换目录

    在 Dockerfile 中,您可以多次使用 WORKDIR 指令来更改当前工作目录。每次使用 WORKDIR 时,它都会将后续的指令(如 RUNCOPY 等)设置在那个指定的目录下执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 进入/data目录  
WORKDIR /data

# 在/data目录下执行wget命令下载xxx.tgz文件
RUN wget http://xxx.com/xxx.tgz

# 在/data目录下执行tar命令解压xxx.tgz文件
RUN tar zxf xxx.tgz

# 进入/tmp目录
WORKDIR /tmp

# 在/tmp目录下执行mkdir命令创建qqq目录
RUN mkdir qqq

每个 WORKDIR 指令都会更改当前工作目录,而紧接着的 RUN 指令(或其他需要路径的指令)会在那个新的工作目录下执行。这种方式使得 Dockerfile 更加模块化和清晰,因为您可以明确知道每个指令在哪个目录下执行。

请注意,如果指定的目录不存在,WORKDIR 会自动创建该目录。所以,在例子中,如果 /data/tmp 目录在镜像中不存在,它们将被自动创建。

文笔流畅美,点赞纷至来;

内容引入胜,分享乐开怀;

佳作值得赏,打赏表心怀。