Files
kim.dev.6789 3fb403dce8
All checks were successful
itom-platform auto build image / build (push) Successful in 2m57s
改为推送到docker hub
2026-01-15 22:06:50 +08:00

610 lines
22 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: OpenIM Chat 构建和发布
on:
push:
branches: [ main,wallet, master, develop, release-* ]
paths:
- '**'
pull_request:
branches: [ main,wallet, master ]
paths:
- '**'
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Tag version to be used for Docker image"
required: true
default: "prod"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: docker.io
# Docker Hub 个人命名空间(需与 DOCKER_USERNAME 一致)
DOCKER_USER: kim6789
# Docker Hub 凭证来自仓库 Secrets
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
GO_VERSION: "1.24"
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
submodules: recursive
- name: 设置 SSH 密钥
run: |
echo "🔑 配置 SSH 密钥..."
# 创建 .ssh 目录
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 解码 base64 编码的 SSH 私钥
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_rsa
# 验证解码是否成功
if [ ! -s ~/.ssh/id_rsa ]; then
echo "❌ SSH 私钥解码失败或为空"
exit 1
fi
# 设置权限
chmod 600 ~/.ssh/id_rsa
# 配置 SSH 主机密钥验证(可选,根据需要调整)
ssh-keyscan git.imall.cloud >> ~/.ssh/known_hosts 2>/dev/null || true
chmod 644 ~/.ssh/known_hosts
echo "✅ SSH 密钥配置完成"
- name: 检出 protocol 仓库
run: |
# 克隆 protocol 仓库到当前目录的 protocol 子目录(使用 SSH
git clone git@git.imall.cloud:openim/protocol.git ./protocol || true
cd ./protocol
git fetch --all
git checkout v1.0.4
- name: 设置 Go 缓存
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3.8.0
with:
platforms: linux/amd64
- name: 登录到 Docker Hub
uses: docker/login-action@v3.1.0
with:
# 使用账号登录,镜像仍推送到 DOCKER_USER 命名空间
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: 构建和推送 Docker 镜像
run: |
set -e
# 设置版本标签 - 默认使用 prod
if [ "${{ github.event_name }}" = "release" ]; then
VERSION_TAG="${{ github.event.release.tag_name }}"
elif [ -n "${{ github.event.inputs.tag }}" ]; then
VERSION_TAG="${{ github.event.inputs.tag }}"
elif [ "${{ github.ref }}" = "refs/heads/main" ] || [ "${{ github.ref }}" = "refs/heads/master" ]; then
VERSION_TAG="prod"
else
VERSION_TAG="prod" # 所有分支默认都使用 prod
fi
echo "版本标签: $VERSION_TAG"
echo "Docker用户: $DOCKER_USER"
# 获取CPU核心数
CPU_CORES=$(nproc)
echo "CPU核心数: $CPU_CORES"
# 定义服务列表
API_SERVICES=(
"openim-chat-api"
)
RPC_SERVICES=(
"openim-chat-rpc"
)
ADMIN_SERVICES=(
"openim-admin-api"
"openim-admin-rpc"
)
# 合并所有服务
ALL_SERVICES=("${API_SERVICES[@]}" "${RPC_SERVICES[@]}" "${ADMIN_SERVICES[@]}")
# 资源监控函数
monitor_resources() {
local cpu_usage=0
local mem_usage=0
local load_1min=0
# 获取CPU使用率
if command -v top >/dev/null 2>&1; then
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//' | cut -d. -f1)
elif [ -f /proc/stat ]; then
local prev_idle=$(cat /tmp/cpu_idle 2>/dev/null || echo "0")
local prev_total=$(cat /tmp/cpu_total 2>/dev/null || echo "0")
local curr_cpu=($(cat /proc/stat | head -1))
local curr_idle=${curr_cpu[4]}
local curr_total=0
for val in "${curr_cpu[@]:1}"; do
curr_total=$((curr_total + val))
done
local diff_idle=$((curr_idle - prev_idle))
local diff_total=$((curr_total - prev_total))
local diff_usage=$((diff_total - diff_idle))
if [ $diff_total -gt 0 ]; then
cpu_usage=$((diff_usage * 100 / diff_total))
fi
echo "$curr_idle" > /tmp/cpu_idle
echo "$curr_total" > /tmp/cpu_total
fi
# 限制CPU使用率在0-100%之间
if [ "$cpu_usage" -gt 100 ]; then
cpu_usage=100
elif [ "$cpu_usage" -lt 0 ]; then
cpu_usage=0
fi
# 获取内存使用率
if command -v free >/dev/null 2>&1; then
mem_usage=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
fi
# 获取1分钟负载
if [ -f /proc/loadavg ]; then
load_1min=$(cat /proc/loadavg | awk '{print $1}' | cut -d. -f1)
fi
echo "CPU: ${cpu_usage}%, 内存: ${mem_usage}%, 负载: ${load_1min}"
}
# 检查资源状态
check_resources() {
local cpu_usage=$(monitor_resources | awk '{print $1}' | sed 's/CPU://' | sed 's/%//')
local mem_usage=$(monitor_resources | awk '{print $2}' | sed 's/内存://' | sed 's/%//')
local load_1min=$(monitor_resources | awk '{print $3}' | sed 's/负载://')
# 如果CPU使用率超过90%或内存使用率超过85%,等待资源释放
if [ "$cpu_usage" -gt 90 ] || [ "$mem_usage" -gt 85 ]; then
echo "资源使用率过高,等待释放..."
local wait_count=0
local max_wait=20
while [ $wait_count -lt $max_wait ]; do
sleep 5
wait_count=$((wait_count + 1))
# 清理Docker缓存
if [ $((wait_count % 5)) -eq 0 ]; then
docker system prune -f >/dev/null 2>&1 || true
docker builder prune -f >/dev/null 2>&1 || true
fi
cpu_usage=$(monitor_resources | awk '{print $1}' | sed 's/CPU://' | sed 's/%//')
mem_usage=$(monitor_resources | awk '{print $2}' | sed 's/内存://' | sed 's/%//')
if [ "$cpu_usage" -le 90 ] && [ "$mem_usage" -le 85 ]; then
echo "资源已释放,继续构建"
break
fi
echo "等待资源释放... ($wait_count/$max_wait)"
done
if [ $wait_count -eq $max_wait ]; then
echo "警告:等待超时,继续执行构建"
fi
fi
}
# 构建服务函数
build_service() {
local service=$1
local dockerfile="build/images/$service/Dockerfile"
echo "检查服务: $service"
echo " Dockerfile 路径: $dockerfile"
echo " Dockerfile 是否存在: $([ -f "$dockerfile" ] && echo "是" || echo "否")"
# 检查资源状态
check_resources
if [ -f "$dockerfile" ]; then
echo "📦 开始构建 Docker 镜像: $service"
echo " 📁 Dockerfile: $dockerfile"
echo " 🏷️ 版本标签: $VERSION_TAG"
echo " 👤 Docker用户: $DOCKER_USER"
# 降低日志噪音:不打印 buildx 状态
# 构建和推送 Docker 镜像
echo " 尝试使用 buildx 构建..."
# 降低日志噪音:省略上下文验证输出
local start_time=$(date +%s)
# 尝试使用 buildx 构建
# 使用 git commit SHA 作为构建参数,确保代码变化时缓存失效
BUILD_SHA=$(git rev-parse --short HEAD)
BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
if docker buildx build \
--platform linux/amd64 \
--file "$dockerfile" \
--tag "$DOCKER_USER/$service:$VERSION_TAG" \
--push \
--cache-from type=gha,scope=openim-chat-build-${{ github.ref_name }}-$service \
--cache-to type=gha,mode=max,scope=openim-chat-build-${{ github.ref_name }}-$service \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg GOCACHE=/go-cache \
--build-arg GOMAXPROCS=$CPU_CORES \
--build-arg BUILD_SHA=$BUILD_SHA \
--build-arg BUILD_TIME=$BUILD_TIME \
--provenance=false \
--sbom=false \
. > "/tmp/build_${service}.log" 2>&1; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "✅ 构建成功: $service"
echo " ⏱️ 耗时: ${duration}秒"
echo " 🏷️ 镜像: $DOCKER_USER/$service:$VERSION_TAG"
echo " 📤 推送状态: 已完成"
else
echo " buildx 构建失败,查看详细错误日志..."
echo " Buildx 构建日志:"
if [ -f "/tmp/build_${service}.log" ]; then
cat "/tmp/build_${service}.log"
fi
echo " 尝试使用传统 docker build..."
# 回退到传统 docker build
local build_cmd="docker build --file $dockerfile --tag $DOCKER_USER/$service:$VERSION_TAG --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg GOCACHE=/go-cache --build-arg GOMAXPROCS=$CPU_CORES ."
echo " 传统构建命令: $build_cmd"
if eval "$build_cmd" > "/tmp/traditional_build_${service}.log" 2>&1; then
echo " 传统构建成功,推送镜像..."
docker push "$DOCKER_USER/$service:$VERSION_TAG" || echo "推送 $VERSION_TAG 失败"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "✅ 构建成功: $service (传统构建)"
echo " ⏱️ 耗时: ${duration}秒"
echo " 🏷️ 镜像: $DOCKER_USER/$service:$VERSION_TAG"
echo " 📤 推送状态: 已完成"
else
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ""
echo "❌ 构建失败: $service"
echo " ⏱️ 耗时: ${duration}秒"
echo " 🔍 查看详细错误日志:"
echo "=== 详细错误日志 ==="
echo "服务: $service"
echo "Dockerfile: $dockerfile"
echo "构建命令:"
echo "docker buildx build --platform linux/amd64 --file $dockerfile --tag $DOCKER_USER/$service:$VERSION_TAG --push --quiet --cache-from type=gha,scope=openim-chat-build-${{ github.ref_name }}-$service --cache-to type=gha,mode=max,scope=openim-chat-build-${{ github.ref_name }}-$service --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg GOCACHE=/go-cache --build-arg GOMAXPROCS=$CPU_CORES --provenance=false --sbom=false ."
echo ""
echo "Buildx 构建日志:"
if [ -f "/tmp/build_${service}.log" ]; then
cat "/tmp/build_${service}.log"
fi
echo ""
echo "传统构建日志:"
if [ -f "/tmp/traditional_build_${service}.log" ]; then
cat "/tmp/traditional_build_${service}.log"
fi
echo "=== 错误日志结束 ==="
return 1
fi
fi
else
echo "❌ 错误: 找不到 Dockerfile: $dockerfile"
echo " 当前目录: $(pwd)"
echo " 目录内容: $(ls -la build/images/ 2>/dev/null || echo 'build/images 目录不存在')"
echo " 查找所有 Dockerfile: $(find . -name "Dockerfile" -type f 2>/dev/null | head -5)"
return 1
fi
}
# 初始化构建统计
failed_services=()
successful_services=()
# 计算总服务数
total_services=$((${#API_SERVICES[@]} + ${#RPC_SERVICES[@]} + ${#ADMIN_SERVICES[@]}))
echo ""
echo "🎯 开始构建 OpenIM Chat 镜像"
echo "📊 构建统计:"
echo " 📦 API服务: ${#API_SERVICES[@]} 个"
echo " 🔧 RPC服务: ${#RPC_SERVICES[@]} 个"
echo " 👑 管理服务: ${#ADMIN_SERVICES[@]} 个"
echo " 📈 总计: $total_services 个服务"
echo " 🏷️ 版本标签: $VERSION_TAG"
echo " 👤 Docker用户: $DOCKER_USER"
echo ""
# 先构建API服务
echo "API服务构建:"
echo "总服务数: ${#API_SERVICES[@]}"
echo "=========================================="
for i in "${!API_SERVICES[@]}"; do
service="${API_SERVICES[$i]}"
current=$((i + 1))
total=${#API_SERVICES[@]}
echo ""
echo "🚀 [${current}/${total}] 开始构建: $service"
echo "⏰ 开始时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
if build_service "$service"; then
successful_services+=("$service")
echo "✅ [${current}/${total}] 构建完成: $service"
else
failed_services+=("$service")
echo "❌ [${current}/${total}] 构建失败: $service"
fi
echo "⏰ 完成时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
done
# 构建RPC服务
echo "RPC服务构建:"
echo "总服务数: ${#RPC_SERVICES[@]}"
echo "=========================================="
for i in "${!RPC_SERVICES[@]}"; do
service="${RPC_SERVICES[$i]}"
current=$((i + 1))
total=${#RPC_SERVICES[@]}
echo ""
echo "🚀 [${current}/${total}] 开始构建: $service"
echo "⏰ 开始时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
if build_service "$service"; then
successful_services+=("$service")
echo "✅ [${current}/${total}] 构建完成: $service"
else
failed_services+=("$service")
echo "❌ [${current}/${total}] 构建失败: $service"
fi
echo "⏰ 完成时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
done
# 构建管理服务
echo "管理服务构建:"
echo "总服务数: ${#ADMIN_SERVICES[@]}"
echo "=========================================="
for i in "${!ADMIN_SERVICES[@]}"; do
service="${ADMIN_SERVICES[$i]}"
current=$((i + 1))
total=${#ADMIN_SERVICES[@]}
echo ""
echo "🚀 [${current}/${total}] 开始构建: $service"
echo "⏰ 开始时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
if build_service "$service"; then
successful_services+=("$service")
echo "✅ [${current}/${total}] 构建完成: $service"
else
failed_services+=("$service")
echo "❌ [${current}/${total}] 构建失败: $service"
fi
echo "⏰ 完成时间: $(date '+%H:%M:%S')"
echo "------------------------------------------"
done
# 输出构建结果统计
echo ""
echo "🎉 构建完成总结"
echo "=========================================="
echo "✅ 成功构建: ${#successful_services[@]} 个服务"
echo "❌ 构建失败: ${#failed_services[@]} 个服务"
echo "📊 成功率: $(( ${#successful_services[@]} * 100 / total_services ))%"
echo ""
if [ ${#successful_services[@]} -gt 0 ]; then
echo "✅ 成功构建的服务:"
for service in "${successful_services[@]}"; do
echo " - $service"
done
echo ""
fi
if [ ${#failed_services[@]} -gt 0 ]; then
echo "❌ 构建失败的服务:"
for service in "${failed_services[@]}"; do
echo " - $service"
done
echo ""
fi
if [ ${#failed_services[@]} -gt 0 ]; then
echo ""
echo "失败服务错误日志:"
for service in "${failed_services[@]}"; do
echo "=== $service 错误日志 ==="
if [ -f "/tmp/build_${service}.log" ]; then
echo "Buildx 构建日志:"
cat "/tmp/build_${service}.log"
fi
if [ -f "/tmp/traditional_build_${service}.log" ]; then
echo "传统构建日志:"
cat "/tmp/traditional_build_${service}.log"
fi
echo "=== $service 错误日志结束 ==="
echo ""
done
fi
# 如果有失败的服务退出码为1
if [ ${#failed_services[@]} -gt 0 ]; then
echo "❌ 构建失败,有 ${#failed_services[@]} 个服务构建失败"
exit 1
else
echo "🎉 所有服务构建成功!"
fi
- name: 配置 kubectl
if: success()
run: |
echo "🔧 配置 kubectl 连接到阿里云 ACK..."
# 创建 kubeconfig 目录
mkdir -p ~/.kube
# 直接解码 base64 内容
echo "解码 KUBECONFIG..."
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
# 验证解码是否成功
if [ ! -s ~/.kube/config ]; then
echo "❌ KUBECONFIG 解码失败或为空"
exit 1
fi
# 设置权限
chmod 600 ~/.kube/config
echo "✅ kubeconfig 配置完成"
# 降低日志噪音:不输出 kubeconfig 内容
- name: 部署到阿里云 ACK
if: success()
run: |
echo "🚀 开始部署到阿里云 ACK..."
# 简单验证 kubectl
if ! kubectl version --client >/dev/null 2>&1; then
echo "❌ kubectl 不可用,跳过部署步骤"
exit 0
fi
echo "✅ kubectl 可用,尝试部署..."
# 获取版本标签
if [ "${{ github.event_name }}" = "release" ]; then
VERSION_TAG="${{ github.event.release.tag_name }}"
else
VERSION_TAG="${{ github.event.inputs.tag || github.ref_name || 'prod' }}"
fi
DOCKER_USER="${{ env.DOCKER_USER }}"
NS=${NS:-default}
echo "部署参数:"
echo "版本标签: $VERSION_TAG"
echo "Docker用户: $DOCKER_USER"
# 检查部署文件是否存在
DEPLOY_DIR=".gitea/deployments/deploy"
if [ ! -d "$DEPLOY_DIR" ]; then
echo "❌ 部署目录不存在: $DEPLOY_DIR"
echo "📁 当前目录内容:"
ls -la
exit 1
fi
echo "📁 部署目录: $DEPLOY_DIR"
echo "使用命名空间: $NS"
# 创建命名空间(如果不存在)
kubectl get ns "$NS" >/dev/null 2>&1 || kubectl create ns "$NS"
kubectl config set-context --current --namespace="$NS"
# 尝试部署,如果失败则继续
echo "🚀 开始部署 Chat 服务..."
# 定义所有部署文件
DEPLOY_FILES=(
"openim-chat-api-deployment.yml"
"openim-chat-api-service.yml"
"openim-chat-rpc-deployment.yml"
"openim-chat-rpc-service.yml"
"openim-admin-api-deployment.yml"
"openim-admin-api-service.yml"
"openim-admin-rpc-deployment.yml"
"openim-admin-rpc-service.yml"
)
# 部署所有文件
for file in "${DEPLOY_FILES[@]}"; do
if [ -f "$DEPLOY_DIR/$file" ]; then
echo "📄 部署文件: $file"
# 更新镜像标签
sed -i "s|image: .*/openim-|image: $DOCKER_USER/openim-|g" "$DEPLOY_DIR/$file"
sed -i "s|:latest|:$VERSION_TAG|g" "$DEPLOY_DIR/$file"
# 应用文件
kubectl apply -f "$DEPLOY_DIR/$file" || echo "⚠️ $file 部署失败,但继续"
else
echo "⚠️ $file 不存在,跳过"
fi
done
echo ""
echo "🔍 验证部署状态..."
# 简单检查,如果失败也不阻塞流程
kubectl get pods -l app=openim-chat-api || echo "⚠️ 无法获取 chat-api Pod 状态"
kubectl get pods -l app=openim-chat-rpc || echo "⚠️ 无法获取 chat-rpc Pod 状态"
echo "✅ 验证完成"
- name: 清理
if: always()
run: |
echo "🧹 清理临时文件..."
rm -f /tmp/build_*.log
echo "✅ 清理完成"