Dockerfile是一个文本文件,包含用户可以在命令行上调用的所有命令,用于构建Docker镜像。它定义了镜像的内容以及在容器启动时要执行的操作。 Dockerfile基本指令 以下是一些常用的Dockerfile指令:
- FROM: 指定基础镜像。所有Dockerfile都必须以FROM指令开始。
- 示例:FROM ubuntu:22.04 (使用Ubuntu 22.04作为基础镜像)
- 示例:FROM python:3.11-slim-bookworm (使用Python 3.11的轻量级版本,适用于Python应用)
- WORKDIR: 设置工作目录。后续的RUN, CMD, ENTRYPOINT, COPY, ADD指令都会在这个目录执行。
- 示例:WORKDIR /app
- COPY: 将本地文件或目录拷贝到镜像中。
- 示例:COPY . /app (将当前目录下的所有文件拷贝到镜像的/app目录)
- 示例:COPY requirements.txt /app/ (拷贝指定文件)
- ADD: 类似于COPY,但它支持从URL下载文件,并且可以自动解压压缩包(如tar, gzip, bzip2)。通常推荐使用COPY,因为它更透明。
- 示例:ADD http://example.com/software.tar.gz /app/
- RUN: 在镜像构建过程中执行命令。通常用于安装软件包、创建目录等。
- 示例:RUN apt-get update && apt-get install -y nginx
- 示例:RUN pip install -r requirements.txt
- EXPOSE: 声明容器运行时监听的端口。这仅仅是文档性质的,实际端口映射需要在docker run命令中使用-p或在Docker Compose文件中指定。
- 示例:EXPOSE 80 (声明容器将监听80端口)
- 示例:EXPOSE 80/tcp 443/udp (可以指定协议)
- ENV: 设置环境变量。这些环境变量在镜像构建时和容器运行时都可用。
- 示例:ENV PYTHONUNBUFFERED 1 (Python应用常用,禁用缓冲)
- 示例:ENV DATABASE_URL postgres://user:password@host:port/dbname
- ARG: 定义在构建时可以传递给Dockerfile的变量。它们在构建完成后就不再可用。
- 示例:ARG VERSION=1.0
- 示例:RUN echo “Building version $VERSION”
- CMD: 提供容器启动时默认执行的命令。如果docker run命令指定了其他命令,则CMD会被覆盖。一个Dockerfile中只能有一个CMD指令,如果有多个,只有最后一个生效。
- 示例:CMD [“python”, “app.py”] (推荐使用exec形式,参数是JSON数组)
- 示例:CMD python app.py (shell形式,命令会在shell中执行)
- ENTRYPOINT: 提供容器启动时始终执行的命令。ENTRYPOINT定义的命令不会被docker run指定的命令覆盖,而是作为docker run命令的参数。通常用于定义容器的主要用途。
- 示例:ENTRYPOINT [“nginx”, “-g”, “daemon off;”]
- 示例:ENTRYPOINT [“/usr/bin/supervisord”, “-c”, “/etc/supervisord.conf”]
- CMD与ENTRYPOINT的组合使用: 当ENTRYPOINT使用exec形式时,CMD可以为其提供默认参数。
- ENTRYPOINT [“ls”]
- CMD [“-l”]
- 容器启动时实际执行的命令是:ls -l
Dockerfile编写最佳实践
- 精简镜像: 使用更小的基础镜像(如Alpine版本或slim版本)。
- 多阶段构建 (Multi-stage builds): 将构建环境和运行环境分离,以减少最终镜像的大小。例如,在构建阶段编译代码,然后只将编译好的可执行文件拷贝到最终的运行时镜像中。
- 缓存利用: 将不常变化的指令放在前面,以便Docker能够有效利用构建缓存。例如,先拷贝requirements.txt并安装依赖,再拷贝应用代码。
- 链式操作: 将多个RUN命令合并为一个,减少镜像层数。使用&&连接命令,并在每行末尾使用\以便于阅读。
- 非root用户: 尽量使用非root用户运行容器,增加安全性。
- 环境变量: 使用ENV指令定义配置参数,而不是硬编码。 示例:一个简单的Python Flask应用Dockerfile 假设你有一个简单的Flask应用,文件结构如下:
my_flask_app/
├── app.py
├── requirements.txt
└── Dockerfile
app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello from Dockerized Flask App!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt:
Flask==2.3.3Dockerfile:
# 阶段1:构建阶段 - 使用一个包含Python的完整镜像来安装依赖
FROM python:3.11-slim-bookworm AS builder
# 设置工作目录(容器内)
WORKDIR /app
# 拷贝依赖文件,并安装依赖。将这一步放在拷贝应用代码之前,可以更好地利用Docker缓存。
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 阶段2:运行时阶段 - 使用一个更轻量级的镜像来运行应用
FROM python:3.11-slim-bookworm
# 设置工作目录
WORKDIR /app
# 从构建阶段拷贝安装好的依赖包
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
# 拷贝应用程序代码
COPY . .
# 暴露端口
EXPOSE 5000
# 设置环境变量,禁用Python的输出缓冲,这在容器日志中很有用
ENV PYTHONUNBUFFERED 1
# 容器启动时执行的命令
CMD ["python", "app.py"]构建和运行示例
- 构建镜像: 在my_flask_app目录下打开终端,执行: docker build -t my-flask-app:latest . -t用于给镜像命名和打标签,.表示Dockerfile在当前目录。
- 运行容器:
docker run -d -p 5000:5000 —name flask_container my-flask-app:lates
- -d:后台运行
- -p 5000:5000:将宿主机的5000端口映射到容器的5000端口
- —name flask_container:给容器命名
- 访问应用: 打开浏览器,访问http://localhost:5000,你应该能看到“Hello from Dockerized Flask App!”。