找回密码
 立即注册
首页 业界区 业界 Docker镜像

Docker镜像

旱由 昨天 15:57
Docker 镜像是一个轻量级、可执行的独立软件包,它包含了运行某个软件所需的一切:代码、运行时环境、库、环境变量和配置文件。它是一切容器运行的基石。
1、核心概念

1.1 分层存储 (Union File System)

这是 Docker 镜像最核心的特性。
只读层 (Read-only Layers): 一个镜像由多个只读层叠加而成。每一层代表 Dockerfile 中的一条指令。例如:
  1. FROM ubuntu:22.04 # 会创建一层。
  2. RUN apt-get update && apt-get install -y nginx # 会创建新的一层。
  3. 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 创建一个项目目录
  1. mkdir my-nginx-app
  2. cd my-nginx-app
复制代码
2.2.2 创建一个自定义的 index.html
  1. echo "<h1>Hello, Docker Image Tutorial!</h1>" > index.html
复制代码
2.2.3 创建 Dockerfile
  1. # 使用官方 Nginx 镜像作为基础层 (Base Layer)
  2. FROM nginx:alpine
  3. # 维护者信息(已弃用,可用 LABEL 替代)
  4. LABEL maintainer="your.email@example.com"
  5. # 删除默认的欢迎页面
  6. RUN rm /usr/share/nginx/html/index.html
  7. # 将宿主机的 index.html 文件复制到镜像的指定路径
  8. # 这一步会创建一个新的镜像层
  9. COPY index.html /usr/share/nginx/html/
  10. # 暴露容器运行时监听的端口
  11. EXPOSE 80
  12. # 容器启动时执行的命令
  13. # 因为基础镜像 nginx:alpine 已经定义了启动 nginx 的命令,所以这里可以省略
  14. # CMD ["nginx", "-g", "daemon off;"]
复制代码
2.3 构建镜像

使用 docker build命令根据 Dockerfile 构建镜像
  1. docker build -t my-custom-nginx:1.0 .
  2. # -t my-custom-nginx:1.0 指定镜像名为 my-custom-nginx,标签为 1.0
  3. # . 表示 Dockerfile 和构建上下文(index.html文件)在当前目录
复制代码
构建过程输出:
  1. Sending build context to Docker daemon  3.072kB
  2. Step 1/5 : FROM nginx:alpine
  3. alpine: Pulling from library/nginx
  4. ... (下载 nginx:alpine 的各个层)
  5. Step 2/5 : LABEL maintainer="your.email@example.com"
  6. ---> Running in a1b2c3d4e5f6
  7. ---> 8a7b6c5d4e3f (新生成一层的ID)
  8. Step 3/5 : RUN rm /usr/share/nginx/html/index.html
  9. ---> Running in f5e4d3c2b1a0
  10. ---> 1a2b3c4d5e6f (新生成一层的ID)
  11. Step 4/5 : COPY index.html /usr/share/nginx/html/
  12. ---> a1b2c3d4e5f6 (新生成一层的ID)
  13. Step 5/5 : EXPOSE 80
  14. ---> Running in 1234567890ab
  15. ---> abcdef123456 (最终镜像的ID)
  16. Successfully built abcdef123456
  17. 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"]
例子:
  1. RUN apt-get update && apt-get install -y \  
  2. bzr \
  3. cvs \
  4. git \
  5. mercurial \
  6. 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 片段
  1. ENTRYPOINT ["/bin/echo", "Hello"]  
  2. 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 等轻量级镜像
  1. # 好的实践
  2. FROM node:16-alpine  # 约100MB
  3. # 不推荐(过大)
  4. FROM node:16         # 约900MB
复制代码
4.2 合理组织指令顺序

将频繁变动的文件放在后面,利用缓存
  1. # 好的实践
  2. FROM node:16-alpine
  3. WORKDIR /app
  4. COPY package*.json ./  # 不常变动,先复制以利用缓存
  5. RUN npm install
  6. COPY . .               # 经常变动,放在后面
复制代码
4.3 清理不必要的文件

在同一层清理构建依赖
  1. # 好的实践
  2. RUN apt-get update && \
  3.     apt-get install -y gcc && \
  4.     # 编译操作... && \
  5.     apt-get remove -y gcc && \
  6.     apt-get clean && \
  7.     rm -rf /var/lib/apt/lists/*
复制代码
4.4 合并 RUN 指令

将多个 RUN指令通过 &&连接成一个,减少镜像的层数,从而减小体积。
  1. # 不推荐
  2. RUN apt-get update
  3. RUN apt-get install -y package
  4. # 推荐
  5. RUN apt-get update && apt-get install -y package
复制代码
4.5 多阶段构建

用于需要编译的应用程序(如 Go, Java)。它允许你在一个 Dockerfile 中使用多个 FROM 语句,并且可以选择性地将前一阶段的构建产物复制到后一阶段,而丢弃不需要的庞大编译环境和中间文件。
  1. # 第一阶段:构建阶段
  2. FROM golang:1.19 AS builder
  3. WORKDIR /app
  4. COPY . .
  5. RUN go build -o myapp .
  6. # 第二阶段:运行阶段
  7. FROM alpine:latest
  8. WORKDIR /root/
  9. # 从 builder 阶段只复制编译好的二进制文件
  10. COPY --from=builder /app/myapp .
  11. CMD ["./myapp"]
复制代码
4.6 使用 .dockerignore 文件

类似于 .gitignore,它可以排除构建上下文中不需要的文件,避免它们被发送到 Docker 守护进程,加速构建过程,并避免将敏感文件意外打入镜像
  1. # 忽略版本控制文件
  2. .git
  3. .gitignore
  4. # 忽略依赖目录
  5. node_modules
  6. vendor
  7. # 忽略环境配置文件
  8. .env
  9. .env.local
  10. # 忽略日志和临时文件
  11. logs/
  12. tmp/
  13. # 忽略IDE配置
  14. .idea/
  15. .vscode/
  16. # 忽略构建产物
  17. dist/
  18. build/
复制代码
5、实战示例:构建一个 Python Flask 应用镜像

5.1 项目结构
  1. ```
  2. my-flask-app/
  3. ├── app.py
  4. ├── requirements.txt
  5. └── Dockerfile
  6. ```
复制代码
5.2 Dockerfile 内容
  1. ```dockerfile
  2. # 使用官方 Python 精简版基础镜像
  3. FROM python:3.9-slim
  4. # 设置元数据和维护者信息(推荐方式)
  5. LABEL maintainer="your-name@example.com"
  6. # 设置工作目录
  7. WORKDIR /app
  8. # 先将依赖文件复制到工作目录
  9. COPY requirements.txt .
  10. # 安装依赖
  11. RUN pip install --no-cache-dir -r requirements.txt
  12. # 复制应用代码
  13. COPY . .
  14. # 声明容器暴露的端口
  15. EXPOSE 5000
  16. # 定义环境变量
  17. ENV FLASK_APP=app.py
  18. ENV FLASK_RUN_HOST=0.0.0.0
  19. # 容器启动时运行命令
  20. CMD ["flask", "run"]
  21. ```
复制代码
5.3 构建与运行
  1. ```bash
  2. # 构建镜像
  3. docker build -t my-flask-app:latest .
  4. # 运行容器
  5. docker run -d -p 5000:5000 --name my-flask-container my-flask-app
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册