【Dockerv1】高频十大面试题&&基础知识&&常用指令

📋 文档内容概览:

第一部分: 高频面试题 (10题)

  1. Docker核心概念
  2. Docker vs 虚拟机
  3. 网络模式详解
  4. Dockerfile指令对比
  5. 数据持久化方案
  6. 镜像优化技巧
  7. 容器间通信
  8. Docker Compose
  9. 分层存储原理
  10. 故障排查方法

1. 什么是Docker?Docker的核心概念是什么?

答案:

Docker是一个开源的容器化平台,用于自动化应用程序的部署、扩展和管理。它将应用程序及其依赖项打包到一个可移植的容器中。

核心概念:

  • 镜像(Image): 只读模板,包含运行应用所需的代码、运行时、库、环境变量和配置文件
  • 容器(Container): 镜像的运行实例,是一个独立的可执行软件包
  • 仓库(Repository): 存储和分发镜像的地方,如Docker Hub
  • Dockerfile: 用于构建镜像的文本文件,包含一系列指令
  • Docker Engine: Docker的核心组件,负责创建和运行容器

2. Docker与虚拟机的区别?

答案:

特性 Docker容器 虚拟机
启动速度 秒级启动 分钟级启动
性能 接近原生性能 有一定性能损耗
资源占用 MB级别 GB级别
隔离级别 进程级隔离 操作系统级隔离
操作系统 共享宿主机内核 每个VM有独立OS
可移植性 强,一次构建到处运行 较弱,依赖虚拟化平台

详细说明:

  • Docker容器共享主机操作系统内核,只打包应用及其依赖
  • 虚拟机包含完整的操作系统,运行在Hypervisor之上
  • Docker更轻量级,适合微服务架构
  • 虚拟机提供更强的隔离性,适合需要完全隔离的场景

3. Docker的网络模式有哪些?

答案:

Docker支持以下网络模式:

:::tips

1 网络命名空间

每个容器就像一个独立的小房间,有自己的网络设备。

  • 网卡:计算机与网络之间的物理接口 ,负责将计算机的数据转换为网络信号(电信号/光信号/无线电波),并在网络中传输。<font style="color:rgba(0, 0, 0, 0.9);">ifconfig</font>方式查看
  • IP地址:设备在网络中的逻辑地址,用于在网络层标识设备位置,实现跨网络通信。
  • 端口:端口是应用程序的逻辑标识,用于区分同一IP地址上的不同网络服务。IP地址找到主机,端口找到主机上的具体服务。
2 为什么需要Docker网络?

docker网络让容器之间可以相互通信、访问外网、以及让外部访问容器

:::

1. Bridge模式(默认)

  • 容器连接到docker0虚拟网桥
  • 容器间可以通过IP通信
  • 通过NAT访问外网
  • 适用场景: 单机容器间通信

:::tips

  • 外部访问容器A的nginx服务:docker run -d -p 8080:80 --name web nginx===>在宿主机的8080端口访问容器内部网络的80端口(容器内nginx的服务端口)

:::

2. Host模式

  • 容器与宿主机共享网络命名空间
  • 容器直接使用宿主机IP和端口
  • 性能最好,但端口冲突风险高
  • 适用场景: 需要高性能网络的应用

:::tips

  • 优点:性能最好,不需要端口映射,容器直接使用宿主机端口
  • 缺点:端口冲突、安全性较低
  • 运行:docker run -d --network host --name web nginx===>直接在宿主机的80端口访问

:::

3. None模式

  • 容器有独立网络命名空间,但不配置网络
  • 需要手动配置网络
  • 适用场景: 自定义网络配置

:::tips

docker run -d --network none --name isolated alpine sleep 3600

:::

4. Container模式

  • 新容器与已存在容器共享网络命名空间
  • 适用场景: 容器间需要紧密网络耦合

:::tips

先启动容器A:docker run -d --name nginx nginx;再启动容器B,共享容器A的网络:docker run -d --network container:nginx --name php php-fpm====>在php容器中可以用localhost:80访问nginx

:::

5. 自定义网络(Overlay/Macvlan)

  • 为什么要使用自定义网络?
    • 默认bridge网络的问题:容器只能用IP通信,不能用容器名。可是IP在容器重启后会变化。
    • 自定义网络的优势:可以用容器名通信、更好隔离、更灵活的配置。

:::tips

:::

bash 复制代码
# 1. 创建自定义网络
docker network create mynet
# 2. 在自定义网络中启动容器
docker run -d --name web --network mynet nginx # -d 在后台运行
docker run -d --name cache --network mynet redis

4. Dockerfile中常用指令有哪些?区别是什么?

核心指令:

  1. FROM: 指定基础镜像
dockerfile 复制代码
FROM ubuntu:20.04
  1. RUN: 构建时执行命令,生成新层
dockerfile 复制代码
RUN apt-get update && apt-get install -y nginx
  1. CMD: 容器启动时默认执行的命令(可被覆盖)
dockerfile 复制代码
CMD ["nginx", "-g", "daemon off;"]
  1. ENTRYPOINT: 容器启动时执行的命令(不易被覆盖)
dockerfile 复制代码
ENTRYPOINT ["python", "app.py"]
  1. COPY vs ADD :
    • COPY: 简单复制文件
    • ADD: 复制+自动解压+支持URL(推荐用COPY)
dockerfile 复制代码
COPY app.py /app/
   ADD app.tar.gz /app/
  1. WORKDIR: 设置工作目录
dockerfile 复制代码
WORKDIR /app
  1. ENV: 设置环境变量
dockerfile 复制代码
ENV APP_ENV=production
  1. EXPOSE: 声明容器监听端口
dockerfile 复制代码
EXPOSE 80
  1. VOLUME: 创建挂载点
dockerfile 复制代码
VOLUME /data
  1. CMD vs ENTRYPOINT 区别:
  • CMD可以被docker run的参数覆盖
  • ENTRYPOINT不容易被覆盖,适合设置容器主进程
  • 可以组合使用: ENTRYPOINT定义执行程序,CMD定义默认参数

5. Docker数据持久化有哪些方式?

:::tips
为什么需要数据持久化?

容器默认是临时的,容器删除后,数据就没有了,为了让数据持久化,我们将数据保存在容器外部,容器删了数据还在。

三种持久化方式

:::

1. Volume(数据卷) - 推荐方式

  • 由Docker管理,存储在 /var/lib/docker/volumes/
  • 独立于容器生命周期
  • 可在容器间共享

bash

bash 复制代码
# 1. 创建一个数据卷
docker volume create mydata

# 2. 查看所有数据卷
docker volume ls

# 3. 查看某个卷的详细信息
docker volume inspect mydata

# 4. 删除数据卷
docker volume rm mydata

# 方式一:使用已创建的卷
docker volume create mydata
docker run -d --name my-nginx -v mydata:/usr/share/nginx/html nginx

# 方式二:运行时自动创建(如果卷不存在)
docker run -d --name my-nginx -v mydata:/usr/share/nginx/html nginx

# -v mydata:/usr/share/nginx/html
#     │           │
#     │           └── 容器内的路径(数据要存到容器的这个位置)
#     │
#     └── 卷的名字(宿主机上 Docker 管理的存储)

2. Bind Mount(绑定挂载)

  • 挂载宿主机的目录或文件
  • 完全依赖宿主机文件系统
  • 适合开发环境

bash

bash 复制代码
docker run -v /host/path:/container/path nginx
# docker run -v /宿主机路径:/容器内路径 镜像名

3. tmpfs Mount(临时挂载)

  • 存储在宿主机内存中
  • 容器停止后数据消失
  • 适合临时敏感数据###

bash

bash 复制代码
docker run --tmpfs /tmp nginx

区别对比:

  • Volume: 最佳实践,Docker管理,跨平台
  • Bind Mount: 灵活但依赖主机路径结构
  • tmpfs: 临时数据,性能最好

6. 如何优化Docker镜像大小?

优化策略:

  1. 使用更小的基础镜像
dockerfile 复制代码
# 不推荐
FROM ubuntu:20.04
# 推荐
FROM alpine:3.14
  1. 多阶段构建(Multi-stage Build)
  • 多阶段构建 = 同一条 Dockerfile 里写多个 FROM
  • 前一个阶段当工具人(这个阶段里 gcc、go mod、源码、临时对象文件全都有,体积巨大),后一个阶段只 COPY --from= 拿成品(最终镜像 = alpine + 一个静态二进制,可能 < 10 MB;而 builder 阶段几百 MB 的层不会被打包进最终镜像)。
  • 既能让编译环境齐全,又能让运行镜像瘦身,还只生成一个镜像(后面的阶段)。
dockerfile 复制代码
# 构建阶段
FROM golang:1.17 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .

# 运行阶段
FROM alpine:3.14
COPY --from=builder /app/main /main
CMD ["/main"]
  1. 合并RUN指令,减少层数
dockerfile 复制代码
# 不推荐
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean

# 推荐
RUN apt-get update && \
   apt-get install -y nginx && \
   apt-get clean && \
   rm -rf /var/lib/apt/lists/*
  1. 使用.dockerignore文件

:::info

  • docker build会先把整个构建上下文(通常是Dockerfile所在目录)打包成tar发给docker引擎,目录越大-->tar越大-->传输时间越长,缓存越容易失效。
  • .dockerignore的作用:在打包前按照规则排除文件/目录,减小上下文体积。避免敏感文件被打进去,让Copy指令跳过这些文件,镜像层更干净

:::

plain 复制代码
node_modules
.git
*.log
  1. 清理缓存和临时文件
dockerfile 复制代码
RUN apt-get update && apt-get install -y package \
       && rm -rf /var/lib/apt/lists/*
  1. 使用特定版本标签,避免使用latest
  • "latest"让缓存失效 → 反复拉新层 → 旧层堆积 → 体积膨胀;
  • 钉死版本号 → 缓存一直有效 → 不再重复存相同层 → 实际占用最小。

7. Docker容器间如何通信?

1. 自定义通信网络

bash 复制代码
┌──────────────────────────────────────────┐
│           自定义网络 (mynet)               │
│                                          │
│   ┌─────────┐         ┌─────────┐       │
│   │   web   │ ←─────→ │   app   │       │
│   │  容器   │  可以用   │  容器   │       │
│   │         │  名字通信 │         │       │
│   └─────────┘         └─────────┘       │
│                                          │
│   web容器可以直接用 "app" 这个名字访问      │
│   app容器可以直接用 "web" 这个名字访问      │
└──────────────────────────────────────────┘

# 第一步:创建一个自定义网络
docker network create mynet
# 第二步:启动容器并加入这个网络
docker run -d --name web --network mynet nginx
docker run -d --name app --network mynet python:3.9 sleep 3600
# 第三步:容器间通过名字通信
# 进入 app 容器
docker exec -it app bash

# 在 app 容器内部,可以直接用 "web" 这个名字访问
curl http://web:80
ping web

为什么可以用名字访问?===>Docker的自定义网络内置了DNS服务

bash 复制代码
┌────────────────────────────────────────────┐
│              mynet 网络                     │
│                                            │
│   ┌─────────────────────────────────┐     │
│   │        Docker DNS 服务器         │     │
│   │                                 │     │
│   │   web → 172.18.0.2              │     │
│   │   app → 172.18.0.3              │     │
│   └─────────────────────────────────┘     │
│                                            │
│   当 app 容器访问 "web" 时:                │
│   1. 询问 DNS:"web 的 IP 是多少?"         │
│   2. DNS 回答:"172.18.0.2"               │
│   3. app 连接到 172.18.0.2                │
└────────────────────────────────────────────┘

2. 使用Link(已废弃,不推荐)

bash 复制代码
docker run --name db mysql
docker run --name web --link db:database nginx
# --link db:database 的意思是:
# 在 web 容器中,可以用 "database" 这个名字访问 db 容器

3. 通过宿主机端口转发

  • 适用场景:临时测试、外部访问容器服务
bash 复制代码
┌─────────────────────────────────────────────────┐
│                   宿主机                         │
│                 IP: 192.168.1.100               │
│                                                 │
│  端口 3306 ←───────────────────┐               │
│       ↑                        │               │
│       │ 端口映射               │               │
│       ↓                        ↓               │
│  ┌─────────┐              ┌─────────┐         │
│  │  MySQL  │              │   App   │         │
│  │  容器   │              │  容器   │         │
│  │ :33     │              │         │         │
│  └─────────┘              └─────────┘         │
│                                                 │
│  App 容器通过 192.168.1.100:3306 访问 MySQL     │
└─────────────────────────────────────────────────┘

# 启动 MySQL,把容器的 33 端口映射到宿主机的 3306
docker run -d --name mysql -p 3306:33 -e MYSQL_ROOT_PASSWORD=123456 mysql

# 启动应用容器
docker run -d --name app myapp

# 在 app 容器中,通过宿主机 IP 访问 MySQL
# 连接地址:宿主机IP:3306

# 获取宿主机的方法
# 方法1:在容器内使用特殊域名(Docker Desktop 支持)
host.docker.internal

# 方法2:在容器内获取网关 IP
ip route | grep default
# 输出类似:default via 172.17.0.1 dev eth0
# 172.17.0.1 就是宿主机在 Docker 网络中的 IP

4. 使用Docker Compose

用一个配置文件定义多个容器,自动创建网络,容器间自动可以用名字通信。

yaml 复制代码
# docker-compose.yml

version: '3'          # Compose 文件版本

services:             # 定义服务(每个服务 = 一个容器)
  
  web:                # 服务名(也是容器在网络中的名字)
    image: nginx      # 使用的镜像
    ports:
      - "80:80"       # 端口映射
  
  app:                # 另一个服务
    image: python:3.9
    depends_on:       # 依赖关系:app 要等 web 启动后才启动
      - web
  
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      
执行 docker-compose up 后:

1. 自动创建网络(项目名_default)
2. 启动所有容器并加入该网络
3. 容器间可以用服务名通信

┌─────────────────────────────────────────┐
│        自动创建的网络                     │
│        (myproject_default)              │
│                                         │
│   ┌─────┐   ┌─────┐   ┌─────┐         │
│   │ web │ ↔ │ app │ ↔ │ db  │         │
│   └─────┘   └─────┘   └─────┘         │
│                                         │
│   app 中可以:                           │
│   - curl http://web:80                 │
│   - mysql -h db -u root -p             │
└─────────────────────────────────────────┘

:::success

不指定网络时,容器使用默认bridge网络,此时不能用容器名通信。

:::

8. 什么是Docker Compose?有什么作用?

答案:

Docker Compose是用于定义和运行多容器Docker应用的工具。

yaml 复制代码
┌─────────────────────────────────────────────────────────┐
│                                                          │
│   Docker Compose = 多容器应用的「配置文件 + 管理工具」     │
│                                                          │
│   ┌──────────────────┐      ┌──────────────────┐        │
│   │                  │      │                  │        │
│   │  YAML 配置文件    │  +   │  命令行工具       │        │
│   │  (声明式定义)     │      │  (docker-compose)│        │
│   │                  │      │                  │        │
│   └──────────────────┘      └──────────────────┘        │
│                                                          │
└─────────────────────────────────────────────────────────┘

核心作用:

  1. 使用YAML文件配置应用服务
  2. 一条命令启动所有服务
  3. 管理服务间的依赖关系
  4. 简化复杂应用的部署

示例配置:

服务配置详解

bash 复制代码
# -------------------------- image:指定镜像 -------------------------------
services:
  web:
    image: nginx:latest        # 使用官方镜像
  
  app:
    image: myregistry/myapp:v1 # 使用私有仓库镜像

# --------------------------- build:从Dockerfile构建 ------------------------
services:
  app:
    build: ./app               # 简单写法:指定 Dockerfile 所在目录
    
  api:
    build:                     # 详细写法
      context: ./backend       # 构建上下文目录
      dockerfile: Dockerfile.prod  # 指定 Dockerfile 文件名
      args:                    # 构建参数
        - VERSION=1.0

# --------------------------- ports:端口映射 -----------------------------
services:
  web:
    ports:
      - "80:80"              # 宿主机:容器
      - "443:443"
      - "8080:80"            # 宿主机8080 映射到 容器80
      - "127.0.0.1:3000:3000"  # 只绑定到本机

# ---------------------------- volumes:数据卷挂载 --------------------------
services:
  db:
    volumes:
      # 命名卷(推荐用于数据持久化)
      - db-data:/var/lib/mysql
      
      # Bind Mount(开发时挂载代码)
      - ./src:/app/src
      
      # 只读挂载
      - ./config:/etc/app/config:ro

volumes:
  db-data:    # 声明命名卷

# --------------------------- environment:环境变量 ---------------------------
services:
  app:
    environment:
      # 方式1:列表格式
      - DB_HOST=db
      - DB_PORT=3306
      - DEBUG=true
      
  db:
    environment:
      # 方式2:字典格式
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp

# ----------------------------- env_file:从文件读取环境变量 -----------------------
services:
  app:
    env_file:
      - .env           # 读取 .env 文件
      - .env.local     # 可以指定多个文件
# .env 文件内容
DB_HOST=db
DB_PASSWORD=secret
DEBUG=true

# ----------------------------- depends_on:依赖关系 -----------------------------
services:
  web:
    depends_on:
      - app          # web 会等 app 启动后再启动
  
  app:
    depends_on:
      - db           # app 会等 db 启动后再启动
      - redis
启动顺序:db → redis → app → web
停止顺序:web → app → redis → db(反过来)
⚠️ 注意:depends_on 只保证启动顺序,不保证服务"就绪"

# --------------------------- networks:网络配置 --------------------------------
services:
  web:
    networks:
      - frontend       # 只加入前端网络
  
  app:
    networks:          # 同时加入两个网络
      - frontend
      - backend
  
  db:
    networks:
      - backend        # 只加入后端网络

networks:
  frontend:            # 定义前端网络
  backend:             # 定义后端网络

网络隔离效果:

┌────────────────┐     ┌────────────────┐
│   frontend     │     │    backend     │
│                │     │                │
│  ┌────┐ ┌────┐│     │┌────┐ ┌────┐  │
│  │web │↔│app ││     ││app │↔│ db │  │
│  └────┘ └────┘│     │└────┘ └────┘  │
│                │     │                │
└────────────────┘     └────────────────┘

web 和 db 不在同一网络,无法直接通信(更安全)
app 同时在两个网络,可以连接 web 和 db

# ----------------------------- restart:重启策略 ----------------------------
services:
  web:
    restart: always    # 总是重启(推荐生产环境)
    
  app:
    restart: unless-stopped  # 除非手动停止,否则重启
    
  worker:
    restart: on-failure      # 只在失败时重启
    
  test:
    restart: "no"            # 不重启(默认)

# --------------------------- command:默认覆盖指令 --------------------------
services:
  app:
    image: python:3.9
    command: python app.py   # 覆盖镜像默认的 CMD
    
  worker:
    image: python:3.9
    command: ["python", "worker.py", "--queue", "high"]

# --------------------------- networks:网络定义 ----------------------------
networks:
  frontend:              # 使用默认配置创建网络
  
  backend:
    driver: bridge       # 指定驱动(bridge 是默认值)
    
  existing-net:
    external: true       # 使用已存在的外部网络

常用命令:

bash 复制代码
# 启动所有服务(后台运行)
docker-compose up -d

# 启动所有服务(前台运行,看日志)
docker-compose up

# 停止并删除所有服务、网络
docker-compose down

# 停止并删除所有服务、网络、数据卷
docker-compose down -v

# 只停止服务(不删除)
docker-compose stop

# 启动已停止的服务
docker-compose start

# 重启服务
docker-compose restart

# 查看服务状态
docker-compose ps

# 输出示例:
#    Name        Command         State          Ports
# ---------------------------------------------------------
# myapp_web_1   nginx -g ...   Up      0.0.0.0:80->80/tcp
# myapp_app_1   python app.py  Up      
# myapp_db_1    docker-entry.. Up      3306/tcp

# 查看所有服务的日志
docker-compose logs

# 查看特定服务的日志
docker-compose logs web

# 实时跟踪日志(类似 tail -f)
docker-compose logs -f

# 只看最后 100 行
docker-compose logs --tail=100 web

# 构建/重新构建镜像
docker-compose build

# 构建并启动
docker-compose up --build

# 强制重新构建(不使用缓存)
docker-compose build --no-cache

# 扩展服务实例数量
docker-compose up -d --scale app=3

# 这会启动 3 个 app 容器:
# myapp_app_1
# myapp_app_2
# myapp_app_3

9. Docker的分层存储是什么?有什么优势?

Docker镜像采用UnionFS(联合文件系统),将多个层叠加在一起形成最终的镜像。

什么是镜像分层?:

bash 复制代码
┌─────────────────────────────────────────────────┐
│                                                  │
│   Docker 镜像就像「千层饼」或「汉堡」              │
│                                                  │
│   每一层 = Dockerfile 中的一条指令的结果          │
│                                                  │
└─────────────────────────────────────────────────┘

        ┌───────────────────────┐
        │   COPY index.html     │  ← 第4层(最上层)
        ├───────────────────────┤
        │   RUN apt install     │  ← 第3层
        ├───────────────────────┤
        │   RUN apt update      │  ← 第2层
        ├───────────────────────┤
        │   FROM ubuntu:20.04   │  ← 第1层(基础层)
        └───────────────────────┘

UnionFS(联合文件系统)是什么?:

bash 复制代码
想象一下「透明胶片叠加」:

┌─────────────────────────────────────────────────────────┐
│                                                          │
│   📄 第1张透明片:画了一个房子的轮廓                       │
│   📄 第2张透明片:画了窗户                                │
│   📄 第3张透明片:画了门                                  │
│   📄 第4张透明片:画了烟囱                                │
│                                                          │
│   把它们叠在一起 → 看到的是一个完整的房子 🏠              │
│                                                          │
│   这就是 UnionFS 的原理!                                │
│   多个「层」叠加在一起,呈现为一个统一的文件系统           │
│                                                          │
└─────────────────────────────────────────────────────────┘
UnionFS 把多个目录「联合」挂载到同一个目录:

目录A (Layer 1)    目录B (Layer 2)    目录C (Layer 3)
    │                   │                   │
    ├── /bin           ├── /etc            ├── /app
    ├── /lib           ├── /var            └── index.html
    └── /usr           └── nginx.conf
                  
                        ↓ 联合挂载
                        
            ┌──────────────────────────┐
            │     最终看到的文件系统     │
            ├──────────────────────────┤
            │  /bin     (来自 Layer 1) │
            │  /lib     (来自 Layer 1) │
            │  /usr     (来自 Layer 1) │
            │  /etc     (来自 Layer 2) │
            │  /var     (来自 Layer 2) │
            │  /app     (来自 Layer 3) │
            │  index.html (来自 Layer 3)│
            └──────────────────────────┘

Dockerfile指令和层的关系:

dockerfile 复制代码
构建过程:

┌────────────────────────────────────────────────────────────┐
│ Step 1: FROM ubuntu:20.04                                   │
├────────────────────────────────────────────────────────────┤
│                                                             │
│   拉取 ubuntu:20.04 镜像作为基础                            │
│   这个镜像本身已经有很多层了                                 │
│                                                             │
│   ┌─────────────────────┐                                  │
│   │  ubuntu:20.04 的层   │  Layer 1 (约 72MB)               │
│   └─────────────────────┘                                  │
│                                                             │
└────────────────────────────────────────────────────────────┘
                              ↓
┌────────────────────────────────────────────────────────────┐
│ Step 2: RUN apt-get update                                  │
├────────────────────────────────────────────────────────────┤
│                                                             │
│   执行命令,产生的文件变化形成新的一层                        │
│                                                             │
│   ┌─────────────────────┐                                  │
│   │  apt-get update 的层 │  Layer 2 (约 27MB)               │
│   ├─────────────────────┤                                  │
│   │  ubuntu:20.04 的层   │  Layer 1                         │
│   └─────────────────────┘                                  │
│                                                             │
└────────────────────────────────────────────────────────────┘
                              ↓
┌────────────────────────────────────────────────────────────┐
│ Step 3: RUN apt-get install -y nginx                        │
├────────────────────────────────────────────────────────────┤
│                                                             │
│   安装 nginx,产生的文件形成新的一层                         │
│                                                             │
│   ┌─────────────────────┐                                  │
│   │  安装 nginx 的层     │  Layer 3 (约 56MB)               │
│   ├─────────────────────┤                                  │
│   │  apt-get update 的层 │  Layer 2                         │
│   ├─────────────────────┤                                  │
│   │  ubuntu:20.04 的层   │  Layer 1                         │
│   └─────────────────────┘                                  │
│                                                             │
└────────────────────────────────────────────────────────────┘
                              ↓
┌────────────────────────────────────────────────────────────┐
│ Step 4: COPY index.html /var/www                            │
├────────────────────────────────────────────────────────────┤
│                                                             │
│   复制文件,形成最终的一层                                   │
│                                                             │
│   ┌─────────────────────┐                                  │
│   │  COPY 文件的层       │  Layer 4 (几KB)                  │
│   ├─────────────────────┤                                  │
│   │  安装 nginx 的层     │  Layer 3                         │
│   ├─────────────────────┤                                  │
│   │  apt-get update 的层 │  Layer 2                         │
│   ├─────────────────────┤                                  │
│   │  ubuntu:20.04 的层   │  Layer 1                         │
│   └─────────────────────┘                                  │
│                                                             │
│   最终镜像 = 4层叠加                                        │
│                                                             │
└────────────────────────────────────────────────────────────┘

优势:

  1. 节省磁盘空间: 相同的层只存储一次
bash 复制代码
场景:你有两个镜像都基于 ubuntu:20.04

镜像A (Web服务器)              镜像B (数据库)
┌─────────────────┐          ┌─────────────────┐
│ 应用层 (10MB)    │          │ MySQL层 (200MB)  │
├─────────────────┤          ├─────────────────┤
│ Nginx层 (50MB)  │          │ 依赖层 (100MB)   │
├─────────────────┤          ├─────────────────┤
│ ubuntu (72MB)   │  ←共享→   │ ubuntu (72MB)   │
└─────────────────┘          └─────────────────┘

不分层: 72 + 72 = 144MB (基础层存两份)
分层:   72MB (基础层只存一份)

节省了 72MB!镜像越多,节省越多!
  1. 加速构建: 未改变的层使用缓存
bash 复制代码
# 假设这是你的 Dockerfile
FROM node:18
COPY package.json .
RUN npm install          # 这一步很慢(要下载很多依赖)
COPY . .
CMD ["node", "app.js"]

第一次构建:
Step 1: FROM node:18              ← 拉取镜像
Step 2: COPY package.json .       ← 复制文件
Step 3: RUN npm install           ← 安装依赖(耗时 2 分钟)
Step 4: COPY . .                  ← 复制代码
Step 5: CMD ["node", "app.js"]    ← 设置命令

总耗时: 约 3 分钟

---------------------------------------------

你修改了 app.js,再次构建:

Step 1: FROM node:18              ← Using cache ✓
Step 2: COPY package.json .       ← Using cache ✓
Step 3: RUN npm install           ← Using cache ✓ (package.json 没变)
Step 4: COPY . .                  ← 重新执行(代码变了)
Step 5: CMD ["node", "app.js"]    ← 重新执行

总耗时: 约 10 秒!省了 2 分钟 50 秒!
  1. 快速分发: 只传输新增或修改的层
bash 复制代码
场景:推送/拉取镜像到仓库

┌─────────────────────────────────────────────────────────┐
│                                                          │
│   你的镜像 (500MB)                                       │
│   ┌─────────────────┐                                   │
│   │ 你的代码 (5MB)   │  ← 只有这层需要传输!             │
│   ├─────────────────┤                                   │
│   │ 依赖包 (200MB)   │  ← 服务器已有,跳过               │
│   ├─────────────────┤                                   │
│   │ 基础镜像 (295MB) │  ← 服务器已有,跳过               │
│   └─────────────────┘                                   │
│                                                          │
│   实际传输: 5MB(而不是 500MB)                          │
│   节省 99% 的带宽!                                      │
│                                                          │
└─────────────────────────────────────────────────────────┘
  1. 版本控制: 每层有唯一ID,易于追踪
bash 复制代码
# 每个层都有唯一的 SHA256 哈希值
docker inspect nginx --format='{{.RootFS.Layers}}'

# 输出类似:
# [sha256:a1b2c3d4...
#  sha256:e5f6g7h8...
#  sha256:i9j0k1l2...]

# 作用:
# - 精确知道镜像由哪些层组成
# - 验证镜像完整性
# - 追踪变化

层的只读特性:

bash 复制代码
┌─────────────────────────────────────────────────────────┐
│                                                          │
│   镜像的每一层都是 READ-ONLY(只读)                      │
│                                                          │
│   ┌─────────────────┐                                   │
│   │   Layer 4       │  只读 🔒                          │
│   ├─────────────────┤                                   │
│   │   Layer 3       │  只读 🔒                          │
│   ├─────────────────┤                                   │
│   │   Layer 2       │  只读 🔒                          │
│   ├─────────────────┤                                   │
│   │   Layer 1       │  只读 🔒                          │
│   └─────────────────┘                                   │
│                                                          │
│   一旦创建,永不改变!                                    │
│                                                          │
└─────────────────────────────────────────────────────────┘
那容器里怎么写文件呢? → 这就需要「容器层」和「Copy-on-Write」机制

Copy-on-Write机制:

bash 复制代码
当你运行容器时:

docker run nginx

┌─────────────────────────────────────────────────────────┐
│                       容器                               │
│  ┌─────────────────────────────────────────────────┐   │
│  │           Container Layer (容器层)               │   │
│  │                可读写 📝                          │   │
│  │        (容器运行时产生的所有改动都在这里)          │   │
│  └─────────────────────────────────────────────────┘   │
│                          ↑                              │
│  ┌─────────────────────────────────────────────────┐   │
│  │                                                  │   │
│  │              镜像的只读层                         │   │
│  │                                                  │   │
│  │   ┌─────────────────┐                           │   │
│  │   │   Layer 4       │  只读 🔒                  │   │
│  │   ├─────────────────┤                           │   │
│  │   │   Layer 3       │  只读 🔒                  │   │
│  │   ├─────────────────┤                           │   │
│  │   │   Layer 2       │  只读 🔒                  │   │
│  │   ├─────────────────┤                           │   │
│  │   │   Layer 1       │  只读 🔒                  │   │
│  │   └─────────────────┘                           │   │
│  │                                                  │   │
│  └─────────────────────────────────────────────────┘   │
│                                                          │
└─────────────────────────────────────────────────────────┘
场景:容器要修改一个文件(比如 /etc/nginx/nginx.conf)

┌────────────────────────────────────────────────────────────┐
│                                                             │
│  原始状态:                                                  │
│  ┌───────────────────────┐                                 │
│  │    容器层 (空的)       │                                 │
│  ├───────────────────────┤                                 │
│  │  Layer 3: nginx.conf  │ ← 文件在只读层                   │
│  ├───────────────────────┤                                 │
│  │      ...其他层...      │                                 │
│  └───────────────────────┘                                 │
│                                                             │
└────────────────────────────────────────────────────────────┘
                              ↓
                        容器修改文件
                              ↓
┌────────────────────────────────────────────────────────────┐
│                                                             │
│  Copy-on-Write 发生:                                       │
│                                                             │
│  1. 从只读层「复制」nginx.conf 到容器层                      │
│  2. 在容器层的副本上进行修改                                 │
│  3. 原只读层的文件保持不变                                   │
│                                                             │
│  ┌───────────────────────┐                                 │
│  │ 容器层: nginx.conf ✏️  │ ← 修改后的副本在这里             │
│  ├───────────────────────┤                                 │
│  │ Layer 3: nginx.conf 🔒│ ← 原文件不变!                   │
│  ├───────────────────────┤                                 │
│  │      ...其他层...      │                                 │
│  └───────────────────────┘                                 │
│                                                             │
│  读取时:优先读容器层的文件,找不到才往下层找                 │
│                                                             │
└────────────────────────────────────────────────────────────┘

实际演示:

bash 复制代码
# 启动一个 nginx 容器
docker run -d --name test-nginx nginx

# 查看容器内的配置文件
docker exec test-nginx cat /etc/nginx/nginx.conf

# 修改配置文件
docker exec test-nginx sh -c "echo '# modified' >> /etc/nginx/nginx.conf"

# 再次查看,看到了修改
docker exec test-nginx cat /etc/nginx/nginx.conf

# 重要:原镜像没有任何改变!
# 启动另一个容器,配置文件是原始的
docker run --rm nginx cat /etc/nginx/nginx.conf
# 没有 "# modified"!

# 清理
docker rm -f test-nginx

⚠️** 删除文件不会减小镜像体积的常见误区:**

bash 复制代码
# 错误示范:删除了文件但镜像没变小

FROM ubuntu:20.04
RUN apt-get update                    # Layer 1: +27MB
RUN apt-get install -y gcc g++        # Layer 2: +200MB
RUN rm -rf /var/lib/apt/lists/*       # Layer 3: 标记删除,但 0MB 减少!

# 总大小还是 227MB+
# 因为 Layer 1 和 Layer 2 的文件还在!

为什么?

┌─────────────────────────────────────┐
│  Layer 3: rm -rf (标记删除)         │  ← 只是说"这些文件不可见了"
├─────────────────────────────────────┤
│  Layer 2: gcc g++ (200MB)          │  ← 文件还在这里!
├─────────────────────────────────────┤
│  Layer 1: apt-get update (27MB)    │  ← 文件还在这里!
└─────────────────────────────────────┘

层是只读的,不能真正删除之前层的文件!

===============>正确做法:
# 正确示范:在同一个 RUN 中清理

FROM ubuntu:20.04
RUN apt-get update && \
    apt-get install -y gcc g++ && \
    rm -rf /var/lib/apt/lists/*       # 同一层中清理!

# 现在只有一层,清理生效

10. 如何排查Docker容器故障?

常用排查命令:

  1. 查看容器日志

bash

bash 复制代码
docker logs -f --tail 100 container_name
  1. 进入容器排查

bash

bash 复制代码
docker exec -it container_name /bin/bash
docker exec -it container_name sh  # Alpine镜像
  1. 查看容器资源使用

bash

bash 复制代码
docker stats container_name
docker top container_name
  1. 检查容器详细信息

bash

bash 复制代码
docker inspect container_name
docker inspect --format='{{.State.Status}}' container_name
  1. 查看容器进程

bash

bash 复制代码
docker top container_name
  1. 查看容器端口映射

bash

bash 复制代码
docker port container_name
  1. 查看网络连接

bash

bash 复制代码
docker network inspect bridge

常见问题排查:

  • 容器启动失败: 检查日志,查看配置,验证镜像
  • 网络问题: 检查网络模式,端口映射,防火墙规则
  • 性能问题: 使用docker stats查看资源,限制CPU/内存
  • 存储问题: 检查磁盘空间,Volume挂载

第二部分: 语法知识详解

  • 镜像管理 (pull/build/tag/push)
  • 容器管理 (run/exec/logs)
  • 数据卷管理
  • 网络管理
  • Dockerfile完整语法
  • Docker Compose配置
  • 实用组合命令

1. Docker 命令结构

plain 复制代码
docker [OPTIONS] COMMAND [ARG...]

2. 镜像管理命令

2.1 拉取镜像

bash

bash 复制代码
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

# 示例
docker pull nginx                    # 拉取最新版本
docker pull nginx:1.21.0            # 拉取指定版本
docker pull mysql:8.0               # 拉取MySQL 8.0
2.2 查看本地镜像

bash

bash 复制代码
docker images [OPTIONS] [REPOSITORY[:TAG]]

# 示例
docker images                        # 列出所有镜像
docker images nginx                  # 列出nginx相关镜像
docker images -q                     # 只显示镜像ID
docker images --filter "dangling=true"  # 显示悬空镜像
2.3 删除镜像

bash

bash 复制代码
docker rmi [OPTIONS] IMAGE [IMAGE...]

# 示例
docker rmi nginx:1.21.0             # 删除指定镜像
docker rmi $(docker images -q)      # 删除所有镜像
docker rmi -f image_id              # 强制删除
2.4 构建镜像

bash

bash 复制代码
docker build [OPTIONS] PATH | URL | -

# 示例
docker build -t myapp:1.0 .                    # 从当前目录构建
docker build -t myapp:1.0 -f Dockerfile.prod . # 指定Dockerfile
docker build --no-cache -t myapp:1.0 .         # 不使用缓存
docker build --build-arg VERSION=1.0 .         # 传递构建参数
2.5 标记镜像

bash

bash 复制代码
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

# 示例
docker tag myapp:1.0 myregistry.com/myapp:1.0
docker tag nginx:latest nginx:backup
2.6 推送镜像

bash

bash 复制代码
docker push [OPTIONS] NAME[:TAG]

# 示例
docker push myregistry.com/myapp:1.0

3. 容器管理命令

3.1 运行容器

bash

bash 复制代码
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

# 常用选项详解
-d                          # 后台运行
-it                         # 交互式终端
--name container_name       # 指定容器名称
-p host_port:container_port # 端口映射
-P                          # 随机端口映射
-v host_path:container_path # 挂载数据卷
-e KEY=VALUE               # 设置环境变量
--network network_name     # 指定网络
--restart=always           # 自动重启策略
--memory=1g                # 限制内存
--cpus=2                   # 限制CPU
--rm                       # 容器停止后自动删除

# 实战示例
docker run -d \
  --name my-nginx \
  -p 8080:80 \
  -v /host/data:/usr/share/nginx/html \
  -e NGINX_HOST=example.com \
  --restart=unless-stopped \
  nginx:latest

# MySQL容器示例
docker run -d \
  --name mysql-server \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=mydb \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# Redis容器示例
docker run -d \
  --name redis-server \
  -p 6379:6379 \
  -v redis-data:/data \
  redis:alpine redis-server --appendonly yes
3.2 查看容器

bash

bash 复制代码
docker ps [OPTIONS]

# 示例
docker ps                   # 查看运行中的容器
docker ps -a                # 查看所有容器(包括停止的)
docker ps -q                # 只显示容器ID
docker ps -n 5              # 显示最近创建的5个容器
docker ps --filter "status=running"  # 过滤运行中的容器
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"  # 格式化输出
3.3 启动/停止/重启容器

bash

bash 复制代码
docker start [OPTIONS] CONTAINER [CONTAINER...]
docker stop [OPTIONS] CONTAINER [CONTAINER...]
docker restart [OPTIONS] CONTAINER [CONTAINER...]

# 示例
docker start my-nginx
docker stop my-nginx
docker restart my-nginx
docker stop $(docker ps -q)  # 停止所有运行中的容器
3.4 删除容器

bash

bash 复制代码
docker rm [OPTIONS] CONTAINER [CONTAINER...]

# 示例
docker rm my-nginx                  # 删除停止的容器
docker rm -f my-nginx              # 强制删除运行中的容器
docker rm $(docker ps -aq)         # 删除所有容器
docker container prune             # 删除所有停止的容器
3.5 进入容器

bash

bash 复制代码
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
docker attach CONTAINER

# 示例
docker exec -it my-nginx /bin/bash      # 交互式bash
docker exec -it my-nginx sh             # Alpine系统使用sh
docker exec my-nginx ls /etc            # 执行单个命令
docker exec -u root my-nginx whoami     # 以root用户执行

# exec vs attach区别
# exec: 新建进程,exit不会停止容器
# attach: 附加到主进程,exit会停止容器
3.6 查看容器日志

bash

bash 复制代码
docker logs [OPTIONS] CONTAINER

# 示例
docker logs my-nginx                # 查看所有日志
docker logs -f my-nginx            # 实时跟踪日志
docker logs --tail 100 my-nginx    # 查看最后100行
docker logs --since 30m my-nginx   # 查看最近30分钟日志
docker logs -t my-nginx            # 显示时间戳
3.7 容器与宿主机文件复制

bash

bash 复制代码
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

# 示例
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf  # 从容器复制到主机
docker cp ./app.py my-app:/app/app.py                  # 从主机复制到容器

4. 数据卷管理

4.1 Volume命令

bash

bash 复制代码
# 创建数据卷
docker volume create [OPTIONS] [VOLUME]
docker volume create mydata

# 列出数据卷
docker volume ls
docker volume ls --filter "dangling=true"  # 未使用的数据卷

# 查看数据卷详情
docker volume inspect mydata

# 删除数据卷
docker volume rm mydata
docker volume prune  # 删除所有未使用的数据卷

# 使用数据卷
docker run -v mydata:/app/data nginx           # 命名卷
docker run -v /host/path:/container/path nginx # 绑定挂载
docker run --mount type=volume,source=mydata,target=/app/data nginx  # 推荐语法

5. 网络管理

5.1 网络命令

bash

bash 复制代码
# 列出网络
docker network ls

# 创建网络
docker network create [OPTIONS] NETWORK
docker network create mynet                          # 默认bridge
docker network create --driver bridge mynet
docker network create --driver overlay swarm-net     # Swarm模式

# 查看网络详情
docker network inspect mynet

# 连接容器到网络
docker network connect mynet my-container
docker network connect --alias db mynet mysql-server

# 断开容器网络连接
docker network disconnect mynet my-container

# 删除网络
docker network rm mynet
docker network prune  # 删除所有未使用的网络

6. Dockerfile 语法详解

6.1 完整Dockerfile示例

dockerfile

dockerfile 复制代码
# 基础镜像
FROM python:3.9-slim AS base

# 维护者信息
LABEL maintainer="your-email@example.com"
LABEL version="1.0"
LABEL description="Python web application"

# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    APP_HOME=/app

# 设置工作目录
WORKDIR $APP_HOME

# 安装系统依赖
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 复制依赖文件
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户
RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser $APP_HOME
USER appuser

# 暴露端口
EXPOSE 8000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

# 数据卷
VOLUME ["/app/data"]

# 容器启动命令
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
6.2 多阶段构建示例

dockerfile

dockerfile 复制代码
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

7. Docker Compose 语法详解

yaml

yaml 复制代码
version: '3.8'  # Compose文件版本

services:
  web:
    # 使用已有镜像
    image: nginx:latest
    
    # 或从Dockerfile构建
    build:
      context: ./web
      dockerfile: Dockerfile
      args:
        - BUILD_ENV=production
    
    # 容器名称
    container_name: web-server
    
    # 端口映射
    ports:
      - "80:80"
      - "443:443"
    
    # 数据卷
    volumes:
      - ./html:/usr/share/nginx/html:ro  # 只读
      - nginx-logs:/var/log/nginx
    
    # 环境变量
    environment:
      - NGINX_HOST=example.com
      - NGINX_PORT=80
    # 或从文件加载
    env_file:
      - .env
    
    # 网络
    networks:
      - frontend
      - backend
    
    # 依赖关系
    depends_on:
      - app
      - db
    
    # 重启策略
    restart: unless-stopped
    
    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
    
    # 健康检查
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  
  app:
    build: ./app
    volumes:
      - app-data:/data
    networks:
      - backend
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
  
  db:
    image: postgres:13
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_USER: user
      POSTGRES_DB: mydb
    networks:
      - backend

# 网络定义
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络

# 数据卷定义
volumes:
  nginx-logs:
  app-data:
  db-data:
    driver: local

8. 常用组合命令

bash

bash 复制代码
# 清理系统
docker system prune -a              # 清理所有未使用的资源
docker system df                    # 查看磁盘使用情况

# 批量操作
docker stop $(docker ps -q)         # 停止所有容器
docker rm $(docker ps -aq)          # 删除所有容器
docker rmi $(docker images -q)      # 删除所有镜像

# 查看资源使用
docker stats                        # 实时查看资源使用
docker top container_name           # 查看容器进程

# 导出/导入镜像
docker save -o myapp.tar myapp:1.0          # 导出镜像
docker load -i myapp.tar                     # 导入镜像
docker export container_id > container.tar   # 导出容器
docker import container.tar myapp:latest     # 导入容器为镜像

# 查看历史
docker history image_name           # 查看镜像构建历史

9. 最佳实践建议

  1. 使用.dockerignore文件
  2. 一个容器只运行一个进程
  3. 使用多阶段构建减小镜像体积
  4. 不要在容器中存储数据,使用Volume
  5. 使用官方镜像作为基础镜像
  6. 合并RUN指令减少层数
  7. 使用具体版本标签,避免latest
  8. 定期清理未使用的资源
  9. 使用健康检查确保服务可用
  10. 容器以非root用户运行
相关推荐
小p11 小时前
docker学习: 2. 构建镜像Dockerfile
docker
小p1 天前
docker学习: 1. docker基本使用
docker
崔小汤呀1 天前
Docker部署Nacos
docker·容器
缓解AI焦虑1 天前
Docker + K8s 部署大模型推理服务:资源划分与多实例调度
docker·容器
1candobetter2 天前
Docker Compose Build 与 Up 的区别:什么时候必须重建镜像
docker·容器·eureka
シ風箏2 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
BugShare2 天前
继《小爱音响》详细说下怎么部署,尤其是关于Docker部分
docker·nas·xiaomusic
至此流年莫相忘2 天前
Kubernetes实战篇之配置与存储
云原生·容器·kubernetes
小马爱打代码2 天前
Docker:完全指南从入门到精通
运维·docker·容器