Skip to main content

Docker

Docker 完全入门指南:从核心概念到实战演练

在现代软件开发中,你一定听过那句经典的“魔咒”:“在我电脑上明明是好的啊!” Docker 的诞生,就是为了彻底终结这个魔咒。它不仅仅是一个工具,更是一种变革软件开发、测试和部署流程的哲学。

🚀 Docker 解决了什么?

想象一下,你把你的应用程序(代码、依赖库、配置文件、运行时环境)全部打包进一个标准化的、轻量级的“集装箱”里。这个集装箱可以在任何安装了 Docker 的机器上(你的电脑、同事的电脑、测试服务器、云服务器)被完美地、一致地运行,无需关心底层操作系统的差异。

这就是 Docker 的核心价值:构建一次,随处运行 (Build Once, Run Anywhere)。

核心概念:理解 Docker 的世界观

在敲下第一行命令前,理解 Docker 的几个核心概念至关重要。

概念 通俗比喻 技术解释
📜
镜像 (Image)
房屋的建筑蓝图 一个只读的模板,包含了运行应用程序所需的一切:代码、运行时、库、环境变量和配置文件。镜像是分层的,每一层都是对前一层的增量修改。
🏠
容器 (Container)
根据蓝图建造的真实房屋 镜像的运行实例。容器是活动的、可读写的。你可以创建、启动、停止、移动或删除容器。每个容器都与其他容器和宿主机相互隔离。
📄
Dockerfile
一张详细的建筑施工说明书 一个文本文档,包含了一系列指令,用于指导 Docker 如何从头开始自动构建一个镜像。每一条指令都会在镜像中创建一个新的层。
📦
仓库 (Repository)
存放各种蓝图的档案馆 集中存放镜像文件的地方。最著名的公共仓库是 Docker Hub。你也可以搭建自己的私有仓库。
💾
卷 (Volume)
房屋旁边独立的地基/仓库 一种由 Docker 管理的、用于持久化容器数据的机制。它独立于容器的生命周期,即使容器被删除,卷中的数据依然存在。

核心关系:我们使用 Dockerfile 来定义如何构建一个 镜像,然后从这个 镜像 运行一个或多个 容器


安装 Docker (Ubuntu 24.04)

安装过程非常直接。

# 更新包列表
sudo apt update

# 安装需要的软件包以使apt能够通过HTTPS使用仓库
sudo apt-get install ca-certificates curl gnupg lsb-release

# 添加Docker官方的GPG密钥
curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 设置稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null


# 更新源并安装Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

# (可选)安装nvidia-container-toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# 设置Docker开机自启动并重启Docker服务
sudo systemctl enable docker
sudo systemctl restart docker

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 查看Docker是否已启动
sudo systemctl status docker

# (推荐) 将当前用户添加到 docker 组,避免每次都输入 sudo
# 执行后需要重新登录或新开终端才能生效
sudo usermod -aG docker ${USER}
newgrp docker # 立即应用组成员资格


Docker 实战之旅:从入门到精通

让我们通过一系列循序渐进的实战来掌握 Docker。

第一站:运行你的第一个容器

最简单的开始,就是运行一个官方的 hello-world 镜像。

docker run hello-world

当你执行这条命令时,Docker 在幕后做了什么?

  1. 检查本地:Docker 检查本地是否有 hello-world 这个镜像。
  2. 拉取镜像:发现本地没有,于是从 Docker Hub 仓库拉取 (pull) 该镜像。
  3. 创建容器:使用拉取到的镜像创建一个新的容器。
  4. 运行容器:执行容器内的默认命令,打印出欢迎信息。
  5. 停止容器:命令执行完毕,容器自动停止。

来点更有趣的:让我们运行一个 Nginx Web 服务器。

docker run -d -p 8080:80 --name my-nginx nginx:latest

命令解析:

  • docker run: 运行一个容器。
  • -d (--detach): 后台运行。容器将在后台启动并持续运行。
  • -p 8080:80 (--publish): 端口映射。将主机的 8080 端口映射到容器的 80 端口。这样,我们访问主机的 8080 端口,流量就会被转发到容器的 80 端口,也就是 Nginx 监听的端口。
  • --name my-nginx: 给我们的容器起一个友好的名字,方便管理。
  • nginx:latest: 指定要使用的镜像名称和标签(版本)。

现在,打开你的浏览器,访问 http://localhost:8080,你将看到 Nginx 的欢迎页面!

第二站:管理你的容器

容器已经运行起来了,我们如何管理它?

# 查看正在运行的容器
docker ps

# 查看所有容器(包括已停止的)
docker ps -a

# 停止容器
docker stop my-nginx

# 再次启动已停止的容器
docker start my-nginx

# 查看容器的日志
docker logs my-nginx

# 进入正在运行的容器内部(非常有用!)
docker exec -it my-nginx bash

# 删除一个已停止的容器
docker rm my-nginx

# 强制删除一个正在运行的容器
docker rm -f my-nginx

# 清理所有已停止的容器(慎用)
docker container prune -f

第三站:构建你自己的镜像 (Dockerfile)

这是从 Docker 用户到 Docker 玩家的进阶之路。我们将为一个小型的 Python Flask 应用构建一个镜像。

1. 准备应用代码 创建一个项目文件夹,包含以下三个文件:

  • app.py:
    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return "Hello from inside a Docker Container!"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    
  • requirements.txt:
    Flask==2.2.2
    
  • Dockerfile:
    # Step 1: 选择一个基础镜像
    # 这是一个包含 Python 3.10 的轻量级官方镜像
    FROM python:3.10-slim
    
    # Step 2: 设置工作目录
    # 容器内的所有后续操作都将在此目录下进行
    WORKDIR /app
    
    # Step 3: 复制依赖文件并安装依赖
    # 先复制 requirements.txt 并安装,可以利用 Docker 的缓存机制
    # 只有当 requirements.txt 变化时,这一层才会重新构建
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    # Step 4: 复制应用程序代码
    # 将当前目录下的所有文件复制到容器的 /app 目录下
    COPY . .
    
    # Step 5: 暴露端口
    # 声明容器将监听 5000 端口,但不会自动映射
    EXPOSE 5000
    
    # Step 6: 定义容器启动时执行的命令
    # 启动 Flask 应用
    CMD ["python", "app.py"]
    

2. 构建镜像 在项目根目录下,执行 docker build 命令:

docker build -t my-python-app:1.0 .
  • -t: (--tag) 为你的镜像打上标签,格式为 name:version
  • .: 表示 Dockerfile 所在的上下文路径(当前目录)。

3. 运行你的自定义镜像

docker run -d -p 5000:5000 --name web-app my-python-app:1.0

访问 http://localhost:5000,你将看到 "Hello from inside a Docker Container!"。你成功地将你的应用容器化了!

第四站:持久化你的数据 (Volumes)

容器是无状态易逝的。如果你在容器里存了数据,一旦容器被删除,数据就没了。这对于数据库等需要持久化数据的应用是致命的。卷 (Volumes) 就是解决方案。

让我们运行一个 PostgreSQL 数据库,并使用卷来保存它的数据。

docker run -d \
  -p 5432:5432 \
  --name my-postgres \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -v pgdata:/var/lib/postgresql/data \
  postgres:14
  • -v pgdata:/var/lib/postgresql/data: 这就是卷的核心
    • pgdata: Docker 会自动创建一个名为 pgdata 的卷。
    • /var/lib/postgresql/data: 这是 PostgreSQL 在容器内存储数据的默认路径。
    • 这条命令的含义是:将 pgdata 这个卷挂载到容器的 /var/lib/postgresql/data 目录上。所有写入这个目录的数据,实际上都保存在了 pgdata 卷里。

验证持久化:

  1. 停止并删除容器:docker stop my-postgres 然后 docker rm my-postgres
  2. 查看卷是否还在:docker volume ls (你会看到 pgdata)。
  3. 重新启动一个新的容器,但使用同一个卷
    docker run -d -p 5432:5432 --name new-postgres -e POSTGRES_PASSWORD=mysecretpassword -v pgdata:/var/lib/postgresql/data postgres:14
    
    你之前在数据库中创建的任何数据,都会在新容器中完好无损。

Docker 命令参数深度解析

掌握常用命令的参数是提升效率的关键。这里我们对最重要的参数进行详细解析。

docker run:创建并运行一个新容器

参数 详细说明
-d, --detach 后台运行模式。容器将在后台启动并返回容器ID,而不会占用当前终端。
-p, --publish 端口映射。格式:<host_port>:<container_port>。例如,-p 8080:80 将主机的 8080 端口映射到容器的 80 端口。
-v, --volume 挂载卷或绑定挂载
命名卷: -v my-volume:/path/in/container (推荐)
绑定挂载: -v /path/on/host:/path/in/container (直接映射主机目录)
--name 为容器指定一个名称。例如 --name my-web-server。如果没有指定,Docker 会随机生成一个。
-e, --env 设置环境变量。例如 -e MYSQL_ROOT_PASSWORD=secret
--rm 容器退出时自动删除。非常适合用于临时的、一次性的任务,避免留下垃圾容器。
-it 交互模式。通常组合使用,-i (--interactive) 保持 STDIN 打开,-t (--tty) 分配一个伪终端。常用于进入容器内部,如 docker exec -it my-container bash
--restart 重启策略。设置容器在退出时的重启行为。
no: (默认) 不重启
on-failure: 非0状态退出时重启
always: 总是重启
unless-stopped: 总是重启,除非被手动停止
--network 连接到指定网络。例如 --network my-custom-network。用于容器间通信。

docker build:从 Dockerfile 构建镜像

参数 详细说明
-t, --tag 为镜像打上标签。格式:name:tag,例如 -t my-app:1.0。一个好习惯是同时打上版本号。
-f, --file 指定 Dockerfile 的路径。默认为当前目录下的 Dockerfile。例如 -f ./path/to/MyDockerfile
--build-arg 设置构建时变量。可以在 Dockerfile 中通过 ARG 指令使用。例如 --build-arg HTTP_PROXY=http://...
--no-cache 禁用构建缓存。构建时完全不使用之前构建的缓存层,确保从头开始全新构建。
. (上下文路径) 命令末尾的 . 指定了构建上下文 (Build Context) 的路径。Docker 会将此路径下的所有文件发送给 Docker 守护进程,以便在构建过程中使用(如 COPY . .)。

docker ps:列出容器

参数 详细说明
-a, --all 显示所有容器,包括已停止的容器。默认只显示正在运行的。
-q, --quiet 只显示容器ID。非常适合用于脚本中,例如 docker stop $(docker ps -aq)
-f, --filter 根据条件过滤。例如 -f "status=exited"-f "name=my-nginx"

更进一步:Docker Compose

当你的应用由多个服务(如 Web 应用 + 数据库 + 缓存)组成时,手动管理多个 docker run 命令会变得非常繁琐。Docker Compose 允许你使用一个 YAML 文件来定义和运行多容器应用。

docker-compose.yml 示例:

version: '3.8'

services:
  web:
    build: . # 使用当前目录的 Dockerfile 构建
    ports:
      - "5000:5000"
    depends_on:
      - db

  db:
    image: postgres:14
    environment:
      - POSTGRES_PASSWORD=mysecretpassword
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

只需一个命令,即可启动整个应用栈:

docker-compose up -d

关闭并清理:

docker-compose down

结语

你已经完成了从 Docker 零基础到能够独立构建、运行和管理容器化应用的旅程。Docker 的世界远不止于此,但这为你探索微服务、CI/CD、Kubernetes 等更广阔的领域打下了坚实的地基。

记住,Docker 的核心是封装隔离环境一致性。掌握了它,你就掌握了现代软件交付流程中最强大的一环