Docker与Docker Compose部署实战:从入门到生产环境全指南
2026/9/19 5:27:34 网站建设 项目流程

1. 这篇教程能帮你解决什么问题

老读者都知道,我折腾服务器和部署这件事快十年了。早些年部署一个应用,先得装环境、配依赖、处理版本冲突,一套流程下来少说半天,多说一周。后来 Docker 出来了,我终于可以把环境和代码一起打包带走,再也不用在每台新机器上重复造轮子。再后来 Docker Compose 出现,多容器编排变得跟写配置文件一样简单,几个服务一键拉起,整套基础设施像个乐高积木一样按需拼装。

这篇教程不是概念科普,是我在实际项目中反复验证过的完整部署流程,覆盖 Docker 和 Docker Compose 从零到一、再到生产可用的全部环节。无论你是刚入门的新手,还是已经在服务器上手工部署过几次、想提升效率的开发者,都能从中找到可以直接照抄的操作。我尽量不绕弯子,所有命令都是我自己跑过的,坑也提前帮你踩平了。

2. Docker 到底解决了什么问题

2.1 没 Docker 之前部署有多痛

没有 Docker 的时代,部署一个 Web 项目大概是这样的:服务器上得先装好 Nginx、Node.js 或 Python、MySQL、Redis,每个软件还有自己的版本要求。项目 A 需要 Python 3.8,项目 B 需要 Python 3.11,两个版本一冲突,你就要开始研究虚拟环境;数据库数据目录、日志文件、配置文件散落在系统各处,备份和迁移都靠手工;新同事入职,光搭开发环境就能折磨他一整天。

我记得有一次帮朋友迁移一套老系统,原服务器上装了一堆依赖,光 PHP 扩展就编译了半个小时,换到新机器上直接起不来,排查到半夜发现是系统库版本不一致。这种经历,干过运维的人应该都能共鸣。

Docker 的思路特别朴素:把应用连同它需要的环境、依赖、配置全部装进一个“盒子”里,这个盒子就是镜像。无论底层机器是什么系统,只要装了 Docker,这个盒子就能以相同的方式运行。一致性、隔离性、可移植性,全都有了。你自己电脑上能跑的容器,推到服务器上一样能跑,不会再出现“在我机器上是好的啊”这种尴尬。

2.2 镜像、容器和数据卷的关系

这三个概念是 Docker 的基石,我习惯用一个类比帮助理解。

镜像就好比一个“安装光盘”或者“类模板”,它是只读的,里面包含了操作系统的基础文件、运行环境、应用代码,一切就绪,只等启动。容器则是镜像运行起来后的实例,相当于你拿这张光盘装出来的操作系统,可以启动、停止、删除。一个镜像可以同时创建多个容器,互相不影响。

那数据卷(Volume)又是干嘛的?容器是临时的,删掉之后内部的数据就没了。数据卷就是把容器里的某个目录映射到宿主机上,相当于给容器外接了一块“移动硬盘”。数据库的数据、日志文件、上传的图片,这些不能被销毁的东西,都要放到数据卷里。这个设计看起来简单,却是 Docker 能用于生产环境的关键一步。忘了挂载数据卷导致数据丢失的惨案,我在社区里见过不止一次。

2.3 为什么还要 Docker Compose

Docker 命令本身能完成所有操作,但真实项目不止一个容器。一个典型的 Web 应用,通常需要 Nginx 做反向代理、后端服务跑业务逻辑、Redis 做缓存、MySQL 存数据,四个容器之间还要互通网络。如果全用 docker run 命令一个个启动,参数又长又多,维护起来非常痛苦。

Docker Compose 就是干这个的:把一组容器的配置写进一个 YAML 文件里,包括镜像、端口映射、环境变量、数据卷、网络、依赖关系,然后一条 docker compose up -d 命令全部搞定。需要扩容就加一个 scale 参数,需要更新就改配置文件重新拉起,整个生命周期都变得可控。

这也是我为什么推荐所有使用 Docker 的人尽早学习 Compose,哪怕你的项目只有一个容器,用 Compose 管理也比长串的 docker run 命令清晰得多。配置文件就是你的部署文档,提交到 Git 里,全团队都能复用。

3. 环境准备:把 Docker 装起来

3.1 Linux 服务器安装 Docker(以 Ubuntu/Debian 为例)

绝大多数生产环境是 Linux 服务器,这里以 Ubuntu 系为例,其他发行版思路一致,只是包管理器不同。

先更新软件源,然后安装必要的依赖包,让 apt 支持通过 HTTPS 拉取软件源:

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

接下来添加 Docker 官方 GPG 密钥和软件源:

sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 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

更新源并安装:

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

安装完成后,先把当前用户加入 docker 组,免去每次敲 sudo 的麻烦:

sudo usermod -aG docker $USER newgrp docker

然后设置开机自启并验证版本:

sudo systemctl enable docker sudo systemctl start docker docker --version docker compose version

如果 docker compose version 能正常输出版本号,说明 Compose 插件也装好了。这里提醒一下,如果你以前单独装过 docker-compose(带横杠的老版本),那是 Python 写的独立工具,虽然也能用,但新项目建议直接用插件版 compose,功能同步更新,命令也统一为 docker compose。

3.2 Windows 和 macOS 怎么装

Windows 推荐直接装 Docker Desktop。去 Docker 官网下载安装包,双击安装,全程默认选项,登录一下 Docker Hub 账号就能用。安装过程中它会在后台启用 WSL2(Windows Subsystem for Linux),如果系统没有开启相关功能,可能会提示你重启或者手动开启。

macOS 同样使用 Docker Desktop,Intel 芯片和 Apple Silicon 芯片下载对应版本即可。装完之后 Docker Desktop 会在菜单栏常驻,打开就能看到容器状态和日志界面,图形化管理对新手很友好。

有朋友问我服务器上能不能装 Docker Desktop,不推荐。Docker Desktop 本身带一个轻量级虚拟机,适合本机开发调试,生产服务器用社区版引擎就够,省资源也好维护。Windows 上如果只是跑开发环境,也别忘了 Docker Desktop 资源占用较高,内存小于 8G 的机器会明显感觉卡顿。

3.3 安装后必做的两项基础配置

第一件事是配置镜像仓库加速。国内网络直接拉取 Docker Hub 的镜像经常超时,虽然网上各种加速方案变化很快,但通用做法都是一样的:在 /etc/docker/daemon.json 里配置 registry-mirrors 字段。网络环境正常的用户也可以不配置,或者自行选择合法的公共镜像源。这里我给一个标准配置文件格式:

{ "registry-mirrors": ["https://你的加速地址"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

我在配置文件里顺手加了日志轮转。这个很关键,不限制日志大小的容器跑久了,会悄悄把磁盘写满。10m 单个文件、保留 3 份,一般服务足够。

改完配置重启 Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

第二件事是测试拉取一个经典镜像,确认网络和引擎都正常:

docker pull hello-world docker run --rm hello-world

看到 “Hello from Docker!” 的输出,说明环境就绪。

4. 核心概念与常用命令速查

4.1 镜像操作:拉取、查看、删除

docker pull nginx:latest # 拉取镜像,latest 是默认标签 docker images # 查看本地镜像列表 docker image prune -f # 清理悬空镜像 docker rmi nginx:latest # 删除指定镜像

关于镜像标签,我建议生产环境不要随手用 latest,因为它指向的是“当前最新版”,可重复性差。今天拉的和三个月后拉的可能是两个版本。尽量用明确的版本号,比如 nginx:1.27.0,部署什么版本、什么时候升级,心里都有数。

4.2 容器操作:启动、日志、进入

docker ps # 查看运行中的容器 docker ps -a # 查看所有容器 docker logs -f <容器名或ID> # 跟踪日志输出 docker exec -it <容器名> /bin/bash # 进入容器内部 docker stop <容器名> # 停止容器 docker rm <容器名> # 删除容器

容器内部相当于一个精简的 Linux 环境,很多常用命令可能没装,比如 vim、ping。需要排查网络问题时,可以先进入容器再安装工具,也可以直接在宿主机上用 docker exec 执行命令。

4.3 docker run 关键参数拆解

docker run 的参数非常多,但日常最常用的就几个:

docker run -d \ --name myapp \ -p 8080:80 \ -v /host/data:/app/data \ -e MYSQL_ROOT_PASSWORD=123456 \ --restart=always \ nginx:1.27.0

-d 表示后台运行;-p 8080:80 表示把宿主机的 8080 端口映射到容器的 80 端口,访问 http://服务器IP:8080 就能到达容器内的 Nginx;-v 是数据卷挂载,冒号左边是宿主机路径,右边是容器内路径;-e 是设置环境变量;--restart=always 是容器退出或重启时自动拉起,生产环境基本都要加。

端口映射这里有个常见误区:容器内端口是应用实际监听的端口,宿主机端口是外部访问的入口,两者可以不同,例如 -p 80:8080 就是把宿主机的 80 转到容器的 8080。如果服务器上已经有一个服务占用了宿主机端口,换个宿主端口即可,比如 -p 8081:80。

5. Dockerfile 实战:构建自己的应用镜像

5.1 一个 Node.js 项目的基础镜像写法

Docker 最有价值的地方不只是跑现成镜像,而是把自己的项目打包成镜像。以最典型的 Node.js 项目为例,写一个 Dockerfile:

# 阶段一:构建 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 阶段二:运行 FROM node:20-alpine WORKDIR /app ENV NODE_ENV=production COPY --from=builder /app/dist ./dist COPY --from=builder /app/package*.json ./ RUN npm install --omit=dev EXPOSE 3000 CMD ["node", "dist/main.js"]

这段 Dockerfile 用到了多阶段构建,第一阶段的目的是安装依赖并构建产物,第二阶段只把需要的 dist 目录和依赖复制过来。这样最终镜像里不包含源码和构建工具链,体积小、更安全。我见过很多人把所有依赖一股脑装进最终镜像,镜像动辄 1G+,其实多阶段构建能把 Node 项目压到 200M 以内。

5.2 构建并推送到镜像仓库

构建镜像的命令:

docker build -t myapp:1.0.0 .

-t 指定镜像名称和标签,最后的点表示 Dockerfile 所在目录。构建完成后可以本地先跑一遍:

docker run -d -p 3000:3000 --name myapp-test myapp:1.0.0

然后通过 docker push 推送到镜像仓库(Docker Hub 或私有仓库):

docker tag myapp:1.0.0 yourusername/myapp:1.0.0 docker push yourusername/myapp:1.0.0

这里有几个心得供参考:基础镜像优先用 alpine 系列,体积小、攻击面小,进度快;.dockerignore 文件一定要写,把 node_modules、.git、日志这类文件排除在构建上下文之外,否则docker build .时会把整个目录发送给 Docker 守护进程,速度慢不说,还可能把本机敏感文件带进镜像。忘了写 .dockerignore 导致构建镜像巨大、上传速度缓慢的教训我犯过不止一次。

6. Docker Compose 编排:一套配置搞定多容器应用

6.1 编写第一个 docker-compose.yml

现在我以一个真实项目为例:一个 Web 服务,后端用 Node.js,数据存 MySQL,缓存用 Redis,前端用 Nginx 兜底。完整的 docker-compose.yml 如下:

version: "3.8" services: nginx: image: nginx:1.27.0 container_name: web-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/html:/usr/share/nginx/html - ./ssl:/etc/nginx/ssl depends_on: - backend networks: - app-net restart: always backend: image: myapp:1.0.0 container_name: web-backend environment: - NODE_ENV=production - DB_HOST=mysql - DB_PORT=3306 - REDIS_HOST=redis env_file: - .env depends_on: - mysql - redis networks: - app-net restart: always mysql: image: mysql:8.0 container_name: web-mysql ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql - ./mysql/init:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE=myapp networks: - app-net restart: always redis: image: redis:7.2-alpine container_name: web-redis command: redis-server --appendonly yes volumes: - redis-data:/data networks: - app-net restart: always volumes: mysql-data: redis-data: networks: app-net: driver: bridge

逐项解释一下。

services 是 Compose 文件的核心,定义了所有容器。image 指定镜像,container_name 是容器别名,ports 做端口映射,volumes 挂载数据卷,environment 设置环境变量,env_file 从文件导入环境变量,depends_on 声明依赖关系,restart 设置重启策略。

网络配置里我把所有服务放在同一个自定义网络 app-net 中,服务之间通过服务名互相访问,比如后端代码里连接 MySQL 就访问 host 为 mysql,连接 Redis 就访问 host 为 redis。这个自动 DNS 解析是 Compose 最大的便利之一,你不需要去查容器的 IP 地址,服务名就是域名。

6.2 PostgreSQL 与 Redis 主从的进阶编排

MySQL 之外,我另一个常部署的数据系统是 PostgreSQL。用 Compose 编排 PostgreSQL 加 Redis 主从,是很多项目的标准配置。

PostgreSQL 的 Compose 服务定义几乎和 MySQL 一样,只是镜像和配置细节不同:

postgres: image: postgres:16-alpine container_name: app-postgres ports: - "5432:5432" volumes: - pg-data:/var/lib/postgresql/data environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=appdb healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5

healthcheck 是新项目里我越来越常用的配置。它让 Compose 能感知服务是否真正健康,而不仅仅是进程是否启动。配合 depends_on 的 condition 条件,可以实现“等数据库完全就绪再启动应用”,避免应用启动时连不上数据库直接崩溃。不加 healthcheck 的 depends_on 只是控制启动顺序,并不能保证依赖服务已经可用,这个细节非常多人踩过。

Redis 主从的 Compose 配置也不复杂:

redis-master: image: redis:7.2-alpine container_name: app-redis-master command: redis-server --appendonly yes volumes: - redis-master-data:/data networks: - app-net redis-slave: image: redis:7.2-alpine container_name: app-redis-slave command: redis-server --slaveof redis-master 6379 --appendonly yes depends_on: - redis-master volumes: - redis-slave-data:/data networks: - app-net

注意从节点的 --slaveof 参数,它告诉 Redis 将自己挂到主节点的 6379 端口上。主从搭建完成后,应用读多写少就可以配置读写分离,只在主库写、从库读,能让系统并发能力上一个台阶。热词里也有 docker 安装 redis 主从,说明关注这块的人不少。

6.3 用 Compose 启动和停止整套服务

在 docker-compose.yml 所在目录下执行:

docker compose up -d

这个命令会拉取所有镜像、创建网络和数据卷、按依赖顺序启动容器。-d 表示后台运行。第一次启动可能耗时较长,因为要拉镜像。启动完成后看状态:

docker compose ps

停止服务:

docker compose down

这里要提醒一句:docker compose down 默认不会删除数据卷,所以 MySQL、Redis 等有持久化存储的服务,数据还在。如果你加上 -v 参数,即docker compose down -v,数据卷会被删除,这是不可逆的操作,生产环境千万慎用。我自己的习惯是在跑 down -v 之前,第一时间先自动备份一遍数据库。

之后的应用更新流程也很顺:改完代码重新构建镜像,或者改完配置文件,然后执行docker compose up -d,Compose 会自动判断哪些服务的配置或镜像有变化,只重建必要的容器,其他保持不变。

7. 网络、数据卷与资源限制的进阶配置

7.1 三种网络模式的差异

Docker 提供多种网络模式,Compose 里最常见的是 bridge、host 和 none。

默认的 bridge 网络是 NAT 模式,容器共享宿主机的 IP,通过端口映射对外提供服务,同时容器之间使用自定义网络内的服务名互相通信。隔离性和可用性都平衡得不错,Compose 项目默认就走这条路线。

host 模式下容器直接使用宿主机的网络栈,没有端口映射这一步,性能开销最低,适合对网络性能极敏感的服务。缺点是端口冲突的风险大,而且无法同时启动多个同端口的容器。

none 模式就是无网络配置,适合离线的批处理任务或者需要绝对隔离的场景,实际应用中很少碰到。

大部分时候用 bridge 就足够,只有像一些高性能缓存代理之类的场景才需要 host 模式。我遇到有人在 Compose 里给所有服务都用 host 网络,结果两个服务端口重叠直接冲突,反而折腾半天。

7.2 数据卷的备份与恢复

数据卷的备份和恢复直接决定容灾能力。用 mysqldump 备份 MySQL 数据,可以这样操作:

docker exec web-mysql mysqldump -u root -p --all-databases > backup.sql

也通过在容器里执行 tar 打包数据卷目录的方式做全量备份。恢复时反过来导入:

docker exec -i web-mysql mysql -u root -p < backup.sql

对 PostgreSQL 也是一样,用 pg_dump 和 psql 一套下来。这里我特别说一说:数据卷挂载路径不能搞错,MySQL 8.0 的数据目录是 /var/lib/mysql,PostgreSQL 16 是 /var/lib/postgresql/data,Redis 是 /data。你可以在 Compose 文件里通过docker volume inspect查看数据卷的宿主机挂载点,这样想手动备份或者拷贝数据都知道去哪找。

7.3 容器资源限制,防止“一台容器吃垮整台机”

生产中多容器共存一台服务器时,必须给容器配置资源上限。在 Compose 中可以为每个服务设置 deploy 下的 resources 字段:

backend: image: myapp:1.0.0 deploy: resources: limits: cpus: "0.50" memory: 512M reservations: cpus: "0.25" memory: 256M

cpus 中的 0.50 表示最多使用半个 CPU 核心,memory 是硬性上限,超过会被内核杀掉或触发 OOM。reservations 是预留资源,保证容器至少能拿到这么多。没有资源限制的容器就像不设预算的项目,内存泄漏时会让整台服务器响应变慢甚至卡死。我在构建镜像之初只跑一个容器时没这个习惯,后来一个爬虫任务 OOM 把监控系统一起拖垮,才老老实实给每个服务都加上资源限制。

8. 本地部署与前沿案例:Docker 不只是跑 Web 服务

8.1 用 Compose 快速搭建 Harbor 私有镜像仓库

团队大了之后,镜像放在 Docker Hub 公共仓库不方便也不安全。Harbor 是当前很流行的开源私有镜像仓库,支持权限管理、镜像复制、漏洞扫描。用 Compose 部署 Harbor 和部署普通服务一样直接。

Harbor 官方提供了在线安装包,本质上也是通过 docker-compose.yml 拉起一堆组件,包括 Harbor 核心、数据库、Redis、日志收集和 Nginx。推荐的方式是去官网下载离线安装包,解压后配置 harbor.yml 里的 hostname 和端口,然后运行 ./install.sh 一键起服务。这个过程我在测试环境反复跑过,整个启动大概需要几分钟,等所有容器状态变 healthy 就绪了。

私有仓库部署完成后,团队内推送镜像的命令就变成:

docker tag myapp:1.0.0 your-registry.com/library/myapp:1.0.0 docker login your-registry.com docker push your-registry.com/library/myapp:1.0.0

然后在服务器上拉取运行的都是私有地址的镜像,安全性和可控性都上了一个台阶。

8.2 大模型本地部署的 Docker 玩法

最近本地部署大语言模型非常热门,热词里也出现了 deepseek 本地部署、ollama 本地部署、dify 本地部署教程之类的搜索。Docker 在这里依然是最省心的部署方式。

以 Ollama 为例,一条命令就能拉起本地模型服务:

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

之后就能通过 API 接口和本地模型交互。想要一个可视化的聊天界面,可以用 Open WebUI 的镜像:

ollama: image: ollama/ollama:latest container_name: ollama volumes: - ollama:/root/.ollama ports: - "11434:11434" restart: always open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui depends_on: - ollama ports: - "3000:8080" volumes: - open-webui:/app/backend/data restart: always

这种方案的优点在于:模型权重文件放在数据卷里,想换模型就换个标签重新拉取;服务互相独立,接口和界面可以分别扩展;最关键是模型跑在本地,数据不出服务器。Dify 这类 LLM 应用开发平台现在也提供了 docker compose 的安装方式,一条命令拉起整套后端服务,本地调试和私有化部署都快了不少。对于想玩大模型又不搞懂 conda 和 CUDA 环境配制的朋友,用 Docker 往往更容易跑通整套流程。

8.3 GitLab 和 Jellyfin 等典型场景

GitLab 的容器化部署也是热词里频繁出现的方向。官方推荐用 Docker 运行 GitLab Community Edition,一条 run 命令就能拥有一套完整的代码托管平台,但要跑得稳定,数据卷和资源规划要提前做好:

docker run -d \ --name gitlab \ --restart always \ -p 8080:80 \ -p 2222:22 \ -v gitlab-config:/etc/gitlab \ -v gitlab-logs:/var/log/gitlab \ -v gitlab-data:/var/opt/gitlab \ gitlab/gitlab-ce:latest

注意 GitLab 内存消耗很大,官方建议至少 4G 内存,我实测 2G 内存跑起来会频繁 OOM,所以小机器慎入。

Jellyfin 是自建家庭影视服务器的一个热门选择,部署同样简单:

jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin ports: - "8096:8096" volumes: - /path/config:/config - /path/media:/media restart: always

很多朋友把家庭影音库和私有网盘都跑在 Docker 里,一台小主机全搞定,资源占用低,维护还方便。

9. 常见问题与排查技巧实录

9.1 容器启动失败

容器一直 restart 或者启动几秒就退出,先看日志:

docker logs --tail 100 <容器名或ID>

tail 100 是只看最近 100 行,避免刷屏。日志里往往直接写着报错原因,比如内存不足、依赖连接不上、端口被占用。如果日志没看出问题,再确认一下环境变量是否设置正确,特别是数据库连接地址,容器内不能用 localhost 访问宿主机服务,要用宿主机 IP 或者 Compose 网络里的服务名。

9.2 端口映射访问不了

最常见的场景是容器跑起来了,日志也正常,但宿主机就是访问不到。按这个顺序排查:

  1. 端口映射写没写对:docker ps 看 PORTS 列,比如 0.0.0.0:8080->80/tcp 表示访问宿主机 8080 会转发到容器 80。
  2. 宿主机防火墙开没开:Ubuntu 上 ufw status 检查,阿里云、腾讯云等则要去安全组放行对应端口。
  3. 容器内进程是否真的监听在配置的端口:docker exec 进入容器,curl 一下本地服务,确认服务本身没问题。

这三步能解决绝大多数端口问题。之前有个朋友折腾了半天,最后发现是云服务器安全组没放行 8080 端口,Docker 本身完全没毛病。

9.3 容器老停、磁盘被写满

磁盘爆满也是 Docker 部署中很典型的问题。Docker 默认把所有镜像、容器、数据卷都放在 /var/lib/docker 下,日志文件如果不限制会无限增长。我一般在 daemon.json 里统一配置日志轮转,同时定期跑清理命令:

docker system df # 查看磁盘占用情况 docker system prune -f # 清理停止的容器、悬空镜像和无用网络 docker image prune -a -f # 清理所有未被使用的镜像

数据卷没有容器引用后不会自动清除,需要手动 docker volume prune,执行前先确认没有需要保留的数据。

9.4 Docker Desktop 启动失败

热词里出现了好几次“Docker Desktop failed to start because virtualisation support wasn't detected”或者 “virtualization support not detected”,这是 Windows 环境最常见的问题。这个报错的意思是系统虚拟化功能没有开启。解决办法是在 BIOS 设置里打开 Intel VT-x(AMD 的对应功能是 SVM),然后重启系统;同时确保 Windows 的“Hyper-V”和“Windows 虚拟机监控程序平台”这两个功能处于开启状态。开启方式是在“控制面板 - 程序 - 启用或关闭 Windows 功能”里勾选。装完之后如果还报错,检查一下系统里其他虚拟机软件是否占用了 Hyper-V 资源。

另外 Docker Desktop 对 WSL2 有依赖,如果你没装过 WSL2 或者版本过旧,安装 Docker Desktop 时可能会提示你更新内核组件。可以在 PowerShell 里执行wsl --version检查版本,过旧就先更新。

9.5 镜像拉取超时

网络原因导致的镜像拉不下来,解决办法有三个方向:配置镜像加速、重试几次、或者从别的网络环境拉下来导出后再导入。导出导入的命令如下:

docker save nginx:1.27.0 -o nginx.tar docker load -i nginx.tar

在有网的机器上把镜像打包成 tar 文件,拷贝到目标机器 load 一下,适合离线环境。我在一些内网部署场景里,就是靠这种方法把镜像集打包成 tar,带到内网机器上批量导入的。

9.6 常见问题速查表

问题典型原因排查命令/思路
容器反复重启启动命令失败、依赖未就绪docker logs 查看报错
访问不到服务端口映射/防火墙/安全组docker ps 查看端口映射,检查云安全组
磁盘满日志未轮转、镜像堆积docker system df、配置 log-opts
数据库连接失败用 localhost 访问了宿主端口改用服务名或宿主机 IP
Docker Desktop 启动失败BIOS 未开虚拟化、WSL2 未装开启 VT-x/SVM,更新 WSL2
镜像拉取慢网络原因配置镜像加速、save/load 离线导入
容器内命令找不到基础镜像精简,无常用工具容器是 Alpine 等精简镜像,用 apt/apk 现场装

10. 生产环境部署的几点心得

10.1 部署前要养成的三个习惯

第一,所有配置都写进 Compose 文件和环境变量文件,不要直接改容器内部的配置。容器是随时可以删除重建的,手工改完容器一删全没了,而且无法追溯。把配置沉淀到 compose 文件和 .env 文件里,这就是你的部署文档,出问题随时能复现。

第二,所有数据目录必须挂载数据卷。数据库数据、应用上传的文件、日志,统统挂出来。不挂载的数据等于没有,容器一删什么都没了。这个教训是拿真实事故换来的。

第三,给服务设置资源限制和日志轮转。哪怕是小项目,也养成好习惯,等系统上线跑了几个月再回来补,代价会大很多。

10.2 更新与回滚也要提前想好

每次用新的镜像标签更新服务之前,先把当前正在运行的镜像和 Compose 配置备份一下。实践中最稳的是保留旧镜像标签,万一新版本出问题,直接改回旧标签:

docker compose up -d backend=myapp:1.0.0

或者快速切回上一个镜像版本再 up -d 一遍。Compose 的配置即你部署的事实来源,改一行、up 一次、如果不稳立刻改回来,这个循环就是最基础也最可靠的发布流程。

10.3 监控容器状态

生产环境一定要对容器做监控。最简单的方式是写一个定时脚本,检查容器的运行状态和响应时间,异常时发送通知。系统简单时我们通常先做告警,复杂后就会引入 Prometheus 抓取容器指标,Grafana 画看板。热词里也有 prometheus 监控部署,说明监控这一层大家越来越重视。先从最简版本开始,弄一个健康检查接口,定时 curl 一下,不返回 200 就告警,这个投入产出比是最高的。

最后再分享一个小技巧

我这些年几乎所有的部署工作都离不开 Docker 和 Compose,最大的体会是:你要把一切当代码来管理。Dockerfile 是代码,docker-compose.yml 是代码,.env 文件虽然是密钥和变量,但也可以用示例文件占位后进 Git。所有环境从一穷二白到服务齐全,只要一条命令、一份配置就能重建,这种感觉非常踏实。

最后一个实用技巧送给你:在部署新服务之前,先在本机用 Compose 把整套环境跑通,然后docker compose up -d一口气部署到服务器。服务器的底层是什么系统、有没有预装依赖,都不重要,Docker 已经帮你把环境差异全部抹平了。这个工作方式,我在多个项目里反复验证,稳定、高效、可复制,希望你也能用起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询