diff --git a/build.sh b/build.sh index 442c733..eeaa0d6 100644 --- a/build.sh +++ b/build.sh @@ -6,8 +6,35 @@ # 1. 从git拉取最新代码 # 2. 编译后端jar包 # 3. 构建前端dist -# 4. 制作Docker镜像 +# 4. 制作Docker镜像(支持基础镜像和应用镜像) # 5. 保存镜像到文件(用于离线部署) +# +# 使用方法: +# ./build.sh [target] [options] [version] +# +# 目标(target): +# base-serv - 构建后端基础镜像(包含Python依赖) +# base-web - 构建前端基础镜像(Nginx) +# base-all - 构建所有基础镜像 +# serv - 构建后端服务镜像 +# web - 构建前端服务镜像 +# mysql - 构建MySQL镜像 +# all - 构建所有应用镜像(默认) +# +# 选项(options): +# build - 编译代码(serv和web需要) +# save - 保存镜像到tar文件(需要指定version) +# +# 版本(version): +# save=YYYYMMDD_HHMMSS - 指定要保存的镜像版本 +# 不指定则使用当前构建的版本 +# +# 示例: +# ./build.sh base-all # 构建所有基础镜像 +# ./build.sh serv build save # 编译后端+构建镜像+保存当前版本 +# ./build.sh all build save # 完整构建流程 +# ./build.sh serv save=20251124_143025 # 保存指定版本的镜像 +# ./build.sh all build save=20251124_150000 # 构建并保存为指定版本 ############################################## set -e # 遇到错误立即退出 @@ -44,10 +71,48 @@ DOCKER_PATH="${PROJECT_ROOT}/docker" BUILD_OUTPUT="${PROJECT_ROOT}/build-output" IMAGE_VERSION=$(date +%Y%m%d_%H%M%S) +# 解析命令行参数 +BUILD_TARGET="${1:-all}" # 默认构建所有应用镜像 +shift || true +DO_BUILD=false +DO_SAVE=false +SAVE_VERSION="" # 要保存的版本号 + +# 解析选项 +for arg in "$@"; do + case $arg in + build) + DO_BUILD=true + ;; + save) + DO_SAVE=true + # 如果不指定版本,使用当前构建的版本 + ;; + save=*) + DO_SAVE=true + SAVE_VERSION="${arg#save=}" + ;; + *) + log_warn "未知选项: $arg" + ;; + esac +done + +# 如果save时没有指定版本,使用当前时间戳 +if [ "${DO_SAVE}" = true ] && [ -z "${SAVE_VERSION}" ]; then + SAVE_VERSION="${IMAGE_VERSION}" +fi + echo "==========================================" echo "校园新闻管理系统 - 构建脚本" echo "==========================================" +log_info "构建目标: ${BUILD_TARGET}" log_info "构建版本: ${IMAGE_VERSION}" +log_info "编译代码: ${DO_BUILD}" +log_info "保存镜像: ${DO_SAVE}" +if [ "${DO_SAVE}" = true ]; then + log_info "保存版本: ${SAVE_VERSION}" +fi log_info "项目路径: ${PROJECT_ROOT}" echo "==========================================" echo "" @@ -56,312 +121,320 @@ echo "" mkdir -p "${BUILD_OUTPUT}" # ================================================ -# 步骤1: Git Pull +# 构建函数 # ================================================ -log_step "步骤1: 拉取最新代码" -cd "${PROJECT_ROOT}" -# 检查是否有未提交的更改 -if [[ $(git status --porcelain) ]]; then - log_warn "检测到未提交的更改" - read -p "是否继续拉取代码?(y/n): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - log_error "已取消构建" +# 构建后端基础镜像 +build_base_serv() { + log_step "构建后端基础镜像(Base Serv)" + cd "${PROJECT_ROOT}" + + log_info "执行: docker build -t school-news-base-serv:${IMAGE_VERSION} -f docker/Dockerfile.base-serv ." + if docker build -t school-news-base-serv:${IMAGE_VERSION} -f docker/Dockerfile.base-serv .; then + docker tag school-news-base-serv:${IMAGE_VERSION} school-news-base-serv:latest + log_info "✅ 后端基础镜像构建成功" + log_info " 镜像标签: school-news-base-serv:${IMAGE_VERSION}" + log_info " 镜像标签: school-news-base-serv:latest" + else + log_error "❌ 后端基础镜像构建失败" exit 1 fi -fi + echo "" +} -log_info "从远程仓库拉取代码..." -if git pull origin main 2>/dev/null || git pull origin master 2>/dev/null; then - log_info "代码拉取完成" -else - log_warn "代码拉取失败或已是最新版本,继续构建..." -fi -echo "" - -# ================================================ -# 步骤2: 构建后端jar包 -# ================================================ -log_step "步骤2: 构建后端jar包" -cd "${SERV_PATH}" - -log_info "清理旧的构建..." -mvn clean -q - -log_info "开始编译后端项目..." -log_info "执行: mvn package -DskipTests -pl admin -am" -if mvn package -DskipTests -pl admin -am; then - log_info "后端编译成功" -else - log_error "后端编译失败" - exit 1 -fi - -# 验证jar包 -JAR_FILE="${SERV_PATH}/admin/target/admin-1.0.0.jar" -if [ -f "${JAR_FILE}" ]; then - JAR_SIZE=$(du -h "${JAR_FILE}" | cut -f1) - log_info "✅ jar包已生成: ${JAR_SIZE}" - log_info " 路径: ${JAR_FILE}" -else - log_error "❌ jar包未找到" - exit 1 -fi -echo "" - -# ================================================ -# 步骤3: 构建前端dist -# ================================================ -log_step "步骤3: 构建前端项目" -cd "${WEB_PATH}" - -# 检查node_modules -if [ ! -d "node_modules" ]; then - log_info "安装前端依赖..." - npm install -fi - -log_info "清理旧的构建..." -rm -rf dist - -log_info "开始构建前端项目..." -log_info "执行: npm run build" -if npm run build; then - log_info "前端构建成功" -else - log_error "前端构建失败" - exit 1 -fi - -# 验证dist目录 -DIST_DIR="${WEB_PATH}/dist" -if [ -d "${DIST_DIR}" ]; then - DIST_SIZE=$(du -sh "${DIST_DIR}" | cut -f1) - FILE_COUNT=$(find "${DIST_DIR}" -type f | wc -l) - log_info "✅ dist目录已生成: ${DIST_SIZE} (${FILE_COUNT}个文件)" - log_info " 路径: ${DIST_DIR}" -else - log_error "❌ dist目录未找到" - exit 1 -fi -echo "" - -# ================================================ -# 步骤4: 制作Docker镜像 -# ================================================ -log_step "步骤4: 制作Docker镜像" -cd "${PROJECT_ROOT}" +# 构建前端基础镜像 +build_base_web() { + log_step "构建前端基础镜像(Base Web)" + cd "${PROJECT_ROOT}" + + log_info "执行: docker build -t school-news-base-web:${IMAGE_VERSION} -f docker/Dockerfile.base-web ." + if docker build -t school-news-base-web:${IMAGE_VERSION} -f docker/Dockerfile.base-web .; then + docker tag school-news-base-web:${IMAGE_VERSION} school-news-base-web:latest + log_info "✅ 前端基础镜像构建成功" + log_info " 镜像标签: school-news-base-web:${IMAGE_VERSION}" + log_info " 镜像标签: school-news-base-web:latest" + else + log_error "❌ 前端基础镜像构建失败" + exit 1 + fi + echo "" +} # 构建MySQL镜像 -log_info "构建MySQL镜像(包含数据库初始化)..." -log_info "执行: docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql ." -if docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql .; then - log_info "✅ MySQL镜像构建成功" - docker tag school-news-mysql:${IMAGE_VERSION} school-news-mysql:latest - log_info " 镜像标签: school-news-mysql:${IMAGE_VERSION}" - log_info " 镜像标签: school-news-mysql:latest" -else - log_error "❌ MySQL镜像构建失败" - exit 1 -fi -echo "" +build_mysql() { + log_step "构建MySQL镜像" + cd "${PROJECT_ROOT}" + + log_info "执行: docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql ." + if docker build -t school-news-mysql:${IMAGE_VERSION} -f docker/Dockerfile.mysql .; then + docker tag school-news-mysql:${IMAGE_VERSION} school-news-mysql:latest + log_info "✅ MySQL镜像构建成功" + log_info " 镜像标签: school-news-mysql:${IMAGE_VERSION}" + log_info " 镜像标签: school-news-mysql:latest" + else + log_error "❌ MySQL镜像构建失败" + exit 1 + fi + echo "" +} -# 构建后端镜像 -log_info "构建后端镜像..." -log_info "执行: docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv ." -if docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv .; then - log_info "✅ 后端镜像构建成功" - # 同时打上latest标签 - docker tag school-news-serv:${IMAGE_VERSION} school-news-serv:latest - log_info " 镜像标签: school-news-serv:${IMAGE_VERSION}" - log_info " 镜像标签: school-news-serv:latest" -else - log_error "❌ 后端镜像构建失败" - exit 1 -fi -echo "" +# 构建后端服务镜像 +build_serv() { + log_step "构建后端服务镜像(Serv)" + cd "${PROJECT_ROOT}" + + # 检查基础镜像 + if ! docker images | grep -q "school-news-base-serv.*latest"; then + log_error "基础镜像不存在: school-news-base-serv:latest" + log_error "请先构建基础镜像:./build.sh base-serv" + exit 1 + fi + + # 检查jar包 + if [ ! -f "${SERV_PATH}/admin/target/admin-1.0.0.jar" ]; then + log_error "jar包不存在: ${SERV_PATH}/admin/target/admin-1.0.0.jar" + log_error "请先编译后端项目:./build.sh serv build" + exit 1 + fi + + log_info "执行: docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv ." + if docker build -t school-news-serv:${IMAGE_VERSION} -f docker/Dockerfile.serv .; then + docker tag school-news-serv:${IMAGE_VERSION} school-news-serv:latest + log_info "✅ 后端服务镜像构建成功" + log_info " 镜像标签: school-news-serv:${IMAGE_VERSION}" + log_info " 镜像标签: school-news-serv:latest" + else + log_error "❌ 后端服务镜像构建失败" + exit 1 + fi + echo "" +} -# 构建前端镜像 -log_info "构建前端镜像..." -log_info "执行: docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web ." -if docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web .; then - log_info "✅ 前端镜像构建成功" - # 同时打上latest标签 - docker tag school-news-web:${IMAGE_VERSION} school-news-web:latest - log_info " 镜像标签: school-news-web:${IMAGE_VERSION}" - log_info " 镜像标签: school-news-web:latest" -else - log_error "❌ 前端镜像构建失败" - exit 1 +# 构建前端服务镜像 +build_web() { + log_step "构建前端服务镜像(Web)" + cd "${PROJECT_ROOT}" + + # 检查基础镜像 + if ! docker images | grep -q "school-news-base-web.*latest"; then + log_error "基础镜像不存在: school-news-base-web:latest" + log_error "请先构建基础镜像:./build.sh base-web" + exit 1 + fi + + # 检查dist目录 + if [ ! -d "${WEB_PATH}/dist" ]; then + log_error "dist目录不存在: ${WEB_PATH}/dist" + log_error "请先构建前端项目:./build.sh web build" + exit 1 + fi + + log_info "执行: docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web ." + if docker build -t school-news-web:${IMAGE_VERSION} -f docker/Dockerfile.web .; then + docker tag school-news-web:${IMAGE_VERSION} school-news-web:latest + log_info "✅ 前端服务镜像构建成功" + log_info " 镜像标签: school-news-web:${IMAGE_VERSION}" + log_info " 镜像标签: school-news-web:latest" + else + log_error "❌ 前端服务镜像构建失败" + exit 1 + fi + echo "" +} + +# 编译后端 +compile_serv() { + log_step "编译后端jar包" + cd "${SERV_PATH}" + + log_info "清理旧的构建..." + mvn clean -q + + log_info "开始编译后端项目..." + if mvn package -DskipTests -pl admin -am; then + JAR_FILE="${SERV_PATH}/admin/target/admin-1.0.0.jar" + JAR_SIZE=$(du -h "${JAR_FILE}" | cut -f1) + log_info "✅ 后端编译成功: ${JAR_SIZE}" + else + log_error "❌ 后端编译失败" + exit 1 + fi + echo "" +} + +# 构建前端 +compile_web() { + log_step "构建前端dist" + cd "${WEB_PATH}" + + log_info "清理旧的构建..." + rm -rf dist + + log_info "开始构建前端项目..." + if npm run build; then + FILE_COUNT=$(find dist -type f | wc -l) + DIST_SIZE=$(du -sh dist | cut -f1) + log_info "✅ 前端构建成功: ${FILE_COUNT}个文件, ${DIST_SIZE}" + else + log_error "❌ 前端构建失败" + exit 1 + fi + echo "" +} + +# 保存镜像 +save_images() { + log_step "保存Docker镜像" + cd "${BUILD_OUTPUT}" + + case ${BUILD_TARGET} in + base-serv) + save_image "school-news-base-serv" + ;; + base-web) + save_image "school-news-base-web" + ;; + base-all) + save_image "school-news-base-serv" + save_image "school-news-base-web" + ;; + mysql) + save_image "school-news-mysql" + ;; + serv) + save_image "school-news-serv" + ;; + web) + save_image "school-news-web" + ;; + all) + save_image "school-news-mysql" + save_image "school-news-serv" + save_image "school-news-web" + ;; + esac +} + +# 保存单个镜像(使用指定版本或当前构建版本) +save_image() { + local IMAGE_NAME=$1 + local VERSION="${SAVE_VERSION}" + local IMAGE_FILE="${IMAGE_NAME}_${VERSION}.tar" + + # 检查镜像是否存在 + if ! docker images | grep -q "${IMAGE_NAME}.*${VERSION}"; then + log_error "❌ 镜像不存在: ${IMAGE_NAME}:${VERSION}" + log_error " 请先构建该版本的镜像,或使用 docker images 查看可用版本" + exit 1 + fi + + log_info "保存${IMAGE_NAME}:${VERSION}镜像..." + if docker save -o "${IMAGE_FILE}" ${IMAGE_NAME}:${VERSION}; then + IMAGE_SIZE=$(du -h "${IMAGE_FILE}" | cut -f1) + log_info "✅ 镜像已保存: ${IMAGE_SIZE}" + log_info " 版本: ${IMAGE_NAME}:${VERSION}" + log_info " 文件: ${BUILD_OUTPUT}/${IMAGE_FILE}" + else + log_error "❌ 镜像保存失败: ${IMAGE_NAME}:${VERSION}" + exit 1 + fi + echo "" +} + +# ================================================ +# 主流程控制 +# ================================================ + +# Git更新(可选) +if [ "${DO_BUILD}" = true ]; then + log_step "Git Pull" + cd "${PROJECT_ROOT}" + + if [[ $(git status --porcelain) ]]; then + log_warn "检测到未提交的更改" + read -p "是否继续拉取代码?(y/n): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_warn "跳过git pull" + else + git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true + fi + else + git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true + fi + echo "" fi -echo "" + +# 执行编译 +if [ "${DO_BUILD}" = true ]; then + case ${BUILD_TARGET} in + serv|all) + compile_serv + ;& # fallthrough + web|all) + compile_web + ;; + esac +fi + +# 执行镜像构建 +case ${BUILD_TARGET} in + base-serv) + build_base_serv + ;; + base-web) + build_base_web + ;; + base-all) + build_base_serv + build_base_web + ;; + mysql) + build_mysql + ;; + serv) + build_serv + ;; + web) + build_web + ;; + all) + build_mysql + build_serv + build_web + ;; + *) + log_error "未知的构建目标: ${BUILD_TARGET}" + echo "" + echo "可用的构建目标:" + echo " base-serv - 构建后端基础镜像" + echo " base-web - 构建前端基础镜像" + echo " base-all - 构建所有基础镜像" + echo " mysql - 构建MySQL镜像" + echo " serv - 构建后端服务镜像" + echo " web - 构建前端服务镜像" + echo " all - 构建所有应用镜像(默认)" + exit 1 + ;; +esac # 查看镜像信息 log_info "Docker镜像列表:" -docker images | grep -E "school-news-(mysql|serv|web)" | head -10 +docker images | grep -E "school-news-" | head -15 echo "" -# ================================================ -# 步骤5: 保存镜像到文件 -# ================================================ -log_step "步骤5: 保存Docker镜像" -cd "${BUILD_OUTPUT}" - -# 保存MySQL镜像 -MYSQL_IMAGE_FILE="school-news-mysql_${IMAGE_VERSION}.tar" -log_info "保存MySQL镜像到文件..." -log_info "执行: docker save -o ${MYSQL_IMAGE_FILE} school-news-mysql:${IMAGE_VERSION}" -if docker save -o "${MYSQL_IMAGE_FILE}" school-news-mysql:${IMAGE_VERSION}; then - MYSQL_SIZE=$(du -h "${MYSQL_IMAGE_FILE}" | cut -f1) - log_info "✅ MySQL镜像已保存: ${MYSQL_SIZE}" - log_info " 文件: ${BUILD_OUTPUT}/${MYSQL_IMAGE_FILE}" -else - log_error "❌ MySQL镜像保存失败" - exit 1 +# 保存镜像 +if [ "${DO_SAVE}" = true ]; then + save_images fi -echo "" - -# 保存后端镜像 -SERV_IMAGE_FILE="school-news-serv_${IMAGE_VERSION}.tar" -log_info "保存后端镜像到文件..." -log_info "执行: docker save -o ${SERV_IMAGE_FILE} school-news-serv:${IMAGE_VERSION}" -if docker save -o "${SERV_IMAGE_FILE}" school-news-serv:${IMAGE_VERSION}; then - SERV_SIZE=$(du -h "${SERV_IMAGE_FILE}" | cut -f1) - log_info "✅ 后端镜像已保存: ${SERV_SIZE}" - log_info " 文件: ${BUILD_OUTPUT}/${SERV_IMAGE_FILE}" -else - log_error "❌ 后端镜像保存失败" - exit 1 -fi -echo "" - -# 保存前端镜像 -WEB_IMAGE_FILE="school-news-web_${IMAGE_VERSION}.tar" -log_info "保存前端镜像到文件..." -log_info "执行: docker save -o ${WEB_IMAGE_FILE} school-news-web:${IMAGE_VERSION}" -if docker save -o "${WEB_IMAGE_FILE}" school-news-web:${IMAGE_VERSION}; then - WEB_SIZE=$(du -h "${WEB_IMAGE_FILE}" | cut -f1) - log_info "✅ 前端镜像已保存: ${WEB_SIZE}" - log_info " 文件: ${BUILD_OUTPUT}/${WEB_IMAGE_FILE}" -else - log_error "❌ 前端镜像保存失败" - exit 1 -fi -echo "" - -# 压缩镜像文件(可选) -log_info "是否压缩镜像文件?(y/n): " -read -p "" -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - log_info "压缩镜像文件..." - - if command -v gzip &> /dev/null; then - log_info "使用gzip压缩..." - gzip -f "${SERV_IMAGE_FILE}" - gzip -f "${WEB_IMAGE_FILE}" - - SERV_GZ_SIZE=$(du -h "${SERV_IMAGE_FILE}.gz" | cut -f1) - WEB_GZ_SIZE=$(du -h "${WEB_IMAGE_FILE}.gz" | cut -f1) - - log_info "✅ 压缩完成" - log_info " 后端: ${SERV_GZ_SIZE} (${SERV_IMAGE_FILE}.gz)" - log_info " 前端: ${WEB_GZ_SIZE} (${WEB_IMAGE_FILE}.gz)" - else - log_warn "gzip未安装,跳过压缩" - fi -fi -echo "" # ================================================ -# 生成部署说明文件 -# ================================================ -log_info "生成部署说明文件..." -DEPLOY_INFO_FILE="${BUILD_OUTPUT}/部署说明_${IMAGE_VERSION}.txt" - -cat > "${DEPLOY_INFO_FILE}" << EOF -======================================== -校园新闻管理系统 - Docker镜像部署说明 -======================================== - -构建信息: - 构建时间: $(date '+%Y-%m-%d %H:%M:%S') - 版本号: ${IMAGE_VERSION} - 构建主机: $(hostname) - Git分支: $(git branch --show-current 2>/dev/null || echo "未知") - Git提交: $(git rev-parse --short HEAD 2>/dev/null || echo "未知") - -镜像文件: - MySQL镜像: ${MYSQL_IMAGE_FILE}$([ -f "${MYSQL_IMAGE_FILE}.gz" ] && echo ".gz") - 后端镜像: ${SERV_IMAGE_FILE}$([ -f "${SERV_IMAGE_FILE}.gz" ] && echo ".gz") - 前端镜像: ${WEB_IMAGE_FILE}$([ -f "${WEB_IMAGE_FILE}.gz" ] && echo ".gz") - -======================================== -部署步骤: -======================================== - -1. 将镜像文件传输到目标服务器 - -2. 解压镜像文件(如果已压缩): - gunzip ${SERV_IMAGE_FILE}.gz - gunzip ${WEB_IMAGE_FILE}.gz - -3. 加载Docker镜像: - docker load -i ${MYSQL_IMAGE_FILE} - docker load -i ${SERV_IMAGE_FILE} - docker load -i ${WEB_IMAGE_FILE} - -4. 验证镜像: - docker images | grep school-news - -5. 启动服务: - cd docker - docker-compose up -d - -6. 查看服务状态: - docker-compose ps - docker-compose logs -f - -======================================== -访问地址: -======================================== - 前端: http://localhost:8080/schoolNewsWeb/ - 后端: http://localhost:8081/schoolNewsServ - -======================================== -注意事项: -======================================== -1. 确保目标服务器已安装Docker和Docker Compose -2. 确保端口8080、8081、3306、6379未被占用 -3. 首次部署需要准备数据库初始化脚本 -4. 生产环境请修改默认密码(.env文件) - -======================================== -EOF - -log_info "✅ 部署说明已生成: ${DEPLOY_INFO_FILE}" -echo "" - -# ================================================ -# 构建摘要 +# 构建完成 # ================================================ echo "==========================================" -echo "构建完成!" +log_info "✅ 构建完成!" echo "==========================================" +log_info "构建目标: ${BUILD_TARGET}" log_info "构建版本: ${IMAGE_VERSION}" -echo "" -log_info "Docker镜像:" -echo " - school-news-mysql:${IMAGE_VERSION}" -echo " - school-news-serv:${IMAGE_VERSION}" -echo " - school-news-web:${IMAGE_VERSION}" -echo "" -log_info "镜像文件保存在: ${BUILD_OUTPUT}/" -ls -lh "${BUILD_OUTPUT}/" | grep -E "(tar|gz|txt)" | awk '{print " - "$9" ("$5")"}' -echo "" -log_info "下一步操作:" -echo " 1. 查看部署说明: cat ${DEPLOY_INFO_FILE}" -echo " 2. 传输镜像文件到目标服务器" -echo " 3. 在目标服务器加载镜像: docker load -i <镜像文件>" -echo " 4. 启动服务: cd docker && docker-compose up -d" -echo "==========================================" \ No newline at end of file +log_info "输出目录: ${BUILD_OUTPUT}" +echo "==========================================" diff --git a/docker/Dockerfile.base-serv b/docker/Dockerfile.base-serv new file mode 100644 index 0000000..d864ab4 --- /dev/null +++ b/docker/Dockerfile.base-serv @@ -0,0 +1,59 @@ +# ==================================== +# 后端基础镜像 - Base Serv +# 包含:JRE + Python + 系统工具 + 爬虫依赖 +# 用途:作为后端服务镜像的基础,避免每次都安装依赖 +# ==================================== +FROM eclipse-temurin:21-jre + +# 设置环境变量 +ENV LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 \ + TZ=Asia/Shanghai + +# 安装系统依赖和工具 +RUN apt-get update && \ + apt-get install -y \ + # Python环境 + python3 \ + python3-pip \ + python3-venv \ + # 网络和诊断工具 + netcat-traditional \ + curl \ + wget \ + # MySQL客户端 + default-mysql-client \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# 临时复制requirements.txt用于安装依赖 +COPY schoolNewsCrawler/requirements.txt /tmp/requirements.txt + +# 安装Python爬虫依赖(一次性安装到基础镜像) +RUN echo "========================================" && \ + echo "安装Python爬虫依赖到基础镜像" && \ + echo "========================================" && \ + python3 -m pip --version && \ + echo "" && \ + # Python 3.12 引入了 PEP 668 规范,需要添加 --break-system-packages + python3 -m pip install --no-cache-dir --break-system-packages -r /tmp/requirements.txt && \ + echo "" && \ + echo "✅ 爬虫依赖安装完成" && \ + python3 -m pip list | grep -E "(beautifulsoup4|crawl4ai|selenium|pydantic|requests|loguru)" && \ + # 清理临时文件 + rm -f /tmp/requirements.txt + +# 创建应用目录结构 +WORKDIR /app +RUN mkdir -p /app/config /app/logs /app/uploads /app/crawler + +# 镜像元数据 +LABEL maintainer="School News Team" \ + description="Base image for school-news backend service with Python dependencies" \ + version="1.0" + +# 暴露端口(文档用途) +EXPOSE 8081 + +# 默认命令(会被子镜像覆盖) +CMD ["echo", "This is base image, please use school-news-serv image"] diff --git a/docker/Dockerfile.base-web b/docker/Dockerfile.base-web new file mode 100644 index 0000000..6f57b66 --- /dev/null +++ b/docker/Dockerfile.base-web @@ -0,0 +1,41 @@ +# ==================================== +# 前端基础镜像 - Base Web +# 包含:Nginx + 基础配置 +# 用途:作为前端服务镜像的基础 +# ==================================== +FROM nginx:alpine + +# 设置环境变量 +ENV TZ=Asia/Shanghai + +# 安装必要工具 +RUN apk add --no-cache \ + # 时区数据 + tzdata \ + # 基础工具 + bash \ + curl \ + # 日志处理 + && mkdir -p /app/logs + +# 设置时区 +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +# 创建应用目录 +WORKDIR /app +RUN mkdir -p /app/config /app/logs + +# Nginx基础配置(可被覆盖) +RUN rm -f /etc/nginx/conf.d/default.conf + +# 镜像元数据 +LABEL maintainer="School News Team" \ + description="Base image for school-news frontend service with Nginx" \ + version="1.0" + +# 暴露端口 +EXPOSE 80 + +# 默认命令(会被子镜像覆盖) +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/Dockerfile.serv b/docker/Dockerfile.serv index 5f9cdf6..486f95e 100644 --- a/docker/Dockerfile.serv +++ b/docker/Dockerfile.serv @@ -1,40 +1,16 @@ -# 后端服务运行镜像 +# ==================================== +# 后端服务镜像 - School News Serv +# 基于:school-news-base-serv(已包含Python依赖) # 注意:jar包需要在主机中先编译好 -FROM eclipse-temurin:21-jre - -# 设置环境变量 -ENV LANG=C.UTF-8 \ - LC_ALL=C.UTF-8 \ - TZ=Asia/Shanghai - -# 安装Python3和pip(用于爬虫)以及MySQL客户端(用于配置更新) -RUN apt-get update && \ - apt-get install -y python3 python3-pip python3-venv netcat-traditional curl default-mysql-client && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -# 创建应用目录 -WORKDIR /app - -# 创建必要的目录 -RUN mkdir -p /app/config /app/logs /app/uploads /app/crawler +# ==================================== +FROM school-news-base-serv:latest # 从主机复制已编译的jar包 COPY schoolNewsServ/admin/target/admin-1.0.0.jar /app/app.jar -# 复制爬虫脚本 +# 复制爬虫脚本(基础镜像已安装依赖,这里只需复制脚本) COPY schoolNewsCrawler/ /app/crawler/ -# 安装爬虫依赖(根据requirements.txt) -RUN cd /app/crawler && \ - if [ -f requirements.txt ]; then \ - echo "安装爬虫依赖..."; \ - # Python 3.12 引入了 PEP 668 规范,默认禁止在系统环境直接使用 pip 安装包,防止破坏系统 Python 环境。 - python3 -m pip install --no-cache-dir --break-system-packages -r requirements.txt; \ - else \ - echo "警告: 未找到requirements.txt文件"; \ - fi - # 复制默认配置文件(作为备份) COPY schoolNewsServ/admin/src/main/resources/application.yml /app/config/application.yml.template COPY schoolNewsServ/admin/src/main/resources/log4j2-spring.xml /app/config/log4j2-spring.xml.template diff --git a/docker/Dockerfile.web b/docker/Dockerfile.web index 83ffee4..6d0b07e 100644 --- a/docker/Dockerfile.web +++ b/docker/Dockerfile.web @@ -1,15 +1,12 @@ -# 前端服务运行镜像 +# ==================================== +# 前端服务镜像 - School News Web +# 基于:school-news-base-web(Nginx Alpine) # 注意:dist目录需要在主机中先构建好 -FROM nginx:alpine +# ==================================== +FROM school-news-base-web:latest -# 设置环境变量 -ENV TZ=Asia/Shanghai - -# 安装必要的工具 -RUN apk add --no-cache tzdata - -# 创建配置目录 -RUN mkdir -p /app/config /app/logs /usr/share/nginx/html/schoolNewsWeb +# 创建前端目录 +RUN mkdir -p /usr/share/nginx/html/schoolNewsWeb # 从主机复制已构建的dist目录 COPY schoolNewsWeb/dist/ /usr/share/nginx/html/schoolNewsWeb/