ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Docker容器化部署实战:从核心概念到安装配置与常用命令详解

Docker容器化部署实战:从核心概念到安装配置与常用命令详解

1. 从“为什么需要Docker”开始聊起

如果你是一个开发者,或者正在学习运维,那么“Docker”这个词你肯定不陌生。但很多时候,我们只是跟着教程敲命令,docker rundocker ps,却很少停下来想想,为什么我们需要它?它到底解决了什么痛点?在我自己从手动部署到容器化部署的转变过程中,Docker带来的最大改变,不是命令本身,而是一种思维和工作流的革新。在没有Docker之前,我们经常遇到这样的场景:开发环境跑得好好的代码,一到测试环境就报错,排查半天发现是Node.js版本不对;或者运维同事拿着一份长长的环境配置文档,小心翼翼地安装依赖,生怕漏掉某个系统库。这种“在我的机器上能跑”的问题,几乎成了软件开发的一个经典笑话。

Docker的出现,就是为了封装这个“环境”。它通过容器技术,将应用及其所有依赖(包括代码、运行时、系统工具、系统库和设置)打包成一个标准化的单元。这个单元可以在任何安装了Docker引擎的计算机上一致地运行。你可以把它想象成一个极其轻量级的、便携式的虚拟机,但它共享宿主机的操作系统内核,因此启动更快、资源开销更小。理解了这一点,再去看那些安装和配置步骤,你就会明白每一步的目的,而不仅仅是机械地复制粘贴命令。今天,我就结合自己多年的使用经验,从最基础的安装配置讲起,再到那些真正高频、实用的命令,帮你构建一个清晰、可操作的Docker知识框架。

2. Docker核心概念扫盲:镜像、容器与仓库

在动手安装之前,花几分钟搞清楚三个核心概念至关重要,这能让你后面的学习事半功倍。很多新手容易在这里混淆。

镜像(Image):你可以把它理解为一个只读的模板。这个模板包含了运行某个软件所需的所有内容——代码、运行时环境、库、环境变量和配置文件。例如,一个Ubuntu镜像就是一个精简的Ubuntu root文件系统;一个Nginx镜像则包含了编译好的Nginx程序及其运行环境。镜像是创建容器的基础,本身是静态的、分层的。Docker使用联合文件系统(UnionFS)来构建镜像,这意味着每一层都是只读的,并且可以被多个镜像共享,这极大地节省了存储空间。

容器(Container):容器是镜像的运行实例。当你从镜像启动一个容器时,Docker会在镜像的只读层之上,添加一个可写的“容器层”。所有对运行中容器的修改(如创建新文件、安装新软件)都发生在这个可写层。容器可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的应用沙箱。关键点在于:容器是短暂的。通常,我们不应该把需要持久化的数据或状态保存在容器层,而是通过“数据卷”来处理。

仓库(Registry):仓库是集中存放镜像的地方,类似于代码仓库GitHub。最大的公共仓库是Docker官方的Docker Hub,上面有无数官方和个人维护的镜像。你也可以搭建私有的仓库,用于存放企业内部的应用镜像。docker pull命令就是从仓库拉取镜像,docker push则是将本地镜像推送到仓库。

这三者的关系很简单:从仓库拉取(Pull)镜像到本地,然后用这个镜像创建(Create)并启动(Run)一个容器。一个镜像可以创建出多个同时运行的容器实例。

3. 在不同操作系统上安装Docker引擎

Docker引擎的安装因操作系统而异。这里我会覆盖最主流的三个平台:Linux(以Ubuntu为例)、macOS和Windows。我会重点讲Linux,因为它是生产环境中最常见的部署平台,同时也会指出其他平台的关键注意事项。

3.1 在Ubuntu/Linux上安装Docker

这是最经典也是最重要的安装场景。生产环境的服务器绝大多数都是Linux。以下步骤在Ubuntu 20.04 LTS及更新版本上测试通过。

首先,更新现有的软件包列表并安装一些必要的工具,这些工具允许apt通过HTTPS使用仓库:

sudo apt-get update sudo apt-get install ca-certificates curl gnupg lsb-release

接下来,添加Docker的官方GPG密钥和稳定版仓库。这一步是为了确保我们下载的软件包来自可信源,并且能接收到后续的安全更新。

# 创建用于存储密钥的目录 sudo mkdir -p /etc/apt/keyrings # 下载并导入Docker的GPG密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加Docker的APT仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

注意$(lsb_release -cs)会自动获取你的Ubuntu发行版代号(如focal、jammy)。确保Docker支持你的发行版。如果遇到问题,可以去Docker官网查看支持的版本列表。

更新包索引,并安装Docker引擎、命令行工具(CLI)以及容器运行时接口(ContainerD):

sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

安装完成后,启动Docker服务并设置开机自启:

sudo systemctl start docker sudo systemctl enable docker

最后,也是最关键的一步:将当前用户添加到docker用户组。这是因为Docker守护进程默认以root权限运行,而docker命令需要通过Unix socket与守护进程通信,该socket默认由root用户和docker用户组拥有。

sudo usermod -aG docker $USER

执行此命令后,你需要完全退出当前终端会话并重新登录,或者新开一个终端,用户组变更才会生效。之后,你就可以不用sudo直接运行docker命令了。

验证安装是否成功:

docker --version docker run hello-world

如果看到Docker版本信息,并且hello-world镜像能成功运行并输出欢迎信息,说明安装一切正常。

3.2 在macOS上安装Docker Desktop

对于macOS用户,官方推荐使用Docker Desktop。它是一个集成了Docker引擎、CLI、Compose和图形化界面的应用程序。

  1. 访问Docker官网:前往 Docker Hub 的下载页面,选择适用于Mac(Intel芯片或Apple芯片)的Docker Desktop安装包。
  2. 下载并安装:双击下载的.dmg文件,将Docker图标拖拽到“应用程序”文件夹中。
  3. 首次运行:从“应用程序”启动Docker Desktop。首次启动需要一些时间来完成初始化,并会请求系统权限(如访问网络、创建虚拟化环境)。
  4. 验证:启动完成后,可以在顶部菜单栏看到Docker的鲸鱼图标。打开终端,输入docker --versiondocker run hello-world进行验证。

关键点:Docker Desktop for Mac实际上是在macOS上运行了一个轻量级的Linux虚拟机(对于Apple Silicon芯片是Linux ARM64虚拟机),所有的容器都运行在这个虚拟机内。这对于用户是透明的,但当你需要挂载本地目录到容器时(-v参数),需要理解路径是相对于这个虚拟机环境的。通常,/Users目录会被自动共享进去。

3.3 在Windows上安装Docker Desktop

Windows上的安装与macOS类似,但依赖于Windows的Hyper-V或WSL 2(Windows Subsystem for Linux 2)后端。

前提条件

  • 对于Windows 10/11专业版、企业版或教育版:建议使用WSL 2后端。需要先启用“适用于Linux的Windows子系统”和“虚拟机平台”功能,然后安装一个WSL发行版(如Ubuntu)。
  • 对于Windows 10家庭版:旧版本可能需要启用Hyper-V,但Docker Desktop新版强烈推荐并支持WSL 2后端,即使家庭版也能获得更好的体验。

安装步骤

  1. 从Docker官网下载Docker Desktop for Windows安装程序。
  2. 运行安装程序,安装过程中通常会提示启用WSL 2或Hyper-V,按照提示操作即可。
  3. 安装完成后重启电脑。
  4. 启动Docker Desktop,同样在系统托盘区会有图标。首次启动同样需要初始化。
  5. 在PowerShell或CMD中运行docker --versiondocker run hello-world验证。

选择WSL 2后端的好处:性能远超传统的Hyper-V,文件I/O速度更快,并且能实现与本地Windows文件系统的无缝集成,资源消耗也更低。在Docker Desktop的设置中,可以轻松切换后端。

4. 配置国内镜像加速器

从Docker Hub拉取镜像,对于国内用户来说速度可能很慢,甚至经常超时。配置一个国内的镜像加速器是安装后的首要优化步骤。这相当于为Docker换了一个离你更近的下载源。

以阿里云镜像加速器为例(其他如腾讯云、网易云、中科大等也提供类似服务):

  1. 注册并登录阿里云容器镜像服务控制台。
  2. 在左侧菜单找到“镜像工具” -> “镜像加速器”。
  3. 你会看到一个专属于你的加速器地址,格式如https://xxxx.mirror.aliyuncs.com

配置方法根据操作系统不同:

Linux (Ubuntu/CentOS等): 编辑Docker守护进程的配置文件/etc/docker/daemon.json(如果文件不存在则新建):

{ "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"] }

保存后,重启Docker服务使配置生效:

sudo systemctl daemon-reload sudo systemctl restart docker

macOS / Windows (Docker Desktop)

  1. 点击系统托盘区的Docker图标,选择“Settings”(设置)或“Preferences”(偏好设置)。
  2. 找到“Docker Engine”选项。
  3. 在配置JSON中,添加或修改registry-mirrors字段,填入你的加速器地址。
  4. 点击“Apply & Restart”应用并重启。

配置完成后,使用docker info命令,在输出信息中查找Registry Mirrors,确认加速器地址已生效。之后拉取镜像的速度会有显著提升。

5. Docker生命周期管理:从创建到销毁的常用命令

掌握了安装和基础配置,我们进入最核心的部分:命令。我将按照一个容器的完整生命周期来组织这些命令,这样更容易理解和记忆。

5.1 镜像相关操作

镜像是一切的起点。

  • 搜索镜像docker search <镜像名>用于在Docker Hub上搜索公开的镜像。例如docker search nginx。不过,更常见的做法是直接去Docker Hub网站查看,信息更全。

  • 拉取镜像docker pull <镜像名>[:标签]从仓库下载镜像到本地。如果不指定标签,默认拉取latest标签。强烈建议生产环境指定具体版本标签,如docker pull nginx:1.23-alpinealpine版本基于极简的Alpine Linux,镜像体积非常小。

  • 列出本地镜像docker imagesdocker image ls查看所有已下载的镜像,包括仓库名、标签、镜像ID、创建时间和大小。

  • 删除本地镜像docker rmi <镜像ID或镜像名:标签>docker image rm ...删除不再需要的镜像以释放磁盘空间。如果镜像有对应的容器存在(即使已停止),需要先删除容器才能删除镜像。使用-f参数可以强制删除。

  • 查看镜像历史docker history <镜像名>查看镜像的构建历史,即每一层的内容和大小,对于理解镜像构成和优化很有帮助。

  • 导出/导入镜像docker savedocker load用于离线环境迁移镜像。docker save -o nginx.tar nginx:alpine将镜像保存为tar文件。docker load -i nginx.tar从tar文件加载镜像。

5.2 容器运行与管理

这是最频繁的操作集合。

  • 运行容器docker run [选项] <镜像名> [命令]这是最核心的命令。它从镜像创建并启动一个新容器。选项非常多,常用的有:

    • -d:后台运行(守护进程模式)。
    • -it:交互式运行,通常一起使用,分配一个伪终端并保持标准输入打开,用于进入容器内部操作,如docker run -it ubuntu bash
    • --name:为容器指定一个自定义名称,否则Docker会分配一个随机名字。
    • -p:端口映射,格式-p <宿主机端口>:<容器端口>,如-p 8080:80将容器的80端口映射到宿主机的8080端口。
    • -v:挂载数据卷,格式-v <宿主机目录>:<容器目录>,用于持久化数据或共享配置文件。
    • -e:设置环境变量,如-e MYSQL_ROOT_PASSWORD=my-secret-pw
    • --restart:设置重启策略,如--restart=always确保容器退出时总是重启,非常适合运行服务。

    示例:运行一个Nginx容器,后台运行,命名,并映射端口。

    docker run -d --name my-nginx -p 80:80 nginx:alpine
  • 列出容器docker ps [选项]

    • docker ps:列出正在运行的容器。
    • docker ps -a:列出所有容器(包括已停止的)。 输出信息包括容器ID、名称、使用的镜像、状态、端口映射等。
  • 停止/启动/重启容器

    • docker stop <容器名或ID>:优雅地停止运行中的容器(发送SIGTERM信号)。
    • docker start <容器名或ID>:启动一个已停止的容器。
    • docker restart <容器名或ID>:重启容器。
  • 进入运行中的容器docker exec [选项] <容器名或ID> <命令>这是与运行中容器交互的关键命令。常用-it选项启动一个交互式shell。

    docker exec -it my-nginx /bin/sh # alpine镜像通常用sh docker exec -it my-nginx bash # Ubuntu等完整镜像用bash

    注意docker attach也可以附着到容器,但它连接的是容器的主进程(PID 1)的输入输出。如果主进程是shell,attach进去后,退出shell会导致容器停止。而docker exec是新建一个进程,退出不会影响容器主进程。绝大多数情况下,应使用docker exec

  • 查看容器日志docker logs [选项] <容器名或ID>查看容器的标准输出(STDOUT)和错误输出(STDERR),是排查问题最重要的工具之一。

    • -f:实时跟踪日志输出(类似tail -f)。
    • --tail N:仅显示最后N行日志。
    • -t:显示时间戳。
  • 查看容器内进程docker top <容器名或ID>查看容器内部运行的进程列表。

  • 查看容器详细信息docker inspect <容器名或ID>以JSON格式返回容器的底层详细信息,包括配置、网络设置、挂载卷、状态等。信息非常全面,用于深度调试。

  • 复制文件docker cp

    • 从容器复制到主机:docker cp <容器名>:<容器内路径> <主机路径>
    • 从主机复制到容器:docker cp <主机路径> <容器名>:<容器内路径>用于在容器和宿主机之间传输文件,适合临时操作。对于需要频繁同步的数据,应使用数据卷(-v)。
  • 删除容器docker rm <容器名或ID>删除已停止的容器。加-f参数可以强制删除运行中的容器(先发送SIGKILL)。警告:删除容器会丢失容器层所有未持久化的数据。

5.3 数据持久化:数据卷与挂载

容器本身是易失的,数据卷(Volume)是Docker中持久化数据的首选机制。它完全由Docker管理,独立于容器的生命周期。

  • 创建数据卷docker volume create <卷名>
  • 列出数据卷docker volume ls
  • 查看数据卷详情docker volume inspect <卷名>
  • 删除未使用的数据卷docker volume prune(谨慎操作)

在运行容器时,使用-v--mount参数挂载数据卷。--mount语法更明确,推荐使用。

# 使用 -v (旧式,但更简洁) docker run -d --name mysql-db \ -v mysql_data:/var/lib/mysql \ # ‘mysql_data’是卷名,自动创建 -e MYSQL_ROOT_PASSWORD=secret \ mysql:8.0 # 使用 --mount (新式,语法更清晰) docker run -d --name mysql-db \ --mount type=volume,src=mysql_data,dst=/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ mysql:8.0

除了卷挂载(type=volume),还可以进行绑定挂载(type=bind,直接挂载宿主机目录)和临时文件系统挂载(type=tmpfs)。

5.4 网络管理

Docker提供了多种网络模式,默认会创建bridge、host、none等网络。

  • 列出网络docker network ls
  • 创建网络docker network create <网络名>,可以指定子网、网关等。
  • 查看网络详情docker network inspect <网络名>
  • 将容器连接到网络docker network connect <网络名> <容器名>
  • 断开容器与网络的连接docker network disconnect <网络名> <容器名>

docker run时,可以使用--network参数指定网络。创建自定义的bridge网络,可以实现容器间通过容器名进行DNS解析(自动服务发现),这对于多容器应用(如Web应用连接数据库)非常方便。

# 1. 创建一个自定义网络 docker network create my-app-network # 2. 运行数据库容器,加入该网络 docker run -d --name mysql --network my-app-network -e MYSQL_ROOT_PASSWORD=secret mysql:8.0 # 3. 运行Web应用容器,加入同一网络。Web应用可以直接用‘mysql’这个主机名连接到数据库容器。 docker run -d --name webapp --network my-app-network -p 8080:80 my-webapp-image

6. 组合应用:Docker Compose入门

当你的应用需要多个容器协同工作时(例如一个Web应用需要Nginx、Node.js、Redis和PostgreSQL),手动用docker run一个个启动和管理会非常繁琐。Docker Compose就是用来定义和运行多容器Docker应用的工具。它使用一个YAML文件(默认名docker-compose.yml)来配置所有服务。

安装:在Linux上,如果你按照我之前的方法安装了docker-compose-plugin,那么Compose V2已经作为Docker CLI的一个插件(docker compose)可用。命令是docker compose(注意中间没有横线)。在macOS和Windows的Docker Desktop中,它已内置。

一个典型的docker-compose.yml示例,定义一个WordPress应用(包含WordPress和MySQL):

version: '3.8' # 指定Compose文件格式版本 services: db: image: mysql:8.0 volumes: - db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db image: wordpress:latest ports: - "8000:80" restart: always environment: WORDPRESS_DB_HOST: db:3306 # 使用服务名‘db’作为主机名 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress WORDPRESS_DB_NAME: wordpress volumes: - wp_data:/var/www/html volumes: db_data: wp_data:

常用Compose命令

  • docker compose up:启动所有服务。加-d在后台运行。
  • docker compose down:停止并移除所有容器、网络。加-v会同时删除在Compose文件中声明的数据卷(危险操作)。
  • docker compose ps:列出项目中的所有容器。
  • docker compose logs [服务名]:查看服务的日志。加-f跟踪输出。
  • docker compose exec <服务名> <命令>:在运行中的服务容器内执行命令。
  • docker compose build:根据Dockerfile重新构建服务的镜像。

Compose极大地简化了多容器环境的部署和管理,是本地开发和测试复杂应用的利器,也常用于简单的生产部署。

7. 镜像构建基石:理解Dockerfile

虽然从仓库拉取镜像很方便,但更多时候我们需要构建自己的镜像,将应用打包进去。这就需要编写Dockerfile。Dockerfile是一个文本文件,包含了一系列构建镜像所需的指令。

一个简单的Node.js应用Dockerfile示例:

# 第一阶段:构建阶段 FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # 仅安装生产依赖 # 第二阶段:运行阶段 FROM node:18-alpine WORKDIR /app # 从构建阶段复制已安装的node_modules COPY --from=builder /app/node_modules ./node_modules # 复制应用源码 COPY . . # 声明容器运行时监听的端口 EXPOSE 3000 # 定义容器启动时执行的命令 CMD ["node", "server.js"]

常用指令解析

  • FROM:指定基础镜像,所有构建都基于此。选择合适且小的基础镜像(如alpine变体)是优化镜像体积的第一步。
  • WORKDIR:设置工作目录,后续的RUNCOPY等命令都会在此目录下执行。
  • COPY:将文件或目录从构建上下文复制到镜像中。COPY . .是把当前目录所有文件复制到镜像的/app目录。
  • RUN:在构建过程中执行命令,常用于安装软件包、编译代码等。多个RUN命令应尽量合并,并用&&连接,以减少镜像层数。每一条指令都会创建一个新的镜像层。
  • EXPOSE:声明容器运行时监听的端口,这是一个文档性质的指令,实际映射端口仍需在docker run时用-p指定。
  • CMD:指定容器启动时默认执行的命令。一个Dockerfile中只能有一条CMD指令。它有三种格式,推荐使用exec格式CMD ["executable", "param1", "param2"])。

构建镜像:在Dockerfile所在目录执行docker build -t <镜像名:标签> .。最后的.表示构建上下文路径。

多阶段构建:如上例所示,它允许你在一个Dockerfile中使用多个FROM指令。你可以用一个包含完整构建工具(如编译器)的“构建阶段”镜像来编译代码,然后将编译好的产物复制到另一个更精简的“运行阶段”镜像中。这能显著减小最终镜像的体积,并提高安全性(因为运行环境不包含构建工具)。

8. 实战中的经验、技巧与避坑指南

最后这部分,是我在多年使用Docker过程中积累的一些“血泪教训”和实用技巧,这些在官方文档里不一定会强调。

1. 容器内应用日志应输出到标准输出(STDOUT/STDERR)Docker的最佳实践是将应用日志直接打印到控制台,而不是写入容器内的文件。这样你就可以用docker logs命令统一查看和管理日志。对于像Nginx、MySQL这类默认写日志文件的软件,通常可以通过配置将其重定向到标准输出。例如,Nginx可以修改配置,将access log和error log指向/dev/stdout/dev/stderr

2. 谨慎使用docker commit虽然docker commit可以将一个运行中的容器保存为镜像,但这是一种“黑盒”操作,无法追溯镜像的构建历史,不利于维护和复现。构建镜像的标准方式永远是编写Dockerfile,然后使用docker builddocker commit仅适用于临时性的调试或实验。

3. 理解容器与宿主机的时间同步问题容器默认使用UTC时间,且与宿主机共享时钟。但如果宿主机时间不正确,或者你需要在容器内使用特定的时区,可以在运行容器时设置环境变量:-e TZ=Asia/Shanghai。对于基于Alpine的镜像,可能还需要安装tzdata包。

4. 清理无用资源,释放磁盘空间Docker用久了,会积累很多停止的容器、无用的镜像(<none>标签的中间层镜像)、未使用的数据卷和网络,占用大量磁盘空间。定期清理是个好习惯。

  • docker system prune:一键清理所有已停止的容器、未被任何容器使用的网络、悬空镜像(<none>)和构建缓存。加-a会清理所有未被容器使用的镜像(慎用)。加--volumes会一并清理未使用的数据卷(非常危险,确保数据已备份)。
  • 更精确的控制:分别使用docker container prune,docker image prune,docker volume prune,docker network prune

5. 容器内文件权限问题当使用-v挂载宿主机目录到容器时,可能会遇到容器内应用没有权限读写挂载目录的问题。这是因为容器内进程的用户(UID/GID)与宿主机文件的所有者不匹配。解决方法有两种:一是调整宿主机目录的权限(chmodchown);二是在运行容器时,使用-u参数指定容器内进程的用户ID,使其与宿主机目录所有者一致,例如-u $(id -u):$(id -g)

6. 不要在生产环境容器中运行SSH服务有些过时的教程会教你在容器里安装SSH,然后远程登录进去管理。这是一个反模式。容器的哲学是“一个容器一个进程”。你需要进入容器时,应该使用docker exec。如果需要调试或查看文件,可以用docker cp复制出来。在容器内运行额外服务会增加攻击面和安全风险。

7. 关注镜像安全尽量使用官方镜像(Docker Hub上带有“Official Image”标志的),并选择具体版本号,而不是latest。定期更新镜像以获取安全补丁。可以使用docker scan <镜像名>命令(需登录Docker Hub)对本地镜像进行安全漏洞扫描。对于自己构建的镜像,也应遵循最小化原则,只安装必要的包。

返回列表