Docker 镜像是一个轻量级、可执行的独立软件包,它包含了运行某个软件所需的一切:代码、运行时环境、库、环境变量和配置文件。它是一切容器运行的基石。
1、核心概念
1.1 分层存储 (Union File System)
这是 Docker 镜像最核心的特性。
只读层 (Read-only Layers): 一个镜像由多个只读层叠加而成。每一层代表 Dockerfile 中的一条指令。例如:- FROM ubuntu:22.04 # 会创建一层。
- RUN apt-get update && apt-get install -y nginx # 会创建新的一层。
- COPY index.html /var/www/html/ # 又会创建新的一层。
复制代码 容器层 (Container Layer, 即可写层): 当你基于一个镜像启动容器时,Docker 会在所有只读层之上添加一个薄薄的可写层。所有对运行中容器的修改(如创建新文件、修改现有文件、安装新软件)都只发生在这个可写层中。
写时复制 (Copy-on-Write, CoW): 这是实现分层存储的关键策略。如果一个文件存在于较低的只读层,而容器想要修改它,Docker 会先将这个文件复制到可写层,然后进行修改。容器看到的是可写层中的文件版本,而只读层中的原始文件保持不变。
1.2 镜像与容器的关系
- 镜像 (Image): 是一个静态的模板,一个定义文件。类似于面向对象中的“类”。
- 容器 (Container): 是镜像的一个运行实例。类似于“类”的“实例化对象”。
一个镜像可以启动任意多个容器。每个容器都有自己的可写层和唯一的容器 ID。
2、Dockerfile
镜像是通过 Dockerfile 构建的。Dockerfile 是一个文本文件,里面包含了一系列用于构建镜像的指令。
2.1 常用指令及其功能
指令功能说明示例FROM指定基础镜像, 必须是第一条指令。FROM python:3.9-slimLABEL为镜像添加元数据(如维护者信息),推荐替代已废弃的 MAINTAINER。LABEL maintainer="your-name@example.com"RUN在构建过程中执行命令,常用于安装软件包。RUN apt-get update && apt-get install -y nginxCOPY将本地文件或目录复制到镜像中(推荐用于本地文件)。COPY ./app /appADD与 COPY 类似,但支持从 URL 下载文件或自动解压压缩包到镜像。ADD app.tar.gz /app/WORKDIR设置工作目录,后续的 RUN,CMD,ENTRYPOINT,COPY,ADD 等指令都会在此目录下执行。WORKDIR /appEXPOSE声明容器运行时监听的网络端口,但实际映射需在 docker run 通过 -p 参数指定。EXPOSE 80ENV设置环境变量,该变量在构建阶段和容器运行时均可用。ENV APP_ENV productionCMD指定容器启动时默认命令,可以被 docker run 后面参数覆盖。CMD ["python", "app.py"]ENTRYPOINT配置容器启动时的入口命令,使其像一个可执行文件,CMD 的内容作为参数传递给它。ENTRYPOINT ["/nginx","-g","daemon off;"]USER指定运行后续指令以及容器运行时的用户名或 UID。USER appuserARG定义构建时的变量,其值仅在构建阶段可用,不会保留到镜像中。ARG VERSION=1.02.2 示例
构建一个简单的 Nginx 镜像
2.2.1 创建一个项目目录
- mkdir my-nginx-app
- cd my-nginx-app
复制代码 2.2.2 创建一个自定义的 index.html
- echo "<h1>Hello, Docker Image Tutorial!</h1>" > index.html
复制代码 2.2.3 创建 Dockerfile
- # 使用官方 Nginx 镜像作为基础层 (Base Layer)
- FROM nginx:alpine
- # 维护者信息(已弃用,可用 LABEL 替代)
- LABEL maintainer="your.email@example.com"
- # 删除默认的欢迎页面
- RUN rm /usr/share/nginx/html/index.html
- # 将宿主机的 index.html 文件复制到镜像的指定路径
- # 这一步会创建一个新的镜像层
- COPY index.html /usr/share/nginx/html/
- # 暴露容器运行时监听的端口
- EXPOSE 80
- # 容器启动时执行的命令
- # 因为基础镜像 nginx:alpine 已经定义了启动 nginx 的命令,所以这里可以省略
- # CMD ["nginx", "-g", "daemon off;"]
复制代码 2.3 构建镜像
使用 docker build命令根据 Dockerfile 构建镜像- docker build -t my-custom-nginx:1.0 .
- # -t my-custom-nginx:1.0 指定镜像名为 my-custom-nginx,标签为 1.0
- # . 表示 Dockerfile 和构建上下文(index.html文件)在当前目录
复制代码 构建过程输出:- Sending build context to Docker daemon 3.072kB
- Step 1/5 : FROM nginx:alpine
- alpine: Pulling from library/nginx
- ... (下载 nginx:alpine 的各个层)
- Step 2/5 : LABEL maintainer="your.email@example.com"
- ---> Running in a1b2c3d4e5f6
- ---> 8a7b6c5d4e3f (新生成一层的ID)
- Step 3/5 : RUN rm /usr/share/nginx/html/index.html
- ---> Running in f5e4d3c2b1a0
- ---> 1a2b3c4d5e6f (新生成一层的ID)
- Step 4/5 : COPY index.html /usr/share/nginx/html/
- ---> a1b2c3d4e5f6 (新生成一层的ID)
- Step 5/5 : EXPOSE 80
- ---> Running in 1234567890ab
- ---> abcdef123456 (最终镜像的ID)
- Successfully built abcdef123456
- Successfully tagged my-custom-nginx:1.0
复制代码 2.4 ADD 与 COPY 的选择
尽管 ADD功能更多,但出于安全性和清晰度的考虑,建议优先使用 COPY 来复制本地文件,除非你确实需要 ADD的自动解压或从 URL 获取文件的功能。
2.5 RUN、CMD 和 ENTRYPOINT
- RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。
- CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。
- ENTRYPOINT 配置容器启动时运行的命令。
2.5.1 Shell 和 Exec 格式
Shell 格式
例如:
RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"
当指令执行时,shell 格式底层会调用 /bin/sh -c 。
Dockerfile片段:
ENV name LaoTie666
ENTRYPOINT echo "Hello, $name"
执行 docker run 将输出:
Hello, LaoTie666
注意:环境变量 name 已经被值 LaoTie666 替换。
Exec 格式
["executable", "param1", "param2", ...]
例如:
RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]
当指令执行时,会直接调用 ,不会被 shell 解析。
Dockerfile 片段:
ENV name LaoTie666
ENTRYPOINT ["/bin/echo", "Hello, $name"]
输出:
Hello, $name
注意:环境变量“name”没有被替换。
如果希望使用环境变量,照如下修改
ENV name LaoTie666
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
输出:
Hello, LaoTie666
总结:
CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。
2.5.2 RUN
RUN 指令通常用于安装应用和软件包。
RUN 在当前镜像的顶部执行命令,并通过创建新的镜像层。Dockerfile 中常常包含多个 RUN 指令。
两种格式
Shell 格式:RUN
Exec 格式:RUN ["executable", "param1", "param2"]
例子:- RUN apt-get update && apt-get install -y \
- bzr \
- cvs \
- git \
- mercurial \
- subversion
复制代码 注意:apt-get update 和 apt-get install 被放在一个 RUN 指令中执行,这样能够保证每次安装的是最新的包。如果 apt-get install 在单独的 RUN 中执行,则会使用 apt-get update 创建的镜像层,而这一层可能是很久以前缓存的。
2.5.3 CMD
CMD 指令允许用户指定容器的默认执行的命令。
此命令会在容器启动且 docker run 没有指定其他命令时运行。
如果 docker run 指定了其他命令,CMD 指定的默认命令将被忽略。
如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。
三种格式:
- Exec 格式:CMD ["executable","param1","param2"],这是 CMD 的推荐格式。
- CMD ["param1","param2"] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。
- Shell 格式:CMD command param1 param2
第二种格式 CMD ["param1","param2"] 要与 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是为 ENTRYPOINT 设置默认的参数。
Dockerfile片段
CMD echo "Hello world"
运行容器 docker run -it [image] 将输出:
Hello world
但当后面加上一个命令,比如 docker run -it [image] /bin/bash,CMD 会被忽略掉,命令 bash 将被执行:
root@10a32dc7d3d3:/#
2.5.4 ENTRYPOINT
ENTRYPOINT 指令可让容器以应用程序或者服务的形式运行。
ENTRYPOINT 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。
两种格式
- Exec 格式:ENTRYPOINT ["executable", "param1", "param2"] 这是 ENTRYPOINT 的推荐格式。
- Shell 格式:ENTRYPOINT command param1 param2
在为 ENTRYPOINT 选择格式时必须小心,因为这两种格式的效果差别很大。
Exec 格式
ENTRYPOINT 的 Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。
ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。
Dockerfile 片段- ENTRYPOINT ["/bin/echo", "Hello"]
- CMD ["world"]
复制代码 当容器通过 docker run -it [image] 启动时,输出为:
Hello world
而如果通过 docker run -it [image] laotie666 启动,则输出为:
Hello laotie666
Shell 格式
ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
3、镜像管理常用命令
命令说明示例docker images 或 docker image ls列出本地所有镜像docker imagesdocker pull :从仓库拉取镜像docker pull ubuntu:22.04docker push :将镜像推送到仓库docker push my-registry.com/my-app:1.0docker rmi 或 docker image rm 删除本地镜像docker rmi my-custom-nginx:1.0docker tag 给镜像打一个新标签docker tag my-app:latest my-app:v1.0docker history 查看镜像的构建历史和各层信息docker history my-custom-nginx:1.0docker inspect 获取镜像的详细信息(元数据)docker inspect nginx:alpinedocker save -o 将镜像保存为 tar 归档文件docker save -o my-app.tar my-app:latestdocker load -i 从 tar 归档文件加载镜像docker load -i my-app.tar4、Docker镜像优化
4.1 使用合适的基础镜像
优先选择 alpine 等轻量级镜像- # 好的实践
- FROM node:16-alpine # 约100MB
- # 不推荐(过大)
- FROM node:16 # 约900MB
复制代码 4.2 合理组织指令顺序
将频繁变动的文件放在后面,利用缓存- # 好的实践
- FROM node:16-alpine
- WORKDIR /app
- COPY package*.json ./ # 不常变动,先复制以利用缓存
- RUN npm install
- COPY . . # 经常变动,放在后面
复制代码 4.3 清理不必要的文件
在同一层清理构建依赖- # 好的实践
- RUN apt-get update && \
- apt-get install -y gcc && \
- # 编译操作... && \
- apt-get remove -y gcc && \
- apt-get clean && \
- rm -rf /var/lib/apt/lists/*
复制代码 4.4 合并 RUN 指令
将多个 RUN指令通过 &&连接成一个,减少镜像的层数,从而减小体积。- # 不推荐
- RUN apt-get update
- RUN apt-get install -y package
- # 推荐
- RUN apt-get update && apt-get install -y package
复制代码 4.5 多阶段构建
用于需要编译的应用程序(如 Go, Java)。它允许你在一个 Dockerfile 中使用多个 FROM 语句,并且可以选择性地将前一阶段的构建产物复制到后一阶段,而丢弃不需要的庞大编译环境和中间文件。- # 第一阶段:构建阶段
- FROM golang:1.19 AS builder
- WORKDIR /app
- COPY . .
- RUN go build -o myapp .
- # 第二阶段:运行阶段
- FROM alpine:latest
- WORKDIR /root/
- # 从 builder 阶段只复制编译好的二进制文件
- COPY --from=builder /app/myapp .
- CMD ["./myapp"]
复制代码 4.6 使用 .dockerignore 文件
类似于 .gitignore,它可以排除构建上下文中不需要的文件,避免它们被发送到 Docker 守护进程,加速构建过程,并避免将敏感文件意外打入镜像- # 忽略版本控制文件
- .git
- .gitignore
- # 忽略依赖目录
- node_modules
- vendor
- # 忽略环境配置文件
- .env
- .env.local
- # 忽略日志和临时文件
- logs/
- tmp/
- # 忽略IDE配置
- .idea/
- .vscode/
- # 忽略构建产物
- dist/
- build/
复制代码 5、实战示例:构建一个 Python Flask 应用镜像
5.1 项目结构
- ```
- my-flask-app/
- ├── app.py
- ├── requirements.txt
- └── Dockerfile
- ```
复制代码 5.2 Dockerfile 内容
- ```dockerfile
- # 使用官方 Python 精简版基础镜像
- FROM python:3.9-slim
- # 设置元数据和维护者信息(推荐方式)
- LABEL maintainer="your-name@example.com"
- # 设置工作目录
- WORKDIR /app
- # 先将依赖文件复制到工作目录
- COPY requirements.txt .
- # 安装依赖
- RUN pip install --no-cache-dir -r requirements.txt
- # 复制应用代码
- COPY . .
- # 声明容器暴露的端口
- EXPOSE 5000
- # 定义环境变量
- ENV FLASK_APP=app.py
- ENV FLASK_RUN_HOST=0.0.0.0
- # 容器启动时运行命令
- CMD ["flask", "run"]
- ```
复制代码 5.3 构建与运行
- ```bash
- # 构建镜像
- docker build -t my-flask-app:latest .
- # 运行容器
- docker run -d -p 5000:5000 --name my-flask-container my-flask-app
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |