添加qiming-rcoder模块
This commit is contained in:
31
qiming-rcoder/make/agent-runner.mk
Normal file
31
qiming-rcoder/make/agent-runner.mk
Normal file
@@ -0,0 +1,31 @@
|
||||
# ============================================================================
|
||||
# agent_runner 本地开发测试服务
|
||||
# ============================================================================
|
||||
|
||||
AGENT_RUNNER_PORT ?= 8286
|
||||
AGENT_RUNNER_PROJECTS_DIR ?= ./project_workspace
|
||||
AGENT_RUNNER_GRPC ?= no
|
||||
AGENT_RUNNER_LOG_DIR ?= ./logs
|
||||
|
||||
ifeq ($(AGENT_RUNNER_GRPC),yes)
|
||||
AGENT_RUNNER_FEATURES := http-server,grpc-server
|
||||
else
|
||||
AGENT_RUNNER_FEATURES := http-server
|
||||
endif
|
||||
|
||||
agent-runner-up:
|
||||
@mkdir -p $(AGENT_RUNNER_LOG_DIR)
|
||||
@echo "🚀 启动 agent_runner (端口: $(AGENT_RUNNER_PORT), gRPC: $(AGENT_RUNNER_GRPC))..."
|
||||
@echo "📝 日志文件: $(AGENT_RUNNER_LOG_DIR)/agent-runner.log"
|
||||
@echo "💡 按 Ctrl+C 停止服务"
|
||||
@mkdir -p $(AGENT_RUNNER_PROJECTS_DIR)
|
||||
cargo run -p agent_runner \
|
||||
--no-default-features --features "$(AGENT_RUNNER_FEATURES)" \
|
||||
-- --port $(AGENT_RUNNER_PORT) --projects-dir $(AGENT_RUNNER_PROJECTS_DIR) \
|
||||
2>&1 | tee $(AGENT_RUNNER_LOG_DIR)/agent-runner.log
|
||||
|
||||
agent-runner-logs:
|
||||
@tail -f $(AGENT_RUNNER_LOG_DIR)/agent-runner.log
|
||||
|
||||
agent-runner-down:
|
||||
@echo "🛑 前台模式无需单独停止,按 Ctrl+C 即可"
|
||||
48
qiming-rcoder/make/dev.mk
Normal file
48
qiming-rcoder/make/dev.mk
Normal file
@@ -0,0 +1,48 @@
|
||||
# ============================================================================
|
||||
# Docker Compose 开发模式
|
||||
# ============================================================================
|
||||
|
||||
dev-build: docker-build
|
||||
@echo ""
|
||||
@echo "🎉 构建完成!"
|
||||
@echo " ✓ Docker 镜像: master-rcoder:latest"
|
||||
@echo " ✓ Docker 镜像: rcoder-agent-runner:latest"
|
||||
@echo ""
|
||||
@echo "💡 下一步: make dev-up 启动容器"
|
||||
|
||||
dev-up:
|
||||
@echo "🚀 启动开发模式容器服务..."
|
||||
@if [ ! -f "docker/docker-compose.yml" ]; then \
|
||||
echo "❌ 错误: 未找到 docker/docker-compose.yml"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "🔧 使用开发模式配置:"
|
||||
@echo " - 镜像: nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder:latest"
|
||||
@echo " - 启动命令: 直接执行 /app/rcoder"
|
||||
@docker-compose -f docker/docker-compose.yml up -d
|
||||
@echo "📋 开发模式服务状态:"
|
||||
@docker-compose -f docker/docker-compose.yml ps
|
||||
|
||||
dev-down:
|
||||
@echo "🛑 停止开发模式容器服务..."
|
||||
@if [ -f "docker/docker-compose.yml" ]; then \
|
||||
docker-compose -f docker/docker-compose.yml down; \
|
||||
else \
|
||||
echo "⚠️ docker-compose.yml 未找到,跳过停止操作"; \
|
||||
fi
|
||||
|
||||
# 快速重启:依赖 dev-build 确保代码更改生效
|
||||
dev-restart: dev-build
|
||||
@echo "🔄 重启容器服务(使用最新构建的镜像)..."
|
||||
@if [ -f "docker/docker-compose.yml" ]; then \
|
||||
docker-compose -f docker/docker-compose.yml down; \
|
||||
docker-compose -f docker/docker-compose.yml up -d; \
|
||||
echo "✅ 容器已重启!"; \
|
||||
else \
|
||||
echo "❌ 错误: 未找到 docker-compose.yml"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "🎉 完整重启完成!"
|
||||
@echo "🎉 如需构建基础镜像,可以执行: make docker-build-base"
|
||||
@echo "💡 代码更改已生效,因为重新构建了镜像!"
|
||||
152
qiming-rcoder/make/docker.mk
Normal file
152
qiming-rcoder/make/docker.mk
Normal file
@@ -0,0 +1,152 @@
|
||||
# ============================================================================
|
||||
# Docker 镜像构建
|
||||
# ============================================================================
|
||||
|
||||
# Docker 镜像构建(仅构建镜像,不编译)
|
||||
# 串行构建镜像,避免资源竞争
|
||||
docker-build:
|
||||
@echo "🔨 开始构建主镜像..."
|
||||
@$(MAKE) docker-build-master & \
|
||||
$(MAKE) docker-build-agent-runner & \
|
||||
wait
|
||||
@echo ""
|
||||
@echo "✅ 所有 Docker 镜像构建完成!"
|
||||
@echo " ✓ master-rcoder:latest"
|
||||
@echo " ✓ rcoder-agent-runner:latest"
|
||||
@echo ""
|
||||
@echo "🎯 使用方式:"
|
||||
@echo " docker run -d -p 8087:8087 master-rcoder:latest"
|
||||
|
||||
# 构建所有基础镜像(很少需要,只有修改系统依赖时才需要)
|
||||
# 串行构建基础镜像,避免资源竞争
|
||||
docker-build-base: docker-build-master-base docker-build-agent-base
|
||||
@echo ""
|
||||
@echo "✅ 所有基础镜像构建完成!"
|
||||
@echo " ✓ master-rcoder-base:latest"
|
||||
@echo " ✓ rcoder-agent-base:latest"
|
||||
@echo ""
|
||||
@echo "💡 提示: 平时开发只需运行 make dev-restart,无需重新构建基础镜像"
|
||||
|
||||
# 构建主服务镜像(基于基础镜像,快速构建)
|
||||
docker-build-master:
|
||||
@echo "🐳 构建 master-rcoder 镜像..."
|
||||
@echo "📍 镜像名称: master-rcoder:latest"
|
||||
@# 检查基础镜像是否存在
|
||||
@if ! docker image inspect master-rcoder-base:latest >/dev/null 2>&1; then \
|
||||
echo "⚠️ 基础镜像 master-rcoder-base:latest 不存在,先构建基础镜像..."; \
|
||||
$(MAKE) docker-build-master-base; \
|
||||
else \
|
||||
echo "✓ 基础镜像 master-rcoder-base:latest 已存在"; \
|
||||
fi
|
||||
@echo "📦 使用 Dockerfile 多阶段构建(基于基础镜像)..."
|
||||
@# 🔧 根据 CARGO_FEATURES 决定是否启用 eBPF 调试
|
||||
@(if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
MASTER_CARGO_FLAGS="$(CARGO_FEATURES)"; \
|
||||
echo "🔧 master-rcoder 将启用 eBPF 调试模式"; \
|
||||
else \
|
||||
MASTER_CARGO_FLAGS=""; \
|
||||
echo "🔒 master-rcoder 生产模式(无 eBPF 调试)"; \
|
||||
fi; \
|
||||
docker build \
|
||||
--build-arg CARGO_FLAGS="$$MASTER_CARGO_FLAGS" \
|
||||
--build-arg CACHEBUST=$$(date +%s) \
|
||||
-f docker/rcoder-master/Dockerfile -t master-rcoder:latest .;)
|
||||
@echo "✅ master-rcoder 镜像构建完成!"
|
||||
@echo "📤 推送镜像到阿里云仓库..."
|
||||
@docker tag master-rcoder:latest nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder:latest
|
||||
@skopeo copy docker-daemon:nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder:latest docker://nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder:latest
|
||||
@echo "✅ 镜像已推送: nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder:latest"
|
||||
|
||||
# 构建 master-base 基础镜像(包含所有运行时依赖,很少需要重新构建)
|
||||
docker-build-master-base:
|
||||
@echo "🐳 构建 master-rcoder-base 基础镜像..."
|
||||
@echo "📍 镜像名称: master-rcoder-base:latest"
|
||||
@echo "⏳ 这可能需要较长时间(包含所有运行时依赖安装)..."
|
||||
@docker build -f docker/rcoder-master/Dockerfile.base -t master-rcoder-base:latest .
|
||||
@echo "✅ master-rcoder-base 基础镜像构建完成!"
|
||||
@echo "📤 推送基础镜像到阿里云仓库..."
|
||||
@docker tag master-rcoder-base:latest nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder-base:latest
|
||||
@skopeo copy docker-daemon:nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder-base:latest docker://nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder-base:latest
|
||||
@echo "✅ 基础镜像已推送: nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/master-rcoder-base:latest"
|
||||
@echo "💡 提示: 平时开发只需运行 make dev-restart,无需重新构建基础镜像"
|
||||
|
||||
# ============================================================================
|
||||
# Cargo feature 配置
|
||||
# ============================================================================
|
||||
# 开发模式:启用所有调试、监控和追踪功能
|
||||
# ⚠️ 注意:添加新的调试 feature 时,必须同步更新此列表!
|
||||
#
|
||||
# 当前启用的调试 features:
|
||||
# - ebpf-debug (docker_manager, rcoder): eBPF 诊断工具
|
||||
# - pyroscope (agent_runner): 性能分析 (CPU/Memory)
|
||||
# - otel (agent_runner): OpenTelemetry 追踪
|
||||
# - debug (rcoder): 调试路由
|
||||
# - proxy (agent_runner): Pingora 反向代理(Linux 容器默认开启)
|
||||
# - kubernetes (rcoder, docker_manager): Kubernetes 运行时支持
|
||||
# - http-server (agent_runner): HTTP REST API 服务(默认启用)
|
||||
# - grpc-server (agent_runner): gRPC 服务(默认启用)
|
||||
#
|
||||
# 本地开发调试默认开启所有功能(http-server 和 grpc-server 默认启用)
|
||||
CARGO_FEATURES ?= --features ebpf-debug,pyroscope,otel,debug,proxy,kubernetes
|
||||
|
||||
# 构建 agent-runner 镜像(基于基础镜像,快速构建)
|
||||
docker-build-agent-runner:
|
||||
@echo "🐳 构建 rcoder-agent-runner 镜像..."
|
||||
@echo "📍 镜像名称: rcoder-agent-runner:latest"
|
||||
@# 检查基础镜像是否存在
|
||||
@if ! docker image inspect rcoder-agent-base:latest >/dev/null 2>&1; then \
|
||||
echo "⚠️ 基础镜像 rcoder-agent-base:latest 不存在,先构建基础镜像..."; \
|
||||
$(MAKE) docker-build-agent-base; \
|
||||
else \
|
||||
echo "✓ 基础镜像 rcoder-agent-base:latest 已存在"; \
|
||||
fi
|
||||
@echo "📦 步骤1: 在 debian:12 环境中构建 agent_runner 二进制(确保 GLIBC 版本兼容)..."
|
||||
@# 🔧 调试模式:默认启用 ebpf-debug feature,允许使用 eBPF 诊断工具
|
||||
@echo "🔧 Cargo features: $(CARGO_FEATURES)"
|
||||
@# 计算业务代码哈希,只有代码变化时才重新编译(系统依赖和 Rust 安装保持缓存)
|
||||
$(eval CRATES_HASH := $(shell find crates Cargo.toml Cargo.lock -name "*.rs" -o -name "Cargo.toml" -o -name "Cargo.lock" 2>/dev/null | sort | xargs cat 2>/dev/null | md5sum | cut -d' ' -f1))
|
||||
@echo "🔑 业务代码哈希: $(CRATES_HASH)"
|
||||
@# 🔥 关键修改:通过 CARGO_FEATURES 变量控制
|
||||
@docker build --build-arg CRATES_HASH=$(CRATES_HASH) \
|
||||
--build-arg CARGO_FLAGS="$(CARGO_FEATURES)" \
|
||||
-f docker/rcoder-agent-runner/Dockerfile.build -t rcoder-agent-runner-build .
|
||||
@echo "📦 步骤2: 复制二进制文件到 agent-runner 目录..."
|
||||
@# 创建容器并复制 agent_runner 二进制文件
|
||||
@mkdir -p docker/rcoder-agent-runner/bin
|
||||
@docker create --name build-container rcoder-agent-runner-build
|
||||
@docker cp build-container:/build/target/release/agent_runner docker/rcoder-agent-runner/bin/
|
||||
@docker rm build-container
|
||||
@docker rmi rcoder-agent-runner-build
|
||||
@echo "📦 步骤3: 构建最终的 agent-runner 镜像(基于基础镜像,快速)..."
|
||||
@# 🔧 根据 CARGO_FEATURES 决定是否安装 eBPF 工具
|
||||
@(if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
INSTALL_EBPF="true"; \
|
||||
echo "🔧 将安装 eBPF 诊断工具"; \
|
||||
else \
|
||||
INSTALL_EBPF="false"; \
|
||||
echo "🔒 跳过 eBPF 工具安装(生产模式)"; \
|
||||
fi; \
|
||||
cd docker/rcoder-agent-runner && \
|
||||
docker buildx build --platform linux/$(DOCKER_HOST_ARCH) --load \
|
||||
--build-arg CACHEBUST=$$(date +%s) \
|
||||
--build-arg INSTALL_EBPF_TOOLS="$${INSTALL_EBPF}" \
|
||||
--build-arg INSTALL_PYROSCOPE="$${INSTALL_EBPF}" \
|
||||
--build-arg INSTALL_ALLOY="$${INSTALL_EBPF}" \
|
||||
-f Dockerfile -t rcoder-agent-runner:latest .;)
|
||||
@echo "✅ rcoder-agent-runner 镜像构建完成!"
|
||||
@if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
echo "🔧 eBPF 调试模式已启用,容器将以特权模式运行"; \
|
||||
else \
|
||||
echo "🔒 生产模式,容器权限受限"; \
|
||||
fi
|
||||
@echo "📤 推送镜像到阿里云仓库..."
|
||||
@docker tag rcoder-agent-runner:latest $(K8S_IMAGE_REGISTRY)
|
||||
@docker tag rcoder-agent-runner:latest nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-runner:latest
|
||||
@skopeo copy docker-daemon:nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-runner:latest docker://nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-runner:latest
|
||||
@echo "✅ 镜像已推送: nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-runner:latest"
|
||||
|
||||
# 构建生产版本(禁用 eBPF 工具,减小镜像大小)
|
||||
docker-build-agent-production:
|
||||
@echo "🐳 构建 rcoder-agent-runner 生产镜像(无 eBPF 工具)..."
|
||||
@$(MAKE) docker-build-agent-runner CARGO_FEATURES=""
|
||||
@echo "✅ 生产镜像构建完成(无 eBPF 工具,镜像更小)"
|
||||
132
qiming-rcoder/make/k8s-offline.mk
Normal file
132
qiming-rcoder/make/k8s-offline.mk
Normal file
@@ -0,0 +1,132 @@
|
||||
# ============================================================================
|
||||
# K8s 离线部署 bundle
|
||||
#
|
||||
# make k8s-offline-bundle 构建离线交付包 (rcoder-offline-*.tar.gz)
|
||||
# make k8s-offline-import BUNDLE= 解压并执行 install.sh (客户机器用)
|
||||
# make k8s-offline-images-list 仅打印所有离线依赖镜像清单
|
||||
# make k8s-offline-clean 清理构建产物
|
||||
# ============================================================================
|
||||
|
||||
OFFLINE_VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo dev)
|
||||
OFFLINE_ARCH ?= $(shell uname -m)
|
||||
OFFLINE_WORKDIR ?= dist/offline-build
|
||||
OFFLINE_OUTPUT ?= dist/rcoder-offline-$(OFFLINE_VERSION)-$(OFFLINE_ARCH).tar.gz
|
||||
OFFLINE_IMAGES_FILE := k8s/offline/images.txt
|
||||
|
||||
LONGHORN_VERSION ?= v1.7.2
|
||||
JUICEFS_CSI_VERSION ?= v0.31.3
|
||||
|
||||
# 跳过拉取/重新构建镜像 (如已经拉好, 节省时间)
|
||||
OFFLINE_SKIP_PULL ?= 0
|
||||
|
||||
.PHONY: k8s-offline-bundle k8s-offline-import k8s-offline-images-list k8s-offline-clean
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# k8s-offline-images-list: 打印清单 (可 pipe 给其他工具)
|
||||
# ----------------------------------------------------------------------------
|
||||
k8s-offline-images-list:
|
||||
@grep -vE '^\s*#|^\s*$$' $(OFFLINE_IMAGES_FILE)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# k8s-offline-bundle: 打包离线交付物
|
||||
# ----------------------------------------------------------------------------
|
||||
k8s-offline-bundle:
|
||||
@command -v docker >/dev/null || { echo "❌ 需要 docker"; exit 1; }
|
||||
@command -v helm >/dev/null || { echo "❌ 需要 helm"; exit 1; }
|
||||
@command -v curl >/dev/null || { echo "❌ 需要 curl"; exit 1; }
|
||||
|
||||
@echo "📦 构建离线 bundle..."
|
||||
@echo " 版本: $(OFFLINE_VERSION) ($(OFFLINE_ARCH))"
|
||||
@echo " 输出: $(OFFLINE_OUTPUT)"
|
||||
@echo ""
|
||||
|
||||
@rm -rf $(OFFLINE_WORKDIR)
|
||||
@mkdir -p $(OFFLINE_WORKDIR)/images $(OFFLINE_WORKDIR)/charts \
|
||||
$(OFFLINE_WORKDIR)/longhorn $(OFFLINE_WORKDIR)/juicefs-csi
|
||||
|
||||
@echo "[1/5] 拉取镜像 (最多重试 3 次)..."
|
||||
@if [ "$(OFFLINE_SKIP_PULL)" = "1" ]; then \
|
||||
echo " ⏭ 跳过 (OFFLINE_SKIP_PULL=1)"; \
|
||||
else \
|
||||
grep -vE '^\s*#|^\s*$$' $(OFFLINE_IMAGES_FILE) | while read img; do \
|
||||
echo " 🡣 $$img"; \
|
||||
for attempt in 1 2 3; do \
|
||||
if docker pull $$img; then \
|
||||
break; \
|
||||
fi; \
|
||||
if [ $$attempt -eq 3 ]; then \
|
||||
echo "❌ 拉取失败 (3 次重试): $$img"; exit 1; \
|
||||
fi; \
|
||||
echo " ⚠️ 第 $$attempt 次失败, 等 $$((attempt*5))s 后重试..."; \
|
||||
sleep $$((attempt*5)); \
|
||||
done; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
@echo ""
|
||||
@echo "[2/5] 导出 images.tar (包含所有镜像)..."
|
||||
@IMGS=$$(grep -vE '^\s*#|^\s*$$' $(OFFLINE_IMAGES_FILE) | tr '\n' ' '); \
|
||||
docker save $$IMGS -o $(OFFLINE_WORKDIR)/images/all-images.tar
|
||||
@cp $(OFFLINE_IMAGES_FILE) $(OFFLINE_WORKDIR)/images/images.txt
|
||||
@echo " ✅ images.tar 大小: $$(du -sh $(OFFLINE_WORKDIR)/images/all-images.tar | awk '{print $$1}')"
|
||||
|
||||
@echo ""
|
||||
@echo "[3/5] 打包 RCoder Helm chart..."
|
||||
@helm package k8s/helm/rcoder -d $(OFFLINE_WORKDIR)/charts
|
||||
@ls -1 $(OFFLINE_WORKDIR)/charts | sed 's/^/ ✓ /'
|
||||
|
||||
@echo ""
|
||||
@echo "[4/5] 下载 Longhorn + JuiceFS CSI manifests..."
|
||||
@curl -fsSL https://raw.githubusercontent.com/longhorn/longhorn/$(LONGHORN_VERSION)/deploy/longhorn.yaml \
|
||||
-o $(OFFLINE_WORKDIR)/longhorn/longhorn-$(LONGHORN_VERSION).yaml
|
||||
@echo " ✅ longhorn-$(LONGHORN_VERSION).yaml"
|
||||
@curl -fsSL https://raw.githubusercontent.com/juicedata/juicefs-csi-driver/$(JUICEFS_CSI_VERSION)/deploy/k8s.yaml \
|
||||
-o $(OFFLINE_WORKDIR)/juicefs-csi/juicefs-csi-$(JUICEFS_CSI_VERSION).yaml
|
||||
@echo " ✅ juicefs-csi-$(JUICEFS_CSI_VERSION).yaml"
|
||||
|
||||
@echo ""
|
||||
@echo "[5/5] 拷贝脚本和 values..."
|
||||
@cp -r k8s/manifests $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/offline/install.sh $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/offline/rewrite-registry.sh $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/offline/README.md $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/helm/rcoder/values-dev.yaml $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/helm/rcoder/values-prod.yaml $(OFFLINE_WORKDIR)/
|
||||
@cp k8s/helm/rcoder/values-offline.yaml $(OFFLINE_WORKDIR)/
|
||||
@chmod +x $(OFFLINE_WORKDIR)/*.sh
|
||||
|
||||
@mkdir -p dist
|
||||
@echo ""
|
||||
@echo "📦 压缩 bundle..."
|
||||
@tar czf $(OFFLINE_OUTPUT) -C $(OFFLINE_WORKDIR) .
|
||||
|
||||
@echo ""
|
||||
@echo "✅ 离线 bundle 已生成"
|
||||
@echo " 文件: $(OFFLINE_OUTPUT)"
|
||||
@echo " 大小: $$(du -sh $(OFFLINE_OUTPUT) | awk '{print $$1}')"
|
||||
@echo ""
|
||||
@echo "客户使用:"
|
||||
@echo " tar xzf $$(basename $(OFFLINE_OUTPUT)) && bash install.sh --mode=direct --env=dev"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# k8s-offline-import: 解压 bundle 并一键部署 (在离线机器上)
|
||||
# ----------------------------------------------------------------------------
|
||||
k8s-offline-import:
|
||||
@if [ -z "$(BUNDLE)" ]; then \
|
||||
echo "❌ 需要指定 BUNDLE=path/to/rcoder-offline-*.tar.gz"; exit 1; \
|
||||
fi
|
||||
@if [ ! -f "$(BUNDLE)" ]; then \
|
||||
echo "❌ 未找到 $(BUNDLE)"; exit 1; \
|
||||
fi
|
||||
@echo "📥 解压 $(BUNDLE) ..."
|
||||
@mkdir -p dist/offline-import
|
||||
@tar xzf $(BUNDLE) -C dist/offline-import
|
||||
@echo "🚀 执行 install.sh ..."
|
||||
@cd dist/offline-import && bash install.sh $(INSTALL_ARGS)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 清理
|
||||
# ----------------------------------------------------------------------------
|
||||
k8s-offline-clean:
|
||||
@rm -rf $(OFFLINE_WORKDIR) dist/offline-import
|
||||
@echo "✅ 清理完成 (保留 dist/rcoder-offline-*.tar.gz)"
|
||||
233
qiming-rcoder/make/k8s.mk
Normal file
233
qiming-rcoder/make/k8s.mk
Normal file
@@ -0,0 +1,233 @@
|
||||
# ============================================================================
|
||||
# K8s 开发/部署 Makefile (Kustomize)
|
||||
# ============================================================================
|
||||
|
||||
IMAGE ?= rcoder:test-k8s
|
||||
K8S_IMAGE_REGISTRY ?= nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder:latest
|
||||
KUSTOMIZE_DIR ?= k8s/manifests
|
||||
ROLLOUT_TIMEOUT ?= 180s
|
||||
|
||||
# Namespace 按 overlay 推导
|
||||
K8S_NAMESPACE_DEV := nuwax-rcoder-dev
|
||||
K8S_NAMESPACE_PROD := nuwax-rcoder-prod
|
||||
|
||||
# 本地 k3s 同步配置(make dev-build-k8s 构建后自动 import 到本地 k3s)
|
||||
SUDO ?= sudo
|
||||
K3S_CTR ?= $(SUDO) k3s ctr
|
||||
K3S_NAMESPACE ?= k8s.io
|
||||
# 设为 1 跳过本地 k3s import(例如机器没装 k3s 或不想 import)
|
||||
SKIP_K3S_IMPORT ?= 0
|
||||
# 设为 1 跳过 build 后自动 rollout restart
|
||||
SKIP_ROLLOUT_RESTART ?= 0
|
||||
|
||||
# ============================================================================
|
||||
# 镜像构建
|
||||
# ============================================================================
|
||||
|
||||
dev-build-k8s: docker-build-master-base
|
||||
@echo "☸️ 构建 K8s 镜像..."
|
||||
@echo "📍 镜像名称: $(IMAGE)"
|
||||
@echo "⏳ 这可能需要较长时间(包含 Rust 编译)..."
|
||||
@docker build -f docker/rcoder-master/Dockerfile -t $(IMAGE) --build-arg CARGO_FLAGS="--features kubernetes" .
|
||||
@echo "✅ K8s 镜像构建完成!"
|
||||
@echo ""
|
||||
@echo "📤 推送镜像到阿里云仓库..."
|
||||
@docker tag $(IMAGE) $(K8S_IMAGE_REGISTRY)
|
||||
@skopeo copy docker-daemon:$(K8S_IMAGE_REGISTRY) docker://$(K8S_IMAGE_REGISTRY)
|
||||
@echo "✅ 镜像已推送到 $(K8S_IMAGE_REGISTRY)"
|
||||
@$(MAKE) -s --no-print-directory _k8s-import-image
|
||||
@$(MAKE) -s --no-print-directory _k8s-rollout-restart
|
||||
@echo ""
|
||||
@echo "💡 下一步: make dev-up-k8s 启动 K8s 开发模式 (镜像已就位)"
|
||||
|
||||
# 内部 target: 把刚构建的镜像直接导入本地 k3s containerd, 跳过 registry 网络往返
|
||||
# 使用 docker save | k3s ctr image import, 完全离线
|
||||
_k8s-import-image:
|
||||
@if [ "$(SKIP_K3S_IMPORT)" = "1" ]; then \
|
||||
echo ""; echo "⏭ 跳过 k3s 本地 import (SKIP_K3S_IMPORT=1)"; exit 0; \
|
||||
fi
|
||||
@if ! command -v k3s >/dev/null 2>&1; then \
|
||||
echo ""; echo "⚠️ 未检测到 k3s (本机非 k3s 节点), 跳过本地 import"; \
|
||||
echo " 远程 registry 已更新, 其他集群仍可正常拉取"; exit 0; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "📥 导入镜像到本地 k3s containerd (跳过 registry 网络往返)..."
|
||||
@echo " 方式: docker save | k3s ctr image import"
|
||||
@docker save $(K8S_IMAGE_REGISTRY) | $(K3S_CTR) -n $(K3S_NAMESPACE) image import - >/dev/null
|
||||
@echo "✅ 镜像已 import 到本地 k3s 节点 ($(K3S_NAMESPACE) namespace)"
|
||||
|
||||
# 内部 target: 重启已部署的 rcoder Deployment 使其拉取/使用新 image
|
||||
# imagePullPolicy 是 IfNotPresent, 节点上已经是新 image, restart 后即生效
|
||||
_k8s-rollout-restart:
|
||||
@if [ "$(SKIP_ROLLOUT_RESTART)" = "1" ]; then \
|
||||
echo "⏭ 跳过 rollout restart (SKIP_ROLLOUT_RESTART=1)"; exit 0; \
|
||||
fi
|
||||
@if ! command -v kubectl >/dev/null 2>&1; then exit 0; fi
|
||||
@echo ""
|
||||
@echo "🔄 重启已部署的 rcoder Deployment (使新镜像生效)..."
|
||||
@for ns in $(K8S_NAMESPACE_DEV) $(K8S_NAMESPACE_PROD); do \
|
||||
if kubectl get deploy rcoder -n $$ns >/dev/null 2>&1; then \
|
||||
kubectl rollout restart deploy/rcoder -n $$ns >/dev/null && \
|
||||
echo " ✓ $$ns: rollout restart 已触发"; \
|
||||
else \
|
||||
echo " - $$ns: deploy 不存在, 跳过"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# 开发模式快捷命令 (默认指向 dev overlay)
|
||||
# ============================================================================
|
||||
|
||||
# 启动 K8s 开发模式(使用 Kustomize dev overlay)
|
||||
dev-up-k8s:
|
||||
@echo "☸️ 启动 K8s 开发模式 (overlays/dev -> $(K8S_NAMESPACE_DEV))..."
|
||||
@echo ""
|
||||
@echo "[1/4] 检查 JuiceFS CSI Driver..."
|
||||
@kubectl rollout status daemonset/juicefs-csi-driver-node -n kube-system --timeout=120s 2>/dev/null || \
|
||||
(echo "⚠️ JuiceFS CSI 未部署,请先执行: helm install juicefs-csi-driver juicefs/juicefs-csi-driver --namespace kube-system" && exit 1)
|
||||
|
||||
@echo ""
|
||||
@echo "[2/4] 部署 dev overlay..."
|
||||
@kubectl apply -k $(KUSTOMIZE_DIR)/overlays/dev
|
||||
|
||||
@echo ""
|
||||
@echo "[3/4] 等待存储层就绪..."
|
||||
@kubectl wait --for=condition=ready pod -l app=postgresql -n $(K8S_NAMESPACE_DEV) --timeout=180s 2>/dev/null || echo "⚠️ PostgreSQL 等待超时,继续..."
|
||||
@kubectl wait --for=condition=ready pod -l app=minio -n $(K8S_NAMESPACE_DEV) --timeout=180s 2>/dev/null || echo "⚠️ MinIO 等待超时,继续..."
|
||||
@kubectl wait --for=condition=complete job/minio-init -n $(K8S_NAMESPACE_DEV) --timeout=120s 2>/dev/null || echo "⚠️ MinIO 初始化等待超时,继续..."
|
||||
|
||||
@echo ""
|
||||
@echo "[4/4] 等待 RCoder 就绪..."
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE_DEV) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
|
||||
@echo ""
|
||||
@echo "📋 K8s 部署状态:"
|
||||
@kubectl get pods -n $(K8S_NAMESPACE_DEV)
|
||||
@echo ""
|
||||
@kubectl get pvc -n $(K8S_NAMESPACE_DEV)
|
||||
@echo ""
|
||||
@kubectl get storageclass | grep juice || true
|
||||
@echo ""
|
||||
@echo "💡 查看日志: make dev-logs-k8s"
|
||||
|
||||
# 重启 K8s 开发模式(重新构建镜像+部署)
|
||||
dev-restart-k8s: dev-build-k8s
|
||||
@echo "☸️ 重启 K8s 开发模式..."
|
||||
@kubectl apply -k $(KUSTOMIZE_DIR)/overlays/dev
|
||||
@kubectl delete pods -n $(K8S_NAMESPACE_DEV) -l app=rcoder --ignore-not-found
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE_DEV) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
@echo "✅ K8s 部署已重启!"
|
||||
@kubectl get pods -n $(K8S_NAMESPACE_DEV)
|
||||
|
||||
# 停止 K8s 开发模式
|
||||
dev-down-k8s:
|
||||
@echo "☸️ 停止 K8s 开发模式..."
|
||||
@kubectl delete -k $(KUSTOMIZE_DIR)/overlays/dev --ignore-not-found
|
||||
@echo "✅ K8s 开发栈已从集群移除"
|
||||
|
||||
# 查看 K8s 开发模式日志
|
||||
dev-logs-k8s:
|
||||
@echo "☸️ 查看 K8s 开发模式日志..."
|
||||
@kubectl logs -n $(K8S_NAMESPACE_DEV) -l app=rcoder -f
|
||||
|
||||
# ============================================================================
|
||||
# 多环境部署命令
|
||||
# ============================================================================
|
||||
|
||||
# 部署开发环境
|
||||
deploy-dev:
|
||||
@echo "☸️ 部署开发环境..."
|
||||
@kubectl apply -k $(KUSTOMIZE_DIR)/overlays/dev
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE_DEV) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
@echo "✅ 开发环境部署完成!"
|
||||
|
||||
# 部署生产环境
|
||||
deploy-prod:
|
||||
@echo "☸️ 部署生产环境..."
|
||||
@kubectl apply -k $(KUSTOMIZE_DIR)/overlays/prod
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE_PROD) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
@echo "✅ 生产环境部署完成!"
|
||||
|
||||
# 清理指定环境
|
||||
undeploy-dev:
|
||||
@echo "☸️ 清理开发环境..."
|
||||
@kubectl delete -k $(KUSTOMIZE_DIR)/overlays/dev --ignore-not-found
|
||||
|
||||
undeploy-prod:
|
||||
@echo "☸️ 清理生产环境..."
|
||||
@kubectl delete -k $(KUSTOMIZE_DIR)/overlays/prod --ignore-not-found
|
||||
|
||||
# 验证 Kustomize 配置(构建不部署)
|
||||
kustomize-build:
|
||||
@echo "☸️ 验证 Kustomize 配置..."
|
||||
@echo "=== Base ==="
|
||||
@kubectl kustomize $(KUSTOMIZE_DIR)/base >/dev/null && echo " ✓"
|
||||
@echo "=== Dev Overlay ==="
|
||||
@kubectl kustomize $(KUSTOMIZE_DIR)/overlays/dev >/dev/null && echo " ✓"
|
||||
@echo "=== Prod Overlay ==="
|
||||
@kubectl kustomize $(KUSTOMIZE_DIR)/overlays/prod >/dev/null && echo " ✓"
|
||||
|
||||
# ============================================================================
|
||||
# 本地 K8s 测试 (OrbStack / k3d / kind)
|
||||
# ============================================================================
|
||||
|
||||
K8S_LOCAL_NAMESPACE := rcoder-local
|
||||
K8S_LOCAL_IMAGE ?= rcoder-local:latest
|
||||
K8S_LOCAL_AGENT_IMAGE ?= rcoder-agent-runner-local:latest
|
||||
|
||||
# 构建本地 K8s 测试镜像(不推送到远程仓库)
|
||||
dev-build-k8s-local:
|
||||
@echo "🔨 构建本地 K8s 测试镜像..."
|
||||
@echo "📦 [1/2] 构建 rcoder (features=kubernetes)..."
|
||||
@docker build -f docker/rcoder-master/Dockerfile -t $(K8S_LOCAL_IMAGE) --build-arg CARGO_FLAGS="--features kubernetes" .
|
||||
@echo "📦 [2/2] 构建 agent-runner..."
|
||||
@echo " 步骤 1: 在 Docker 中编译 agent_runner 二进制..."
|
||||
@docker build -f docker/rcoder-agent-runner/Dockerfile.build -t rcoder-agent-runner-build-local .
|
||||
@mkdir -p docker/rcoder-agent-runner/bin
|
||||
@docker create --name build-container-local rcoder-agent-runner-build-local 2>/dev/null || true
|
||||
@docker cp build-container-local:/build/target/release/agent_runner docker/rcoder-agent-runner/bin/agent_runner
|
||||
@docker rm build-container-local
|
||||
@docker rmi rcoder-agent-runner-build-local
|
||||
@echo " 步骤 2: 构建最终 agent-runner 镜像..."
|
||||
@cd docker/rcoder-agent-runner && docker build -f Dockerfile --build-arg INSTALL_EBPF_TOOLS=false -t $(K8S_LOCAL_AGENT_IMAGE) .
|
||||
@echo "✅ 本地镜像构建完成!"
|
||||
@docker images | grep -E "rcoder-local|rcoder-agent-runner-local"
|
||||
|
||||
# 启动本地 K8s 测试(OrbStack 自动共享 Docker 镜像,无需手动 import)
|
||||
dev-up-k8s-local:
|
||||
@echo "☸️ 启动本地 K8s 测试 ($(K8S_LOCAL_NAMESPACE))..."
|
||||
@kubectl apply -k $(KUSTOMIZE_DIR)/overlays/local
|
||||
@echo ""
|
||||
@echo "🔄 重启 Pod 使新镜像生效..."
|
||||
@kubectl delete pods -n $(K8S_LOCAL_NAMESPACE) -l app=rcoder --ignore-not-found
|
||||
@echo ""
|
||||
@echo "⏳ 等待 RCoder 就绪..."
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_LOCAL_NAMESPACE) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
@echo ""
|
||||
@echo "📋 部署状态:"
|
||||
@kubectl get pods -n $(K8S_LOCAL_NAMESPACE)
|
||||
@kubectl get pvc -n $(K8S_LOCAL_NAMESPACE)
|
||||
@echo ""
|
||||
@echo "💡 访问: http://localhost:30087"
|
||||
@echo "💡 日志: make dev-logs-k8s-local"
|
||||
|
||||
# 重启本地 K8s 测试
|
||||
dev-restart-k8s-local:
|
||||
@echo "☸️ 重启本地 K8s 测试..."
|
||||
@kubectl delete pods -n $(K8S_LOCAL_NAMESPACE) -l app=rcoder --ignore-not-found
|
||||
@kubectl rollout status deploy/rcoder -n $(K8S_LOCAL_NAMESPACE) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
@echo "✅ 已重启!"
|
||||
|
||||
# 停止本地 K8s 测试
|
||||
dev-down-k8s-local:
|
||||
@echo "☸️ 停止本地 K8s 测试..."
|
||||
@kubectl delete -k $(KUSTOMIZE_DIR)/overlays/local --ignore-not-found
|
||||
@echo "✅ 已清理"
|
||||
|
||||
# 查看本地 K8s 测试日志
|
||||
dev-logs-k8s-local:
|
||||
@kubectl logs -n $(K8S_LOCAL_NAMESPACE) -l app=rcoder -f --tail=100
|
||||
|
||||
# 一键构建+部署(本地 K8s 测试)
|
||||
dev-local-k8s: dev-build-k8s-local dev-up-k8s-local
|
||||
@echo "🎉 本地 K8s 测试环境已就绪!"
|
||||
125
qiming-rcoder/make/libreoffice.mk
Normal file
125
qiming-rcoder/make/libreoffice.mk
Normal file
@@ -0,0 +1,125 @@
|
||||
# ============================================================================
|
||||
# LibreOffice 预下载配置(避免每次构建都重新下载)
|
||||
# ============================================================================
|
||||
# LibreOffice 版本(必须与 Dockerfile.base 保持一致)
|
||||
LIBREOFFICE_VERSION := 25.8.5
|
||||
|
||||
# 下载目录(相对于项目根目录)
|
||||
LIBREOFFICE_DOWNLOAD_DIR := docker/rcoder-agent-runner/downloads
|
||||
|
||||
# x86_64 下载信息
|
||||
LIBREOFFICE_X86_FILE := LibreOffice_$(LIBREOFFICE_VERSION)_Linux_x86-64_deb.tar.gz
|
||||
LIBREOFFICE_X86_URL := https://mirror.csclub.uwaterloo.ca/tdf/libreoffice/stable/$(LIBREOFFICE_VERSION)/deb/x86_64/LibreOffice_$(LIBREOFFICE_VERSION)_Linux_x86-64_deb.tar.gz
|
||||
LIBREOFFICE_X86_FALLBACK := https://download.documentfoundation.org/libreoffice/stable/$(LIBREOFFICE_VERSION)/deb/x86_64/LibreOffice_$(LIBREOFFICE_VERSION)_Linux_x86-64_deb.tar.gz
|
||||
LIBREOFFICE_X86_PATH := $(LIBREOFFICE_DOWNLOAD_DIR)/$(LIBREOFFICE_X86_FILE)
|
||||
|
||||
# aarch64 下载信息
|
||||
LIBREOFFICE_ARM_FILE := LibreOffice_$(LIBREOFFICE_VERSION)_Linux_aarch64_deb.tar.gz
|
||||
LIBREOFFICE_ARM_URL := https://mirror.csclub.uwaterloo.ca/tdf/libreoffice/stable/$(LIBREOFFICE_VERSION)/deb/aarch64/LibreOffice_$(LIBREOFFICE_VERSION)_Linux_aarch64_deb.tar.gz
|
||||
LIBREOFFICE_ARM_FALLBACK := https://download.documentfoundation.org/libreoffice/stable/$(LIBREOFFICE_VERSION)/deb/aarch64/LibreOffice_$(LIBREOFFICE_VERSION)_Linux_aarch64_deb.tar.gz
|
||||
LIBREOFFICE_ARM_PATH := $(LIBREOFFICE_DOWNLOAD_DIR)/$(LIBREOFFICE_ARM_FILE)
|
||||
|
||||
# docker-build-agent-base 构建时使用的架构(检测宿主 Docker 架构)
|
||||
# OrbStack on Apple Silicon = linux/arm64, 普通 Linux = linux/amd64
|
||||
# 注意:docker version --format '{{.Server.Arch}}' 在 OrbStack 上返回 "arm64",不是 "aarch64"
|
||||
DOCKER_HOST_ARCH := $(shell docker version --format '{{.Server.Arch}}' 2>/dev/null || echo "amd64")
|
||||
ifeq ($(DOCKER_HOST_ARCH),arm64)
|
||||
LIBREOFFICE_ARCH := arm64
|
||||
LIBREOFFICE_FILE := $(LIBREOFFICE_ARM_FILE)
|
||||
else ifeq ($(DOCKER_HOST_ARCH),aarch64)
|
||||
LIBREOFFICE_ARCH := arm64
|
||||
LIBREOFFICE_FILE := $(LIBREOFFICE_ARM_FILE)
|
||||
else
|
||||
LIBREOFFICE_ARCH := amd64
|
||||
LIBREOFFICE_FILE := $(LIBREOFFICE_X86_FILE)
|
||||
endif
|
||||
|
||||
# ============================================================================
|
||||
# LibreOffice 预下载目标
|
||||
# ============================================================================
|
||||
|
||||
# 预下载 LibreOffice(下载两个架构的文件,确保 Docker 多平台构建可用)
|
||||
docker-pre-download-libreoffice:
|
||||
@echo "📦 检查并下载 LibreOffice $(LIBREOFFICE_VERSION)..."
|
||||
@mkdir -p $(LIBREOFFICE_DOWNLOAD_DIR)
|
||||
@# ========== 下载 x86_64 ==========
|
||||
@echo ""; echo "=== 检查 x86_64 版本 ==="; \
|
||||
if [ -f "$(LIBREOFFICE_X86_PATH)" ] && [ -f "$(LIBREOFFICE_X86_PATH).sha256" ]; then \
|
||||
SAVED_HASH=$$(cat "$(LIBREOFFICE_X86_PATH).sha256"); \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_X86_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
if [ "$$SAVED_HASH" = "$$CALC_HASH" ]; then \
|
||||
echo "✅ x86_64 已存在且有效,跳过下载"; \
|
||||
else \
|
||||
echo "❌ x86_64 Hash 不匹配,重新下载..."; \
|
||||
rm -f "$(LIBREOFFICE_X86_PATH)" "$(LIBREOFFICE_X86_PATH).sha256"; \
|
||||
echo "📥 下载 x86_64 版本..."; \
|
||||
curl -fSL -o "$(LIBREOFFICE_X86_PATH)" "$(LIBREOFFICE_X86_URL)" 2>/dev/null || \
|
||||
curl -fSL -L -o "$(LIBREOFFICE_X86_PATH)" "$(LIBREOFFICE_X86_FALLBACK)" 2>/dev/null || { \
|
||||
rm -f "$(LIBREOFFICE_X86_PATH)"; echo "❌ x86_64 下载失败"; exit 1; }; \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_X86_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
echo "$$CALC_HASH" > "$(LIBREOFFICE_X86_PATH).sha256"; \
|
||||
echo "✅ x86_64 下载完成: $$CALC_HASH"; \
|
||||
fi; \
|
||||
else \
|
||||
echo "📥 下载 x86_64 版本..."; \
|
||||
curl -fSL -o "$(LIBREOFFICE_X86_PATH)" "$(LIBREOFFICE_X86_URL)" 2>/dev/null || \
|
||||
curl -fSL -L -o "$(LIBREOFFICE_X86_PATH)" "$(LIBREOFFICE_X86_FALLBACK)" 2>/dev/null || { \
|
||||
rm -f "$(LIBREOFFICE_X86_PATH)"; echo "❌ x86_64 下载失败"; exit 1; }; \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_X86_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
echo "$$CALC_HASH" > "$(LIBREOFFICE_X86_PATH).sha256"; \
|
||||
echo "✅ x86_64 下载完成: $$CALC_HASH"; \
|
||||
fi
|
||||
@# ========== 下载 aarch64 ==========
|
||||
@echo ""; echo "=== 检查 aarch64 版本 ==="; \
|
||||
if [ -f "$(LIBREOFFICE_ARM_PATH)" ] && [ -f "$(LIBREOFFICE_ARM_PATH).sha256" ]; then \
|
||||
SAVED_HASH=$$(cat "$(LIBREOFFICE_ARM_PATH).sha256"); \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_ARM_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
if [ "$$SAVED_HASH" = "$$CALC_HASH" ]; then \
|
||||
echo "✅ aarch64 已存在且有效,跳过下载"; \
|
||||
else \
|
||||
echo "❌ aarch64 Hash 不匹配,重新下载..."; \
|
||||
rm -f "$(LIBREOFFICE_ARM_PATH)" "$(LIBREOFFICE_ARM_PATH).sha256"; \
|
||||
echo "📥 下载 aarch64 版本..."; \
|
||||
curl -fSL -o "$(LIBREOFFICE_ARM_PATH)" "$(LIBREOFFICE_ARM_URL)" 2>/dev/null || \
|
||||
curl -fSL -L -o "$(LIBREOFFICE_ARM_PATH)" "$(LIBREOFFICE_ARM_FALLBACK)" 2>/dev/null || { \
|
||||
rm -f "$(LIBREOFFICE_ARM_PATH)"; echo "❌ aarch64 下载失败"; exit 1; }; \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_ARM_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
echo "$$CALC_HASH" > "$(LIBREOFFICE_ARM_PATH).sha256"; \
|
||||
echo "✅ aarch64 下载完成: $$CALC_HASH"; \
|
||||
fi; \
|
||||
else \
|
||||
echo "📥 下载 aarch64 版本..."; \
|
||||
curl -fSL -o "$(LIBREOFFICE_ARM_PATH)" "$(LIBREOFFICE_ARM_URL)" 2>/dev/null || \
|
||||
curl -fSL -L -o "$(LIBREOFFICE_ARM_PATH)" "$(LIBREOFFICE_ARM_FALLBACK)" 2>/dev/null || { \
|
||||
rm -f "$(LIBREOFFICE_ARM_PATH)"; echo "❌ aarch64 下载失败"; exit 1; }; \
|
||||
CALC_HASH=$$(sha256sum "$(LIBREOFFICE_ARM_PATH)" 2>/dev/null | cut -d' ' -f1); \
|
||||
echo "$$CALC_HASH" > "$(LIBREOFFICE_ARM_PATH).sha256"; \
|
||||
echo "✅ aarch64 下载完成: $$CALC_HASH"; \
|
||||
fi
|
||||
@echo ""; echo "✅ LibreOffice $(LIBREOFFICE_VERSION) 下载检查完成"
|
||||
|
||||
# 清理 LibreOffice 下载文件
|
||||
docker-clean-libreoffice-downloads:
|
||||
@echo "🗑️ 清理 LibreOffice 下载文件..."
|
||||
@rm -rf $(LIBREOFFICE_DOWNLOAD_DIR)
|
||||
@echo "✅ LibreOffice 下载文件已清理"
|
||||
|
||||
# 构建 agent-base 基础镜像(包含所有系统依赖,很少需要重新构建)
|
||||
docker-build-agent-base: docker-pre-download-libreoffice
|
||||
@echo "🐳 构建 rcoder-agent-base 基础镜像..."
|
||||
@echo "📍 镜像名称: rcoder-agent-base:latest"
|
||||
@echo "📦 LibreOffice: $(LIBREOFFICE_VERSION) ($(LIBREOFFICE_ARCH))"
|
||||
@echo "⏳ 这可能需要较长时间(包含所有系统依赖安装)..."
|
||||
@# CACHEBUST_NOVNC: 传入时间戳强制每次重新克隆 noVNC
|
||||
@# 使用 --platform 确保 TARGETARCH 正确传递给 Dockerfile.base
|
||||
@cd docker/rcoder-agent-runner && \
|
||||
docker buildx build --platform linux/$(LIBREOFFICE_ARCH) --load \
|
||||
--build-arg LIBREOFFICE_FILE=$(LIBREOFFICE_FILE) \
|
||||
--build-arg CACHEBUST_NOVNC=$$(date +%s) \
|
||||
-f Dockerfile.base -t rcoder-agent-base:latest .
|
||||
@echo "✅ rcoder-agent-base 基础镜像构建完成!"
|
||||
@echo "📤 推送基础镜像到阿里云仓库..."
|
||||
@docker tag rcoder-agent-base:latest nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-base:latest
|
||||
@skopeo copy docker-daemon:nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-base:latest docker://nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-base:latest
|
||||
@echo "✅ 基础镜像已推送: nuwax-docker-images-registry.cn-hangzhou.cr.aliyuncs.com/dev/rcoder-agent-base:latest"
|
||||
@echo "💡 提示: 平时开发只需运行 make dev-restart,无需重新构建基础镜像"
|
||||
31
qiming-rcoder/make/pyroscope.mk
Normal file
31
qiming-rcoder/make/pyroscope.mk
Normal file
@@ -0,0 +1,31 @@
|
||||
# ============================================================================
|
||||
# 📊 Pyroscope 持续剖析服务管理
|
||||
# ============================================================================
|
||||
|
||||
# 启动 Pyroscope Server
|
||||
pyroscope-up:
|
||||
@echo "🚀 启动 Pyroscope Server..."
|
||||
@if [ ! -f "docker/docker-compose.yml" ]; then \
|
||||
echo "❌ 错误: 未找到 docker/docker-compose.yml"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@docker-compose -f docker/docker-compose.yml up -d pyroscope
|
||||
@echo ""
|
||||
@echo "✅ Pyroscope Server 已启动!"
|
||||
@echo "📊 Web UI: http://localhost:4040"
|
||||
@echo "💡 提示: 等待 agent_runner 容器启动并连接到 Pyroscope"
|
||||
|
||||
# 停止 Pyroscope Server
|
||||
pyroscope-down:
|
||||
@echo "🛑 停止 Pyroscope Server..."
|
||||
@if [ -f "docker/docker-compose.yml" ]; then \
|
||||
docker-compose -f docker/docker-compose.yml stop pyroscope || true; \
|
||||
else \
|
||||
echo "⚠️ docker-compose.yml 未找到"; \
|
||||
fi
|
||||
@echo "✅ Pyroscope Server 已停止"
|
||||
|
||||
# 查看 Pyroscope 日志
|
||||
pyroscope-logs:
|
||||
@echo "📋 Pyroscope Server 日志:"
|
||||
@docker logs -f rcoder-pyroscope 2>/dev/null || echo "❌ Pyroscope 容器未运行,请先执行 make pyroscope-up"
|
||||
90
qiming-rcoder/make/test.mk
Normal file
90
qiming-rcoder/make/test.mk
Normal file
@@ -0,0 +1,90 @@
|
||||
# ============================================================================
|
||||
# 测试命令
|
||||
# ============================================================================
|
||||
|
||||
# 运行所有测试
|
||||
test:
|
||||
@echo "🧪 运行所有测试..."
|
||||
@cargo test --workspace
|
||||
|
||||
# 运行单元测试
|
||||
test-unit:
|
||||
@echo "🧪 运行单元测试..."
|
||||
@cargo test --workspace --lib
|
||||
|
||||
# 运行集成测试
|
||||
test-integration:
|
||||
@echo "🧪 运行集成测试..."
|
||||
@cargo test --workspace --test '*'
|
||||
|
||||
# 运行极端场景测试(包含阻塞)
|
||||
test-blocking:
|
||||
@echo "🧪 运行极端场景测试(包含阻塞)..."
|
||||
@cargo test --workspace --features testing --test '*_blocking*' -- --test-threads=1
|
||||
|
||||
# 运行完整测试套件
|
||||
test-all:
|
||||
@echo "🧪 运行完整测试套件..."
|
||||
@cargo test --workspace --all-features
|
||||
|
||||
# ============================================================================
|
||||
# 🧪 eBPF 工具安装测试(快速验证 Makefile 变量传递)
|
||||
# ============================================================================
|
||||
|
||||
# 测试 1: 模拟 Makefile 变量传递(启用 eBPF)
|
||||
test-ebpf-install:
|
||||
@echo "🧪 测试 1: 启用 eBPF 工具安装..."
|
||||
@(if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
INSTALL_EBPF="true"; \
|
||||
echo "✅ CARGO_FEATURES=[$(CARGO_FEATURES)], INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
else \
|
||||
INSTALL_EBPF="false"; \
|
||||
echo "⚠️ CARGO_FEATURES=[$(CARGO_FEATURES)], INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
fi; \
|
||||
cd docker/rcoder-agent-runner && \
|
||||
docker build --build-arg INSTALL_EBPF_TOOLS="$${INSTALL_EBPF}" \
|
||||
-f Dockerfile.test -t test-ebpf-install . 2>&1 | tail -20; \
|
||||
docker run --rm test-ebpf-install which bpftrace && echo "✅ 测试通过: bpftrace 已安装" || echo "❌ 测试失败: bpftrace 未安装")
|
||||
|
||||
# 测试 2: 模拟生产模式(禁用 eBPF)
|
||||
test-ebpf-no-install:
|
||||
@echo "🧪 测试 2: 禁用 eBPF 工具安装(生产模式)..."
|
||||
@(INSTALL_EBPF="false"; \
|
||||
echo "🔒 INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
cd docker/rcoder-agent-runner && \
|
||||
docker build --build-arg INSTALL_EBPF_TOOLS="$${INSTALL_EBPF}" \
|
||||
-f Dockerfile.test -t test-ebpf-no-install . 2>&1 | tail -20; \
|
||||
docker run --rm test-ebpf-no-install which bpftrace && echo "❌ 测试失败: 生产模式不应安装 bpftrace" || echo "✅ 测试通过: 生产模式正确跳过安装")
|
||||
|
||||
# 测试 3: 直接测试变量传递(调试用)
|
||||
test-ebpf-debug:
|
||||
@echo "🧪 测试 3: 变量传递调试..."
|
||||
@echo "CARGO_FEATURES=[$(CARGO_FEATURES)]"
|
||||
@(if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
INSTALL_EBPF="true"; \
|
||||
echo "Shell: INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
echo "Docker: INSTALL_EBPF_TOOLS=\"$${INSTALL_EBPF}\""; \
|
||||
else \
|
||||
INSTALL_EBPF="false"; \
|
||||
echo "Shell: INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
echo "Docker: INSTALL_EBPF_TOOLS=\"$${INSTALL_EBPF}\""; \
|
||||
fi)
|
||||
|
||||
# 测试 4: 完整测试 Pyroscope + Off-CPU 工具
|
||||
test-pyroscope-offcpu:
|
||||
@echo "🧪 测试 4: Pyroscope Agent + Off-CPU 工具完整测试..."
|
||||
@(if [ "$(CARGO_FEATURES)" != "" ]; then \
|
||||
INSTALL_EBPF="true"; \
|
||||
echo "✅ CARGO_FEATURES=[$(CARGO_FEATURES)], INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
else \
|
||||
INSTALL_EBPF="false"; \
|
||||
echo "⚠️ CARGO_FEATURES=[$(CARGO_FEATURES)], INSTALL_EBPF=$${INSTALL_EBPF}"; \
|
||||
fi; \
|
||||
cd docker/rcoder-agent-runner && \
|
||||
docker build --build-arg INSTALL_EBPF_TOOLS="$${INSTALL_EBPF}" \
|
||||
--build-arg INSTALL_PYROSCOPE="$${INSTALL_EBPF}" \
|
||||
-f Dockerfile.test-full -t test-pyroscope-offcpu . 2>&1 | tail -30; \
|
||||
echo "=== 验证 pyroscope ===" && \
|
||||
docker run --rm test-pyroscope-offcpu which pyroscope && echo "✅ pyroscope 已安装" || echo "❌ pyroscope 未安装"; \
|
||||
echo "=== 验证 offcputime-bpfcc ===" && \
|
||||
docker run --rm test-pyroscope-offcpu which offcputime-bpfcc && echo "✅ offcputime-bpfcc 已安装" || echo "❌ offcputime-bpfcc 未安装")
|
||||
Reference in New Issue
Block a user