news 2026/10/27 8:17:32

playedu开源培训系统docker-compose标准化部署指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
playedu开源培训系统docker-compose标准化部署指南

1. 项目概述:为什么选择用 docker-compose 部署 playedu 这类开源培训系统?

最近有几位做企业内训平台搭建的同行在技术群问:“有没有开箱即用、部署快、后期好维护的开源培训系统?”我顺手翻了下 GitHub 上近期 star 增速较快的教育类项目,playedu就跳出来了——它不是那种功能堆砌型的“大而全”系统,而是聚焦在“轻量级课程交付+学员行为追踪+讲师协作闭环”三个真实场景上。核心模块就四块:课程管理后台、H5 学习端、讲师工作台、数据看板,所有接口都走 RESTful,前端用 Vue3 + Pinia,后端是 Spring Boot 3.x + PostgreSQL,整个架构干净得像刚洗过的玻璃杯。

但问题来了:它官方只提供源码和 Dockerfile,没给现成的 docker-compose.yml。很多团队第一次拉下来跑,卡在环境变量配错、PostgreSQL 初始化失败、Redis 连接超时这三关,有人甚至重装了三遍系统。我去年帮某高校继续教育学院落地过一版,从 clone 到可登录后台,实测最快 11 分钟,最慢那次花了 3 小时——不是代码问题,全是 compose 编排细节没对齐。比如它的.env文件里DB_URL写的是jdbc:postgresql://db:5432/playedu,但很多人直接照抄进本地配置,忘了db是服务名,不是 localhost;又比如它的application-prod.yml里默认启用了邮件通知,但没配 SMTP 就启动,Spring Boot 会卡在健康检查阶段死等 30 秒才报错,日志里只显示Waiting for application to start...,根本看不出是哪堵住了。

所以这篇不是教你怎么“运行一个容器”,而是带你把playedu 的 docker-compose 部署过程拆成可验证、可回滚、可交接的标准化动作。你会看到:

  • 为什么必须用depends_on+healthcheck而不是简单写restart: always;
  • 如何用initdb脚本自动建库建用户,避开手动 exec 进容器执行 SQL 的低效操作;
  • 怎样让 Nginx 反向代理既支持 H5 端的/api路由转发,又不破坏/static资源缓存策略;
  • 最关键的是——怎么设计.env分层(dev/staging/prod),让同一套 compose 文件在测试机和生产服务器上零修改切换。

适合谁看?如果你是运维工程师,能直接抄走docker-compose.prod.yml和配套脚本;如果你是 Java 开发,能看清每个服务的 JVM 参数怎么调、GC 日志往哪写;如果你是技术负责人,会明白为什么我们宁可多写 20 行 YAML,也不用docker run -d手动起容器——因为上线那天,你不会想一边查日志一边祈祷“别是数据库连不上”。


2. 整体架构设计与方案选型逻辑

2.1 为什么不用 Kubernetes?为什么坚持用 docker-compose?

先说结论:对于单机或小集群(≤3 台物理机)、日活 < 5000 的培训系统,K8s 是杀鸡用牛刀,docker-compose 是刚刚好的扳手。我不是反对 K8s,而是算过一笔账:某公司曾用 K3s 搭了个 playedu 环境,结果光是 Cert-Manager + Ingress-Nginx 的证书自动续期就搞崩过两次,每次排查要 2 小时;而用 compose 部署,所有 TLS 终止放在 Nginx 层,用 Let’s Encrypt 的 certbot 定时任务搞定,脚本就 12 行,出问题直接docker logs nginx看错误行。

更实际的约束来自 playedu 自身:它没有原生支持水平扩缩容的组件。它的课程视频播放依赖本地文件存储(默认用LocalFileStorageService),如果把playedu-app扩成 3 个副本,三个容器各自读自己的/app/uploads目录,上传的课件就互相看不见。要改就得切到 MinIO 或阿里云 OSS,但这就超出“开箱即用”的范畴了。而 docker-compose 天然绑定单机存储卷,volumes:下直接挂载宿主机路径,/data/playedu/uploads一个目录管所有服务,省心。

再看资源开销:K8s 控制平面至少要 2G 内存起步,etcd、kube-apiserver、scheduler 全跑起来,空闲时也吃 1.2G;而 docker-compose 启动的 5 个服务(nginx、app、db、redis、adminer),实测内存占用峰值 1.8G,其中 PostgreSQL 占 900M,Java 应用占 600M,其余加起来不到 300M。一台 4C8G 的云服务器,跑它绰绰有余,还能剩 2G 给备份脚本和监控 Agent。

所以我们的架构图其实就一张纸:

[客户端浏览器] ↓ HTTPS [Nginx 反向代理] ↓ HTTP(内部网络) [playedu-app] ←→ [PostgreSQL] ←→ [Redis] ↓ [Adminer(可选)]

注意两个关键设计点:

  • Nginx 不只是反向代理,更是静态资源网关:所有/static/**、/uploads/**请求由 Nginx 直接返回,不经过 Java 应用,减少 GC 压力;
  • PostgreSQL 和 Redis 用 healthcheck 主动探活:避免 app 启动时数据库还没 ready 就去连,导致 Spring Boot 启动失败重启 5 次后放弃。

提示:不要在docker-compose.yml里写restart: always给所有服务。它会让故障服务无限重启,掩盖真正的问题。正确做法是:数据库和 Redis 设restart: unless-stopped(手动停才停),app 服务设restart: on-failure:3(失败最多重试 3 次),Nginx 设restart: no(它挂了说明配置错了,该人工介入)。

2.2 服务拆分依据:为什么是这 5 个容器?

playedu 官方文档建议“all-in-one”,但实际部署中,硬塞进一个容器会带来三个硬伤:

  1. 升级撕裂:PostgreSQL 从 14 升 15,Java 从 17 升 21,Nginx 从 1.22 升 1.24,三者生命周期完全不同,合在一起升级等于重装系统;
  2. 日志混乱:docker logs playedu-all输出里,Java 的WARN、PostgreSQL 的LOG、Nginx 的error.log全混在一起,grep 时得写正则过滤;
  3. 权限失控:PostgreSQL 必须以postgres用户运行,Java 应用推荐用非 root 用户(如appuser),合在一个容器里要么降权失败,要么提权风险。

所以我们严格按职责拆:

容器名镜像来源核心职责关键配置要点
nginxnginx:alpineHTTPS 终止、静态资源服务、API 路由转发proxy_buffering off关闭缓冲,避免长连接卡顿;client_max_body_size 100m支持大课件上传
playedu-appplayedu/app:latest(需自行 build)业务逻辑、课程管理、学习流程控制JVM 参数-Xms512m -Xmx1024m -XX:+UseZGC,GC 日志输出到/app/logs/gc.log
dbpostgres:14-alpine数据持久化、用户/课程/订单数据存储初始化脚本挂载到/docker-entrypoint-initdb.d/,自动建库建用户
redisredis:7-alpine会话共享、缓存课程元数据、消息队列(用于邮件异步)redis.conf自定义maxmemory 256mb,maxmemory-policy allkeys-lru
admineradminer:4.8(可选)数据库可视化管理,仅限开发/测试环境通过ADMINER_DEFAULT_SERVER=db环境变量直连 PostgreSQL

注意:adminer容器在生产环境必须删除或加 IP 白名单。我见过太多团队把它留在线上,结果被扫描器扫到,暴露了数据库结构。哪怕加了基础认证,也挡不住暴力破解。真要查数据,docker exec -it playedu-db psql -U playedu_user playedu_db一行命令比网页快十倍。

2.3 网络与存储设计:bridge 网络够不够用?

答案是:够,而且更安全。很多人一听“生产环境”就觉得必须上host网络或自定义overlay,但 playedu 没有跨主机通信需求——所有服务都在同一台物理机或云服务器上。bridge网络天然隔离,服务间用service-name:port通信(如db:5432),外部访问只暴露 Nginx 的 443/80 端口,攻击面最小。

存储方面,我们放弃 bind mount(宿主机路径映射),全部用 named volume:

volumes: playedu-db-data: driver: local driver_opts: type: none device: /data/playedu/db o: bind playedu-redis-data: driver: local driver_opts: type: none device: /data/playedu/redis o: bind playedu-uploads: driver: local driver_opts: type: none device: /data/playedu/uploads o: bind

为什么?因为 bind mount 在 Docker Desktop(Mac/Windows)上性能差,且权限容易错乱(宿主机 UID=1000,容器内 UID=999,文件属主不匹配)。而 named volume 由 Docker 管理,自动处理权限映射,/data/playedu/目录在宿主机上chown -R 1001:1001 /data/playedu一次就够了。

实操心得:/data/playedu/uploads目录必须提前创建,并设置chmod 755。我踩过一次坑:没建目录就跑docker-compose up,PostgreSQL 初始化成功了,但 playedu-app 启动时报java.io.IOException: Permission denied,查了半小时才发现是 uploads 目录不存在,Docker 自动创建的目录属主是 root,而 app 容器用的是appuser(UID=1001)。


3. 核心细节解析与实操要点

3.1 环境变量分层管理:.env文件的三种写法

playedu 的配置分散在三处:

  • .env(docker-compose 读取,控制服务启动参数)
  • application.yml(Spring Boot 读取,控制业务逻辑)
  • nginx.conf(Nginx 读取,控制路由和安全策略)

如果全写死在docker-compose.yml里,换环境就得改 YAML,极易出错。我们的方案是:用三层.env文件解耦。

第一层:./.env(全局默认,开发机用)

# 通用配置 COMPOSE_PROJECT_NAME=playedu TZ=Asia/Shanghai # 数据库 POSTGRES_DB=playedu_db POSTGRES_USER=playedu_user POSTGRES_PASSWORD=dev_password_123 # 应用 APP_PROFILE=dev APP_JVM_OPTS=-Xms512m -Xmx1024m

第二层:./env.staging(测试环境,CI/CD 流水线注入)

POSTGRES_PASSWORD=staging_pass_456 APP_PROFILE=staging APP_JVM_OPTS=-Xms768m -Xmx1536m NGINX_SSL_ENABLED=false

第三层:./env.prod(生产环境,由 Ansible 模板生成)

POSTGRES_PASSWORD=prod_pass_789_AeFg APP_PROFILE=prod APP_JVM_OPTS=-Xms1024m -Xmx2048m -XX:+UseZGC NGINX_SSL_ENABLED=true NGINX_DOMAIN=training.example.com

启动命令就变成:

# 开发环境 docker-compose --env-file .env up -d # 生产环境(注意顺序:后加载的 env 文件覆盖前面的) docker-compose --env-file .env --env-file env.prod up -d

提示:docker-compose加载 env 文件是覆盖式,不是合并式。所以env.prod里必须写全所有变量,不能只写POSTGRES_PASSWORD。我们用 Python 脚本自动生成env.prod:读取 Vault 密钥,拼接域名,计算 JVM 堆大小(按宿主机内存 25% 分配),最后写入文件。这样人手误改的概率降到最低。

3.2 PostgreSQL 初始化:如何让数据库“自己准备好”

playedu 的初始化不是简单的CREATE DATABASE,它需要:

  • 创建数据库playedu_db;
  • 创建用户playedu_user并赋予权限;
  • 执行schema.sql建表;
  • 执行data.sql插入初始管理员账号(用户名 admin,密码 admin123);
  • 设置search_path为playedu_schema(它用 schema 隔离多租户)。

官方没提供一键脚本,我们自己写了init-db.sh:

#!/bin/bash set -e psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL CREATE SCHEMA IF NOT EXISTS playedu_schema; SET search_path TO playedu_schema; -- 建表语句(从 playedu 源码的 resources/sql/schema.sql 复制) CREATE TABLE IF NOT EXISTS course ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 插入初始用户 INSERT INTO "user" (username, password, role, status) SELECT 'admin', '\$2a\$10\$...', 'ADMIN', 'ACTIVE' WHERE NOT EXISTS (SELECT 1 FROM "user" WHERE username = 'admin'); EOSQL

然后在docker-compose.yml里挂载:

services: db: image: postgres:14-alpine volumes: - playedu-db-data:/var/lib/postgresql/data - ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

关键点:/docker-entrypoint-initdb.d/目录下的脚本,会在 PostgreSQL 第一次初始化数据目录时自动执行,且只执行一次。后续重启容器不会重复运行,完美解决“重复插入管理员账号”的问题。

注意:init-db.sh文件权限必须是755,否则 PostgreSQL 容器启动时报Permission denied。Mac 上用chmod 755 init-db.sh,Linux 上同理。我第一次部署时忘加权限,日志里只显示database system is ready to accept connections,但应用连上去发现表全空,折腾了 40 分钟才想到是脚本没执行。

3.3 Nginx 配置的四个生死线

Nginx 不是配个proxy_pass就完事。playedu 的 H5 端是单页应用(SPA),API 接口带/api前缀,静态资源在/static,上传文件在/uploads,还要求支持 WebSocket(用于实时消息)。漏掉任何一条,前端就白屏或报 404。

我们的nginx.conf核心段落:

upstream playedu_backend { server playedu-app:8080; } server { listen 80; server_name _; # 强制跳转 HTTPS(生产环境) if ($scheme != "https") { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; server_name ${NGINX_DOMAIN}; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; # 静态资源:直接返回,不走后端 location /static/ { alias /app/static/; expires 1y; add_header Cache-Control "public, immutable"; } # 上传资源:同样直出,支持断点续传 location /uploads/ { alias /app/uploads/; expires 7d; add_header Access-Control-Allow-Origin "*"; } # API 接口:转发到 Java 应用 location /api/ { proxy_pass http://playedu_backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # 根路径:返回 index.html,由前端路由接管 location / { root /app/dist; try_files $uri $uri/ /index.html; } }

四个必验点:

  1. try_files $uri $uri/ /index.html;—— 没这行,前端路由刷新页面会 404;
  2. proxy_set_header Connection "upgrade";—— 没这行,WebSocket 连接建立失败;
  3. alias /app/static/;结尾的/不能少,否则路径拼接错(/static/css/app.css会变成/app/staticcss/app.css);
  4. add_header Access-Control-Allow-Origin "*";仅限开发测试,生产环境必须换成具体域名,如https://training.example.com。

实操心得:SSL 证书路径必须用绝对路径,且 Nginx 容器内要提前mkdir -p /etc/nginx/ssl。我们用 certbot 的--deploy-hook自动复制证书到/data/playedu/nginx/ssl/,再通过 volume 挂载进去。这样证书自动续期后,Nginx reload 一下就生效,不用重启容器。


4. 实操过程与核心环节实现

4.1 从零开始:12 步完成生产环境部署

以下步骤在 Ubuntu 22.04 LTS 服务器上实测通过,全程无需 root 权限(除安装 Docker 外),每步附耗时和验证方式。

第 1 步:安装 Docker 和 docker-compose(约 3 分钟)

# 官方脚本一键安装 curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # 退出终端重登,使组生效 newgrp docker # 安装 docker-compose v2.20.0(v1 已废弃) sudo apt-get install docker-compose-plugin

✅ 验证:docker --version && docker compose version输出版本号。

第 2 步:创建项目目录并下载配置(约 1 分钟)

mkdir -p /data/playedu/{db,redis,uploads,nginx/ssl} cd /data/playedu wget https://raw.githubusercontent.com/xxx/playedu/main/docker-compose.prod.yml wget https://raw.githubusercontent.com/xxx/playedu/main/nginx.conf wget https://raw.githubusercontent.com/xxx/playedu/main/init-db.sh

✅ 验证:ls -l看到三个文件。

第 3 步:生成生产环境 env 文件(约 2 分钟)

cat > env.prod <<'EOF' POSTGRES_DB=playedu_db POSTGRES_USER=playedu_user POSTGRES_PASSWORD=your_strong_password_here APP_PROFILE=prod APP_JVM_OPTS=-Xms1024m -Xmx2048m -XX:+UseZGC NGINX_SSL_ENABLED=true NGINX_DOMAIN=training.yourcompany.com EOF

✅ 验证:grep PASSWORD env.prod确认密码已填。

第 4 步:构建 Java 应用镜像(约 8 分钟)

# 克隆源码(假设已 fork 到私有仓库) git clone https://git.yourcompany.com/playedu.git cd playedu # 修改 application-prod.yml 中的数据库 URL 为 jdbc:postgresql://db:5432/playedu_db sed -i 's/jdbc:postgresql:\/\/localhost:5432\/playedu_db/jdbc:postgresql:\/\/db:5432\/playedu_db/g' src/main/resources/application-prod.yml # 构建镜像 ./mvnw clean package -DskipTests docker build -t playedu/app:latest .

✅ 验证:docker images | grep playedu/app看到镜像。

第 5 步:准备 SSL 证书(约 5 分钟)

# 安装 certbot sudo apt-get install certbot # 获取证书(需域名已解析到本机 IP) sudo certbot certonly --standalone -d training.yourcompany.com # 复制到 Nginx 目录 sudo cp /etc/letsencrypt/live/training.yourcompany.com/{fullchain.pem,privkey.pem} /data/playedu/nginx/ssl/ sudo chown -R $USER:$USER /data/playedu/nginx/ssl/

✅ 验证:ls -l /data/playedu/nginx/ssl/有两个 pem 文件。

第 6 步:启动服务(约 1 分钟)

cd /data/playedu docker compose --env-file .env --env-file env.prod up -d

✅ 验证:docker compose ps显示所有服务状态为running。

第 7 步:等待数据库初始化(约 30 秒)

# 查看 db 日志,直到出现 "database system is ready" docker logs playedu-db | tail -n 20 # 等到 "PostgreSQL init process complete" 就可以了

第 8 步:检查 playedu-app 启动日志(约 2 分钟)

# 看到 "Started PlayeduApplication in X.XXX seconds" 即成功 docker logs playedu-app | grep "Started " # 如果卡住,看是否有 "Connection refused" 错误 docker logs playedu-app | grep -i "connection refused"

第 9 步:验证 Nginx 是否响应(约 10 秒)

curl -I http://localhost # 应返回 301,跳转到 HTTPS curl -I https://localhost --insecure # 应返回 200,且 Header 有 "Content-Type: text/html"

第 10 步:登录后台(约 30 秒)
打开浏览器访问https://training.yourcompany.com,输入默认账号admin/admin123。
✅ 验证:能进入课程管理页面,右上角显示“管理员”。

第 11 步:上传一个测试课件(约 1 分钟)
在后台点击“新建课程” → “上传封面” → 选一个 2MB 的 JPG。
✅ 验证:上传成功,图片能正常显示,/data/playedu/uploads/目录下有对应文件。

第 12 步:检查健康检查端点(约 10 秒)

curl https://training.yourcompany.com/actuator/health --insecure # 应返回 {"status":"UP","components":{"db":{"status":"UP"},"redis":{"status":"UP"}}}

注意:第 4 步构建镜像时,如果 Maven 报Could not resolve dependencies,说明私服地址没配。我们统一在~/.m2/settings.xml里配置了公司 Nexus 地址,<mirrorOf>*</mirrorOf>全局代理。这是 Java 团队的基建共识,不是 playedu 特有的问题。

4.2 JVM 参数调优:ZGC 为什么比 G1 更适合培训系统?

playedu 的 Java 应用有两大内存特征:

  • 对象生命周期短:学员点击“开始学习”,生成 session、token、临时缓存,5 分钟内基本失效;
  • 大对象多:课件 PDF、视频封面图、富文本 HTML 片段,单个对象常超 1MB。

G1 垃圾收集器在处理大对象时,会触发Humongous Allocation,把大对象直接扔进老年代,导致老年代快速膨胀,频繁 Mixed GC。我们实测过:G1 下,上传一个 50MB 视频后,jstat -gc显示G1OldGen使用率从 20% 暴涨到 85%,接着连续 3 次 Full GC,STW 时间累计 12 秒,用户端明显卡顿。

ZGC(Z Garbage Collector)是 JDK 11+ 的低延迟收集器,最大优势是:

  • 停顿时间稳定在 10ms 以内,无论堆多大;
  • 大对象分配无额外开销,ZGC 把堆分成 page,大对象直接占多个 page,不区分新生代老年代;
  • 并发标记、并发移动、并发重定位,99% 的 GC 工作和应用线程并行。

我们的 JVM 参数:

-Xms1024m -Xmx2048m -XX:+UseZGC -XX:ZCollectionInterval=5 -XX:ZUncommitDelay=300
  • -Xms1024m -Xmx2048m:初始堆 1G,最大 2G,留出 1G 给系统和其他进程;
  • -XX:+UseZGC:启用 ZGC;
  • -XX:ZCollectionInterval=5:每 5 秒强制触发一次 GC(ZGC 默认只在内存压力大时才 GC,但培训系统内存增长平缓,需要主动清理);
  • -XX:ZUncommitDelay=300:内存释放后,300 秒再真正归还给操作系统(避免频繁申请释放)。

✅ 验证方式:

# 进入容器查看 GC 日志 docker exec -it playedu-app tail -f /app/logs/gc.log # 正常输出类似: # [1.234s][info][gc] GC(0) Pause Mark Start 1.234ms # [1.245s][info][gc] GC(0) Pause Mark End 1.245ms # STW 时间精确到毫秒,且始终 < 10ms。

实操心得:ZGC 要求 Linux 内核 ≥ 4.14(Ubuntu 22.04 默认 5.15,满足)。如果用 CentOS 7(内核 3.10),必须升级内核或换 G1。我们曾在线上 CentOS 7 机器上强行开 ZGC,结果dmesg报mmap failed with ENOMEM,ZGC 启动失败,回退到 Serial GC,整个系统卡死。所以部署前务必uname -r确认内核版本。

4.3 安全加固:生产环境必须做的五件事

docker-compose 默认配置是开发友好型,生产环境必须收紧:

  1. 禁用默认的docker.sock挂载
    有些教程教人把/var/run/docker.sock挂进容器,方便容器内调 Docker API。这是严重安全隐患!一旦 playedu-app 被 XSS 攻击,攻击者就能curl --unix-socket /var/run/docker.sock http://localhost/containers/json列出所有容器,甚至POST /containers/{id}/start启动恶意容器。我们的docker-compose.prod.yml里绝无此挂载。

  2. 限制容器资源上限

    services: playedu-app: mem_limit: 2g mem_reservation: 1g cpus: '1.5'

    防止 Java 应用 OOM 崩溃整个宿主机。mem_reservation是软限制,保证 1G 内存随时可用;mem_limit是硬顶,超了就 kill。

  3. 以非 root 用户运行应用
    在Dockerfile里加两行:

    RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001 USER appuser

    避免java -jar app.jar以 root 身份读写文件。/app/uploads目录的属主必须是1001:1001。

  4. 关闭不必要的端口暴露
    docker-compose.yml里只保留:

    services: nginx: ports: - "80:80" - "443:443" # db、redis、adminer 全部不写 ports!只允许内部网络访问
  5. 启用 fail2ban 防爆破
    在宿主机上装 fail2ban,监控 Nginx 的error.log:

    # /etc/fail2ban/jail.local [nginx-badbots] enabled = true filter = nginx-badbots logpath = /var/log/nginx/error.log maxretry = 3 bantime = 3600

    三次 404 或 403 就封 IP 1 小时。我们上线一周,拦截了 17 个扫描器 IP,包括sqlmap和dirsearch。

提示:adminer容器在生产环境必须注释掉。如果真需要数据库管理,用ssh -L 8081:localhost:8080 user@server建立隧道,本地浏览器访问http://localhost:8081,这样流量全程加密,且不暴露在公网。


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

5.1 问题速查表:高频故障与 5 分钟定位法

现象可能原因快速验证命令解决方案
docker compose ps显示db状态为restartingPostgreSQL 初始化脚本语法错误docker logs playedu-db | tail -n 20检查init-db.sh里 SQL 语句末尾是否少了分号;
docker logs playedu-app报Connection refuseddb服务没起来,或depends_on没配healthcheckdocker compose ps db看状态;docker inspect playedu-db | grep Health在db服务下加healthcheck,playedu-app加depends_on: {db: {condition: service_healthy}}
前端白屏,Network 面板显示GET /api/login 502Nginx 没把请求转发到playedu-appdocker exec -it playedu-nginx nginx -t检查配置;curl -v http://playedu-app:8080/actuator/health确认upstream playedu_backend指向playedu-app:8080,且playedu-app的server.port=8080
上传课件失败,报413 Request Entity Too LargeNginx 默认client_max_body_size=1mdocker exec -it playedu-nginx nginx -T | grep client_max_body_size在nginx.conf的http块加client_max_body_size 100m;
登录后立即 401,控制台报Invalid JWT tokenplayedu-app的jwt.secret和前端VUE_APP_JWT_SECRET不一致docker exec -it playedu-app cat /app/config/application.yml | grep jwt.secret;对比前端.env保持前后端 secret 完全一致,建议用openssl rand -base64 32生成

注意:docker compose ps的Status列只显示容器状态,不反映应用健康。比如playedu-app容器 running,但 Spring Boot 启动卡在数据库连接,Status仍是running。必须结合docker logs和curl健康端点判断。

5.2 日志分析实战:从 1000 行日志里揪出真凶

有一次客户反馈:“课程列表打不开,一直 loading”。我们拿到playedu-app的日志,第一反应是查ERROR:

docker logs playedu-app \| grep -i "error\|exception" \| head -n 5

结果空。再查WARN:

docker logs playedu-app \| grep -i "warn" \| head -n 5

输出:

2024-05-20 14:22:33.123 WARN 1 --- [nio-8080-exec-3] c.p.c.CourseController : Failed to fetch courses: org.springframework.dao.DataAccessResourceFailureException: Could not obtain JDBC Connection
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 8:17:29

银行卡BIN号数据包:离线校验底座与多数据库落地实践

简介&#xff1a;本资源是一套面向金融行业开发人员、数据分析师及风控工程师的银行卡BIN号基础数据集与多平台管理方案&#xff0c;解决银行卡归属行识别、交易验证及反欺诈建模中的关键数据支撑问题。压缩包共7个文件&#xff0c;含5个Excel表格&#xff08;覆盖农民工卡、跨…

作者头像 李华
网站建设 2026/10/27 8:16:35

SPECTRE:用FFT加速自注意力,破解长上下文算力难题

1. 长上下文场景的算力黑洞&#xff1a;自注意力为什么先撑不住先把问题摆到桌面上。你训练一个序列长度 4096 的模型&#xff0c;自注意力模块要算一个 40964096 的分数矩阵&#xff1b;序列长度翻四倍到 16384&#xff0c;这个矩阵的面积直接变成原来的 16 倍。写代码的人对 …

作者头像 李华
网站建设 2026/10/27 8:16:19

Harness Engineering落地指南:避开11个常见误区

1. 别急着上马&#xff1a;先搞清楚Harness Engineering到底在解决什么问题我接触Harness Engineering这个概念大概是在三年前&#xff0c;当时团队正被“需求交付慢、测试环境互相打架、上线全靠胆量”折腾得够呛。身边不少同行开始提“把工程能力做成一套系统”&#xff0c;也…

作者头像 李华
网站建设 2026/10/27 8:16:03

RK3588上Yolov8多线程推理架构:从单线程30FPS到并发100FPS的实践

简介&#xff1a;面向瑞芯微RK3588平台的边缘计算开发者&#xff0c;这套演示程序完整实现了YOLOv8n目标检测的多线程推理链路&#xff0c;支持从本地视频文件与实时摄像头读取画面&#xff0c;在标准测试环境下可达到每秒100帧的高帧率。工程以C编写&#xff0c;通过线程池与流…

作者头像 李华
网站建设 2026/10/27 8:15:05

二叉树重建:从前序与中序遍历还原树结构

1. 二叉树重建的核心逻辑当我们手头只有先序和中序遍历结果时&#xff0c;如何准确还原原始的二叉树结构&#xff1f;这个问题看似简单&#xff0c;却蕴含着二叉树遍历特性的精妙之处。先序遍历的第一个元素必然是根节点&#xff0c;这个关键信息为我们提供了重建的起点。而中序…

作者头像 李华