【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用户运行
相关推荐
有风听风有雨看雨2 小时前
【Critical】docker unauthorized 2375
docker·容器·eureka
Trank-Lw5 小时前
Docker Devcontainer 管理命令
运维·docker·容器
科技观察6 小时前
告别镜像拉取困境:毫秒镜像以“正规军”姿态重塑国内Docker加速生态
运维·docker·容器
热爱生活的五柒6 小时前
docker里面的文件没有写入权限,也无法使用sudo。docker镜像里某个文件夹没有创建文件夹权限。如何解决?
运维·docker·容器
愈努力俞幸运8 小时前
windows 安装 docker
windows·docker·容器
2301_767902649 小时前
第 5 章 docker网络
网络·docker·php
huizhixue-IT9 小时前
收藏-Kubernetes怎么从私有仓库拉取镜像?(K8S系列)
云原生·容器·kubernetes
守护砂之国泰裤辣11 小时前
React项目Docker部署的简单配置
运维·docker·容器
@Ma11 小时前
使用 Docker 部署 PostgreSQL + pgvector 完整步骤(映射端口 5433),适用于memu项目数据库支持!
docker·postgresql·容器