fix: 将 k3s-ansible 作为普通目录添加

This commit is contained in:
fei
2026-02-04 23:43:40 +08:00
commit 7f6c8b9b92
40 changed files with 10909 additions and 0 deletions

521
scripts/complete-automation.sh Executable file
View File

@@ -0,0 +1,521 @@
#!/bin/bash
# JPD集群完整自动化配置脚本
# 包括Ingress配置、cert-manager、ArgoCD配置、测试应用部署
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 JPD集群完整自动化配置"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# ============================================
# 步骤 1: 配置Gitea Ingress
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 1/6: 配置Gitea Ingress"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: gitea
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
ingressClassName: traefik
rules:
- host: git.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
echo "✅ Gitea Ingress配置完成"
echo " 访问地址: http://git.jpd.net3w.com"
echo ""
# ============================================
# 步骤 2: 配置ArgoCD访问
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 2/6: 配置ArgoCD访问"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 配置ArgoCD为NodePort
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
# 创建ArgoCD Ingress
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: argocd-stripprefix@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: argocd.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
ARGOCD_PORT=$(kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.ports[0].nodePort}')
ARGOCD_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo "✅ ArgoCD访问配置完成"
echo " NodePort访问: http://149.13.91.216:$ARGOCD_PORT"
echo " 域名访问: http://argocd.jpd.net3w.com"
echo " 用户名: admin"
echo " 密码: $ARGOCD_PASSWORD"
echo ""
# ============================================
# 步骤 3: 部署cert-manager
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 3/6: 部署cert-manager"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
echo "⏳ 等待cert-manager就绪..."
sleep 30
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=300s || true
kubectl wait --for=condition=ready pod -l app=webhook -n cert-manager --timeout=300s || true
# 创建Let's Encrypt ClusterIssuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@jpd.net3w.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
EOF
echo "✅ cert-manager部署完成"
echo ""
# ============================================
# 步骤 4: 配置HTTPS Ingress
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 4/6: 配置HTTPS Ingress"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 更新Gitea Ingress支持HTTPS
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-https
namespace: gitea
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- git.jpd.net3w.com
secretName: gitea-tls
rules:
- host: git.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
# 更新ArgoCD Ingress支持HTTPS
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-https
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- argocd.jpd.net3w.com
secretName: argocd-server-tls
rules:
- host: argocd.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
echo "✅ HTTPS Ingress配置完成"
echo " Gitea HTTPS: https://git.jpd.net3w.com"
echo " ArgoCD HTTPS: https://argocd.jpd.net3w.com"
echo ""
# ============================================
# 步骤 5: 部署测试应用
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 5/6: 部署测试应用"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 创建测试应用命名空间
kubectl create namespace demo-app --dry-run=client -o yaml | kubectl apply -f -
# 部署Nginx测试应用
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo-app
labels:
app: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: nginx-html
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-html
namespace: demo-app
data:
index.html: |
<!DOCTYPE html>
<html>
<head>
<title>JPD集群测试应用</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
text-align: center;
max-width: 600px;
}
h1 {
color: #667eea;
margin-bottom: 20px;
}
.status {
background: #10b981;
color: white;
padding: 10px 20px;
border-radius: 5px;
display: inline-block;
margin: 20px 0;
}
.info {
text-align: left;
background: #f3f4f6;
padding: 20px;
border-radius: 5px;
margin-top: 20px;
}
.info p {
margin: 10px 0;
}
.emoji {
font-size: 48px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="emoji">🚀</div>
<h1>JPD K3s集群测试应用</h1>
<div class="status">✅ 运行正常</div>
<div class="info">
<p><strong>集群名称:</strong> JPD Cluster</p>
<p><strong>部署方式:</strong> Kubernetes Deployment</p>
<p><strong>副本数:</strong> 3</p>
<p><strong>容器镜像:</strong> nginx:alpine</p>
<p><strong>访问域名:</strong> demo.jpd.net3w.com</p>
<p><strong>GitOps工具:</strong> ArgoCD</p>
<p><strong>Git仓库:</strong> Gitea</p>
</div>
<p style="margin-top: 20px; color: #6b7280;">
主机名: <span id="hostname">加载中...</span>
</p>
</div>
<script>
fetch('/hostname.txt')
.then(r => r.text())
.then(h => document.getElementById('hostname').textContent = h)
.catch(() => document.getElementById('hostname').textContent = '未知');
</script>
</body>
</html>
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo-app
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo
namespace: demo-app
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
ingressClassName: traefik
rules:
- host: demo.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo-https
namespace: demo-app
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- demo.jpd.net3w.com
secretName: nginx-demo-tls
rules:
- host: demo.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
EOF
echo "⏳ 等待测试应用就绪..."
kubectl wait --for=condition=ready pod -l app=nginx-demo -n demo-app --timeout=120s
echo "✅ 测试应用部署完成"
echo " 访问地址: http://demo.jpd.net3w.com"
echo " HTTPS访问: https://demo.jpd.net3w.com"
echo ""
# ============================================
# 步骤 6: 部署自动化测试
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 6/6: 部署自动化测试"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 创建自动化测试CronJob
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
name: health-check
namespace: demo-app
spec:
schedule: "*/5 * * * *" # 每5分钟运行一次
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: curl
image: curlimages/curl:latest
command:
- /bin/sh
- -c
- |
echo "=== 健康检查开始 ==="
echo "时间: \$(date)"
echo ""
# 测试Gitea
echo "测试 Gitea..."
if curl -f -s http://gitea-http.gitea.svc.cluster.local:3000 > /dev/null; then
echo "✅ Gitea: 正常"
else
echo "❌ Gitea: 异常"
exit 1
fi
# 测试ArgoCD
echo "测试 ArgoCD..."
if curl -f -s -k http://argocd-server.argocd.svc.cluster.local > /dev/null; then
echo "✅ ArgoCD: 正常"
else
echo "❌ ArgoCD: 异常"
exit 1
fi
# 测试Demo应用
echo "测试 Demo应用..."
if curl -f -s http://nginx-demo.demo-app.svc.cluster.local > /dev/null; then
echo "✅ Demo应用: 正常"
else
echo "❌ Demo应用: 异常"
exit 1
fi
echo ""
echo "=== 所有服务健康检查通过 ==="
restartPolicy: OnFailure
EOF
# 立即运行一次测试
kubectl create job --from=cronjob/health-check health-check-manual -n demo-app || true
echo "✅ 自动化测试部署完成"
echo " 测试频率: 每5分钟"
echo " 查看测试日志: kubectl logs -n demo-app -l job-name=health-check-manual"
echo ""
# ============================================
# 最终状态检查
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 部署完成!最终状态"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 集群资源:"
kubectl get nodes -o wide
echo ""
echo "📦 所有Pod:"
kubectl get pods --all-namespaces | grep -E "NAMESPACE|Running|Completed"
echo ""
echo "🌐 所有Ingress:"
kubectl get ingress --all-namespaces
echo ""
echo "🔐 访问信息:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Gitea:"
echo " HTTP: http://git.jpd.net3w.com"
echo " HTTPS: https://git.jpd.net3w.com"
echo " 用户名: gitea_admin"
echo " 密码: GitAdmin@2026"
echo ""
echo "ArgoCD:"
echo " HTTP: http://argocd.jpd.net3w.com"
echo " HTTPS: https://argocd.jpd.net3w.com"
echo " NodePort: http://149.13.91.216:$ARGOCD_PORT"
echo " 用户名: admin"
echo " 密码: $ARGOCD_PASSWORD"
echo ""
echo "测试应用:"
echo " HTTP: http://demo.jpd.net3w.com"
echo " HTTPS: https://demo.jpd.net3w.com"
echo ""
echo "💡 提示:"
echo " - HTTPS证书需要1-2分钟签发"
echo " - 自动化测试每5分钟运行一次"
echo " - 查看测试日志: kubectl logs -n demo-app -l job-name=health-check-manual"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

52
scripts/create-argocd-app.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 创建ArgoCD Application ==="
# 读取配置
GIT_REPO=$(yq eval '.git_repo_url' "$CONFIG_FILE")
GIT_USERNAME=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GIT_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
# 配置Gitea仓库凭证
echo "🔐 配置Gitea仓库凭证..."
kubectl create secret generic gitea-creds \
-n argocd \
--from-literal=username="$GIT_USERNAME" \
--from-literal=password="$GIT_PASSWORD" \
--dry-run=client -o yaml | kubectl apply -f -
# 生成Application配置
cat > /tmp/argocd-app.yaml <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-app
namespace: argocd
spec:
project: default
source:
repoURL: $GIT_REPO
targetRevision: main
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF
# 应用配置
kubectl apply -f /tmp/argocd-app.yaml
echo "✅ ArgoCD Application创建成功"
echo "📊 查看状态: kubectl get application -n argocd"
echo "🌐 访问ArgoCD查看同步状态"

View File

@@ -0,0 +1,106 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 创建Nginx应用的ArgoCD Application ==="
# 读取配置
NGINX_GIT_REPO=$(yq eval '.nginx_app_git_repo_url' "$CONFIG_FILE")
GIT_USERNAME=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GIT_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
# 配置Gitea仓库凭证如果不存在
echo "🔐 配置Gitea仓库凭证..."
kubectl create secret generic gitea-creds \
-n argocd \
--from-literal=username="$GIT_USERNAME" \
--from-literal=password="$GIT_PASSWORD" \
--dry-run=client -o yaml | kubectl apply -f -
# 生成Application配置
cat > /tmp/nginx-argocd-app.yaml <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-app
namespace: argocd
labels:
app: nginx-test
spec:
project: default
source:
repoURL: $NGINX_GIT_REPO
targetRevision: main
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# 健康检查配置
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
EOF
# 应用配置
echo "📝 创建ArgoCD Application..."
kubectl apply -f /tmp/nginx-argocd-app.yaml
echo ""
echo "✅ ArgoCD Application创建成功"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 查看状态:"
echo " kubectl get application nginx-app -n argocd"
echo ""
echo "📝 查看详细信息:"
echo " kubectl describe application nginx-app -n argocd"
echo ""
echo "🌐 访问ArgoCD查看同步状态:"
echo " https://argocd.jpc.net3w.com"
echo ""
echo "⏳ 等待同步完成约3分钟..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 等待并监控同步状态
echo ""
echo "🔍 监控同步状态..."
for i in {1..12}; do
sleep 5
STATUS=$(kubectl get application nginx-app -n argocd -o jsonpath='{.status.sync.status}' 2>/dev/null || echo "Unknown")
HEALTH=$(kubectl get application nginx-app -n argocd -o jsonpath='{.status.health.status}' 2>/dev/null || echo "Unknown")
echo "[$i/12] Sync: $STATUS | Health: $HEALTH"
if [ "$STATUS" = "Synced" ] && [ "$HEALTH" = "Healthy" ]; then
echo ""
echo "✅ 同步完成!应用已成功部署"
break
fi
done
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 部署完成!"
echo ""
echo "📊 验证部署:"
echo " kubectl get pods -l app=nginx-test -n default"
echo " kubectl get svc nginx-test -n default"
echo " kubectl get ingress nginx-test -n default"
echo ""
echo "🌐 访问应用:"
echo " https://ng.jpc.net3w.com"
echo ""
echo "💡 提示:"
echo " - 修改Git仓库中的配置ArgoCD会自动同步"
echo " - 查看ArgoCD UI了解详细的同步状态"
echo " - 首次HTTPS访问需等待证书签发1-2分钟"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

243
scripts/deploy-all-on-master.sh Executable file
View File

@@ -0,0 +1,243 @@
#!/bin/bash
# JPD集群完整部署脚本 - 在Master节点上运行
# 使用方法: bash deploy-all-on-master.sh
set -e
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 JPD集群GitOps自动化部署"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 检查是否在master节点上
if ! command -v kubectl &> /dev/null; then
echo "❌ kubectl未找到请确保在K3s master节点上运行此脚本"
exit 1
fi
# 配置kubectl
echo "📝 配置kubectl..."
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
sudo chmod 644 /etc/rancher/k3s/k3s.yaml
# 验证集群
echo "🔍 验证集群状态..."
kubectl get nodes -o wide
echo ""
# 检查Helm
if ! command -v helm &> /dev/null; then
echo "📦 安装Helm..."
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
echo "✅ Helm安装完成"
else
echo "✅ Helm已安装"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 1/4: 部署Gitea"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 添加Gitea Helm仓库
echo "📝 添加Gitea Helm仓库..."
helm repo add gitea-charts https://dl.gitea.com/charts/
helm repo update
# 创建gitea命名空间
echo "📝 创建gitea命名空间..."
kubectl create namespace gitea --dry-run=client -o yaml | kubectl apply -f -
# 部署Gitea
echo "🚀 部署Gitea..."
helm upgrade --install gitea gitea-charts/gitea \
--namespace gitea \
--set gitea.admin.username=gitea_admin \
--set gitea.admin.password=GitAdmin@2026 \
--set gitea.admin.email=admin@jpd.net3w.com \
--set service.http.type=NodePort \
--set service.http.nodePort=30080 \
--set postgresql-ha.enabled=true \
--set redis-cluster.enabled=true \
--wait --timeout=10m
echo "✅ Gitea部署完成"
echo ""
# 等待Gitea就绪
echo "⏳ 等待Gitea Pod就绪..."
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=gitea -n gitea --timeout=300s
# 获取Gitea访问信息
GITEA_PORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
echo "✅ Gitea访问地址: http://$NODE_IP:$GITEA_PORT"
echo " 域名访问: http://git.jpd.net3w.com"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 2/4: 部署ArgoCD"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 创建argocd命名空间
echo "📝 创建argocd命名空间..."
kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f -
# 部署ArgoCD
echo "🚀 部署ArgoCD..."
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 等待ArgoCD就绪
echo "⏳ 等待ArgoCD Pod就绪..."
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=300s
# 修改ArgoCD服务为NodePort
echo "📝 配置ArgoCD NodePort..."
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
# 获取ArgoCD访问信息
ARGOCD_PORT=$(kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.ports[0].nodePort}')
ARGOCD_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo "✅ ArgoCD部署完成"
echo " 访问地址: https://$NODE_IP:$ARGOCD_PORT"
echo " 域名访问: https://argocd.jpd.net3w.com"
echo " 用户名: admin"
echo " 密码: $ARGOCD_PASSWORD"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 3/4: 部署cert-manager"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 部署cert-manager
echo "🚀 部署cert-manager..."
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# 等待cert-manager就绪
echo "⏳ 等待cert-manager Pod就绪..."
kubectl wait --for=condition=ready pod -l app=cert-manager -n cert-manager --timeout=300s
kubectl wait --for=condition=r app=webhook -n cert-manager --timeout=300s
# 创建Let's Encrypt ClusterIssuer
echo "📝 配置Let's Encrypt..."
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@jpd.net3w.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
EOF
echo "✅ cert-manager部署完成"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 4/4: 配置Ingress"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 创建Gitea Ingress
echo "📝 创建Gitea Ingress..."
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: gitea
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: traefik
tls:
- hosts:
- git.jpd.net3w.com
secretName: gitea-tls
rules:
- host: git.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
# 创建ArgoCD Ingress
echo "📝 创建ArgoCD I"
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: traefik
tls:
- hosts:
- argocd.jpd.net3w.com
secretName: argocd-server-tls
rules:
- host: argocd.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
EOF
echo "✅ Ingress配置完成"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 部署完成!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 部署摘要:"
echo " ✅ Gitea: http://git.jpd.net3w.com"
echo " ✅ ArgoCD: https://argocd.jpd.net3w.com"
echo " ✅ cert-manager: 已配置Let's Encrypt"
echo ""
echo "🔑 访问凭证:"
echo " Gitea:"
echo " - 用户名: gitea_admin"
echo " - 密码: GitAdmin@2026"
echo ""
echo " ArgoCD:"
echo " - 用户名: admin"
echo " - 密码: $ARGOCD_PASSWORD"
echo ""
echo "📝 验证命令:"
echo " kubectl get pods --all-namespaces"
echo " kubectl get ingress --all-namespaces"
echo " kubectl get certificate --all-namespaces"
echo ""
echo "💡 提示:"
echo " - 确保DNS已配置: *.jpd.net3w.com -> 149.13.91.216"
echo " - 首次HTTPS访问需等待1-2分钟证书签发"
echo " - 可以通过NodePort直接访问服务"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

333
scripts/deploy-all.sh Executable file
View File

@@ -0,0 +1,333 @@
#!/bin/bash
set -euo pipefail
# Load common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# Source common library
source "$SCRIPT_DIR/lib/common.sh"
# Configuration
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
# Step definitions
STEPS=(
"check_prerequisites:检查前置条件"
"generate_inventory:生成Ansible Inventory"
"deploy_k3s:部署K3s集群"
"deploy_gitea:部署Gitea"
"setup_gitea:初始化Gitea"
"deploy_argocd:部署ArgoCD"
"deploy_https:配置HTTPS"
"create_demo_app:创建示例应用"
)
# Step functions
check_prerequisites() {
log_step "检查前置条件"
# Check configuration file
check_config_file "$CONFIG_FILE" || return 1
# Check required tools
check_required_tools || return 1
# Check network connectivity
check_network_with_retry "https://www.google.com" 3 || {
log_warn "Network connectivity check failed, but continuing..."
}
# Install Python YAML library
if ! python3 -c "import yaml" 2>/dev/null; then
log "Installing python3-yaml..."
sudo apt update && sudo apt install -y python3-yaml
fi
log "✓ All prerequisites checked"
return 0
}
generate_inventory() {
log_step "生成Ansible Inventory"
if [ ! -f "$SCRIPT_DIR/generate-inventory.py" ]; then
log_error "generate-inventory.py not found"
return 1
fi
cd "$PROJECT_DIR"
python3 "$SCRIPT_DIR/generate-inventory.py" || return 1
log "✓ Ansible inventory generated"
return 0
}
deploy_k3s() {
log_step "部署K3s集群"
if [ ! -d "$PROJECT_DIR/k3s-ansible" ]; then
log "Cloning k3s-ansible repository..."
cd "$PROJECT_DIR"
git clone https://github.com/k3s-io/k3s-ansible.git || return 1
fi
# Check if kubectl is already available and cluster is running
if check_kubectl; then
log "K3s cluster is already running, skipping deployment"
return 0
fi
log "Running Ansible playbook..."
cd "$PROJECT_DIR/k3s-ansible"
ansible-playbook site.yml \
-i inventory/hosts.ini \
-e "@$CONFIG_FILE" || return 1
# Configure kubectl
log "Configuring kubectl..."
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
# Verify cluster
log "Verifying cluster..."
sleep 10
kubectl get nodes || return 1
log "✓ K3s cluster deployed successfully"
return 0
}
deploy_gitea() {
log_step "部署Gitea"
if [ ! -f "$SCRIPT_DIR/deploy-gitea.sh" ]; then
log_error "deploy-gitea.sh not found"
return 1
fi
# Check if Gitea is already deployed
if kubectl get namespace gitea &>/dev/null && \
kubectl get deployment gitea -n gitea &>/dev/null 2>&1; then
log "Gitea is already deployed, skipping"
return 0
fi
bash "$SCRIPT_DIR/deploy-gitea.sh" || return 1
log "✓ Gitea deployed successfully"
return 0
}
setup_gitea() {
log_step "初始化Gitea"
if [ ! -f "$SCRIPT_DIR/setup-gitea.sh" ]; then
log_error "setup-gitea.sh not found"
return 1
fi
bash "$SCRIPT_DIR/setup-gitea.sh" || return 1
log "✓ Gitea initialized successfully"
return 0
}
deploy_argocd() {
log_step "部署ArgoCD"
if [ ! -f "$SCRIPT_DIR/deploy-argocd.sh" ]; then
log_error "deploy-argocd.sh not found"
return 1
fi
# Check if ArgoCD is already deployed
if kubectl get namespace argocd &>/dev/null && \
kubectl get deployment argocd-server -n argocd &>/dev/null 2>&1; then
log "ArgoCD is already deployed, skipping"
return 0
fi
bash "$SCRIPT_DIR/deploy-argocd.sh" || return 1
log "✓ ArgoCD deployed successfully"
return 0
}
deploy_https() {
log_step "配置HTTPS"
if [ ! -f "$SCRIPT_DIR/deploy-https.sh" ]; then
log_warn "deploy-https.sh not found, skipping HTTPS configuration"
return 0
fi
bash "$SCRIPT_DIR/deploy-https.sh" || {
log_warn "HTTPS configuration failed, but continuing..."
return 0
}
log "✓ HTTPS configured successfully"
return 0
}
create_demo_app() {
log_step "创建示例应用"
if [ ! -f "$SCRIPT_DIR/create-argocd-app.sh" ]; then
log_warn "create-argocd-app.sh not found, skipping demo app creation"
return 0
fi
bash "$SCRIPT_DIR/create-argocd-app.sh" || {
log_warn "Demo app creation failed, but continuing..."
return 0
}
log "✓ Demo app created successfully"
return 0
}
# Execute step
execute_step() {
local step_name="$1"
if type "$step_name" &>/dev/null; then
"$step_name"
return $?
else
log_error "Step function not found: $step_name"
return 1
fi
}
# Main function
main() {
echo "=========================================="
echo " K3s集群自动化部署"
echo "=========================================="
echo ""
log "开始部署流程"
log "日志文件: $LOG_FILE"
log "状态文件: $STATE_FILE"
echo ""
local failed_steps=()
local completed_steps=()
local skipped_steps=()
for step in "${STEPS[@]}"; do
step_name="${step%%:*}"
step_desc="${step##*:}"
echo ""
echo "=========================================="
if is_step_completed "$step_name"; then
log "✓ 跳过已完成的步骤: $step_desc"
skipped_steps+=("$step_desc")
continue
fi
log_step "执行步骤: $step_desc"
if execute_step "$step_name"; then
mark_step_completed "$step_name"
log "✓ 完成: $step_desc"
completed_steps+=("$step_desc")
else
log_error "✗ 失败: $step_desc"
failed_steps+=("$step_desc")
echo ""
echo "=========================================="
echo " 部署失败"
echo "=========================================="
echo ""
log_error "步骤失败: $step_desc"
log_error "请检查日志文件: $LOG_FILE"
log_error "修复问题后,可以重新运行此脚本继续部署"
echo ""
print_summary
echo "已完成步骤: ${#completed_steps[@]}"
for s in "${completed_steps[@]}"; do
echo "$s"
done
echo ""
echo "跳过步骤: ${#skipped_steps[@]}"
for s in "${skipped_steps[@]}"; do
echo " - $s"
done
echo ""
echo "失败步骤: ${#failed_steps[@]}"
for s in "${failed_steps[@]}"; do
echo "$s"
done
echo ""
exit 1
fi
done
echo ""
echo "=========================================="
echo " 部署完成!"
echo "=========================================="
echo ""
print_summary
echo "总步骤数: ${#STEPS[@]}"
echo "已完成: ${#completed_steps[@]}"
echo "已跳过: ${#skipped_steps[@]}"
echo ""
if [ ${#completed_steps[@]} -gt 0 ]; then
echo "本次完成的步骤:"
for s in "${completed_steps[@]}"; do
echo "$s"
done
echo ""
fi
if [ ${#skipped_steps[@]} -gt 0 ]; then
echo "跳过的步骤:"
for s in "${skipped_steps[@]}"; do
echo " - $s"
done
echo ""
fi
log "✓ K3s集群部署完成"
echo ""
echo "下一步操作:"
echo " 1. 验证部署: ./scripts/verify-deployment.sh"
echo " 2. 查看集群状态: kubectl get nodes"
echo " 3. 查看所有Pod: kubectl get pods -A"
echo ""
}
# Handle script arguments
case "${1:-}" in
--reset)
log "重置部署状态..."
reset_deployment_state
log "状态已重置,可以重新开始部署"
exit 0
;;
--help|-h)
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " --reset 重置部署状态,从头开始"
echo " --help 显示此帮助信息"
echo ""
exit 0
;;
esac
# Run main function
main

135
scripts/deploy-argocd.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
# Source common library if available
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
source "$SCRIPT_DIR/lib/common.sh"
else
# Fallback logging functions
log() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
log_warn() { echo "[WARN] $1"; }
fi
log "=== 部署ArgoCD ==="
# Check and install required tools
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
ensure_yq || exit 1
ensure_htpasswd || exit 1
else
# Fallback: Install yq with retry
if ! command -v yq &> /dev/null; then
log "安装yq..."
for attempt in 1 2 3; do
if sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && \
sudo chmod +x /usr/local/bin/yq; then
log "✓ yq安装成功"
break
else
log_warn "yq安装失败 (尝试 $attempt/3)"
[ $attempt -lt 3 ] && sleep 5
fi
done
if ! command -v yq &> /dev/null; then
log_error "yq安装失败请手动安装"
exit 1
fi
fi
# Install htpasswd if not present
if ! command -v htpasswd &> /dev/null; then
log "安装htpasswd (apache2-utils)..."
if sudo apt update && sudo apt install -y apache2-utils; then
log "✓ htpasswd安装成功"
else
log_error "htpasswd安装失败请手动安装: sudo apt install apache2-utils"
exit 1
fi
fi
fi
# 读取配置变量
ARGOCD_DOMAIN=$(yq eval '.argocd_domain' "$CONFIG_FILE")
ARGOCD_PASSWORD=$(yq eval '.argocd_admin_password' "$CONFIG_FILE")
# 创建命名空间
kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f -
# 安装ArgoCD with retry
log "安装ArgoCD..."
ARGOCD_MANIFEST_URL="https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml"
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
retry 3 5 "kubectl apply -n argocd -f $ARGOCD_MANIFEST_URL" || {
log_error "ArgoCD安装失败"
exit 1
}
else
for attempt in 1 2 3; do
if kubectl apply -n argocd -f "$ARGOCD_MANIFEST_URL"; then
log "✓ ArgoCD清单应用成功"
break
else
log_warn "ArgoCD清单应用失败 (尝试 $attempt/3)"
[ $attempt -lt 3 ] && sleep 5
fi
done
fi
# 等待就绪
log "等待ArgoCD就绪..."
kubectl wait --for=condition=available --timeout=600s deployment/argocd-server -n argocd || {
log_error "ArgoCD部署超时"
log_error "请检查: kubectl get pods -n argocd"
exit 1
}
# 配置NodePort访问
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}' || {
log_warn "NodePort配置可能已存在"
}
# 更新admin密码
log "设置admin密码..."
BCRYPT_PASSWORD=$(htpasswd -nbBC 10 "" "$ARGOCD_PASSWORD" | tr -d ':\n' | sed 's/$2y/$2a/')
if [ -z "$BCRYPT_PASSWORD" ]; then
log_error "密码加密失败"
exit 1
fi
kubectl -n argocd patch secret argocd-secret \
-p "{\"stringData\": {\"admin.password\": \"$BCRYPT_PASSWORD\", \"admin.passwordMtime\": \"$(date +%FT%T%Z)\"}}" || {
log_error "密码设置失败"
exit 1
}
# 重启argocd-server
log "重启ArgoCD服务器..."
kubectl -n argocd rollout restart deployment argocd-server
kubectl -n argocd rollout status deployment argocd-server --timeout=300s || {
log_error "ArgoCD服务器重启超时"
exit 1
}
# 获取访问信息
NODEPORT=$(kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
log "=== ArgoCD部署完成 ==="
echo "🌐 访问地址: https://$NODE_IP:$NODEPORT"
echo "🌐 域名访问: https://$ARGOCD_DOMAIN (需配置Ingress)"
echo "👤 用户名: admin"
echo "🔑 密码: $ARGOCD_PASSWORD"
echo ""
log "提示: 首次访问可能需要接受自签名证书"

70
scripts/deploy-gitea.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 部署Gitea私有Git服务器 ==="
# 安装yq如果未安装
if ! command -v yq &> /dev/null; then
echo "📦 安装yq..."
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
fi
# 安装Helm如果未安装
if ! command -v helm &> /dev/null; then
echo "📦 安装Helm..."
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
fi
# 读取配置
GITEA_DOMAIN=$(yq eval '.gitea_domain' "$CONFIG_FILE")
GITEA_ADMIN_USER=$(yq eval '.gitea_admin_user' "$CONFIG_FILE")
GITEA_ADMIN_PASSWORD=$(yq eval '.gitea_admin_password' "$CONFIG_FILE")
GITEA_ADMIN_EMAIL=$(yq eval '.gitea_admin_email' "$CONFIG_FILE")
# 创建命名空间
kubectl create namespace gitea --dry-run=client -o yaml | kubectl apply -f -
# 添加Gitea Helm仓库
helm repo add gitea-charts https://dl.gitea.com/charts/
helm repo update
# 部署Gitea
echo "📦 部署Gitea..."
helm upgrade --install gitea gitea-charts/gitea \
--namespace gitea \
--set gitea.admin.username="$GITEA_ADMIN_USER" \
--set gitea.admin.password="$GITEA_ADMIN_PASSWORD" \
--set gitea.admin.email="$GITEA_ADMIN_EMAIL" \
--set service.http.type=NodePort \
--set service.ssh.type=NodePort \
--set gitea.config.server.DOMAIN="$GITEA_DOMAIN" \
--set gitea.config.server.ROOT_URL="http://$GITEA_DOMAIN" \
--set persistence.enabled=true \
--set persistence.size=10Gi \
--wait --timeout=10m
# 等待Gitea就绪
echo "⏳ 等待Gitea就绪..."
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=gitea -n gitea --timeout=600s
# 获取访问信息
HTTP_NODEPORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
SSH_NODEPORT=$(kubectl get svc gitea-ssh -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
echo "=== Gitea部署完成 ==="
echo "🌐 HTTP访问: http://$NODE_IP:$HTTP_NODEPORT"
echo "🌐 域名访问: http://$GITEA_DOMAIN (需配置Ingress)"
echo "🔐 SSH端口: $SSH_NODEPORT"
echo "👤 管理员用户: $GITEA_ADMIN_USER"
echo "🔑 管理员密码: $GITEA_ADMIN_PASSWORD"
echo ""
echo "⚠️ 请运行 ./scripts/setup-gitea.sh 完成初始化配置"

261
scripts/deploy-https.sh Executable file
View File

@@ -0,0 +1,261 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
# Source common library if available
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
source "$SCRIPT_DIR/lib/common.sh"
else
log() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
log_warn() { echo "[WARN] $1"; }
fi
log "=== 配置HTTPS证书 ==="
echo ""
# Ensure yq is available
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
ensure_yq || exit 1
else
if ! command -v yq &> /dev/null; then
log_error "yq未安装请先运行: sudo apt install yq"
exit 1
fi
fi
# Read configuration
ARGOCD_DOMAIN=$(yq eval '.argocd_domain' "$CONFIG_FILE")
GITEA_DOMAIN=$(yq eval '.gitea_domain' "$CONFIG_FILE")
DOMAIN_NAME=$(yq eval '.domain_name' "$CONFIG_FILE")
log "域名配置:"
echo " ArgoCD: $ARGOCD_DOMAIN"
echo " Gitea: $GITEA_DOMAIN"
echo " 主域名: $DOMAIN_NAME"
echo ""
# Step 1: Install cert-manager CRDs
log "步骤 1/4: 安装cert-manager CRDs..."
CERT_MANAGER_VERSION="v1.13.3"
CERT_MANAGER_CRD_URL="https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.crds.yaml"
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
retry 3 5 "kubectl apply -f $CERT_MANAGER_CRD_URL" || {
log_error "cert-manager CRDs安装失败"
exit 1
}
else
kubectl apply -f "$CERT_MANAGER_CRD_URL" || {
log_error "cert-manager CRDs安装失败"
exit 1
}
fi
log "✓ cert-manager CRDs安装成功"
echo ""
# Step 2: Install cert-manager
log "步骤 2/4: 安装cert-manager..."
kubectl create namespace cert-manager --dry-run=client -o yaml | kubectl apply -f -
CERT_MANAGER_URL="https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
retry 3 5 "kubectl apply -f $CERT_MANAGER_URL" || {
log_error "cert-manager安装失败"
exit 1
}
else
kubectl apply -f "$CERT_MANAGER_URL" || {
log_error "cert-manager安装失败"
exit 1
}
fi
log "等待cert-manager就绪..."
kubectl wait --for=condition=available --timeout=300s deployment/cert-manager -n cert-manager || {
log_error "cert-manager部署超时"
exit 1
}
kubectl wait --for=condition=available --timeout=300s deployment/cert-manager-webhook -n cert-manager || {
log_error "cert-manager-webhook部署超时"
exit 1
}
log "✓ cert-manager安装成功"
echo ""
# Step 3: Create ClusterIssuers
log "步骤 3/4: 创建Let's Encrypt ClusterIssuers..."
# Create staging issuer (for testing)
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: admin@${DOMAIN_NAME}
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: traefik
EOF
log "✓ Staging ClusterIssuer创建成功"
# Create production issuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@${DOMAIN_NAME}
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
EOF
log "✓ Production ClusterIssuer创建成功"
echo ""
# Wait for ClusterIssuers to be ready
log "等待ClusterIssuers就绪..."
sleep 5
if kubectl get clusterissuer letsencrypt-staging -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | grep -q "True"; then
log "✓ Staging ClusterIssuer就绪"
else
log_warn "Staging ClusterIssuer可能未就绪请检查: kubectl describe clusterissuer letsencrypt-staging"
fi
if kubectl get clusterissuer letsencrypt-prod -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | grep -q "True"; then
log "✓ Production ClusterIssuer就绪"
else
log_warn "Production ClusterIssuer可能未就绪请检查: kubectl describe clusterissuer letsencrypt-prod"
fi
echo ""
# Step 4: Create HTTPS Ingresses
log "步骤 4/4: 创建HTTPS Ingress..."
# ArgoCD HTTPS Ingress
if kubectl get namespace argocd &>/dev/null; then
log "创建ArgoCD HTTPS Ingress..."
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-https
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- ${ARGOCD_DOMAIN}
secretName: argocd-tls-cert
rules:
- host: ${ARGOCD_DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
log "✓ ArgoCD HTTPS Ingress创建成功"
else
log_warn "ArgoCD未部署跳过Ingress创建"
fi
# Gitea HTTPS Ingress
if kubectl get namespace gitea &>/dev/null; then
log "创建Gitea HTTPS Ingress..."
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-https
namespace: gitea
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- ${GITEA_DOMAIN}
secretName: gitea-tls-cert
rules:
- host: ${GITEA_DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
log "✓ Gitea HTTPS Ingress创建成功"
else
log_warn "Gitea未部署跳过Ingress创建"
fi
echo ""
log "=== HTTPS配置完成 ==="
echo ""
log "证书申请状态检查:"
echo " 查看证书: kubectl get certificate -A"
echo " 查看ClusterIssuer: kubectl get clusterissuer"
echo ""
log "注意事项:"
echo " 1. 证书申请需要几分钟时间"
echo " 2. 确保DNS已正确解析到集群IP"
echo " 3. 确保80端口可从外网访问用于HTTP-01验证"
echo " 4. 首次使用建议先用staging测试避免触发速率限制"
echo ""
log "验证HTTPS访问:"
echo " ArgoCD: https://${ARGOCD_DOMAIN}"
echo " Gitea: https://${GITEA_DOMAIN}"
echo ""
log "故障排查:"
echo " 查看cert-manager日志: kubectl logs -n cert-manager deployment/cert-manager"
echo " 查看证书详情: kubectl describe certificate <cert-name> -n <namespace>"
echo " 查看证书请求: kubectl get certificaterequest -A"
echo ""

186
scripts/deploy-nginx-app.sh Executable file
View File

@@ -0,0 +1,186 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 Nginx测试应用 - 自动化部署"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 检查依赖
echo "🔍 检查依赖..."
command -v kubectl >/dev/null 2>&1 || { echo "❌ kubectl未安装"; exit 1; }
command -v yq >/dev/null 2>&1 || { echo "❌ yq未安装"; exit 1; }
echo "✅ 依赖检查通过"
echo ""
# 检查kubectl连接
echo "🔍 检查K3s集群连接..."
if ! kubectl cluster-info >/dev/null 2>&1; then
echo "❌ 无法连接到K3s集群"
echo "💡 请确保已配置kubectl访问权限"
exit 1
fi
echo "✅ K3s集群连接正常"
echo ""
# 检查Gitea是否运行
echo "🔍 检查Gitea服务..."
if ! kubectl get svc gitea-http -n gitea >/dev/null 2>&1; then
echo "❌ Gitea服务未运行"
echo "💡 请先运行: ./scripts/deploy-gitea.sh"
exit 1
fi
echo "✅ Gitea服务运行正常"
echo ""
# 检查ArgoCD是否运行
echo "🔍 检查ArgoCD服务..."
if ! kubectl get namespace argocd >/dev/null 2>&1; then
echo "❌ ArgoCD未安装"
echo "💡 请先运行: ./scripts/deploy-argocd.sh"
exit 1
fi
echo "✅ ArgoCD服务运行正常"
echo ""
# 步骤1: 创建Gitea仓库
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 1/3: 创建Gitea仓库"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 读取配置
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
GITEA_USER=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GITEA_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
GITEA_ORG=$(yq eval '.gitea_org_name' "$CONFIG_FILE")
NGINX_REPO=$(yq eval '.nginx_app_repo_name' "$CONFIG_FILE")
# 获取Gitea访问地址
GITEA_NODEPORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
GITEA_URL="http://$NODE_IP:$GITEA_NODEPORT"
# 检查仓库是否已存在
echo "🔍 检查仓库是否存在..."
REPO_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
-u "$GITEA_USER:$GITEA_PASSWORD" \
"$GITEA_URL/api/v1/repos/$GITEA_ORG/$NGINX_REPO")
if [ "$REPO_EXISTS" = "200" ]; then
echo "⚠️ 仓库已存在: $GITEA_ORG/$NGINX_REPO"
echo ""
read -p "是否删除并重新创建?(y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🗑️ 删除现有仓库..."
curl -s -X DELETE \
-u "$GITEA_USER:$GITEA_PASSWORD" \
"$GITEA_URL/api/v1/repos/$GITEA_ORG/$NGINX_REPO"
echo "✅ 仓库已删除"
else
echo "⏭️ 跳过仓库创建"
SKIP_PUSH=true
fi
fi
if [ "$SKIP_PUSH" != "true" ]; then
echo "📝 创建新仓库..."
curl -s -X POST \
-u "$GITEA_USER:$GITEA_PASSWORD" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$NGINX_REPO\",\"description\":\"Nginx test application for GitOps demo\",\"private\":false,\"auto_init\":false}" \
"$GITEA_URL/api/v1/org/$GITEA_ORG/repos" > /dev/null
echo "✅ 仓库创建成功: $GITEA_ORG/$NGINX_REPO"
fi
echo ""
# 步骤2: 推送应用到Gitea
if [ "$SKIP_PUSH" != "true" ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📤 步骤 2/3: 推送应用到Gitea"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
"$SCRIPT_DIR/push-nginx-app.sh"
echo ""
else
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "⏭️ 步骤 2/3: 跳过推送(仓库已存在)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
fi
# 步骤3: 创建ArgoCD Application
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎯 步骤 3/3: 创建ArgoCD Application"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 检查Application是否已存在
if kubectl get application nginx-app -n argocd >/dev/null 2>&1; then
echo "⚠️ ArgoCD Application已存在"
echo ""
read -p "是否删除并重新创建?(y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🗑️ 删除现有Application..."
kubectl delete application nginx-app -n argocd
echo "✅ Application已删除"
sleep 2
else
echo "⏭️ 跳过Application创建"
SKIP_ARGOCD=true
fi
fi
if [ "$SKIP_ARGOCD" != "true" ]; then
"$SCRIPT_DIR/create-nginx-argocd-app.sh"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 Nginx测试应用部署完成"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 部署信息:"
echo " - 应用名称: nginx-test"
echo " - 命名空间: default"
echo " - 域名: https://ng.jpc.net3w.com"
echo " - Git仓库: $GITEA_URL/$GITEA_ORG/$NGINX_REPO"
echo ""
echo "🔍 验证命令:"
echo " # 查看Pod状态"
echo " kubectl get pods -l app=nginx-test -n default"
echo ""
echo " # 查看Service"
echo " kubectl get svc nginx-test -n default"
echo ""
echo " # 查看Ingress"
echo " kubectl get ingress nginx-test -n default"
echo ""
echo " # 查看ArgoCD Application"
echo " kubectl get application nginx-app -n argocd"
echo ""
echo "🌐 访问地址:"
echo " - 应用: https://ng.jpc.net3w.com"
echo " - ArgoCD: https://argocd.jpc.net3w.com"
echo " - Gitea: $GITEA_URL/$GITEA_ORG/$NGINX_REPO"
echo ""
echo "💡 更新应用:"
echo " 1. SSH到master节点"
echo " 2. cd /home/fei/k3s/nginx-app"
echo " 3. ./update-app.sh v2.0"
echo " 4. 等待ArgoCD自动同步约3分钟"
echo ""
echo "📝 注意事项:"
echo " - 确保DNS已配置: ng.jpc.net3w.com -> $NODE_IP"
echo " - 首次HTTPS访问需等待证书签发1-2分钟"
echo " - ArgoCD每3分钟检查一次Git仓库更新"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

46
scripts/deploy.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== K3s集群自动化部署 ==="
# 检查配置文件
if [ ! -f "$CONFIG_FILE" ]; then
echo "❌ 错误: 配置文件不存在: $CONFIG_FILE"
echo "请复制 config/cluster-vars.yml.example 为 config/cluster-vars.yml 并填写配置"
exit 1
fi
# 安装依赖
if ! command -v ansible &> /dev/null; then
echo "📦 安装Ansible..."
sudo apt update
sudo apt install -y ansible python3-pip python3-yaml
fi
# 生成inventory
echo "📝 生成Ansible inventory..."
cd "$PROJECT_DIR"
python3 "$SCRIPT_DIR/generate-inventory.py"
# 部署K3s集群
echo "🚀 部署K3s集群..."
cd "$PROJECT_DIR/k3s-ansible"
ansible-playbook site.yml \
-i inventory/hosts.ini \
-e "@$CONFIG_FILE"
# 配置kubectl
echo "⚙️ 配置kubectl..."
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
# 验证集群
echo "✅ 验证集群状态..."
kubectl get nodes
echo "=== K3s集群部署完成 ==="

50
scripts/generate-inventory.py Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import yaml
import sys
import os
# 读取变量文件
config_file = 'config/cluster-vars.yml'
if not os.path.exists(config_file):
print(f"错误: 配置文件不存在: {config_file}")
sys.exit(1)
with open(config_file, 'r') as f:
config = yaml.safe_load(f)
# 生成inventory (k3s-ansible需要server和agent组)
inventory = "[server]\n"
for node in config['master_nodes']:
line = f"{node['private_ip']} ansible_host={node['public_ip']} ansible_user={node['ssh_user']}"
# 支持密码认证
if 'ssh_password' in node:
line += f" ansible_ssh_pass={node['ssh_password']} ansible_become_pass={node['ssh_password']}"
elif 'ssh_key_path' in node:
line += f" ansible_ssh_private_key_file={node['ssh_key_path']}"
inventory += line + "\n"
inventory += "\n[agent]\n"
for node in config['worker_nodes']:
line = f"{node['private_ip']} ansible_host={node['public_ip']} ansible_user={node['ssh_user']}"
if 'ssh_password' in node:
line += f" ansible_ssh_pass={node['ssh_password']} ansible_become_pass={node['ssh_password']}"
elif 'ssh_key_path' in node:
line += f" ansible_ssh_private_key_file={node['ssh_key_path']}"
inventory += line + "\n"
inventory += "\n[k3s_cluster:children]\nserver\nagent\n"
inventory += "\n[k3s_cluster:vars]\n"
inventory += "ansible_python_interpreter=/usr/bin/python3\n"
inventory += f"k3s_version={config.get('k3s_version', 'v1.28.5+k3s1')}\n"
inventory += f"token={config.get('k3s_token', 'changeme!')}\n"
# 使用master节点的内网IP作为API endpoint
master_private_ip = config['master_nodes'][0]['private_ip']
inventory += f"api_endpoint={master_private_ip}\n"
inventory += "flannel_iface=eth0\n"
# 写入inventory文件
output_file = 'k3s-ansible/inventory/hosts.ini'
with open(output_file, 'w') as f:
f.write(inventory)
print(f"✓ Inventory生成成功: {output_file}")

553
scripts/idempotent-deploy.sh Executable file
View File

@@ -0,0 +1,553 @@
#!/bin/bash
# JPD集群幂等性自动化部署脚本
# 可以安全地重复运行,不会产生错误或不一致状态
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 JPD集群幂等性自动化部署"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 辅助函数:检查资源是否存在
resource_exists() {
local resource_type=$1
local resource_name=$2
local namespace=${3:-default}
if [ "$namespace" = "cluster" ]; then
kubectl get "$resource_type" "$resource_name" &>/dev/null
else
kubectl get "$resource_type" "$resource_name" -n "$namespace" &>/dev/null
fi
}
# 辅助函数:等待资源就绪
wait_for_pods() {
local namespace=$1
local label=$2
local timeout=${3:-300}
echo "⏳ 等待 $namespace/$label Pod就绪..."
kubectl wait --for=condition=ready pod -l "$label" -n "$namespace" --timeout="${timeout}s" 2>/dev/null || true
}
# ============================================
# 步骤 1: 配置Gitea Ingress
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 1/6: 配置Gitea Ingress"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# HTTP Ingress
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-http
namespace: gitea
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
ingressClassName: traefik
rules:
- host: git.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
echo "✅ Gitea HTTP Ingress配置完成"
echo ""
# ============================================
# 步骤 2: 配置ArgoCD访问
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 2/6: 配置ArgoCD访问"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 配置ArgoCD为NodePort幂等
if ! kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.type}' | grep -q "NodePort"; then
echo "配置ArgoCD Service为NodePort..."
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
else
echo "ArgoCD Service已经是NodePort类型"
fi
# 配置ArgoCD允许HTTP访问幂等
if ! kubectl get cm argocd-cmd-params-cm -n argocd -o jsonpath='{.data.server\.insecure}' | grep -q "true"; then
echo "配置ArgoCD允许HTTP访问..."
kubectl patch cm argocd-cmd-params-cm -n argocd --type merge -p '{"data":{"server.insecure":"true"}}'
kubectl rollout restart deployment argocd-server -n argocd
sleep 10
else
echo "ArgoCD已配置为允许HTTP访问"
fi
# HTTP Ingress简化版不引用不存在的middleware
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-http
namespace: argocd
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
ingressClassName: traefik
rules:
- host: argocd.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
ARGOCD_PORT=$(kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.ports[0].nodePort}')
ARGOCD_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" 2>/dev/null | base64 -d || echo "密码已删除或不存在")
echo "✅ ArgoCD访问配置完成"
echo " NodePort: http://149.13.91.216:$ARGOCD_PORT"
echo " 域名: http://argocd.jpd.net3w.com"
echo " 用户名: admin"
echo " 密码: $ARGOCD_PASSWORD"
echo ""
# ============================================
# 步骤 3: 部署cert-manager
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 3/6: 部署cert-manager"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if ! resource_exists namespace cert-manager cluster; then
echo "部署cert-manager..."
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
wait_for_pods cert-manager app=cert-manager 300
wait_for_pods cert-manager app=webhook 300
sleep 10
else
echo "cert-manager已存在跳过部署"
# 确保Pod就绪
wait_for_pods cert-manager app=cert-manager 60
wait_for_pods cert-manager app=webhook 60
fi
# 创建ClusterIssuer幂等
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@jpd.net3w.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
EOF
echo "✅ cert-manager配置完成"
echo ""
# ============================================
# 步骤 4: 配置HTTPS Ingress
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 4/6: 配置HTTPS Ingress"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Gitea HTTPS
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-https
namespace: gitea
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- git.jpd.net3w.com
secretName: gitea-tls
rules:
- host: git.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
EOF
# ArgoCD HTTPS
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-https
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- argocd.jpd.net3w.com
secretName: argocd-server-tls
rules:
- host: argocd.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
echo "✅ HTTPS Ingress配置完成"
echo ""
# ============================================
# 步骤 5: 部署测试应用
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 5/6: 部署测试应用"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 创建命名空间(幂等)
kubectl create namespace demo-app --dry-run=client -o yaml | kubectl apply -f -
# 部署应用(幂等)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-html
namespace: demo-app
data:
index.html: |
<!DOCTYPE html>
<html>
<head>
<title>JPD集群测试应用</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
text-align: center;
max-width: 600px;
}
h1 {
color: #667eea;
margin-bottom: 20px;
}
.status {
background: #10b981;
color: white;
padding: 10px 20px;
border-radius: 5px;
display: inline-block;
margin: 20px 0;
}
.info {
text-align: left;
background: #f3f4f6;
padding: 20px;
border-radius: 5px;
margin-top: 20px;
}
.info p {
margin: 10px 0;
}
.emoji {
font-size: 48px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="emoji">🚀</div>
<h1>JPD K3s集群测试应用</h1>
<div class="status">✅ 运行正常</div>
<div class="info">
<p><strong>集群名称:</strong> JPD Cluster</p>
<p><strong>部署方式:</strong> Kubernetes Deployment</p>
<p><strong>副本数:</strong> 3</p>
<p><strong>容器镜像:</strong> nginx:alpine</p>
<p><strong>访问域名:</strong> demo.jpd.net3w.com</p>
<p><strong>GitOps工具:</strong> ArgoCD</p>
<p><strong>Git仓库:</strong> Gitea</p>
<p><strong>幂等性:</strong> ✅ 已实现</p>
</div>
</div>
</body>
</html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo-app
labels:
app: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: nginx-html
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo-app
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo-http
namespace: demo-app
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
ingressClassName: traefik
rules:
- host: demo.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo-https
namespace: demo-app
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- demo.jpd.net3w.com
secretName: nginx-demo-tls
rules:
- host: demo.jpd.net3w.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
EOF
wait_for_pods demo-app app=nginx-demo 120
echo "✅ 测试应用部署完成"
echo ""
# ============================================
# 步骤 6: 部署自动化测试
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 步骤 6/6: 部署自动化测试"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
name: health-check
namespace: demo-app
spec:
schedule: "*/5 * * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: curl
image: curlimages/curl:latest
command:
- /bin/sh
- -c
- |
echo "=== 健康检查开始 ==="
echo "时间: \$(date)"
echo ""
FAILED=0
# 测试Gitea
echo "测试 Gitea..."
if curl -f -s http://gitea-http.gitea.svc.cluster.local:3000 > /dev/null; then
echo "✅ Gitea: 正常"
else
echo "❌ Gitea: 异常"
FAILED=1
fi
# 测试ArgoCD
echo "测试 ArgoCD..."
if curl -f -s -k http://argocd-server.argocd.svc.cluster.local > /dev/null; then
echo "✅ ArgoCD: 正常"
else
echo "❌ ArgoCD: 异常"
FAILED=1
fi
# 测试Demo应用
echo "测试 Demo应用..."
if curl -f -s http://nginx-demo.demo-app.svc.cluster.local > /dev/null; then
echo "✅ Demo应用: 正常"
else
echo "❌ Demo应用: 异常"
FAILED=1
fi
echo ""
if [ \$FAILED -eq 0 ]; then
echo "=== 所有服务健康检查通过 ==="
exit 0
else
echo "=== 健康检查失败 ==="
exit 1
fi
restartPolicy: OnFailure
EOF
echo "✅ 自动化测试部署完成"
echo ""
# ============================================
# 最终验证
# ============================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎉 部署完成!最终验证"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "📊 集群节点:"
kubectl get nodes -o wide
echo ""
echo "🌐 Ingress资源:"
kubectl get ingress --all-namespaces
echo ""
echo "🔐 证书状态:"
kubectl get certificate --all-namespaces
echo ""
echo "🔑 访问信息:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Gitea:"
echo " HTTP: http://git.jpd.net3w.com"
echo " HTTPS: https://git.jpd.net3w.com"
echo " 用户名: gitea_admin"
echo " 密码: GitAdmin@2026"
echo ""
echo "ArgoCD:"
echo " HTTP: http://argocd.jpd.net3w.com"
echo " HTTPS: https://argocd.jpd.net3w.com"
echo " NodePort: http://149.13.91.216:$ARGOCD_PORT"
echo " 用户名: admin"
echo " 密码: $ARGOCD_PASSWORD"
echo ""
echo "测试应用:"
echo " HTTP: http://demo.jpd.net3w.com"
echo " HTTPS: https://demo.jpd.net3w.com"
echo ""
echo "💡 提示:"
echo " - 此脚本是幂等的,可以安全地重复运行"
echo " - HTTPS证书会自动签发和续期"
echo " - 自动化测试每5分钟运行一次"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

354
scripts/lib/common.sh Executable file
View File

@@ -0,0 +1,354 @@
#!/bin/bash
# Common utility functions for K3s deployment scripts
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Project directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
STATE_FILE="$PROJECT_DIR/.deployment-state"
LOG_FILE="$PROJECT_DIR/deployment.log"
# Logging functions
log() {
local message="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${GREEN}[INFO]${NC} $message"
echo "[$timestamp] [INFO] $message" >> "$LOG_FILE"
}
log_error() {
local message="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${RED}[ERROR]${NC} $message" >&2
echo "[$timestamp] [ERROR] $message" >> "$LOG_FILE"
}
log_warn() {
local message="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${YELLOW}[WARN]${NC} $message"
echo "[$timestamp] [WARN] $message" >> "$LOG_FILE"
}
log_step() {
local message="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${BLUE}[STEP]${NC} $message"
echo "[$timestamp] [STEP] $message" >> "$LOG_FILE"
}
# State management functions
mark_step_completed() {
local step_name="$1"
echo "$step_name" >> "$STATE_FILE"
log "✓ Marked step as completed: $step_name"
}
is_step_completed() {
local step_name="$1"
if [ ! -f "$STATE_FILE" ]; then
return 1
fi
grep -q "^$step_name$" "$STATE_FILE" 2>/dev/null
}
reset_deployment_state() {
if [ -f "$STATE_FILE" ]; then
rm -f "$STATE_FILE"
log "Deployment state reset"
fi
}
# Tool checking functions
check_tool() {
local tool_name="$1"
local install_cmd="$2"
if command -v "$tool_name" &> /dev/null; then
log "✓ Tool available: $tool_name"
return 0
else
log_warn "Tool not found: $tool_name"
if [ -n "$install_cmd" ]; then
log "Installing $tool_name..."
if eval "$install_cmd"; then
log "✓ Successfully installed: $tool_name"
return 0
else
log_error "Failed to install: $tool_name"
return 1
fi
else
log_error "Please install $tool_name manually"
return 1
fi
fi
}
check_required_tools() {
local all_ok=true
log_step "Checking required tools..."
check_tool "python3" "sudo apt update && sudo apt install -y python3" || all_ok=false
check_tool "ansible" "sudo apt update && sudo apt install -y ansible" || all_ok=false
check_tool "git" "sudo apt update && sudo apt install -y git" || all_ok=false
if [ "$all_ok" = false ]; then
log_error "Some required tools are missing"
return 1
fi
log "✓ All required tools are available"
return 0
}
# Network checking functions
check_network() {
local test_url="${1:-https://www.google.com}"
local timeout="${2:-5}"
if curl -s --max-time "$timeout" --head "$test_url" > /dev/null 2>&1; then
return 0
else
return 1
fi
}
check_network_with_retry() {
local test_url="${1:-https://www.google.com}"
local max_attempts="${2:-3}"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if check_network "$test_url"; then
log "✓ Network connection OK"
return 0
fi
log_warn "Network check failed (attempt $attempt/$max_attempts)"
attempt=$((attempt + 1))
sleep 2
done
log_error "Network connection failed after $max_attempts attempts"
return 1
}
# Retry mechanism
retry() {
local max_attempts="$1"
local delay="$2"
shift 2
local cmd="$@"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if eval "$cmd"; then
return 0
fi
if [ $attempt -lt $max_attempts ]; then
log_warn "Command failed (attempt $attempt/$max_attempts), retrying in ${delay}s..."
sleep "$delay"
fi
attempt=$((attempt + 1))
done
log_error "Command failed after $max_attempts attempts: $cmd"
return 1
}
# Configuration file validation
check_config_file() {
local config_file="${1:-$PROJECT_DIR/config/cluster-vars.yml}"
if [ ! -f "$config_file" ]; then
log_error "Configuration file not found: $config_file"
log_error "Please copy config/cluster-vars.yml.example to config/cluster-vars.yml and configure it"
return 1
fi
log "✓ Configuration file exists: $config_file"
# Check if yq is available for validation
if command -v yq &> /dev/null; then
if yq eval '.' "$config_file" > /dev/null 2>&1; then
log "✓ Configuration file is valid YAML"
else
log_error "Configuration file has invalid YAML syntax"
return 1
fi
fi
return 0
}
# Kubernetes cluster checking
check_kubectl() {
if ! command -v kubectl &> /dev/null; then
log_warn "kubectl not found, will be available after K3s installation"
return 1
fi
if ! kubectl cluster-info &> /dev/null; then
log_warn "kubectl cannot connect to cluster"
return 1
fi
log "✓ kubectl is available and connected"
return 0
}
wait_for_pods() {
local namespace="$1"
local label="$2"
local timeout="${3:-600}"
log "Waiting for pods in namespace $namespace with label $label..."
if kubectl wait --for=condition=ready pod \
-l "$label" \
-n "$namespace" \
--timeout="${timeout}s" 2>/dev/null; then
log "✓ Pods are ready"
return 0
else
log_error "Pods failed to become ready within ${timeout}s"
return 1
fi
}
wait_for_deployment() {
local namespace="$1"
local deployment="$2"
local timeout="${3:-600}"
log "Waiting for deployment $deployment in namespace $namespace..."
if kubectl wait --for=condition=available \
--timeout="${timeout}s" \
deployment/"$deployment" \
-n "$namespace" 2>/dev/null; then
log "✓ Deployment is available"
return 0
else
log_error "Deployment failed to become available within ${timeout}s"
return 1
fi
}
# Download with retry
download_file() {
local url="$1"
local output="$2"
local max_attempts="${3:-3}"
log "Downloading: $url"
if retry "$max_attempts" 5 "curl -fsSL '$url' -o '$output'"; then
log "✓ Downloaded successfully: $output"
return 0
else
log_error "Failed to download: $url"
return 1
fi
}
# Install yq if not present
ensure_yq() {
if command -v yq &> /dev/null; then
log "✓ yq is already installed"
return 0
fi
log "Installing yq..."
local yq_url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64"
local yq_path="/usr/local/bin/yq"
if download_file "$yq_url" "/tmp/yq" 3; then
sudo mv /tmp/yq "$yq_path"
sudo chmod +x "$yq_path"
log "✓ yq installed successfully"
return 0
else
log_error "Failed to install yq"
return 1
fi
}
# Install htpasswd if not present
ensure_htpasswd() {
if command -v htpasswd &> /dev/null; then
log "✓ htpasswd is already installed"
return 0
fi
log "Installing htpasswd (apache2-utils)..."
if sudo apt update && sudo apt install -y apache2-utils; then
log "✓ htpasswd installed successfully"
return 0
else
log_error "Failed to install htpasswd"
return 1
fi
}
# Install helm if not present
ensure_helm() {
if command -v helm &> /dev/null; then
log "✓ Helm is already installed"
return 0
fi
log "Installing Helm..."
if retry 3 5 "curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"; then
log "✓ Helm installed successfully"
return 0
else
log_error "Failed to install Helm"
return 1
fi
}
# Cleanup function for temporary files
cleanup_temp_files() {
local temp_dir="$1"
if [ -n "$temp_dir" ] && [ -d "$temp_dir" ]; then
rm -rf "$temp_dir"
log "Cleaned up temporary directory: $temp_dir"
fi
}
# Trap for cleanup on exit
setup_cleanup_trap() {
local temp_dir="$1"
trap "cleanup_temp_files '$temp_dir'" EXIT INT TERM
}
# Print summary
print_summary() {
echo ""
echo "=========================================="
echo " Deployment Summary"
echo "=========================================="
echo ""
}
# Export functions for use in other scripts
export -f log log_error log_warn log_step
export -f mark_step_completed is_step_completed reset_deployment_state
export -f check_tool check_required_tools
export -f check_network check_network_with_retry
export -f retry
export -f check_config_file check_kubectl
export -f wait_for_pods wait_for_deployment
export -f download_file
export -f ensure_yq ensure_htpasswd ensure_helm
export -f cleanup_temp_files setup_cleanup_trap
export -f print_summary

135
scripts/push-demo-app.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 推送示例应用到Gitea ==="
# 读取配置
GITEA_USER=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GITEA_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
GITEA_ORG=$(yq eval '.gitea_org_name' "$CONFIG_FILE")
GITEA_REPO=$(yq eval '.gitea_repo_name' "$CONFIG_FILE")
# 获取Gitea NodePort
GITEA_NODEPORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
GITEA_URL="http://$NODE_IP:$GITEA_NODEPORT"
REPO_URL="$GITEA_URL/$GITEA_ORG/$GITEA_REPO.git"
# 创建临时目录
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
echo "📝 创建示例应用清单..."
# 创建manifests目录
mkdir -p manifests
# 创建示例Deployment
cat > manifests/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-nginx
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: demo-nginx
template:
metadata:
labels:
app: demo-nginx
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
EOF
# 创建示例Service
cat > manifests/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: demo-nginx
namespace: default
spec:
type: NodePort
selector:
app: demo-nginx
ports:
- port: 80
targetPort: 80
nodePort: 30080
EOF
# 创建README
cat > README.md <<EOF
# Demo Application
这是一个由ArgoCD管理的示例应用。
## 应用信息
- **应用名称**: demo-nginx
- **镜像**: nginx:1.25-alpine
- **副本数**: 2
- **访问端口**: NodePort 30080
## 更新应用
修改 \`manifests/\` 目录下的文件并提交到GitArgoCD会自动同步部署。
## 测试访问
\`\`\`bash
curl http://<NODE_IP>:30080
\`\`\`
EOF
# 初始化Git仓库
echo "🔧 初始化Git仓库..."
git init -b main
git config user.name "$GITEA_USER"
git config user.email "$GITEA_USER@example.com"
git add .
git commit -m "Initial commit: Add demo nginx application
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
# 推送到Gitea
echo "📤 推送到Gitea..."
git remote add origin "$REPO_URL"
# 使用用户名密码推送(临时方案)
# URL encode the password to handle special characters
ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$GITEA_PASSWORD'))")
git push -u origin main || {
echo "⚠️ 首次推送失败,尝试使用凭证..."
git remote set-url origin "http://$GITEA_USER:$ENCODED_PASSWORD@$NODE_IP:$GITEA_NODEPORT/$GITEA_ORG/$GITEA_REPO.git"
git push -u origin main
}
# 清理
cd "$PROJECT_DIR"
rm -rf "$TEMP_DIR"
echo "✅ 示例应用推送成功!"
echo "📊 仓库地址: $REPO_URL"
echo "🌐 访问Gitea查看: $GITEA_URL/$GITEA_ORG/$GITEA_REPO"
echo "⏳ 等待ArgoCD同步约3分钟..."
echo "📊 查看同步状态: kubectl get application -n argocd"

538
scripts/push-nginx-app.sh Executable file
View File

@@ -0,0 +1,538 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 推送Nginx测试应用到Gitea ==="
# 读取配置
GITEA_USER=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GITEA_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
GITEA_ORG=$(yq eval '.gitea_org_name' "$CONFIG_FILE")
NGINX_REPO=$(yq eval '.nginx_app_repo_name' "$CONFIG_FILE")
NGINX_DOMAIN=$(yq eval '.nginx_app_domain' "$CONFIG_FILE")
# 获取Gitea NodePort
GITEA_NODEPORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
GITEA_URL="http://$NODE_IP:$GITEA_NODEPORT"
REPO_URL="$GITEA_URL/$GITEA_ORG/$NGINX_REPO.git"
# 创建临时目录
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
echo "📝 创建Nginx应用清单..."
# 创建manifests目录
mkdir -p manifests
# 创建Nginx Deployment
cat > manifests/deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
namespace: default
labels:
app: nginx-test
spec:
replicas: 2
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
name: http
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
- name: html
mountPath: /usr/share/nginx/html
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: html
configMap:
name: nginx-html
EOF
# 创建Nginx ConfigMap
cat > manifests/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: default
data:
default.conf: |
server {
listen 80;
server_name ${NGINX_DOMAIN};
location / {
root /usr/share/nginx/html;
index index.html;
}
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-html
namespace: default
data:
index.html: |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nginx Test - GitOps Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 60px;
max-width: 800px;
text-align: center;
}
h1 {
color: #667eea;
font-size: 3em;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.version {
display: inline-block;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 10px 30px;
border-radius: 50px;
font-size: 1.2em;
font-weight: bold;
margin: 20px 0;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.info {
background: #f8f9fa;
border-radius: 10px;
padding: 30px;
margin: 30px 0;
text-align: left;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 15px 0;
border-bottom: 1px solid #e9ecef;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-weight: bold;
color: #495057;
}
.info-value {
color: #667eea;
font-family: 'Courier New', monospace;
}
.badge {
display: inline-block;
background: #28a745;
color: white;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9em;
margin: 10px 5px;
}
.footer {
margin-top: 30px;
color: #6c757d;
font-size: 0.9em;
}
.emoji {
font-size: 3em;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="emoji">🚀</div>
<h1>Nginx Test Application</h1>
<div class="version">Version: v1.0</div>
<div class="info">
<div class="info-item">
<span class="info-label">域名:</span>
<span class="info-value">${NGINX_DOMAIN}</span>
</div>
<div class="info-item">
<span class="info-label">应用名称:</span>
<span class="info-value">nginx-test</span>
</div>
<div class="info-item">
<span class="info-label">镜像:</span>
<span class="info-value">nginx:1.25-alpine</span>
</div>
<div class="info-item">
<span class="info-label">副本数:</span>
<span class="info-value">2</span>
</div>
<div class="info-item">
<span class="info-label">部署方式:</span>
<span class="info-value">GitOps (ArgoCD)</span>
</div>
</div>
<div>
<span class="badge">✓ Kubernetes</span>
<span class="badge">✓ GitOps</span>
<span class="badge">✓ ArgoCD</span>
<span class="badge">✓ Nginx</span>
</div>
<div class="footer">
<p>🎯 这是一个通过GitOps自动部署的Nginx测试应用</p>
<p>修改Git仓库中的配置ArgoCD会自动同步部署</p>
</div>
</div>
</body>
</html>
EOF
# 创建Service
cat > manifests/service.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-test
namespace: default
labels:
app: nginx-test
spec:
type: ClusterIP
selector:
app: nginx-test
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
EOF
# 创建Ingress
cat > manifests/ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-test
namespace: default
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- ${NGINX_DOMAIN}
secretName: nginx-test-tls
rules:
- host: ${NGINX_DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-test
port:
number: 80
EOF
# 创建README
cat > README.md <<EOF
# Nginx Test Application
这是一个由ArgoCD管理的Nginx测试应用用于演示GitOps自动化部署。
## 应用信息
- **应用名称**: nginx-test
- **镜像**: nginx:1.25-alpine
- **副本数**: 2
- **域名**: ${NGINX_DOMAIN}
- **命名空间**: default
## 架构说明
\`\`\`
Git仓库 (Gitea) → ArgoCD监控 → 自动同步 → K3s集群部署
\`\`\`
## 访问方式
### 通过域名访问(推荐)
\`\`\`bash
curl https://${NGINX_DOMAIN}
\`\`\`
### 通过NodePort访问
\`\`\`bash
# 获取Service信息
kubectl get svc nginx-test -n default
# 访问应用
curl http://<NODE_IP>:<NODE_PORT>
\`\`\`
## 更新应用
### 方式1: 修改版本号
编辑 \`manifests/configmap.yaml\` 中的 HTML 内容,修改版本号:
\`\`\`html
<div class="version">Version: v2.0</div>
\`\`\`
### 方式2: 修改副本数
编辑 \`manifests/deployment.yaml\`
\`\`\`yaml
spec:
replicas: 3 # 修改副本数
\`\`\`
### 方式3: 更新Nginx配置
编辑 \`manifests/configmap.yaml\` 中的 nginx 配置。
提交更改后ArgoCD会在3分钟内自动检测并部署新版本。
## 监控部署状态
\`\`\`bash
# 查看ArgoCD Application状态
kubectl get application nginx-app -n argocd
# 查看Pod状态
kubectl get pods -l app=nginx-test -n default
# 查看Ingress状态
kubectl get ingress nginx-test -n default
# 查看应用日志
kubectl logs -l app=nginx-test -n default --tail=50
\`\`\`
## 健康检查
应用提供了健康检查端点:
\`\`\`bash
curl https://${NGINX_DOMAIN}/health
\`\`\`
## 故障排查
### 检查Pod状态
\`\`\`bash
kubectl describe pod -l app=nginx-test -n default
\`\`\`
### 检查Ingress
\`\`\`bash
kubectl describe ingress nginx-test -n default
\`\`\`
### 检查ArgoCD同步状态
\`\`\`bash
kubectl describe application nginx-app -n argocd
\`\`\`
## GitOps工作流
1. 开发者修改 \`manifests/\` 目录下的配置文件
2. 提交并推送到Git仓库
3. ArgoCD自动检测到变化每3分钟轮询一次
4. ArgoCD自动同步并部署到K3s集群
5. 应用自动更新无需手动执行kubectl命令
## 回滚操作
如果需要回滚到之前的版本:
\`\`\`bash
# 查看Git历史
git log --oneline
# 回滚到指定commit
git revert <commit-hash>
git push
# 或者通过ArgoCD UI进行回滚
\`\`\`
## 技术栈
- **容器编排**: Kubernetes (K3s)
- **Web服务器**: Nginx 1.25
- **GitOps工具**: ArgoCD
- **Git仓库**: Gitea
- **Ingress控制器**: Nginx Ingress Controller
- **证书管理**: cert-manager (Let's Encrypt)
## 注意事项
1. 确保DNS已正确配置${NGINX_DOMAIN} 指向K3s集群节点IP
2. 首次访问HTTPS可能需要等待证书签发约1-2分钟
3. ArgoCD默认每3分钟检查一次Git仓库更新
4. 可以通过ArgoCD UI手动触发同步以立即部署更改
## 相关链接
- ArgoCD Dashboard: https://argocd.jpc.net3w.com
- Gitea Repository: http://<NODE_IP>:<GITEA_PORT>/k3s-apps/nginx-app
- Application URL: https://${NGINX_DOMAIN}
EOF
# 创建更新脚本
cat > update-app.sh <<'SCRIPT_EOF'
#!/bin/bash
set -e
VERSION=${1:-v2.0}
echo "🔄 更新Nginx应用到版本 $VERSION"
# 修改版本号
sed -i "s/Version: v[0-9.]*/Version: $VERSION/" manifests/configmap.yaml
# 根据版本修改背景色
case $VERSION in
v1.0)
COLOR="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
;;
v2.0)
COLOR="linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
;;
v3.0)
COLOR="linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)"
;;
*)
COLOR="linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)"
;;
esac
sed -i "s|background: linear-gradient([^)]*)|background: $COLOR|" manifests/configmap.yaml
# 提交更改
git add manifests/configmap.yaml
git commit -m "Update nginx-app to $VERSION
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
git push
echo "✅ 更新完成!"
echo "⏳ 等待ArgoCD同步约3分钟..."
echo "🌐 访问 https://ng.jpc.net3w.com 查看更新"
SCRIPT_EOF
chmod +x update-app.sh
# 初始化Git仓库
echo "🔧 初始化Git仓库..."
git init -b main
git config user.name "$GITEA_USER"
git config user.email "$GITEA_USER@example.com"
git add .
git commit -m "Initial commit: Add nginx test application
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
# 推送到Gitea
echo "📤 推送到Gitea..."
git remote add origin "$REPO_URL"
# URL encode the password to handle special characters
ENCODED_PASSWORD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$GITEA_PASSWORD'))")
git push -u origin main || {
echo "⚠️ 首次推送失败,尝试使用凭证..."
git remote set-url origin "http://$GITEA_USER:$ENCODED_PASSWORD@$NODE_IP:$GITEA_NODEPORT/$GITEA_ORG/$NGINX_REPO.git"
git push -u origin main
}
# 清理
cd "$PROJECT_DIR"
rm -rf "$TEMP_DIR"
echo ""
echo "✅ Nginx测试应用推送成功"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 仓库信息:"
echo " - 仓库地址: $REPO_URL"
echo " - Gitea访问: $GITEA_URL/$GITEA_ORG/$NGINX_REPO"
echo ""
echo "🌐 应用信息:"
echo " - 域名: https://${NGINX_DOMAIN}"
echo " - 应用名称: nginx-test"
echo " - 命名空间: default"
echo ""
echo "📝 下一步:"
echo " 1. 运行: ./scripts/create-nginx-argocd-app.sh"
echo " 2. 等待ArgoCD同步约3分钟"
echo " 3. 访问: https://${NGINX_DOMAIN}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

51
scripts/setup-gitea.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
echo "=== 初始化Gitea配置 ==="
# 读取配置
GITEA_ADMIN_USER=$(yq eval '.gitea_admin_user' "$CONFIG_FILE")
GITEA_ADMIN_PASSWORD=$(yq eval '.gitea_admin_password' "$CONFIG_FILE")
GITEA_ORG_NAME=$(yq eval '.gitea_org_name' "$CONFIG_FILE")
GITEA_REPO_NAME=$(yq eval '.gitea_repo_name' "$CONFIG_FILE")
GITEA_USER_NAME=$(yq eval '.gitea_user_name' "$CONFIG_FILE")
GITEA_USER_PASSWORD=$(yq eval '.gitea_user_password' "$CONFIG_FILE")
GITEA_USER_EMAIL=$(yq eval '.gitea_user_email' "$CONFIG_FILE")
# 获取Gitea服务地址
GITEA_POD=$(kubectl get pod -n gitea -l app.kubernetes.io/name=gitea -o jsonpath='{.items[0].metadata.name}')
GITEA_URL="http://gitea-http.gitea.svc.cluster.local:3000"
echo "📝 创建用户: $GITEA_USER_NAME"
kubectl exec -n gitea "$GITEA_POD" -- su git -c "gitea admin user create \
--username '$GITEA_USER_NAME' \
--password '$GITEA_USER_PASSWORD' \
--email '$GITEA_USER_EMAIL' \
--must-change-password=false" || echo "用户可能已存在"
echo "📝 创建组织: $GITEA_ORG_NAME"
kubectl exec -n gitea "$GITEA_POD" -- su git -c "gitea admin org create \
--username '$GITEA_ADMIN_USER' \
--name '$GITEA_ORG_NAME'" || echo "组织可能已存在"
echo "📦 创建仓库: $GITEA_REPO_NAME"
kubectl exec -n gitea "$GITEA_POD" -- su git -c "gitea admin repo create \
--owner '$GITEA_ORG_NAME' \
--name '$GITEA_REPO_NAME' \
--private=false" || echo "仓库可能已存在"
echo "👥 添加用户到组织"
# 使用Gitea API添加用户到组织
kubectl exec -n gitea "$GITEA_POD" -- su git -c "curl -X PUT \
-H 'Content-Type: application/json' \
-u '$GITEA_ADMIN_USER:$GITEA_ADMIN_PASSWORD' \
'$GITEA_URL/api/v1/orgs/$GITEA_ORG_NAME/members/$GITEA_USER_NAME'" || true
echo "✅ Gitea初始化完成"
echo "📊 仓库地址: $GITEA_URL/$GITEA_ORG_NAME/$GITEA_REPO_NAME.git"
echo "👤 ArgoCD用户: $GITEA_USER_NAME"
echo "🔑 ArgoCD密码: $GITEA_USER_PASSWORD"

280
scripts/test-idempotency.sh Executable file
View File

@@ -0,0 +1,280 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# Source common library if available
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
source "$SCRIPT_DIR/lib/common.sh"
else
log() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
log_warn() { echo "[WARN] $1"; }
fi
log "=== K3s集群幂等性测试 ==="
echo ""
# Test counters
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
# Test function
test_case() {
local name="$1"
local description="$2"
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo ""
echo "=========================================="
echo "测试 #$TOTAL_TESTS: $name"
echo "=========================================="
echo "描述: $description"
echo ""
}
test_pass() {
PASSED_TESTS=$((PASSED_TESTS + 1))
log "✓ 测试通过"
}
test_fail() {
local reason="$1"
FAILED_TESTS=$((FAILED_TESTS + 1))
log_error "✗ 测试失败: $reason"
}
# Capture initial state
capture_state() {
local state_file="$1"
log "捕获系统状态..."
{
echo "=== Nodes ==="
kubectl get nodes -o yaml 2>/dev/null || echo "N/A"
echo "=== Namespaces ==="
kubectl get namespaces -o yaml 2>/dev/null || echo "N/A"
echo "=== Deployments ==="
kubectl get deployments -A -o yaml 2>/dev/null || echo "N/A"
echo "=== Services ==="
kubectl get services -A -o yaml 2>/dev/null || echo "N/A"
echo "=== ConfigMaps ==="
kubectl get configmaps -A -o yaml 2>/dev/null || echo "N/A"
echo "=== Secrets (names only) ==="
kubectl get secrets -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}' 2>/dev/null || echo "N/A"
echo "=== PVCs ==="
kubectl get pvc -A -o yaml 2>/dev/null || echo "N/A"
echo "=== Ingresses ==="
kubectl get ingress -A -o yaml 2>/dev/null || echo "N/A"
echo "=== ClusterIssuers ==="
kubectl get clusterissuer -o yaml 2>/dev/null || echo "N/A"
echo "=== Certificates ==="
kubectl get certificate -A -o yaml 2>/dev/null || echo "N/A"
echo "=== ArgoCD Applications ==="
kubectl get application -n argocd -o yaml 2>/dev/null || echo "N/A"
} > "$state_file"
log "✓ 状态已保存到: $state_file"
}
# Compare states
compare_states() {
local before="$1"
local after="$2"
log "比较部署前后状态..."
if diff -u "$before" "$after" > /dev/null 2>&1; then
log "✓ 状态完全一致(幂等性验证通过)"
return 0
else
log_warn "状态存在差异,检查差异详情..."
# Check for acceptable differences (timestamps, resourceVersion, etc.)
local significant_diff=false
# Filter out expected differences
diff -u "$before" "$after" | grep -v "resourceVersion" | \
grep -v "creationTimestamp" | \
grep -v "generation:" | \
grep -v "uid:" | \
grep -v "selfLink" | \
grep -v "lastTransitionTime" | \
grep -v "observedGeneration" > /tmp/filtered_diff.txt || true
if [ -s /tmp/filtered_diff.txt ]; then
log_warn "发现显著差异:"
head -50 /tmp/filtered_diff.txt
significant_diff=true
fi
rm -f /tmp/filtered_diff.txt
if [ "$significant_diff" = true ]; then
return 1
else
log "✓ 仅存在预期的元数据差异(幂等性验证通过)"
return 0
fi
fi
}
# Main test flow
main() {
log "开始幂等性测试"
log "此测试将验证部署脚本的幂等性"
echo ""
# Check if cluster is accessible
if ! kubectl cluster-info &>/dev/null; then
log_error "无法连接到K3s集群请先部署集群"
exit 1
fi
# Test 1: Capture initial state
test_case "初始状态捕获" "捕获当前集群状态作为基准"
STATE_BEFORE="/tmp/k3s-state-before-$$.yaml"
capture_state "$STATE_BEFORE"
test_pass
# Test 2: Run deploy-all.sh
test_case "重复执行部署脚本" "运行deploy-all.sh验证幂等性"
log "执行部署脚本..."
if bash "$SCRIPT_DIR/deploy-all.sh"; then
log "✓ 部署脚本执行成功"
test_pass
else
log_error "部署脚本执行失败"
test_fail "deploy-all.sh执行失败"
fi
# Test 3: Capture state after redeployment
test_case "重新部署后状态捕获" "捕获重新部署后的集群状态"
STATE_AFTER="/tmp/k3s-state-after-$$.yaml"
capture_state "$STATE_AFTER"
test_pass
# Test 4: Compare states
test_case "状态一致性验证" "比较部署前后状态,验证幂等性"
if compare_states "$STATE_BEFORE" "$STATE_AFTER"; then
test_pass
else
test_fail "部署前后状态存在显著差异"
fi
# Test 5: Verify all services are still healthy
test_case "服务健康检查" "验证所有服务仍然正常运行"
log "运行验证脚本..."
if bash "$SCRIPT_DIR/verify-deployment.sh" > /tmp/verify-output.txt 2>&1; then
log "✓ 所有服务健康"
test_pass
else
log_error "服务验证失败"
cat /tmp/verify-output.txt
test_fail "服务健康检查失败"
fi
# Test 6: Test individual script idempotency
test_case "单个脚本幂等性" "测试各个部署脚本的幂等性"
local scripts=(
"deploy-argocd.sh"
"deploy-gitea.sh"
"deploy-https.sh"
)
local script_tests_passed=0
local script_tests_total=0
for script in "${scripts[@]}"; do
if [ -f "$SCRIPT_DIR/$script" ]; then
script_tests_total=$((script_tests_total + 1))
log "测试脚本: $script"
if bash "$SCRIPT_DIR/$script" > /tmp/script-test-$$.log 2>&1; then
log "$script 执行成功"
script_tests_passed=$((script_tests_passed + 1))
else
log_warn "$script 执行失败"
tail -20 /tmp/script-test-$$.log
fi
fi
done
if [ $script_tests_passed -eq $script_tests_total ]; then
test_pass
else
test_fail "$script_tests_passed/$script_tests_total 脚本通过测试"
fi
# Cleanup
log "清理临时文件..."
rm -f "$STATE_BEFORE" "$STATE_AFTER" /tmp/verify-output.txt /tmp/script-test-$$.log
# Print summary
echo ""
echo "=========================================="
echo " 幂等性测试总结"
echo "=========================================="
echo ""
echo "总测试数: $TOTAL_TESTS"
echo "通过: $PASSED_TESTS"
echo "失败: $FAILED_TESTS"
echo ""
if [ $FAILED_TESTS -eq 0 ]; then
log "✓ 所有幂等性测试通过!"
echo ""
echo "结论: 部署脚本完全支持幂等性,可以安全地重复执行。"
echo ""
exit 0
else
log_error "存在 $FAILED_TESTS 个失败的测试"
echo ""
echo "结论: 部署脚本的幂等性存在问题,需要修复。"
echo ""
exit 1
fi
}
# Handle script arguments
case "${1:-}" in
--help|-h)
echo "用法: $0 [选项]"
echo ""
echo "此脚本测试K3s部署的幂等性验证脚本可以安全地重复执行。"
echo ""
echo "测试内容:"
echo " 1. 捕获初始集群状态"
echo " 2. 重复执行部署脚本"
echo " 3. 比较部署前后状态"
echo " 4. 验证服务健康"
echo " 5. 测试单个脚本幂等性"
echo ""
echo "选项:"
echo " --help 显示此帮助信息"
echo ""
exit 0
;;
esac
# Run main function
main

276
scripts/verify-deployment.sh Executable file
View File

@@ -0,0 +1,276 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$PROJECT_DIR/config/cluster-vars.yml"
# Source common library if available
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
source "$SCRIPT_DIR/lib/common.sh"
else
log() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
log_warn() { echo "[WARN] $1"; }
fi
log "=== 验证K3s集群部署 ==="
echo ""
# Counters
TOTAL_CHECKS=0
PASSED_CHECKS=0
FAILED_CHECKS=0
WARNING_CHECKS=0
# Check function
check() {
local name="$1"
local command="$2"
local is_critical="${3:-true}"
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
echo -n "检查: $name ... "
if eval "$command" &>/dev/null; then
echo "✓ 通过"
PASSED_CHECKS=$((PASSED_CHECKS + 1))
return 0
else
if [ "$is_critical" = "true" ]; then
echo "✗ 失败"
FAILED_CHECKS=$((FAILED_CHECKS + 1))
else
echo "⚠ 警告"
WARNING_CHECKS=$((WARNING_CHECKS + 1))
fi
return 1
fi
}
# Detailed check with output
check_detailed() {
local name="$1"
local command="$2"
echo ""
echo "=========================================="
echo " $name"
echo "=========================================="
eval "$command"
echo ""
}
echo "=========================================="
echo " 1. 基础环境检查"
echo "=========================================="
echo ""
check "kubectl命令可用" "command -v kubectl"
check "kubectl连接集群" "kubectl cluster-info"
check "配置文件存在" "test -f $CONFIG_FILE"
if command -v yq &>/dev/null; then
check "yq工具可用" "command -v yq"
else
check "yq工具可用" "false" "false"
fi
echo ""
echo "=========================================="
echo " 2. K3s集群状态"
echo "=========================================="
echo ""
check "所有节点Ready" "kubectl get nodes | grep -v NotReady | grep Ready"
check "kube-system命名空间存在" "kubectl get namespace kube-system"
check "CoreDNS运行正常" "kubectl get deployment coredns -n kube-system -o jsonpath='{.status.availableReplicas}' | grep -v '^0$'"
check_detailed "节点状态" "kubectl get nodes -o wide"
check_detailed "系统Pod状态" "kubectl get pods -n kube-system"
echo ""
echo "=========================================="
echo " 3. Gitea服务检查"
echo "=========================================="
echo ""
if kubectl get namespace gitea &>/dev/null; then
check "Gitea命名空间存在" "kubectl get namespace gitea"
check "Gitea部署存在" "kubectl get deployment gitea -n gitea"
if kubectl get deployment gitea -n gitea &>/dev/null; then
check "Gitea Pod运行正常" "kubectl get pods -n gitea -l app.kubernetes.io/name=gitea -o jsonpath='{.items[0].status.phase}' | grep Running"
check "Gitea服务可访问" "kubectl get svc gitea-http -n gitea"
check_detailed "Gitea服务详情" "kubectl get all -n gitea"
# Get Gitea access info
GITEA_NODEPORT=$(kubectl get svc gitea-http -n gitea -o jsonpath='{.spec.ports[0].nodePort}' 2>/dev/null || echo "N/A")
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}' 2>/dev/null)
if [ -z "$NODE_IP" ]; then
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}' 2>/dev/null)
fi
echo "Gitea访问信息:"
echo " URL: http://$NODE_IP:$GITEA_NODEPORT"
echo ""
fi
else
check "Gitea命名空间存在" "false" "false"
log_warn "Gitea未部署"
fi
echo ""
echo "=========================================="
echo " 4. ArgoCD服务检查"
echo "=========================================="
echo ""
if kubectl get namespace argocd &>/dev/null; then
check "ArgoCD命名空间存在" "kubectl get namespace argocd"
check "ArgoCD Server部署存在" "kubectl get deployment argocd-server -n argocd"
if kubectl get deployment argocd-server -n argocd &>/dev/null; then
check "ArgoCD Server运行正常" "kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].status.phase}' | grep Running"
check "ArgoCD Application Controller运行正常" "kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-application-controller -o jsonpath='{.items[0].status.phase}' | grep Running"
check "ArgoCD Repo Server运行正常" "kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-repo-server -o jsonpath='{.items[0].status.phase}' | grep Running"
check_detailed "ArgoCD服务详情" "kubectl get all -n argocd"
# Get ArgoCD access info
ARGOCD_NODEPORT=$(kubectl get svc argocd-server -n argocd -o jsonpath='{.spec.ports[0].nodePort}' 2>/dev/null || echo "N/A")
echo "ArgoCD访问信息:"
echo " URL: https://$NODE_IP:$ARGOCD_NODEPORT"
echo " 用户名: admin"
echo ""
fi
else
check "ArgoCD命名空间存在" "false" "false"
log_warn "ArgoCD未部署"
fi
echo ""
echo "=========================================="
echo " 5. HTTPS证书检查"
echo "=========================================="
echo ""
if kubectl get namespace cert-manager &>/dev/null; then
check "cert-manager命名空间存在" "kubectl get namespace cert-manager"
check "cert-manager部署存在" "kubectl get deployment cert-manager -n cert-manager"
if kubectl get deployment cert-manager -n cert-manager &>/dev/null; then
check "cert-manager运行正常" "kubectl get pods -n cert-manager -l app=cert-manager -o jsonpath='{.items[0].status.phase}' | grep Running"
# Check ClusterIssuers
if kubectl get clusterissuer &>/dev/null 2>&1; then
check_detailed "ClusterIssuer状态" "kubectl get clusterissuer"
fi
# Check Certificates
if kubectl get certificate -A &>/dev/null 2>&1; then
check_detailed "证书状态" "kubectl get certificate -A"
fi
fi
else
check "cert-manager命名空间存在" "false" "false"
log_warn "cert-manager未部署HTTPS功能不可用"
fi
echo ""
echo "=========================================="
echo " 6. GitOps工作流检查"
echo "=========================================="
echo ""
if kubectl get namespace argocd &>/dev/null; then
# Check for ArgoCD Applications
if kubectl get application -n argocd &>/dev/null 2>&1; then
APP_COUNT=$(kubectl get application -n argocd --no-headers 2>/dev/null | wc -l)
if [ "$APP_COUNT" -gt 0 ]; then
check "ArgoCD应用已创建" "test $APP_COUNT -gt 0"
check_detailed "ArgoCD应用状态" "kubectl get application -n argocd"
else
check "ArgoCD应用已创建" "false" "false"
log_warn "未找到ArgoCD应用"
fi
else
check "ArgoCD应用已创建" "false" "false"
log_warn "ArgoCD CRD可能未就绪"
fi
else
log_warn "ArgoCD未部署跳过GitOps检查"
fi
echo ""
echo "=========================================="
echo " 7. 存储检查"
echo "=========================================="
echo ""
check "PersistentVolume存在" "kubectl get pv" "false"
check "PersistentVolumeClaim存在" "kubectl get pvc -A" "false"
if kubectl get pvc -A &>/dev/null 2>&1; then
check_detailed "存储卷状态" "kubectl get pv,pvc -A"
fi
echo ""
echo "=========================================="
echo " 验证总结"
echo "=========================================="
echo ""
echo "总检查项: $TOTAL_CHECKS"
echo "通过: $PASSED_CHECKS"
echo "失败: $FAILED_CHECKS"
echo "警告: $WARNING_CHECKS"
echo ""
if [ $FAILED_CHECKS -eq 0 ]; then
log "✓ 所有关键检查通过!"
if [ $WARNING_CHECKS -gt 0 ]; then
log_warn "存在 $WARNING_CHECKS 个警告项,建议检查"
fi
echo ""
echo "=========================================="
echo " 快速访问指南"
echo "=========================================="
echo ""
if [ -n "${NODE_IP:-}" ]; then
if [ -n "${GITEA_NODEPORT:-}" ] && [ "$GITEA_NODEPORT" != "N/A" ]; then
echo "Gitea:"
echo " http://$NODE_IP:$GITEA_NODEPORT"
echo ""
fi
if [ -n "${ARGOCD_NODEPORT:-}" ] && [ "$ARGOCD_NODEPORT" != "N/A" ]; then
echo "ArgoCD:"
echo " https://$NODE_IP:$ARGOCD_NODEPORT"
echo " 用户名: admin"
echo ""
fi
fi
echo "常用命令:"
echo " 查看所有Pod: kubectl get pods -A"
echo " 查看节点: kubectl get nodes"
echo " 查看服务: kubectl get svc -A"
echo ""
exit 0
else
log_error "发现 $FAILED_CHECKS 个失败项,请检查并修复"
echo ""
echo "故障排查建议:"
echo " 1. 查看Pod日志: kubectl logs <pod-name> -n <namespace>"
echo " 2. 查看Pod详情: kubectl describe pod <pod-name> -n <namespace>"
echo " 3. 查看事件: kubectl get events -A --sort-by='.lastTimestamp'"
echo " 4. 重新部署: ./scripts/deploy-all.sh"
echo ""
exit 1
fi