#!/bin/bash # Kubernetes 自动部署脚本 # 用于从私有 Harbor 仓库拉取 NGINX 镜像并部署到 K8s 集群 set -e # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 打印带颜色的信息 print_info() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # 检查必要的环境变量 check_env_vars() { print_info "检查环境变量..." required_vars=("HARBOR_REGISTRY" "HARBOR_USERNAME" "HARBOR_PASSWORD" "NGINX_IMAGE_TAG") missing_vars=() for var in "${required_vars[@]}"; do if [[ -z "${!var}" ]]; then missing_vars+=("$var") fi done if [[ ${#missing_vars[@]} -gt 0 ]]; then print_error "缺少必要的环境变量:" for var in "${missing_vars[@]}"; do echo " - $var" done print_info "请设置以下环境变量:" echo " export HARBOR_REGISTRY=<你的Harbor仓库地址>" echo " export HARBOR_USERNAME=" echo " export HARBOR_PASSWORD=" echo " export NGINX_IMAGE_TAG=<镜像标签,如: \${GITHUB_SHA} 或 latest>" exit 1 fi print_success "环境变量检查通过" } # 检查 kubectl 连接 check_kubectl() { print_info "检查 kubectl 连接..." if ! command -v kubectl &> /dev/null; then print_error "kubectl 未安装或不在 PATH 中" exit 1 fi if ! kubectl cluster-info &> /dev/null; then print_error "无法连接到 Kubernetes 集群" print_info "请确保:" echo " 1. kubectl 已正确配置" echo " 2. 能够访问 Kubernetes 集群" echo " 3. 当前用户有适当的权限" exit 1 fi print_success "kubectl 连接正常" kubectl cluster-info } # 创建命名空间(如果不存在) create_namespace() { local namespace=${1:-default} print_info "检查命名空间: $namespace" if kubectl get namespace "$namespace" &> /dev/null; then print_success "命名空间 '$namespace' 已存在" else print_info "创建命名空间: $namespace" kubectl create namespace "$namespace" print_success "命名空间 '$namespace' 创建成功" fi } # 创建或更新 Harbor 仓库访问凭据 create_harbor_secret() { local namespace=${1:-default} print_info "创建/更新 Harbor 仓库访问凭据..." # 删除已存在的 secret(如果存在) kubectl delete secret harbor-registry-secret -n "$namespace" --ignore-not-found=true # 创建新的 secret kubectl create secret docker-registry harbor-registry-secret \ --docker-server="$HARBOR_REGISTRY" \ --docker-username="$HARBOR_USERNAME" \ --docker-password="$HARBOR_PASSWORD" \ --namespace="$namespace" print_success "Harbor 仓库访问凭据创建成功" } # 更新部署文件中的镜像标签 update_deployment_image() { local deployment_file="$1" local image_tag="$2" print_info "更新部署文件中的镜像标签: $image_tag" # 使用镜像完整路径 local full_image="${HARBOR_REGISTRY}/test/nginx:${image_tag}" print_info "使用镜像: $full_image" # 备份原文件 cp "$deployment_file" "${deployment_file}.bak" # 使用 sed 替换环境变量 sed -i.tmp "s|\$HARBOR_REGISTRY|${HARBOR_REGISTRY}|g" "$deployment_file" sed -i.tmp "s|\$NGINX_IMAGE_TAG|${image_tag}|g" "$deployment_file" sed -i.tmp "s|\$NAMESPACE|${DEPLOY_NAMESPACE}|g" "$deployment_file" # 添加命名空间替换 # 如果有其他环境变量需要替换,可以继续添加类似的 sed 命令 # 例如:sed -i.tmp "s|\$NAMESPACE|${NAMESPACE}|g" "$deployment_file" # 清理临时文件 rm -f "${deployment_file}.tmp" print_success "镜像标签和环境变量替换完成,使用镜像: $full_image" } # 应用 Kubernetes 配置 apply_k8s_config() { local deployment_file="$1" print_info "应用 Kubernetes 配置..." if [[ ! -f "$deployment_file" ]]; then print_error "部署文件不存在: $deployment_file" exit 1 fi kubectl apply -f "$deployment_file" print_success "Kubernetes 配置应用成功" } # 等待部署就绪 wait_for_deployment() { local deployment_name="nginx-deployment" local namespace=${1:-default} local timeout=${2:-300} print_info "等待部署就绪..." # 添加初始检查和调试信息 if ! kubectl get deployment "$deployment_name" -n "$namespace" &> /dev/null; then print_error "找不到部署 $deployment_name" print_info "检查部署状态..." kubectl get deployments -n "$namespace" -o wide print_info "检查Pod状态..." kubectl get pods -n "$namespace" -o wide print_info "检查最近事件..." kubectl get events -n "$namespace" --sort-by='.lastTimestamp' exit 1 fi if kubectl wait --for=condition=available deployment/"$deployment_name" \ --namespace="$namespace" --timeout="${timeout}s"; then print_success "部署已就绪" else print_error "部署超时,请检查部署状态" # 添加更详细的错误信息 print_info "Pod状态:" kubectl get pods -n "$namespace" -l app=nginx -o wide print_info "描述deployment:" kubectl describe deployment "$deployment_name" -n "$namespace" print_info "最近的事件:" kubectl get events -n "$namespace" --sort-by='.lastTimestamp' | tail -n 20 print_info "Pod日志:" kubectl logs -n "$namespace" -l app=nginx --tail=50 exit 1 fi } # 显示部署状态 show_deployment_status() { local namespace=${1:-default} print_info "部署状态:" echo echo "=== Pods ===" kubectl get pods -l app=nginx -n "$namespace" -o wide echo echo "=== Services ===" kubectl get services -l app=nginx -n "$namespace" echo echo "=== Deployments ===" kubectl get deployments -l app=nginx -n "$namespace" echo echo "=== HPA ===" kubectl get hpa -l app=nginx -n "$namespace" 2>/dev/null || echo "HPA 未启用" echo echo "=== Events ===" kubectl get events --sort-by=.metadata.creationTimestamp -n "$namespace" | tail -10 } # 获取访问信息 get_access_info() { local namespace=${1:-default} print_info "获取访问信息..." local service_type=$(kubectl get service nginx-service -n "$namespace" -o jsonpath='{.spec.type}') case "$service_type" in "NodePort") local node_port=$(kubectl get service nginx-service -n "$namespace" -o jsonpath='{.spec.ports[0].nodePort}') local 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 print_success "NodePort 访问地址: http://${node_ip}:${node_port}" ;; "LoadBalancer") print_info "等待 LoadBalancer 外部 IP..." local external_ip=$(kubectl get service nginx-service -n "$namespace" -o jsonpath='{.status.loadBalancer.ingress[0].ip}') if [[ -n "$external_ip" ]]; then print_success "LoadBalancer 访问地址: http://${external_ip}" else print_warning "LoadBalancer 外部 IP 仍在分配中" fi ;; "ClusterIP") local cluster_ip=$(kubectl get service nginx-service -n "$namespace" -o jsonpath='{.spec.clusterIP}') print_success "ClusterIP 访问地址: http://${cluster_ip}" print_info "注意: ClusterIP 只能在集群内部访问" ;; esac } # 主函数 main() { print_info "开始 NGINX Kubernetes 自动部署..." # 修改命名空间处理逻辑,优先使用 CI 变量 local namespace="app-nginx" # 默认值 if [[ -n "${K8S_NAMESPACE}" ]]; then namespace="${K8S_NAMESPACE}" # 使用 GitHub Actions 变量 fi print_info "使用命名空间: $namespace" local deployment_file=${DEPLOYMENT_FILE:-./nginx-deployment.yaml} local image_tag=${NGINX_IMAGE_TAG:-latest} # 执行部署步骤 check_env_vars check_kubectl create_namespace "$namespace" create_harbor_secret "$namespace" # 添加命名空间环境变量替换 export DEPLOY_NAMESPACE="$namespace" update_deployment_image "$deployment_file" "$image_tag" apply_k8s_config "$deployment_file" wait_for_deployment "$namespace" show_deployment_status "$namespace" get_access_info "$namespace" print_success "🎉 NGINX 部署完成!" } # 清理函数 cleanup() { print_info "执行清理操作..." # 恢复备份文件 if [[ -f "./nginx-deployment.yaml.bak" ]]; then mv "./nginx-deployment.yaml.bak" "./nginx-deployment.yaml" fi } # 设置清理陷阱 trap cleanup EXIT # 如果直接运行此脚本 if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi