上一篇 下一篇 分享链接 返回 返回顶部

Docker 踩坑排查手册:从启动失败到数据丢失,附完整示例源码

发布人:慈云数据-客服中心 发布时间:22小时前 阅读量:1

Docker 常见问题汇总|附源码

Docker 作为当前最流行的容器化技术之一,已经广泛应用于开发、测试、部署、运维等多个环节。无论是个人开发者,还是企业级项目,Docker 都能帮助我们解决“环境不一致”“部署复杂”“依赖冲突”“服务迁移困难”等问题。

不过,很多人在使用 Docker 的过程中,经常会遇到各种各样的问题。例如:镜像拉取失败、容器无法启动、端口无法访问、数据丢失、容器之间无法通信、Dockerfile 构建失败、权限不足、磁盘空间占满等。

本文将系统整理 Docker 使用过程中常见的问题,并结合示例源码进行说明,帮助你快速定位问题、理解原因并掌握解决方案。


一、Docker 是什么?

Docker 是一种开源的容器化平台,它可以将应用程序以及运行所需的依赖、配置、系统库等打包到一个轻量级、可移植的容器中。

简单来说:

Docker 可以让你的应用在任何支持 Docker 的环境中以相同方式运行。

例如,你在本地开发了一个 Java、Node.js 或 Python 项目,部署到服务器时,经常会遇到以下问题:

  • 本地可以运行,服务器运行失败;
  • 本地 Node.js 是 18,服务器是 16;
  • 本地 MySQL 是 8,服务器是 5.7;
  • 系统库版本不同;
  • 配置文件路径不一致;
  • 部署步骤复杂,容易遗漏。

Docker 通过镜像和容器机制,将这些问题大幅简化。


二、Docker 常用概念

在解决问题之前,先了解几个核心概念。

1. 镜像 Image

镜像可以理解为应用运行环境的模板,里面包含了操作系统基础环境、应用代码、依赖包和启动命令等。

例如:

docker pull nginx
docker pull mysql:8.0
docker pull redis:7

这些命令会从镜像仓库拉取对应镜像。


2. 容器 Container

容器是镜像运行起来之后的实例。

例如:

docker run -d --name my-nginx nginx

这里 nginx 是镜像,my-nginx 是运行出来的容器。


3. Dockerfile

Dockerfile 是用于构建镜像的脚本文件,里面描述了镜像如何生成。

示例:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

4. Docker Compose

Docker Compose 用于管理多个容器,非常适合开发环境、微服务项目和依赖多个组件的应用。

例如一个 Web 项目可能依赖:

  • Web 服务;
  • MySQL;
  • Redis;
  • Nginx。

可以通过一个 docker-compose.yml 一次性启动。


三、Docker 安装后命令无法使用

问题现象

安装 Docker 后执行命令:

docker version

提示:

docker: command not found

或:

Cannot connect to the Docker daemon

原因分析

常见原因有:

  1. Docker 没有安装成功;
  2. Docker 服务没有启动;
  3. 当前用户没有权限访问 Docker;
  4. 环境变量没有配置好。

解决方案

1. 检查 Docker 是否安装

docker --version

如果没有输出版本信息,需要重新安装 Docker。


2. 启动 Docker 服务

Linux 系统:

sudo systemctl start docker

设置开机自启:

sudo systemctl enable docker

查看状态:

sudo systemctl status docker

3. 当前用户加入 docker 用户组

如果执行 Docker 命令需要一直加 sudo,可以执行:

sudo usermod -aG docker $USER

然后退出终端重新登录,或者执行:

newgrp docker

再次测试:

docker ps

四、Docker 镜像拉取失败

问题现象

执行:

docker pull nginx

出现:

Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled

或者拉取速度非常慢。

原因分析

这通常是由于网络环境导致访问 Docker Hub 不稳定,或者 DNS 解析异常。

解决方案

1. 配置镜像加速器

编辑 Docker 配置文件:

sudo mkdir -p /etc/docker
sudo vim /etc/docker/daemon.json

写入如下内容:

{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://mirror.baidubce.com"
  ]
}

重启 Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

验证配置是否生效:

docker info

查看输出中是否包含 Registry Mirrors


五、容器启动后马上退出

问题现象

执行:

docker run my-app

容器启动后立即退出。

查看容器:

docker ps -a

发现状态为:

Exited

原因分析

Docker 容器的生命周期依赖于主进程。如果容器中的主进程执行完毕,容器就会退出。

例如:

CMD ["echo", "hello docker"]

这个命令执行完后,容器自然退出。

解决方案

1. 查看日志

docker logs 容器ID或容器名

例如:

docker logs my-app

2. 检查启动命令

如果是 Node.js 项目,正确写法可能是:

CMD ["npm", "start"]

如果是 Java 项目:

CMD ["java", "-jar", "app.jar"]

3. 临时进入容器排查

docker run -it my-app sh

如果镜像基于 Ubuntu 或 Debian:

docker run -it my-app bash

六、端口映射后无法访问

问题现象

启动 Nginx:

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

访问:

http://服务器IP:8080

无法打开页面。

原因分析

可能原因包括:

  1. 容器没有正常运行;
  2. 端口映射写错;
  3. 服务器防火墙未开放端口;
  4. 云服务器安全组未放行端口;
  5. 应用监听地址错误。

排查步骤

1. 查看容器状态

docker ps

确认容器是否正在运行。

2. 查看端口映射

docker port nginx-test

输出类似:

80/tcp -> 0.0.0.0:8080

3. 本机测试

curl http://127.0.0.1:8080

如果本机可以访问,但外网不能访问,通常是防火墙或云安全组问题。

4. 开放防火墙端口

CentOS:

sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

Ubuntu:

sudo ufw allow 8080/tcp

七、容器内无法访问宿主机服务

问题现象

容器中的应用需要访问宿主机上的 MySQL、Redis 或其他服务,但使用 127.0.0.1 连接失败。

原因分析

在容器内部,127.0.0.1 指的是容器自身,而不是宿主机。

解决方案

1. 使用 host.docker.internal

在 Docker Desktop 环境中可以使用:

host.docker.internal

例如:

mysql://host.docker.internal:3306/test

2. Linux 中添加 host 映射

Linux 环境下可以使用:

docker run --add-host=host.docker.internal:host-gateway my-app

3. 使用宿主机网关 IP

查看 Docker 默认网桥:

ip addr show docker0

通常宿主机地址为:

172.17.0.1

容器内可以访问:

172.17.0.1:3306

八、容器之间无法通信

问题现象

一个 Web 容器需要访问 MySQL 容器,但连接失败。

错误示例:

ECONNREFUSED
Unknown host mysql
Connection timed out

原因分析

容器之间如果不在同一个 Docker 网络中,默认无法通过容器名通信。

解决方案

1. 创建自定义网络

docker network create app-network

启动 MySQL:

docker run -d \
  --name mysql \
  --network app-network \
  -e MYSQL_ROOT_PASSWORD=123456 \
  mysql:8.0

启动应用:

docker run -d \
  --name web \
  --network app-network \
  -e DB_HOST=mysql \
  my-web-app

在同一个自定义网络中,应用可以通过容器名 mysql 访问数据库。


九、Docker Compose 示例源码

下面给出一个完整的 Docker Compose 示例,包含:

  • Node.js Web 服务;
  • MySQL 数据库;
  • Redis 缓存服务。

项目目录结构如下:

docker-demo/
├── app/
│   ├── Dockerfile
│   ├── package.json
│   └── server.js
└── docker-compose.yml

1. Node.js 示例源码:server.js

const express = require("express");
const mysql = require("mysql2/promise");
const redis = require("redis");

const app = express();
const port = process.env.PORT || 3000;

const dbConfig = {
  host: process.env.DB_HOST || "mysql",
  user: process.env.DB_USER || "root",
  password: process.env.DB_PASSWORD || "123456",
  database: process.env.DB_NAME || "demo"
};

const redisClient = redis.createClient({
  url: `redis://${process.env.REDIS_HOST || "redis"}:6379`
});

async function initRedis() {
  redisClient.on("error", err => {
    console.error("Redis error:", err);
  });

  await redisClient.connect();
}

app.get("/", async (req, res) => {
  res.send("Hello Docker!");
});

app.get("/health", async (req, res) => {
  res.json({
    status: "ok",
    time: new Date().toISOString()
  });
});

app.get("/db", async (req, res) => {
  try {
    const connection = await mysql.createConnection(dbConfig);
    const [rows] = await connection.execute("SELECT NOW() AS currentTime");
    await connection.end();

    res.json({
      success: true,
      data: rows
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

app.get("/redis", async (req, res) => {
  try {
    const count = await redisClient.incr("visit_count");

    res.json({
      success: true,
      visitCount: count
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

app.listen(port, async () => {
  await initRedis();
  console.log(`Server is running on port ${port}`);
});

2. package.json

{
  "name": "docker-demo",
  "version": "1.0.0",
  "description": "Docker common issues demo",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.3",
    "mysql2": "^3.9.0",
    "redis": "^4.6.13"
  }
}

3. Dockerfile

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install --production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

4. docker-compose.yml

version: "3.8"

services:
  web:
    build:
      context: ./app
    container_name: docker-demo-web
    ports:
      - "3000:3000"
    environment:
      PORT: 3000
      DB_HOST: mysql
      DB_USER: root
      DB_PASSWORD: 123456
      DB_NAME: demo
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis
    networks:
      - demo-network

  mysql:
    image: mysql:8.0
    container_name: docker-demo-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: demo
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - demo-network

  redis:
    image: redis:7-alpine
    container_name: docker-demo-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - demo-network

networks:
  demo-network:
    driver: bridge

volumes:
  mysql-data:
  redis-data:

十、Docker Compose 启动失败

问题现象

执行:

docker compose up -d

出现错误:

services.web.depends_on contains an invalid type

或者:

docker-compose: command not found

原因分析

Docker Compose 目前存在两个常见版本:

  1. 老版本命令:docker-compose
  2. 新版本命令:docker compose

如果环境安装的是 Docker Compose V2,推荐使用:

docker compose up -d

常用命令

启动服务:

docker compose up -d

查看服务:

docker compose ps

查看日志:

docker compose logs -f

只查看某个服务日志:

docker compose logs -f web

停止服务:

docker compose down

停止并删除数据卷:

docker compose down -v

重新构建:

docker compose up -d --build

十一、数据卷挂载后数据丢失

问题现象

MySQL 容器删除后,数据库数据丢失。

原因分析

如果没有使用 Docker Volume 或宿主机目录挂载,容器删除后,容器内的数据也会随之删除。

错误示例:

docker run -d --name mysql mysql:8.0

正确示例:

docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

查看数据卷

docker volume ls

查看数据卷详情

docker volume inspect mysql-data

删除无用数据卷

docker volume prune

注意:该命令会删除未被容器使用的数据卷,执行前务必确认。


十二、Dockerfile 构建速度慢

问题现象

执行:

docker build -t my-app .

构建时间很长,每次都重新下载依赖。

原因分析

Docker 构建镜像时会使用缓存,但 Dockerfile 顺序不合理会导致缓存失效。

错误写法:

FROM node:18-alpine

WORKDIR /app

COPY . .

RUN npm install

CMD ["npm", "start"]

这种写法中,只要业务代码发生变化,就会导致 npm install 重新执行。

优化写法

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install --production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

这样只要 package.json 没有变化,依赖安装步骤就可以复用缓存。


十三、镜像体积过大

问题现象

构建出来的镜像非常大,例如几百 MB 甚至几个 GB。

原因分析

常见原因:

  1. 使用了过大的基础镜像;
  2. 没有清理缓存;
  3. 将无关文件复制进镜像;
  4. 没有使用多阶段构建;
  5. .dockerignore 文件缺失。

解决方案

1. 使用 Alpine 镜像

FROM node:18-alpine

相比完整 Linux 发行版,Alpine 镜像体积更小。

2. 添加 .dockerignore

node_modules
.git
.gitignore
Dockerfile
docker-compose.yml
npm-debug.log
README.md

3. 多阶段构建示例

以 Go 项目为例:

FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY . .

RUN go build -o server main.go

FROM alpine:3.19

WORKDIR /app

COPY --from=builder /app/server .

EXPOSE 8080

CMD ["./server"]

这样最终镜像只包含运行所需的二进制文件。


十四、容器时间不正确

问题现象

容器内时间与宿主机或实际时区不一致。

查看容器时间:

docker exec -it 容器名 date

解决方案

1. 挂载宿主机时间配置

docker run -d \
  --name app \
  -v /etc/localtime:/etc/localtime:ro \
  -v /etc/timezone:/etc/timezone:ro \
  my-app

2. 设置环境变量

docker run -d \
  -e TZ=Asia/Shanghai \
  my-app

在 Docker Compose 中:

environment:
  TZ: Asia/Shanghai

十五、Docker 磁盘空间占满

问题现象

服务器磁盘空间不足:

df -h

发现 /var/lib/docker 占用很大。

原因分析

Docker 长期运行后会产生大量:

  • 已停止容器;
  • 未使用镜像;
  • 构建缓存;
  • 未使用网络;
  • 未使用数据卷;
  • 容器日志文件。

查看 Docker 占用

docker system df

清理无用资源

清理停止的容器、未使用网络、悬空镜像和构建缓存:

docker system prune

清理所有未使用镜像:

docker system prune -a

清理未使用数据卷:

docker volume prune

清理构建缓存:

docker builder prune

谨慎执行:

docker system prune -a --volumes

该命令会删除所有未使用镜像、容器、网络和数据卷,可能导致数据丢失。


十六、容器日志过大

问题现象

Docker 容器运行一段时间后,日志文件非常大,占满磁盘。

查看日志路径:

docker inspect 容器名 | grep LogPath

解决方案

配置 Docker 日志轮转。

编辑:

sudo vim /etc/docker/daemon.json

添加:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

重启 Docker:

sudo systemctl restart docker

注意:该配置通常对新创建的容器生效,已有容器建议重建。


十七、容器权限不足

问题现象

容器内执行某些操作时报错:

Permission denied

常见于:

  • 挂载宿主机目录;
  • 写入文件;
  • 访问设备;
  • 执行脚本。

解决方案

1. 检查宿主机目录权限

ls -l /data/app

修改权限:

sudo chown -R 1000:1000 /data/app

或者:

sudo chmod -R 755 /data/app

2. 使用指定用户运行容器

docker run -u 1000:1000 my-app

3. 脚本缺少执行权限

chmod +x start.sh

Dockerfile 中可添加:

RUN chmod +x start.sh

十八、容器内无法解析域名

问题现象

容器内访问外部域名失败:

ping www.baidu.com

提示:

Temporary failure in name resolution

原因分析

DNS 配置异常,或者宿主机网络存在问题。

解决方案

1. 启动容器时指定 DNS

docker run --dns=8.8.8.8 --dns=114.114.114.114 my-app

2. 配置 Docker 全局 DNS

编辑:

sudo vim /etc/docker/daemon.json

写入:

{
  "dns": ["8.8.8.8", "114.114.114.114"]
}

重启:

sudo systemctl restart docker

十九、Docker 常用排查命令汇总

1. 查看 Docker 信息

docker info

2. 查看镜像

docker images

3. 查看运行中容器

docker ps

4. 查看所有容器

docker ps -a

5. 查看容器日志

docker logs -f 容器名

6. 进入容器

docker exec -it 容器名 sh

或:

docker exec -it 容器名 bash

7. 查看容器详情

docker inspect 容器名

8. 查看容器资源占用

docker stats

9. 查看 Docker 网络

docker network ls

10. 查看数据卷

docker volume ls

二十、实战建议

1. 生产环境不要直接使用 latest 标签

不推荐:

image: mysql:latest

推荐:

image: mysql:8.0

原因是 latest 可能随时变化,导致部署结果不可控。


2. 配置健康检查

示例:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
  interval: 30s
  timeout: 5s
  retries: 3

健康检查可以帮助我们判断容器是否真正可用。


3. 敏感信息不要写死在 Dockerfile 中

不推荐:

ENV MYSQL_PASSWORD=123456

推荐使用:

  • 环境变量;
  • .env 文件;
  • Docker Secret;
  • Kubernetes Secret;
  • CI/CD 平台密钥管理。

4. 保持镜像最小化

镜像越小:

  • 拉取越快;
  • 启动越快;
  • 安全风险越低;
  • 部署效率越高。

5. 日志交给标准输出

容器化应用推荐将日志输出到标准输出和标准错误:

console.log("info message");
console.error("error message");

这样可以通过:

docker logs 容器名

统一查看。


总结

Docker 能显著提升应用交付和部署效率,但在实际使用过程中,也会遇到镜像拉取、容器启动、网络通信、数据持久化、权限、日志、磁盘空间等问题。

排查 Docker 问题时,可以遵循以下思路:

  1. 先看容器是否运行:docker ps -a
  2. 再看日志:docker logs
  3. 检查端口映射:docker port
  4. 检查网络:docker network inspect
  5. 检查挂载:docker inspect
  6. 检查资源占用:docker stats
  7. 最后检查 Dockerfile 和 Compose 配置。

只要掌握这些常见问题的原因和解决方式,Docker 的使用难度会明显降低。对于日常开发来说,推荐优先使用 Docker Compose 管理多容器项目;对于生产环境,则需要进一步结合 CI/CD、镜像仓库、日志系统、监控系统和容器编排平台进行统一管理。

目录结构
全文