1. 引言:为什么需要 Docker?

在传统的软件开发与部署流程中,我们常常会遇到令人头疼的问题:

  • 环境不一致: 开发人员的本地环境、测试环境、生产环境配置不同,导致“在我机器上能跑”的经典问题。
  • 依赖冲突: 应用依赖的库或服务版本冲突,安装配置过程繁琐且易出错。
  • 部署复杂: 在新机器上部署应用需要重新安装所有依赖、配置环境,耗时耗力。
  • 资源浪费: 为每个应用分配虚拟机(VM)导致资源利用率低,启动速度慢。

Docker 带来的解决方案:容器化。

Docker 是一个开源的应用容器引擎,它可以将应用及其所有依赖打包到一个标准化的单元中,称为容器。容器在操作系统层面上实现了进程隔离,具有以下核心优势

  • 轻量: 容器共享主机操作系统内核,无需像虚拟机那样运行完整的操作系统,因此体积更小,资源占用更少。
  • 快速: 容器启动速度极快,通常在秒级。
  • 一致: “一次构建,随处运行”。容器保证了开发、测试、生产环境的高度一致性,消除了“环境依赖”问题。
  • 可移植: 容器可以在任何支持 Docker 的环境中运行(Linux, Windows, macOS, 云服务器)。
  • 资源高效: 可以在单台主机上运行更多容器应用,充分利用硬件资源。

目标读者: 如果你是一名开发者、运维人员或技术爱好者,希望快速了解 Docker 的核心概念并掌握其基本操作,能够运行和管理容器化应用,那么这篇指南就是为你准备的!我们将专注于动手实践,用最简洁的方式带你入门。

2. Docker 核心概念 (1分钟理解)

理解 Docker 的几个核心概念是入门的关键:

  • Docker 引擎 (Docker Engine): Docker 的核心后台服务,负责创建和运行容器、管理镜像等。它包括守护进程、REST API 和命令行接口 (CLI)。
  • 镜像 (Image): 一个只读的模板,包含了运行应用所需的所有文件系统结构、依赖库、环境变量、配置文件和启动命令等。你可以把它看作一个应用程序及其运行环境的“快照”或“蓝图”。镜像本身不能运行。
  • 容器 (Container): 镜像的一个运行实例。当你启动一个镜像时,Docker 引擎会创建一个隔离的、轻量级的运行环境,这就是容器。容器内部运行着应用进程,拥有自己的文件系统、网络和进程空间。容器是可读写的。
  • 仓库 (Registry): 存放 Docker 镜像的地方。最大的公共仓库是 Docker Hub,类似于代码托管中的 GitHub,你可以从中拉取(下载)各种官方或社区维护的镜像。你也可以搭建自己的私有仓库。

类比理解: 想象一下面向对象编程:

  • 镜像 (Image) = 类 (Class): 定义了对象的属性和行为(应用环境)。
  • 容器 (Container) = 对象 (Object): 根据类创建出来的具体实例(运行中的应用)。

3. 安装 Docker (极简步骤)

安装 Docker 非常简单:

  • Windows / macOS: 下载并安装 Docker Desktop。安装完成后,Docker 会作为一个应用程序运行。
  • Linux: 根据你的发行版使用包管理器安装。例如 Ubuntu/Debian 可以使用 apt,CentOS/RHEL 可以使用 yumdnf。具体命令请参考 Docker 官方文档。
    • 例如 Ubuntu: sudo apt update && sudo apt install docker.io

核心要求: 安装完成后,打开终端(Windows 用 PowerShell 或 CMD,macOS/Linux 用 Terminal),运行以下命令验证安装是否成功:

docker --version

如果看到类似 Docker version 20.10.17, build 100c701 的输出,恭喜你,Docker 已经成功安装!

4. 初体验:运行你的第一个容器

让我们用 Docker 运行一个最经典的入门容器:

docker run hello-world

运行这条命令后发生了什么?

  1. 检查本地镜像: Docker 引擎首先检查你的本地机器上是否存在名为 hello-world 的镜像。
  2. 拉取镜像: 如果本地没有,Docker 引擎会自动从默认的公共仓库 Docker Hub 拉取(下载)最新的 hello-world 镜像。
  3. 创建并运行容器: Docker 引擎基于 hello-world 镜像创建一个新的容器实例,并启动它。
  4. 输出信息: 容器内部运行一个简单的程序,将一段 "Hello from Docker!" 的信息打印到你的终端上。
  5. 退出: 程序执行完毕后,容器自动停止并退出。

关键命令初识:

  • docker run: 这是 Docker 最常用的命令之一,它负责创建并启动一个新容器。
  • docker pull: 显式地从仓库拉取镜像(在上面的例子中,docker run 在需要时自动执行了拉取操作)。

看到 "Hello from Docker!" 了吗?你已经成功运行了你的第一个 Docker 容器!

5. 实战操作:常用 Docker 命令速查

掌握以下常用命令,你就能自如地管理镜像和容器了。

镜像管理:

  • 拉取镜像: 从仓库下载镜像到本地。
    docker pull <image_name>:<tag>  # 例如:docker pull ubuntu:22.04
    

  • 列出本地镜像: 查看所有已下载到本地的镜像。
    docker images
    # 或
    docker image ls
    

  • 删除镜像: 删除不再需要的本地镜像。
    docker rmi <image_id>  # 使用 docker images 查看到的 IMAGE ID
    # 或
    docker image rm <image_id>
    

容器生命周期管理:

  • 创建并运行容器: 这是最核心的命令。
    docker run [options] <image> [command] [args...]
    

    • 常用选项:
      • -d: 在后台运行容器 (Detached mode)。如果不加此选项,容器会在前台运行并占用你的终端。
      • --name <name>: 给容器指定一个自定义名称,方便后续管理。如果不指定,Docker 会随机生成一个名字。
      • -p <host_port>:<container_port>: 将主机的端口映射到容器的端口。这样你才能通过主机访问容器内部的服务(如 Web 服务器)。
      • -v <host_dir>:<container_dir>: 将主机的一个目录或文件挂载到容器内部的一个路径。用于持久化存储容器数据或共享文件。
      • -it: 通常与 /bin/bash/bin/sh 结合使用,以交互模式运行容器并进入其 Shell(需要容器内有 Shell)。-i 保持 STDIN 打开,-t 分配一个伪终端。
    • 示例:
      docker run -it --name myubuntu ubuntu:22.04 /bin/bash  # 运行Ubuntu并进入其bash
      docker run -d --name webserver -p 80:80 nginx           # 后台运行Nginx,映射主机80端口到容器80端口
      

  • 列出容器:
    docker ps       # 查看当前正在运行的容器
    docker ps -a    # 查看所有容器,包括已停止的
    

  • 停止容器:
    docker stop <container_id_or_name>  # 例如:docker stop webserver
    

  • 启动已停止的容器:
    docker start <container_id_or_name>
    

  • 重启容器:
    docker restart <container_id_or_name>
    

  • 删除容器: 删除已停止的容器(运行中的容器需要先停止才能删除)。
    docker rm <container_id_or_name>
    # 强制删除运行中的容器(慎用)
    docker rm -f <container_id_or_name>
    

其他实用命令:

  • 进入运行中的容器: 对运行中的容器执行命令。常用于进入容器的 Shell 进行调试或操作。
    docker exec -it <container_id_or_name> /bin/bash  # 或其他Shell如 /bin/sh
    

  • 查看容器日志: 查看容器的标准输出和错误日志,对于调试非常有用。
    docker logs <container_id_or_name>
    # 实时查看日志
    docker logs -f <container_id_or_name>
    

6. 实战案例:部署一个简单的 Web 应用 (Nginx)

让我们来点更实用的,用 Docker 快速部署一个 Nginx Web 服务器。

目标: 在本地运行一个 Nginx 容器,并通过浏览器访问它。

步骤:

  1. 拉取 Nginx 镜像: (如果本地没有,docker run 也会自动拉取,这里显式执行)

    docker pull nginx:latest
    

  2. 运行容器: 这是关键一步。

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

    • -d: 让容器在后台运行。
    • --name my-nginx: 给容器起一个名字叫 my-nginx,方便后续操作(启动、停止、删除等)。
    • -p 8080:80: 进行端口映射。将主机(你的电脑)的 8080 端口映射到容器内部的 80 端口(Nginx 默认监听端口)。这意味着访问你本机的 8080 端口就相当于访问容器内的 80 端口。
    • nginx: 指定要运行的镜像名称(这里使用了默认的 latest 标签)。
  3. 验证: 打开你的网页浏览器,访问地址:http://localhost:8080。你应该能看到 Nginx 的默认欢迎页面!这证明你的容器化 Nginx 服务已经成功运行。

  4. 查看运行状态: 在终端运行:

    docker ps
    

    你应该能看到名为 my-nginx 的容器正在运行 (STATUS 显示为 Up)。

  5. 停止容器: 当你不需要这个 Nginx 服务时:

    docker stop my-nginx
    

    再次运行 docker ps,这个容器就不见了。运行 docker ps -a 可以看到它处于 Exited 状态。

  6. (可选) 删除容器: 如果你确定不再需要这个容器:

    docker rm my-nginx
    

恭喜!你已经成功地在 Docker 容器中部署并运行了一个 Web 服务器。

7. 进阶实战:构建自定义镜像 (Dockerfile)

运行别人的镜像很方便,但如何将自己的应用也打包成镜像运行呢?这就需要用到 Dockerfile

为什么需要? 为了封装你自己的应用程序代码、依赖环境和启动命令,创建一个可移植、可重复部署的镜像。

Dockerfile 是什么? Dockerfile 是一个纯文本文件,里面包含了一系列指令(Instruction),告诉 Docker 引擎如何一步一步地构建一个新的镜像。

关键指令:

  • FROM <base_image>:<tag>: 指定基础镜像。你的镜像通常基于一个已有的镜像开始构建(如 ubuntu, python, node)。这是 Dockerfile 的第一条指令。
  • RUN <command>: 执行命令。在构建过程中,在容器内部执行命令。常用于安装软件包、更新系统、编译代码等。
    • 示例:RUN apt update && apt install -y python3
  • COPY <src> <dest> / ADD <src> <dest>: 复制文件。将主机上的文件或目录复制到正在构建的镜像内部。
    • COPY 更常用,语义更清晰。
    • 示例:COPY . /app 将当前目录(主机)下的所有内容复制到镜像的 /app 目录下。
  • WORKDIR <path>: 设置工作目录。指定后续 RUN, CMD, ENTRYPOINT, COPY, ADD 等指令在容器内的默认工作路径。
    • 示例:WORKDIR /app
  • EXPOSE <port>: 声明端口。声明容器在运行时将监听的网络端口。这只是一个声明,方便使用者知道应该映射哪个端口,实际映射需要在 docker run 时用 -p 参数指定。
    • 示例:EXPOSE 5000
  • CMD ["executable", "param1", "param2"] / ENTRYPOINT ["executable", "param1", "param2"]: 指定容器启动命令。定义当容器启动时默认执行的命令。
    • CMD: 提供容器运行时的默认命令和参数。可以被 docker run 后面跟的命令覆盖。
    • ENTRYPOINT: 指定容器启动时运行的可执行文件。CMD 的内容会作为参数传递给 ENTRYPOINT。不易被 docker run 覆盖。
    • 通常两者结合使用:ENTRYPOINT 定义要运行的程序,CMD 定义默认参数。
    • 示例:CMD ["python", "app.py"]ENTRYPOINT ["python"] CMD ["app.py"]

简单示例:构建一个运行 Python 脚本的镜像

假设你有一个简单的 Python Flask 应用,结构如下:

my-python-app/
├── app.py
└── requirements.txt

创建一个名为 Dockerfile 的文件(无后缀),内容如下:

# 使用官方的 Python 轻量级镜像作为基础
FROM python:3.9-slim

# 在镜像内设置工作目录为 /app
WORKDIR /app

# 先将依赖文件复制到工作目录
COPY requirements.txt .

# 安装 Python 依赖
# --no-cache-dir 避免缓存,减小镜像体积
RUN pip install --no-cache-dir -r requirements.txt

# 将当前目录下的所有文件复制到镜像的 /app 目录
COPY . .

# 声明容器运行时监听的端口 (假设 Flask 运行在 5000)
EXPOSE 5000

# 定义容器启动时运行的命令
CMD ["python", "app.py"]

构建镜像:

在包含 Dockerfile 和应用程序代码的目录 (my-python-app/) 中打开终端,运行:

docker build -t my-python-app .

  • docker build: 构建镜像的命令。
  • -t my-python-app: -t 用于给构建出来的镜像指定一个名称 (my-python-app) 和标签(这里省略标签,默认为 latest)。
  • .: 指定构建上下文 (Context) 的路径(. 表示当前目录)。Docker 会将这个目录下的文件发送给 Docker 守护进程用于构建。

构建过程会一步步执行 Dockerfile 中的指令。

运行自定义镜像:

镜像构建成功后,就可以像运行其他镜像一样运行它了:

docker run -d -p 5000:5000 my-python-app

  • -d: 后台运行。
  • -p 5000:5000: 将主机的 5000 端口映射到容器声明的 5000 端口。
  • my-python-app: 指定要运行的镜像名称。

现在,你的自定义 Python 应用就在 Docker 容器中运行起来了!访问 http://localhost:5000 应该能看到你的应用。

8. 实战利器:Docker Compose (管理多容器应用)

现代应用往往由多个服务组成,比如一个 Web 应用需要搭配一个数据库(如 Redis, MySQL, PostgreSQL)。手动用 docker run 启动每个容器并配置它们之间的网络连接会很麻烦。Docker Compose 就是解决这个问题的工具。

为什么需要? Docker Compose 允许你使用一个 YAML 文件 (docker-compose.yml) 来定义和运行多个相关联的容器组成的应用。它简化了多容器应用的部署、管理和网络配置。

docker-compose.yml 文件: 这个文件定义了你的应用需要的服务(Service)、网络(Network)、数据卷(Volume)等配置。

核心命令:

  • 启动所有服务: 根据 docker-compose.yml 创建并启动所有定义的服务(容器)。-d 表示后台运行。
    docker-compose up -d
    

  • 停止并移除所有资源: 停止运行中的容器,并移除由 docker-compose up 创建的网络、数据卷(默认不删除持久化卷)等资源。
    docker-compose down
    

  • 查看服务状态: 列出项目中各个服务的状态。
    docker-compose ps
    

简单示例:定义一个 Web + Redis 的应用

假设你的 Web 应用(假设已经构建好镜像)需要连接一个 Redis 服务。创建一个 docker-compose.yml 文件:

version: '3'  # 指定使用的 Compose 文件版本

services:     # 定义服务
  web:        # 服务名:web
    build: .  # 指定构建上下文为当前目录,根据 Dockerfile 构建镜像
    ports:
      - "5000:5000"  # 端口映射,主机端口:容器端口
    # 可以添加环境变量、依赖等其他配置
    # depends_on:
    #   - redis
  redis:      # 服务名:redis
    image: "redis:alpine"  # 直接使用 Redis 的官方 alpine 镜像

运行:

在包含 docker-compose.yml 文件的目录下运行:

docker-compose up -d

Docker Compose 会自动:

  1. 为两个服务(webredis)创建一个默认的网络,使它们能够相互通信(通常通过服务名作为主机名)。
  2. 根据 Dockerfile 构建 web 服务的镜像(如果本地不存在)。
  3. 拉取 redis:alpine 镜像(如果本地不存在)。
  4. 创建并启动两个容器。

现在你的 Web 应用就可以在容器内通过主机名 redis 连接到 Redis 容器了。访问 http://localhost:5000 即可访问 Web 服务。

停止:

docker-compose down

9. 常见问题与小技巧

  • 权限问题 (Linux):
    • 错误: Got permission denied while trying to connect to the Docker daemon socket
    • 原因: 默认情况下,只有 root 用户和 docker 组的成员才有权限与 Docker 守护进程通信。
    • 解决: 将你的用户加入 docker 组:
      sudo usermod -aG docker $USER  # $USER 是你的用户名
      

      重要: 执行此命令后,你需要注销并重新登录系统,或者开启一个新的终端会话,权限更改才会生效。
  • 清理: Docker 运行一段时间后,可能会积累很多未使用的镜像、停止的容器、网络等,占用磁盘空间。
    docker system prune
    

    这个命令会删除:
    • 所有已停止的容器
    • 所有未被任何容器使用的网络
    • 所有未被任何容器使用的卷(默认安全选项)
    • 所有未被任何容器引用的镜像
    • 所有构建缓存 注意: 使用前请确认这些资源确实不再需要。加 -f 可以跳过确认提示。
  • 数据持久化: 容器内部的文件系统是临时的,容器停止或删除后,里面的数据就会丢失(除非使用卷)。使用 -v--mount 将主机目录挂载到容器内是实现数据持久化的标准做法。
    docker run -v /path/on/host:/path/in/container ...
    

  • 查看帮助: Docker 命令众多,记不住参数很正常,善用帮助:
    docker --help                 # 查看所有命令
    docker <command> --help       # 查看具体命令的帮助,如 docker run --help
    

10. 总结与下一步

核心回顾:

  • 镜像 (Image): 应用的静态模板。
  • 容器 (Container): 镜像的运行实例。
  • 常用命令: run, ps, stop, start, rm, images, rmi, exec, logs, build
  • Dockerfile: 定义如何构建自定义镜像。
  • Docker Compose: 管理多容器应用。

你已掌握: 通过本篇实战指南,你应该已经能够:

  • 理解 Docker 的核心概念和优势。
  • 安装 Docker。
  • 运行、停止、管理容器。
  • 拉取和使用公共镜像。
  • 部署简单的容器化应用(如 Nginx)。
  • 使用 Dockerfile 构建自定义应用镜像。
  • 使用 Docker Compose 定义和运行多服务应用。

下一步建议:

  • 深入学习 Dockerfile 最佳实践: 学习如何编写高效、安全、体积小的 Dockerfile(如多阶段构建)。
  • 探索 Docker 网络: 了解 Docker 提供的不同网络驱动(bridge, host, none),学习如何自定义网络和连接容器。
  • 了解容器编排: 当需要在多台机器上管理和扩展大量容器时,就需要用到 Kubernetes 等容器编排工具。
  • 尝试部署更复杂的应用: 用 Docker 部署一个包含 Web 后端(如 Django/Flask/Spring Boot)、前端(React/Vue)、数据库(PostgreSQL/MySQL)、缓存(Redis)的完整应用栈。
  • 探索云服务: AWS、Azure、GCP 等云平台都提供了强大的容器服务(如 ECS, AKS, GKE)。

最重要的是:实践! 多动手操作,尝试部署不同的应用,遇到问题查阅文档和社区。Docker 的世界很大,祝你探索愉快!


更多推荐