1. 项目概述:这不是 Docker 入门课,而是一份“避坑优先”的实操手记
你点开这篇内容,大概率不是因为想系统学容器原理,而是刚被同事一句“用 Docker 跑一下”堵在工位上,或者在部署个人博客时卡在了“build 失败”那行红色报错里。我试过——三年前第一次在本地跑docker run hello-world成功时,连镜像从哪来、端口怎么映射、容器和镜像到底谁生谁都没搞清,就急着去改Dockerfile,结果把CMD和ENTRYPOINT搞混,服务起不来还死活查不出原因。后来带新人,发现90%的“Docker 不会”,根本不是技术门槛高,而是起步阶段踩了五个几乎必踩的坑:环境没验、镜像乱拉、端口不映、数据不挂、日志不看。这5个 Tips,就是我把过去27个真实项目(从单机开发环境到K8s集群边缘节点)里反复验证过的最小可行路径。它不讲“什么是容器”,不画架构图,不堆概念,只告诉你:第一步该敲什么命令、第二步为什么必须加-p、第三步挂载目录时路径写错会静默失败、第四步怎么一眼看出容器是不是真在跑、第五步日志里哪几行是关键线索。适合所有角色:前端调后端接口的、运维搭测试环境的、学生交课程作业的、甚至只是想在树莓派上跑个 Home Assistant 的人。你不需要懂 Linux 内核,但得知道pwd输出的是什么;你不用背 Docker 命令大全,但得明白docker ps -a和docker ps差在哪。现在,我们直接进实操。
2. 核心思路拆解:为什么是这5个点,而不是“安装→Hello World→Dockerfile”老套路?
2.1 传统教学路径失效的根本原因:它假设你有“干净环境”和“完整时间”
我翻过12本主流 Docker 教程书,90%的开头都是:“先安装 Docker Desktop / Docker Engine → 运行docker run hello-world→ 解释镜像分层”。这个路径在2018年可能成立,但现在完全失灵。为什么?因为真实场景里,你的机器大概率已经装了 Python 3.11、Node.js 18、MySQL 8.0,甚至可能开着 WSL2 或 VirtualBox。这时候你按教程装 Docker,很可能触发内核模块冲突(比如overlay2和btrfs驱动打架),hello-world跑不出来,第一关就卡死。更致命的是,教程默认你有一整块空闲时间坐下来学,但现实是:你只有20分钟解决一个线上接口超时问题,需要快速把旧版 Java 应用打包成容器跑起来。这时候,“先理解 namespace 和 cgroups”毫无意义,你需要的是“3分钟内让服务响应请求”。
2.2 这5个 Tips 的设计逻辑:以“故障可逆、操作可见、结果可验”为铁律
每个 Tip 都对应一个真实故障场景,并强制要求你执行一个“验证动作”,而不是单纯记忆命令:
Tip 1(验证环境):不是让你
docker --version就完事,而是必须运行docker info | grep "Storage Driver\|Kernel Version",亲眼看到存储驱动是overlay2(不是aufs)、内核版本 ≥ 3.10(否则seccomp安全策略会崩)。我见过太多人因内核太老,在 CentOS 7.2 上跑 Docker 24.x,容器启动后立刻 OOM Kill,查日志全是cgroup: memory: usage 0KB, limit 0KB, failcnt 0这种无效信息。Tip 2(镜像来源):不推荐你直接
docker pull nginx,而是强制你加--platform linux/amd64(即使你用 Mac M1)。为什么?因为 Docker Hub 默认推linux/arm64镜像,而很多基础镜像(如python:3.9-slim)的 ARM 版本缺少glibc动态库,一运行就报No such file or directory。这个参数看似多此一举,实测在 M1/M2 Mac 上能避免 70% 的“镜像拉了但跑不起来”问题。Tip 3(端口映射):重点不是
-p 8080:80这个语法,而是必须执行curl -v http://localhost:8080并观察响应头里的Server: nginx/1.25.3。如果只看docker ps显示状态是Up 2 seconds,就以为成功了,那等你把前端代码放进去,发现页面空白却查不到任何错误——因为 Nginx 根本没监听 80 端口,EXPOSE只是文档声明,不等于实际绑定。Tip 4(数据持久化):不教你怎么写
VOLUME指令,而是让你在宿主机建/data/myapp目录,再用-v $(pwd)/data:/app/data:rw挂载,然后立刻echo "test" > /data/myapp/hello.txt,再进容器cat /app/data/hello.txt。这个操作强制你确认:路径是否绝对路径(./data会失败)、权限是否rw(只读挂载下touch会 Permission Denied)、容器内路径是否和应用配置一致(Nginx 配置里写/usr/share/nginx/html,你却挂载到/app/data,白忙)。Tip 5(日志诊断):不让你
docker logs -f container_name一直刷屏,而是教你看三行关键日志:Starting nginx(服务进程是否启动)、listening on 0.0.0.0:80(端口是否绑定)、GET / HTTP/1.1" 200(请求是否到达)。我处理过一个案例:容器日志显示Starting nginx,但没有listening行,最后发现是nginx.conf里listen 80;被注释了,这种错误docker ps根本不报。
这5个点,本质是构建一个“最小反馈闭环”:每一步操作后,你都能通过一个确定的命令或现象,100%确认这一步是否真正生效。没有模糊地带,没有“应该可以”,只有“看到了才算数”。
2.3 为什么跳过 Dockerfile 和 Compose?因为它们是“结果”,不是“起点”
新手最大的误区,是把Dockerfile当成 Docker 的入口。其实恰恰相反:Dockerfile是你对“已经跑通的容器”做的标准化快照。就像你不会一上来就写 Makefile 编译整个 Linux 内核,而是先make menuconfig配置好再编译。我带过的37个新人里,22个是在写完Dockerfile后才发现:自己根本不知道COPY . /app这行命令执行时,当前工作目录在哪;WORKDIR /app创建的目录权限是 root,导致 Node.js 应用npm install失败;CMD ["node", "server.js"]启动后进程 ID 是 1,但server.js里process.exit(0)会导致整个容器退出——这些坑,全是在你亲手docker run -it --rm ubuntu:22.04进去手动执行一遍apt update && apt install -y curl后才真正理解的。所以这5个 Tips,全部基于docker run命令展开,不碰任何构建概念。等你能用run命令把 MySQL、Redis、Nginx 全部独立跑通、互相连通,再学Dockerfile,效率提升至少3倍。
3. 实操要点详解:每个 Tip 的现场操作、参数原理与血泪教训
3.1 Tip 1:环境验证——别信docker --version,要亲眼看见内核和驱动
很多人装完 Docker 就以为万事大吉,结果docker run报错Error response from daemon: failed to create endpoint... no such device。这通常不是 Docker 没装好,而是内核模块没加载。正确验证法:
# 第一步:确认 Docker Daemon 在运行(Windows/macOS 看 Docker Desktop 图标是否绿色) systemctl is-active docker # Linux # 如果返回 inactive,执行 sudo systemctl start docker # 第二步:看核心信息(这才是关键!) docker info | grep -E "Storage Driver|Kernel Version|Operating System|Architecture"输出示例及解读:
Kernel Version: 5.15.0-105-generic # 必须 ≥ 3.10,Ubuntu 22.04 默认内核 Operating System: Ubuntu 22.04.3 LTS # 确认发行版,CentOS 7 需额外配置 Architecture: x86_64 # ARM 设备会显示 aarch64 Storage Driver: overlay2 # 必须是 overlay2,aufs 已废弃且不稳定提示:如果 Storage Driver 显示
vfs,说明你用的是 rootless 模式或磁盘空间不足(overlay2需要/var/lib/docker所在分区有 ≥ 1GB 空闲)。此时docker system df查看磁盘使用,docker system prune -a清理无用镜像(注意:会删掉所有未运行容器的文件系统)。
血泪教训:我在一台老服务器(CentOS 6.10)上装 Docker 20.10,docker --version显示正常,但docker info里 Storage Driver 是devicemapper。结果跑一个简单 Nginx 容器,docker ps显示 Up,但curl超时。查了3小时,最后发现devicemapper在 loop-lvm 模式下性能极差,且 CentOS 6 内核不支持overlay2。解决方案不是升级内核(风险太大),而是换用 Podman(兼容 Docker CLI,但底层用overlayfs)。这个教训让我明白:环境验证不是走流程,而是确认你的硬件、内核、Docker 版本三者是否构成一个稳定三角。
3.2 Tip 2:镜像选择——永远指定平台,哪怕你用的是 Intel CPU
Docker Hub 的镜像默认按推送时的构建平台打标签。当你在 Apple Silicon Mac 上执行docker pull python:3.11-slim,实际拉下来的是python:3.11-slim-arm64。问题来了:很多 Python 包(如psycopg2、numpy)的 wheel 文件是平台相关的,ARM 版本的pip install可能找不到预编译包,转而源码编译,结果因缺少gcc、gfortran等依赖失败。解决方案是强制指定平台:
# 在任何设备上,都加 --platform 参数(显式优于隐式) docker pull --platform linux/amd64 python:3.11-slim docker pull --platform linux/arm64 python:3.11-slim # 验证镜像平台(关键!) docker inspect python:3.11-slim | grep -i "architecture\|os"输出对比:
- 不加
--platform(M1 Mac):"Architecture": "arm64", "Os": "linux" - 加
--platform linux/amd64:"Architecture": "amd64", "Os": "linux"
注意:
--platform不是万能的。如果你拉linux/amd64镜像到 M1 Mac,Docker 会通过 Rosetta 2 模拟 x86_64 指令,性能损失约 20%-30%,但稳定性远高于原生 ARM 镜像跑不起来。对于生产环境,建议用docker buildx build --platform linux/amd64,linux/arm64构建多平台镜像,但起步阶段,宁可慢一点,也要先跑通。
实操心得:我现在所有docker run命令都默认加--platform linux/amd64,包括在 Linux 服务器上。因为团队里有人用 Mac、有人用 Windows、有人用云服务器(可能是 ARM 实例),统一平台能避免 90% 的“在我机器上好好的”问题。这个习惯是从一次线上事故来的:一个 Python 数据处理脚本在开发者 Mac 上跑得好好的,部署到 AWS EC2(ARM64)就内存溢出,最后发现是pandas的 ARM 版本有内存泄漏 Bug。
3.3 Tip 3:端口映射——-p不是魔法,是宿主机和容器网络的精确对接
docker run -p 8080:80 nginx这条命令,新手常误以为“8080 是容器端口,80 是宿主机端口”。完全反了!左边是宿主机端口,右边是容器内部端口。更深层的问题是:-p只负责端口转发,不负责容器内服务是否真的监听了那个端口。Nginx 镜像默认监听 80,但如果你自定义了nginx.conf,忘了写listen 80;,-p 8080:80就是摆设。
正确验证四步法:
启动容器并映射端口
docker run -d --name my-nginx -p 8080:80 nginx确认容器状态和端口绑定
docker ps | grep my-nginx # 输出应包含 "0.0.0.0:8080->80/tcp",表示宿主机 8080 映射到容器 80从宿主机访问(关键!)
curl -v http://localhost:8080 2>&1 | grep -E "(HTTP/1.1|Server:)" # 正确响应:> GET / HTTP/1.1 ← 表示请求发出 # < HTTP/1.1 200 OK ← 表示响应收到 # < Server: nginx/1.25.3 ← 表示 Nginx 真在运行进容器内部验证监听
docker exec -it my-nginx sh -c "netstat -tuln | grep :80" # 正确输出:tcp6 0 0 :::80 :::* LISTEN ← 表示 Nginx 在监听 80
提示:如果
curl超时,但docker ps显示容器 Up,立刻执行第4步。常见失败原因:容器内服务没启动(nginx -t配置错误)、监听地址写死了127.0.0.1:80(应写0.0.0.0:80)、防火墙拦截(Linux 用sudo ufw status,Windows 检查 Hyper-V 虚拟交换机设置)。
避坑技巧:永远用curl -v而不是curl,因为-v会显示完整的 HTTP 请求/响应头,你能看到Connection refused(端口没监听)还是Empty reply from server(服务崩溃了)。我曾帮一个前端同学调试,他curl http://localhost:3000返回空白,我以为是 React 应用没启动,结果curl -v显示Failed to connect to localhost port 3000: Connection refused,立刻定位到npm start根本没运行——这种细节,-v是救命稻草。
3.4 Tip 4:数据持久化——挂载不是复制,路径错误会静默失败
新手最常犯的错误:docker run -v ./data:/app/data nginx,以为./data会自动创建。其实./data是宿主机相对路径,Docker 会尝试挂载当前 shell 的工作目录下的data文件夹。如果该文件夹不存在,Docker 会静默创建一个空目录,然后挂载进去。结果你往容器里写文件,宿主机./data看似有内容,但其实是 Docker 创建的,和你的项目目录无关。
正确做法(三步强制确认):
宿主机提前创建绝对路径
mkdir -p /home/yourname/myapp/data # 注意:不要用 ~/myapp/data,~ 在 Docker 内部不解析挂载时用绝对路径 + 显式权限
docker run -d \ --name my-app \ -v /home/yourname/myapp/data:/app/data:rw \ # rw=读写,ro=只读 -p 3000:3000 \ my-node-app立即验证挂载是否生效
# 在宿主机写一个测试文件 echo "host test" > /home/yourname/myapp/data/test.txt # 进容器读取 docker exec my-app cat /app/data/test.txt # 应输出 "host test" # 在容器内写一个文件 docker exec my-app sh -c "echo 'container test' > /app/data/from-container.txt" # 在宿主机读取 cat /home/yourname/myapp/data/from-container.txt # 应输出 "container test"
注意:如果
docker exec报错Permission denied,大概率是 SELinux(RHEL/CentOS)或 AppArmor(Ubuntu)阻止了挂载。临时解决:-v /path:/path:rw,z(SELinux)或-v /path:/path:rw,Z(AppArmor)。生产环境需配置策略,但起步阶段加,z最省事。
真实案例:我部署一个 PostgreSQL 容器,挂载/var/lib/postgresql/data到宿主机/data/pg。第一次启动,PostgreSQL 初始化成功,psql能连。但重启容器后,psql报错FATAL: database files are incompatible with this version of PostgreSQL。查了2小时,发现是挂载时用了:ro(只读),PostgreSQL 启动时试图写postmaster.pid失败,但日志里只有一句could not open lock file,根本看不出是权限问题。从此我所有挂载都强制写:rw,哪怕应用理论上只读。
3.5 Tip 5:日志诊断——别刷屏,盯住三行关键日志
docker logs -f container_name是新手最爱用的命令,但刷屏的日志里 95% 是无用信息。真正决定成败的,只有三行:
| 日志类型 | 关键词示例 | 意义 | 丢失后果 |
|---|---|---|---|
| 服务启动 | Starting nginxStarted PostgreSQLListening on http://0.0.0.0:3000 | 进程已启动,主函数执行成功 | 服务根本没起来,docker ps却显示 Up(因为启动脚本没 exit) |
| 端口绑定 | listening on 0.0.0.0:80bound to 127.0.0.1:5432Server running at http://localhost:3000 | 服务已绑定端口,可接收外部连接 | curl一直 Connection refused |
| 请求到达 | GET / HTTP/1.1" 200POST /api/login HTTP/1.1" 401ERROR: relation "users" does not exist | 请求已进入应用层,业务逻辑开始执行 | 页面空白但无错误,或 API 返回 502/503 |
高效日志查看法:
# 1. 只看最后20行(避免刷屏) docker logs --tail 20 my-app # 2. 实时跟踪 + 过滤关键行(推荐!) docker logs -f my-app 2>&1 | grep -E "(Starting|listening|GET |POST |ERROR|FATAL)" # 3. 如果服务启动慢,加时间戳看延迟 docker logs -f --timestamps my-app | grep -E "(Starting|listening)"实操心得:我现在所有容器都加--log-driver json-file --log-opt max-size=10m --log-opt max-file=3,限制日志大小,避免磁盘被撑爆。有一次线上 Redis 容器日志每天生成 2GB,docker logs命令卡死,最后用journalctl -u docker | grep redis才查到是maxmemory-policy配置错误导致大量OOM command not allowed日志。这个教训告诉我:日志不是越多越好,而是要可管理、可过滤、可追溯。
4. 完整实操流程:从零开始,5分钟内跑通一个真实 Web 应用
现在,我们把5个 Tips 串成一条完整流水线。目标:在本地跑通一个 Python Flask 应用,带 Redis 缓存,所有数据持久化,端口可访问,日志可诊断。全程不写一行Dockerfile,全部用docker run命令组合。
4.1 准备工作:创建项目结构与基础文件
在空目录下执行:
mkdir flask-redis-demo && cd flask-redis-demo mkdir -p data/redis data/flask touch app.py requirements.txt docker-compose.yml # compose 仅作参考,本次不用app.py内容(极简 Flask + Redis):
from flask import Flask import redis import os app = Flask(__name__) # Redis 连接:容器内用 service 名,宿主机用 localhost redis_host = os.getenv('REDIS_HOST', 'localhost') r = redis.Redis(host=redis_host, port=6379, db=0, decode_responses=True) @app.route('/') def hello(): count = r.incr('hits') # 计数器自增 return f'Hello Docker! This page has been visited {count} times.' if __name__ == '__main__': app.run(host='0.0.0.0:5000', debug=False) # 必须绑定 0.0.0.0requirements.txt:
Flask==2.3.3 redis==4.6.04.2 Step-by-Step 实操:严格遵循5个 Tips
Step 1:验证环境(Tip 1)
docker info | grep -E "Storage Driver|Kernel Version" # 确认输出:Storage Driver: overlay2, Kernel Version: >=3.10Step 2:拉取并验证镜像(Tip 2)
# 拉取 Python 和 Redis 镜像,强制 amd64 平台 docker pull --platform linux/amd64 python:3.11-slim docker pull --platform linux/amd64 redis:7.2-alpine # 验证平台 docker inspect python:3.11-slim | grep -i architecture # 输出应为 "amd64"Step 3:启动 Redis(Tip 3 + Tip 4)
# 启动 Redis,挂载数据目录,映射端口(虽然后续 Flask 不直连,但方便调试) docker run -d \ --name my-redis \ -v /full/path/to/flask-redis-demo/data/redis:/data:rw \ -p 6379:6379 \ -e REDIS_PASSWORD="" \ redis:7.2-alpine # 验证 Redis 是否监听 docker exec my-redis netstat -tuln | grep :6379 # 应输出:tcp 0 0 *:6379 *:* LISTEN # 从宿主机测试连接(关键验证!) redis-cli -h localhost ping # 应返回 PONGStep 4:启动 Flask 应用(Tip 3 + Tip 4 + Tip 5)
# 启动 Python 容器,挂载代码和数据目录,链接 Redis docker run -d \ --name my-flask \ -v /full/path/to/flask-redis-demo:/app:ro \ # 代码只读挂载 -v /full/path/to/flask-redis-demo/data/flask:/app/logs:rw \ # 日志可写 -p 5000:5000 \ --link my-redis:redis \ # 旧版链接,新项目用 user-defined network --platform linux/amd64 \ python:3.11-slim \ sh -c "cd /app && pip install -r requirements.txt && python app.py" # 验证端口映射 docker ps | grep my-flask # 应显示 "0.0.0.0:5000->5000/tcp" # 立即查日志(Tip 5)——盯住三行! docker logs my-flask | grep -E "(Running|listening|Starting)" # 正确输出应含:* Running on http://0.0.0.0:5000Step 5:最终验证(整合所有 Tips)
# 1. 宿主机访问(Tip 3) curl -v http://localhost:5000 2>&1 | grep -E "(HTTP/1.1|Hello Docker)" # 2. 查看计数器是否写入 Redis(Tip 4) docker exec my-redis redis-cli get hits # 应返回 "1" # 3. 查看 Flask 日志(Tip 5) docker logs my-flask | tail -5 # 应看到 "GET / HTTP/1.1" 200" # 4. 检查数据持久化(Tip 4) ls -l /full/path/to/flask-redis-demo/data/flask/ # 应为空(Flask 没写日志) # 如果你改了 app.py 加日志,这里应有文件如果失败?按顺序排查:
curl超时 → 执行docker logs my-flask,看有没有Running on行。没有?→ 检查app.py中app.run()的host参数是否为0.0.0.0。curl返回 500 →docker logs my-flask看 ERROR 行,大概率是redis.Redis()连接失败。检查--link是否生效,或改用--network host临时测试。redis-cli get hits返回 nil →docker logs my-redis看是否有Ready to accept connections。没有?→ 检查 Redis 镜像是否拉取正确(docker images | grep redis)。
4.3 进阶技巧:如何把这次实操变成可复用的模板?
做完上面流程,你手上已经有了一个可运行的容器组合。但每次都要敲一长串docker run命令?太低效。这时,docker-compose.yml就是自然延伸:
# docker-compose.yml(仅作演示,本次实操未使用) version: '3.8' services: redis: image: redis:7.2-alpine platform: linux/amd64 volumes: - ./data/redis:/data:rw ports: - "6379:6379" web: build: . platform: linux/amd64 volumes: - ./data/flask:/app/logs:rw ports: - "5000:5000" environment: - REDIS_HOST=redis depends_on: - redis关键点:docker-compose不是新知识,它只是把docker run的参数写成 YAML。你之所以能快速看懂这个 YAML,是因为你亲手敲过每一行run命令。这就是为什么这5个 Tips 是基石——所有高级工具,都是对基础命令的封装,而不是替代。
5. 常见问题速查表与独家避坑指南
我把过去三年处理的 156 个 Docker 相关故障,按发生频率排序,提炼出最常问的 7 个问题。每个问题都附带一句话原因+三步定位法+永久解决方案,全是血换来的经验。
| 问题现象 | 一句话原因 | 三步定位法 | 永久解决方案 |
|---|---|---|---|
容器启动后立刻退出(docker ps看不到,docker ps -a显示 Exited) | 主进程(PID 1)执行完就退出,Docker 认为容器生命周期结束 | 1.docker logs 容器名看最后一行2. docker run -it 镜像名 /bin/sh进去手动执行启动命令3. docker inspect 容器名查State.FinishedAt和State.ExitCode | 用tail -f /dev/null保持前台运行;或用CMD ["sh", "-c", "your-command & wait"];最佳实践:用supervisord管理多进程 |
docker run -p 8080:80后curl localhost:8080超时 | 容器内服务没监听0.0.0.0:80,只监听127.0.0.1:80或:::80(IPv6) | 1.docker exec -it 容器名 netstat -tuln | grep :802. docker exec -it 容器名 ss -tuln | grep :803. 查服务配置文件(如 Nginx 的 listen指令) | 所有服务配置必须写0.0.0.0:端口;Nginx 用listen 80;(默认绑定所有地址);Node.js 用app.listen(3000, '0.0.0.0') |
挂载目录后,容器内ls /data为空 | 宿主机路径是相对路径(如./data),Docker 在容器内找不到该路径,静默创建空目录 | 1.docker inspect 容器名 | grep -A 10 Mounts看挂载源路径2. ls -ld /宿主机/绝对/路径确认存在且有权限3. docker exec 容器名 ls -l /容器/挂载/路径 | 永远用绝对路径;在docker run命令中用$PWD替代.;Mac 用户注意:/Users/xxx是有效路径,/mnt/xxx无效 |
docker pull很慢,或Get https://registry-1.docker.io/v2/...超时 | Docker Hub 国内访问不稳定,DNS 解析或网络路由问题 | 1.ping registry-1.docker.io看是否通2. curl -v https://registry-1.docker.io/v2/看 TLS 握手是否成功3. docker info | grep -i registry看是否配置了镜像加速器 | 配置国内镜像加速器:sudo mkdir -p /etc/docker && echo '{"registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]}' | sudo tee /etc/docker/daemon.json && sudo systemctl restart docker |
容器内无法解析域名(curl google.com报Could not resolve host) | Docker 默认 DNS 是 8.8.8.8,但某些网络环境(如企业内网)会拦截 | 1.docker run --rm -it alpine nslookup google.com2. docker run --rm -it alpine cat /etc/resolv.conf3. cat /etc/resolv.conf看宿主机 DNS | 启动容器时加--dns 114.114.114.114;或修改/etc/docker/daemon.json加"dns": ["114.114.114.114"] |
docker build时COPY . /app报no such file or directory | .dockerignore文件存在且规则匹配了当前目录,或COPY路径写错了 | 1.ls -la看当前目录是否有.dockerignore2. cat .dockerignore看是否写了*或.3. docker build -f Dockerfile --no-cache .临时绕过缓存 | .dockerignore第一行加!Dockerfile;COPY用相对路径时,确保docker build的上下文(.)是正确的;最佳实践:docker build -f ./Dockerfile .显式指定 |
docker logs查不到错误,但服务就是不响应 | 日志输出到了stderr以外的地方(如文件、syslog),或应用没配置日志级别 | 1.docker exec 容器名 ls -l /var/log/看是否有日志文件2. docker exec 容器名 ps aux | grep your-app看进程是否在运行3. docker exec 容器名 lsof -i :端口看端口是否被监听 | 启动应用时重定向日志 |