Files
open-im-server-deploy/.gitea/workflows/build.yml
kim.dev.6789 e50142a3b9 复制项目
2026-01-14 22:16:44 +08:00

535 lines
19 KiB
YAML
Raw 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 Server 构建和发布
on:
push:
branches: [ main, master, wallet, develop, release-* ]
paths:
- '**'
pull_request:
branches: [ main, master, wallet ]
paths:
- '**'
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Tag version to be used for Docker image"
required: true
default: "latest"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: docker.io
DOCKER_USER: ${{ secrets.DOCKER_USERNAME || 'mag1666888' }}
GO_VERSION: "1.24"
jobs:
build-and-push:
runs-on: openim
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
# 从 secrets 中获取 SSH 私钥并 base64 解码
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# 配置 SSH 主机密钥验证(可选,避免首次连接时的确认提示)
ssh-keyscan -H git.imall.cloud >> ~/.ssh/known_hosts 2>/dev/null || true
chmod 644 ~/.ssh/known_hosts
# 验证 SSH 密钥
if [ -f ~/.ssh/id_rsa ]; then
echo "✅ SSH 密钥配置成功"
else
echo "❌ SSH 密钥配置失败"
exit 1
fi
- name: 检出 protocol 仓库
run: |
echo "📦 克隆 protocol 仓库..."
# 使用 SSH 方式克隆 protocol 仓库
git clone git@git.imall.cloud:openim/protocol.git ./protocol || true
cd ./protocol
git fetch --all
git checkout v1.0.4
cd ..
# 创建父目录的protocol符号链接使replace ../protocol 能正确找到
ln -sf $PWD/protocol ../protocol || true
echo "✅ protocol 仓库检出完成"
- 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: 检查 Go 环境
run: |
if command -v go &> /dev/null; then
echo "Go: $(go version)"
go mod verify >/dev/null 2>&1
go mod tidy >/dev/null 2>&1
go mod download >/dev/null 2>&1
echo "Go modules verified"
else
echo "Go not found, skipping Go steps"
fi
- name: 检查系统资源
run: |
echo "系统资源信息:"
echo "CPU核心数: $(nproc)"
echo "内存总量: $(free -h | awk '/^Mem:/ {print $2}')"
echo "可用内存: $(free -h | awk '/^Mem:/ {print $7}')"
echo "磁盘空间: $(df -h / | awk 'NR==2 {print $4}')"
echo ""
- name: 清理 Docker 环境
run: |
echo "清理Docker环境..."
docker image prune -f >/dev/null 2>&1 || true
- name: 登录到 Docker Hub
uses: docker/login-action@v3.3.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3.8.0
with:
driver-opts: |
image=moby/buildkit:buildx-stable-1
network=host
- name: 构建和推送所有服务镜像
working-directory: ${{ github.workspace }}
run: |
# 设置错误处理
trap 'echo "脚本在行 $LINENO 处失败,退出状态: $?"' ERR
set -e
# 获取版本标签(使用分支名)
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 }}"
else
# 使用分支名作为标签
VERSION_TAG="${{ github.ref_name }}"
fi
echo "构建OpenIM Server镜像 (标签: $VERSION_TAG)"
echo "Docker用户: $DOCKER_USER"
# 定义构建顺序openim-msggateway 和 openim-push 优先构建)
CORE_SERVICES=(
"openim-msggateway"
"openim-push"
"openim-rpc-msg"
"openim-api"
"openim-msgtransfer"
"openim-crontask"
)
RPC_SERVICES=(
"openim-rpc-auth"
"openim-rpc-user"
"openim-rpc-friend"
"openim-rpc-group"
"openim-rpc-conversation"
"openim-rpc-third"
)
# 构建函数
build_service() {
local service=$1
local dockerfile="build/images/$service/Dockerfile"
local start_time=$(date +%s)
if [ -f "$dockerfile" ]; then
echo "📦 构建 $service..."
if docker buildx build \
--platform linux/amd64 \
--file "$dockerfile" \
--tag "$DOCKER_USER/$service:$VERSION_TAG" \
--tag "$DOCKER_USER/$service:prod" \
--push \
--progress=plain \
--cache-from type=gha,scope=openim-build-${{ github.ref_name }}-$service \
--cache-to type=gha,mode=max,scope=openim-build-${{ github.ref_name }}-$service \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg GOCACHE=/go-cache \
--build-arg GOMAXPROCS=$(nproc) \
--provenance=false \
--sbom=false \
. > "/tmp/build_${service}.log" 2>&1; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "✅ $service 构建成功 (${duration}秒)"
else
echo "❌ $service 构建失败,查看详细错误日志:"
echo "=========================================="
cat "/tmp/build_${service}.log"
echo "=========================================="
echo "最后 50 行错误日志:"
tail -50 "/tmp/build_${service}.log"
return 1
fi
else
echo "❌ $service - 未找到 Dockerfile: $dockerfile"
return 1
fi
}
# 初始化构建统计
failed_services=()
successful_services=()
# 计算总服务数
total_services=$((${#CORE_SERVICES[@]} + ${#RPC_SERVICES[@]}))
echo "开始构建 $total_services 个服务..."
# 先构建核心服务
for service in "${CORE_SERVICES[@]}"; do
if build_service "$service"; then
successful_services+=("$service")
else
failed_services+=("$service")
fi
done
# 构建RPC服务
for service in "${RPC_SERVICES[@]}"; do
if build_service "$service"; then
successful_services+=("$service")
else
failed_services+=("$service")
fi
done
# 输出构建结果统计
echo ""
echo "🎉 构建完成总结"
echo "✅ 成功构建: ${#successful_services[@]} 个服务"
echo "❌ 构建失败: ${#failed_services[@]} 个服务"
echo "📊 成功率: $(( ${#successful_services[@]} * 100 / total_services ))%"
if [ ${#failed_services[@]} -gt 0 ]; then
echo ""
echo "失败服务错误日志:"
for service in "${failed_services[@]}"; do
echo "=== $service ==="
cat "/tmp/build_${service}.log" 2>/dev/null || echo "无法读取日志文件"
echo ""
done
exit 1
fi
- name: 配置 kubectl
if: success()
run: |
echo "🔧 配置 kubectl 连接到阿里云 ACK..."
# 创建 kubeconfig 目录
mkdir -p ~/.kube
# 从 secrets 中获取 kubeconfig 内容并处理
echo "📝 处理 kubeconfig 文件..."
# 直接解码 base64 内容
echo "🔍 解码 base64 编码的 kubeconfig..."
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
# 验证解码是否成功
if [ ! -s ~/.kube/config ]; then
echo "❌ kubeconfig 解码失败或文件为空"
exit 1
fi
# 显示 kubeconfig 内容供手动检查
echo "🔍 解码后的 kubeconfig 内容:"
cat ~/.kube/config
chmod 600 ~/.kube/config
echo "✅ kubeconfig 配置完成"
# 详细验证 kubectl 配置
echo "🔍 验证 kubectl 配置..."
echo "当前上下文:"
kubectl config current-context 2>&1 || echo "无法获取当前上下文"
echo "可用上下文:"
kubectl config get-contexts 2>&1 || echo "无法获取上下文列表"
echo "集群信息:"
kubectl cluster-info 2>&1 || echo "无法获取集群信息"
echo "API 版本:"
kubectl version --short 2>&1 || echo "无法获取版本信息"
- 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 }}"
elif [ -n "${{ github.event.inputs.tag }}" ]; then
VERSION_TAG="${{ github.event.inputs.tag }}"
else
# 使用分支名作为标签
VERSION_TAG="${{ github.ref_name }}"
fi
echo "版本标签: $VERSION_TAG"
echo "Docker用户: $DOCKER_USER"
# 检查部署文件是否存在
DEPLOY_DIR="deployments/deploy"
if [ ! -d "$DEPLOY_DIR" ]; then
echo "❌ 部署目录不存在: $DEPLOY_DIR"
echo "📁 当前目录内容:"
ls -la
echo "⚠️ 部署失败,但构建流程继续"
exit 0
fi
echo "📁 进入部署目录: $DEPLOY_DIR"
cd "$DEPLOY_DIR"
# 设置命名空间
NS=${NS:-default}
echo "使用命名空间: $NS"
# 创建命名空间(如果不存在)
kubectl get ns "$NS" >/dev/null 2>&1 || kubectl create ns "$NS"
kubectl config set-context --current --namespace="$NS"
# 尝试部署,如果失败则继续
echo "🚀 开始部署OpenIM Server服务..."
# 定义所有部署文件
DEPLOYMENT_FILES=(
"openim-api-deployment.yml"
"openim-crontask-deployment.yml"
"openim-rpc-user-deployment.yml"
"openim-msggateway-deployment.yml"
"openim-push-deployment.yml"
"openim-msgtransfer-deployment.yml"
"openim-rpc-conversation-deployment.yml"
"openim-rpc-auth-deployment.yml"
"openim-rpc-group-deployment.yml"
"openim-rpc-friend-deployment.yml"
"openim-rpc-msg-deployment.yml"
"openim-rpc-third-deployment.yml"
)
# 更新镜像标签并部署
DEPLOYMENT_ERRORS=()
for file in "${DEPLOYMENT_FILES[@]}"; do
if [ -f "$file" ]; then
echo "📦 处理 $file..."
# 备份原文件(保留 imagePullSecrets 等配置)
cp "$file" "$file.bak"
# 更新镜像标签(替换用户名和标签,但保留 imagePullSecrets
sed -i "s|image: .*/openim-|image: $DOCKER_USER/openim-|g" "$file"
sed -i "s|:prod|:prod|g" "$file"
# 验证 imagePullSecrets 是否保留
if ! grep -q "imagePullSecrets:" "$file"; then
echo "⚠️ 警告: $file 中缺少 imagePullSecrets从备份恢复..."
cp "$file.bak" "$file"
# 只更新镜像,不修改其他内容
sed -i "s|image: .*/openim-|image: $DOCKER_USER/openim-|g" "$file"
sed -i "s|:prod|:prod|g" "$file"
fi
# 应用文件并捕获详细错误
echo "🔍 执行: kubectl apply -f $file"
if kubectl apply -f "$file" 2>&1; then
echo "✅ $file 部署成功"
else
ERROR_OUTPUT=$(kubectl apply -f "$file" 2>&1)
echo "❌ $file 部署失败:"
echo "$ERROR_OUTPUT"
DEPLOYMENT_ERRORS+=("$file: $ERROR_OUTPUT")
echo "⚠️ 继续处理下一个文件..."
fi
# 清理备份文件
rm -f "$file.bak"
else
echo "⚠️ $file 不存在,跳过"
fi
done
# 显示部署错误总结
if [ ${#DEPLOYMENT_ERRORS[@]} -gt 0 ]; then
echo ""
echo "🚨 部署错误总结:"
echo "=========================================="
for error in "${DEPLOYMENT_ERRORS[@]}"; do
echo "❌ $error"
done
echo "=========================================="
fi
echo "✅ 部署步骤完成"
# 强制重启所有部署以使用新镜像
echo "🔄 强制重启所有部署以使用新镜像..."
DEPLOYMENTS=(
"openim-api"
"openim-crontask"
"messagegateway-rpc-server"
"openim-msgtransfer-server"
"push-rpc-server"
"auth-rpc-server"
"user-rpc-server"
"friend-rpc-server"
"group-rpc-server"
"conversation-rpc-server"
"third-rpc-server"
"msg-rpc-server"
)
for deployment in "${DEPLOYMENTS[@]}"; do
echo "🔄 重启部署: $deployment"
# 先删除 pod 以强制拉取新镜像(即使 imagePullPolicy 是 Always有时也需要删除 pod
if kubectl delete pods -l app="$deployment" --grace-period=0 --force 2>&1; then
echo "✅ $deployment Pod 已删除,将重新创建并拉取新镜像"
fi
# 然后重启 deployment
if kubectl rollout restart deployment "$deployment" 2>&1; then
echo "✅ $deployment 重启成功"
else
echo "⚠️ $deployment 重启失败,可能不存在"
fi
done
echo "⏳ 等待部署完成..."
for deployment in "${DEPLOYMENTS[@]}"; do
echo "⏳ 等待 $deployment 就绪..."
if kubectl rollout status deployment "$deployment" --timeout=300s; then
echo "✅ $deployment 部署完成"
else
echo "⚠️ $deployment 超时检查Pod状态..."
echo "Pod状态:"
kubectl get pods -l app="$deployment" 2>&1 || echo "无法获取Pod状态"
echo "Pod事件:"
kubectl get events --field-selector involvedObject.name="$deployment" --sort-by='.lastTimestamp' 2>&1 | tail -10 || echo "无法获取事件"
echo "部署详情:"
kubectl describe deployment "$deployment" 2>&1 | tail -20 || echo "无法获取部署详情"
fi
done
- name: 验证部署状态
if: success()
run: |
echo "🔍 验证部署状态..."
# 详细检查集群连接和权限
echo "🔍 检查集群连接..."
if kubectl cluster-info 2>&1; then
echo "✅ 集群连接正常"
else
CLUSTER_ERROR=$(kubectl cluster-info 2>&1)
echo "❌ 集群连接失败: $CLUSTER_ERROR"
fi
echo "🔍 检查命名空间权限..."
if kubectl get namespaces 2>&1; then
echo "✅ 命名空间权限正常"
else
NS_ERROR=$(kubectl get namespaces 2>&1)
echo "❌ 命名空间权限不足: $NS_ERROR"
fi
echo "🔍 检查 Pod 状态..."
if kubectl get pods -l app=openim 2>&1; then
echo "✅ Pod 状态检查完成"
else
POD_ERROR=$(kubectl get pods -l app=openim 2>&1)
echo "❌ Pod 状态检查失败: $POD_ERROR"
fi
echo "🔍 检查部署状态..."
if kubectl get deployments -l app=openim 2>&1; then
echo "✅ 部署状态检查完成"
else
DEPLOY_ERROR=$(kubectl get deployments -l app=openim 2>&1)
echo "❌ 部署状态检查失败: $DEPLOY_ERROR"
fi
echo "✅ 验证完成"
- name: 生成构建报告
run: |
# 获取版本标签(使用分支名)
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 }}"
else
# 使用分支名作为标签
VERSION_TAG="${{ github.ref_name }}"
fi
echo "## OpenIM Server Build Complete" >> $GITHUB_STEP_SUMMARY
echo "**Tag:** $VERSION_TAG" >> $GITHUB_STEP_SUMMARY
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "**Platform:** linux/amd64" >> $GITHUB_STEP_SUMMARY
echo "**Mode:** 完整构建和部署" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Images:**" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-api:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-msggateway:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-msgtransfer:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-push:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-crontask:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-auth:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-user:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-friend:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-group:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-conversation:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-third:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY
echo "- \`$DOCKER_USER/openim-rpc-msg:$VERSION_TAG\`" >> $GITHUB_STEP_SUMMARY