Files
k3s_auto_deploy/scripts/idempotent-deploy.sh

554 lines
16 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"