
简介这是一套面向高校计算机专业学生与Java开发学习者的毕业设计级项目源码围绕大学生兼职信息发布与交流场景采用Docker容器化部署方案适合需要完成课程设计、毕业设计或希望积累Spring Boot全栈实战经验的开发者参考。压缩包共535个文件约2.99MB以188个Java源文件、99个JavaScript脚本、48个HTML页面及24个CSS样式文件为主体另含2个SQL数据库脚本、2份Word文档开题报告与任务书以及yml配置、md说明等辅助文件覆盖前后端代码、数据库设计与项目文档。资源完整呈现了从需求分析、系统设计到编码实现的开发链路读者可据此快速搭建测试环境、理解容器化部署流程并在此基础上进行功能扩展与二次开发。目前已有64人学习下载适合作为全栈项目实践与Docker入门的参考素材。1. 从一台裸机到能跑兼职平台Docker 到底帮你省掉了什么很多同学做毕业设计时选题是「大学生兼职平台」本地跑得好好的一换电脑就崩MySQL 版本不对、Redis 没装、Node 版本冲突、PHP 扩展缺失。这套东西如果只靠手动装环境答辩前一周基本都在修环境而不是改代码。Docker 的价值就在这里——它把「兼职平台」这个应用连同它的运行环境一起打包成镜像换台机器只要docker compose up就能起来不用再问「你装的是 MySQL 几」。这篇笔记讲的是如何用 Docker 把一个大学生兼职平台从零部署起来包括镜像选型、compose 编排、数据库初始化、容器网络打通以及那些第一次部署几乎必踩的坑。适合正在做类似课设/毕设、或者想把一个多服务 Web 项目容器化的同学。读完你应该能自己写出 compose 文件把前端、后端、MySQL、Redis 一次性拉起来并且知道出问题时先看哪里。2. 兼职平台的服务拆分与 Docker 选型为什么不是全都塞进一个容器2.1 先想清楚这个平台有几个进程一个典型的大学生兼职平台业务上至少包含这几块用户学生/商家注册登录、兼职信息发布与检索、报名与录用、简单的消息通知。落到技术实现常见做法是前后端分离前端Vue 或 React 打包出的静态资源用 Nginx 托管后端Spring BootJava或 Express/KoaNode对外提供 REST 接口数据库MySQL 存用户、岗位、报名记录缓存/会话Redis 存登录 token、热门岗位缓存可选Nginx 做反向代理把/api转发到后端这五个东西如果全装在一台机器上手动配问题不大但不可复现。Docker 的思路是每个进程一个容器各管各的依赖通过自定义网络互相访问。这里有个新手最容易犯的错——把 MySQL 和后端塞进同一个容器。这样做镜像会变得巨大而且数据库数据没法独立持久化容器一删数据全没。提示一个容器只跑一个主进程这是 Docker 官方一直强调的原则也是后面能单独升级、单独备份的前提。2.2 镜像怎么选官方镜像 vs 自己 build选型上有两条路。第一条是全部用官方镜像mysql:8.0、redis:7-alpine、nginx:alpine后端自己写 Dockerfile 基于eclipse-temurin:17-jre或node:20-alpine构建。第二条是找现成的全套镜像但兼职平台这种业务系统基本没有现成的所以实际就是「官方基础镜像 自己写后端 Dockerfile」。基础镜像选择上有几个经验值组件推荐镜像理由MySQLmysql:8.08.0 是当前主流认证插件默认 caching_sha2_password注意老客户端兼容Redisredis:7-alpinealpine 体积小7.x 稳定Nginxnginx:alpine托管静态资源足够配置挂载即可后端(Java)eclipse-temurin:17-jre只跑 jar用 jre 不用 jdk镜像小一半后端(Node)node:20-alpine多阶段构建时 build 阶段用完整镜像运行阶段用 alpine选 alpine 的理由很直接镜像小拉取快。但 alpine 用的是 musl libc某些依赖 glibc 的二进制比如部分 native 模块会出问题。如果后端有这类依赖就老老实实用 debian 系的基础镜像别为了省几十 MB 折腾半天。2.3 后端 Dockerfile 怎么写才不臃肿以 Spring Boot 为例常见做法是先在本地或 CI 里mvn package打出 jar再 COPY 进镜像。这样 Dockerfile 极简# 基于 JRE 而非 JDK运行阶段不需要编译器 FROM eclipse-temurin:17-jre # 设置时区否则日志时间差 8 小时排查问题时很坑 ENV TZAsia/Shanghai WORKDIR /app # 只拷贝构建产物不拷贝源码和 target 里的其他东西 COPY target/*.jar app.jar # 声明容器对外端口和后端 application.yml 里的 server.port 保持一致 EXPOSE 8080 # 用 exec 形式启动保证 Java 进程是 PID 1能正确接收停止信号 ENTRYPOINT [java, -jar, app.jar]逻辑说明COPY target/*.jar依赖你本地已经构建过所以构建镜像前必须先mvn clean package -DskipTests。参数上-DskipTests在部署阶段跳过测试能省几分钟但正式发布前建议至少跑一遍。ENTRYPOINT用数组形式而不是 shell 形式是为了让 Java 进程成为容器 1 号进程docker stop时能优雅关闭而不是被强杀。Node 后端则推荐多阶段构建把node_modules的安装和运行分开# 构建阶段装依赖、编译 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY . . RUN npm run build # 运行阶段只带产物和依赖 FROM node:20-alpine WORKDIR /app COPY --frombuilder /app/node_modules ./node_modules COPY --frombuilder /app/dist ./dist EXPOSE 3000 CMD [node, dist/main.js]npm ci比npm install更适合构建它严格按 lock 文件装保证每次构建依赖一致。--onlyproduction跳过 devDependencies运行镜像更小。3. 用 docker compose 把五个服务编排起来3.1 compose 文件骨架与网络设计单容器docker run命令一长串参数服务一多就没法维护。compose 用一个 YAML 描述所有服务一条命令拉起。核心结构是services下面每个服务一段加上volumes和networks顶层声明。services: mysql: image: mysql:8.0 container_name: parttime-mysql environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: parttime MYSQL_USER: app MYSQL_PASSWORD: app123456 volumes: # 数据持久化容器删了数据还在 - mysql-data:/var/lib/mysql # 初始化脚本首次启动自动执行 - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql networks: - parttime-net healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: parttime-redis command: redis-server --appendonly yes volumes: - redis-data:/data networks: - parttime-net backend: build: ./backend container_name: parttime-backend depends_on: mysql: condition: service_healthy redis: condition: service_started environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/parttime?useSSLfalseserverTimezoneAsia/Shanghai SPRING_DATASOURCE_USERNAME: app SPRING_DATASOURCE_PASSWORD: app123456 SPRING_REDIS_HOST: redis networks: - parttime-net frontend: build: ./frontend container_name: parttime-frontend networks: - parttime-net nginx: image: nginx:alpine container_name: parttime-nginx ports: - 80:80 volumes: - ./nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - backend - frontend networks: - parttime-net volumes: mysql-data: redis-data: networks: parttime-net: driver: bridge逻辑说明所有服务挂在同一个自定义网络parttime-net上容器之间可以直接用服务名当主机名访问比如后端连数据库写mysql:3306而不是 IP。这是 compose 最省心的地方——不用管容器 IP 会变。参数说明depends_on配合condition: service_healthy能保证 MySQL 真正就绪后后端才启动。很多人只写depends_on: [mysql]那只保证容器启动顺序不保证 MySQL 初始化完成后端照样会因为连不上库而崩。healthcheck里的mysqladmin ping是判断 MySQL 是否可接受连接的标准做法。3.2 数据库初始化脚本与字符集docker-entrypoint-initdb.d目录下的.sql文件只在数据卷为空时执行一次。也就是说如果你改了 init.sql 想重新初始化必须先docker compose down -v删掉数据卷否则脚本不会重跑。这是新手最常问的「为什么我改了 SQL 没生效」。-- 建库时指定字符集避免中文乱码 CREATE DATABASE IF NOT EXISTS parttime DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE parttime; CREATE TABLE IF NOT EXISTS user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT NOT NULL COMMENT 1学生 2商家, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE IF NOT EXISTS job ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, salary DECIMAL(10,2), publisher_id BIGINT NOT NULL, status TINYINT DEFAULT 1 COMMENT 1招聘中 2已结束, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_publisher (publisher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字符集一定要在建库和建表两处都写utf8mb4。只写一处另一处会继承服务器默认值某些镜像默认是 latin1存中文直接变问号。utf8mb4_unicode_ci排序规则对中文和 emoji 都友好。3.3 Nginx 反向代理配置前端静态资源和后端 API 通过 Nginx 统一入口避免跨域问题server { listen 80; server_name localhost; # 前端静态资源 location / { root /usr/share/nginx/html; index index.html; # 单页应用路由回退刷新子路由不 404 try_files $uri $uri/ /index.html; } # 后端 API 转发 location /api/ { proxy_pass http://backend:8080/; 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_pass末尾带不带斜杠差别很大http://backend:8080/会把/api/user转成/user不带斜杠则保留/api/user。这个细节对不上后端接口全部 404。try_files那行是给 Vue Router history 模式用的不加的话用户刷新/job/123会 404。4. 部署实操从零到能访问的完整命令序列4.1 环境准备与 Docker 安装确认先确认 Docker 和 compose 都在。Ubuntu 上常见做法是用官方脚本或 apt 源安装装完执行# 查看 Docker 版本确认装的是 20.10 以上 docker --version # compose v2 是插件形式命令是 docker compose 而不是 docker-compose docker compose version # 确认当前用户能免 sudo 操作 docker docker ps如果docker ps报permission denied while trying to connect to the Docker API说明当前用户不在 docker 组里。解决方式是sudo usermod -aG docker $USER然后重新登录不是重开终端要重新登录会话才生效。这个坑几乎每个人都会踩一次改完组不重登一直报权限错。4.2 构建与启动在项目根目录有 compose 文件那层执行# 构建自定义镜像backend 和 frontend-d 后台运行 docker compose up -d --build # 查看所有服务状态重点看 STATUS 是不是 Up 和 healthy docker compose ps # 跟踪后端日志排查启动失败 docker compose logs -f backend--build会强制重新构建有 Dockerfile 的服务。第一次跑会拉取基础镜像国内网络可能慢可以配置镜像加速器在 Docker Desktop 或/etc/docker/daemon.json里加 registry-mirrors。启动后如果docker compose ps里 backend 一直重启先看日志八成是连不上 MySQL 或 Redis。4.3 验证服务是否真的通了不要只看容器 Up 就以为成了要实际验证链路# 进 MySQL 容器执行查询确认库和表建好了 docker exec -it parttime-mysql mysql -uapp -papp123456 -e USE parttime; SHOW TABLES; # 进 Redis 容器 ping 一下 docker exec -it parttime-redis redis-cli ping # 从宿主机访问 Nginx确认前端能打开 curl -I http://localhost/docker exec是进容器排查的主力命令。-it分配交互终端-e直接执行 SQL 不进入交互模式。如果 MySQL 查询报Access denied检查 compose 里的MYSQL_USER和MYSQL_PASSWORD是否和连接串一致——注意MYSQL_USER不能设成rootroot 用户由MYSQL_ROOT_PASSWORD单独控制。5. 部署兼职平台最容易翻车的几个地方5.1 后端启动就退出日志报连不上数据库现象docker compose ps显示 backend 状态是Restarting或Exited (1)日志里一堆Communications link failure。原因depends_on只写了服务名没写condition: service_healthyMySQL 容器虽然起来了但还在初始化后端抢跑连不上。解决给 MySQL 加healthcheck后端depends_on改成带 condition 的写法。如果已经这么写了还报错检查 healthcheck 的test命令在容器里能不能跑通mysqladmin路径对不对。5.2 中文数据存进去变问号现象前端提交的中文岗位名称存到数据库查出来是???。原因建库或建表时字符集不是utf8mb4或者 JDBC 连接串没指定编码。解决建库建表都显式写utf8mb4JDBC URL 加上characterEncodingutf8。已经建好的库要改字符集得ALTER DATABASE和ALTER TABLE ... CONVERT TO比较麻烦所以一开始就写对最省事。5.3 改了初始化 SQL 但没生效现象往init.sql加了新表docker compose up -d之后进数据库发现新表不存在。原因docker-entrypoint-initdb.d只在数据目录为空时执行数据卷mysql-data已经有数据脚本被跳过。解决docker compose down -v删掉数据卷再up。注意-v会删掉所有数据生产环境千万别随手加开发阶段无所谓。想保留数据又要改结构就手动进容器执行 SQL 或用迁移工具。5.4 容器之间网络不通现象后端日志报UnknownHostException: mysql或连接超时。原因服务没挂在同一个自定义网络上或者用了默认的bridge网络但没配links老写法。解决所有需要互相访问的服务在 compose 里都声明同一个networks用服务名当主机名。默认的bridge网络不支持自动 DNS 解析服务名必须用自定义网络。检查方法docker exec -it parttime-backend ping mysql能通说明网络没问题。5.5 端口被占用导致启动失败现象docker compose up报Bind for 0.0.0.0:80 failed: port is already allocated。原因宿主机 80 端口被别的进程比如系统自带的 Nginx 或 Apache占了。解决sudo lsof -i:80找到占用进程要么停掉它要么把 compose 里的端口映射改成8080:80用 8080 访问。改端口映射只影响宿主机侧容器内部还是 80Nginx 配置不用动。6. 让这套部署真正能交付镜像瘦身与一键重建部署能跑起来只是第一步答辩或交付时你还需要两件事镜像别太大、重建别太慢。这里说几个我实际用下来最有效的技巧。镜像瘦身最直接的是多阶段构建和选对基础镜像。Java 后端从eclipse-temurin:17-jdk换成17-jre镜像能从 400 多 MB 降到 200 MB 出头。Node 用 alpine 版本配合npm ci --onlyproduction运行镜像能控制在 150 MB 以内。前端构建产物 COPY 进 nginx:alpine整个前端镜像不到 50 MB。这些数字不是抠门是拉取和分发时实打实的时间。另一个常被忽略的是.dockerignore。默认情况下COPY . .会把node_modules、.git、target全塞进构建上下文构建慢还容易把本地依赖带进镜像。在项目根目录加一个# .dockerignore 内容 node_modules .git target *.log .env构建上下文小了docker build明显快。这个文件很多人不知道加上之后构建时间能省一半。验证部署是否可复现最靠谱的方法是模拟一次全新环境docker compose down -v清掉所有容器和数据卷再docker compose up -d --build看能不能从零起来。如果这一步能过说明你的 compose 和初始化脚本是自洽的换台机器也能跑。我一般会在答辩前一天做一次这个全清重建把「本地能跑别人跑不了」的风险提前排掉。最后一个习惯把常用命令写成 Makefile 或 shell 脚本比如make up、make logs、make reset。不是为了炫技是避免每次手敲一长串命令敲错参数。血泪经验是答辩现场手忙脚乱敲错一个-v数据全没了那才是真的没有后悔药。希望帮到你。本文还有配套的精品资源点击获取