← 返回总目录

Day 178 — 容器编排(K8s 入门)

自动化运维与DevOps 786 行 1 个可运行脚本

Day 178: 容器编排(K8s 入门)

概念解释

1. Kubernetes(K8s)是什么

2. K8s 核心概念

3. K8s 体系结构

原理解释

1. Pod 的设计原理

2. ReplicaSet 的工作原理

3. Service 的网络模型

4. 命名空间隔离原理

5. Ingress 控制器原理

API 速查表

Deployment API

bash
# 创建 Deployment
cat > deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
EOF

# 应用
cat deployment.yaml | kubectl apply -f -

# 查看状态
kubectl get deployment nginx-deployment

# 扩缩容
kubectl scale deployment nginx-deployment --replicas=5

# 更新
cat deployment.yaml | kubectl apply -f -

# 删除
kubectl delete deployment nginx-deployment
EOF

Service API

bash
# 创建 Service
cat > service.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: NodePort
EOF

# 应用
cat service.yaml | kubectl apply -f -

# 获取服务地址
kubectl get service nginx-service

# 删除
service.yaml | kubectl delete -f -
EOF

Pod 管理

bash
# 创建 Pod
cat > pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
EOF

# 查看 Pod 详情
kubectl describe pod nginx-pod

# 执行命令
kubectl exec -it nginx-pod -- nginx -t

# 删除
kubectl delete pod nginx-pod
EOF

配置文件管理

bash
# 创建 ConfigMap
cat > configmap.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  DATABASE_URL: "mysql://localhost:3306/app"
EOF

# 应用到 Deployment
cat deployment-with-configmap.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
    spec:
      containers:
      - name: app
        image: myapp:1.0
        envFrom:
        - configMapRef:
            name: app-config
      volumes:
      - name: config-volume
        configMap:
          name: app-config
EOF

kubectl apply -f configmap.yaml
kubectl apply -f deployment-with-configmap.yaml
EOF

持久化存储

bash
# 创建 PersistentVolume
cat > pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: /data/nginx
EOF

# 创建 PersistentVolumeClaim
cat > pvc.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard
EOF

# 应用
cat pv.yaml pvc.yaml | kubectl apply -f -

# 挂载到 Pod
cat pod-with-pv.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pv-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
    volumeMounts:
    - name: nginx-storage
      mountPath: /usr/share/nginx/html
  volumes:
  - name: nginx-storage
    persistentVolumeClaim:
      claimName: nginx-pvc
EOF

kubectl apply -f pod-with-pv.yaml
EOF

图解

Mermaid: K8s 架构图

graph TB subgraph "Control Plane" apiserver[API Server] scheduler[Scheduler] controllerManager[Controller Manager] etcd[Etcd] end subgraph "Worker Nodes" kubelet[Kubelet] containerRuntime[Container Runtime] end apiserver --> scheduler apiserver --> controllerManager scheduler --> kubelet controllerManager --> kubelet kubelet --> containerRuntime subgraph "Pods" pod1[Pod 1] pod2[Pod 2] pod3[Pod 3] end containerRuntime --> pod1 containerRuntime --> pod2 containerRuntime --> pod3
graph TB
    subgraph "Control Plane"
        apiserver[API Server]
        scheduler[Scheduler]
        controllerManager[Controller Manager]
        etcd[Etcd]
    end
    
    subgraph "Worker Nodes"
        kubelet[Kubelet]
        containerRuntime[Container Runtime]
    end
    
    apiserver --> scheduler
    apiserver --> controllerManager
    scheduler --> kubelet
    controllerManager --> kubelet
    kubelet --> containerRuntime
    
    subgraph "Pods"
        pod1[Pod 1]
        pod2[Pod 2]
        pod3[Pod 3]
    end
    
    containerRuntime --> pod1
    containerRuntime --> pod2
    containerRuntime --> pod3

ASCII: 部署流程

Kubernetes 部署流程:

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Docker Image  │───▶│   Container Reg │───▶│   kubelet      │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                       │
                                                       ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Deployment     │───▶│  Scheduler       │───▶│  Pod Creation   │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                       │
                                                       ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  ReplicaSet    │───▶│  Pod Management  │───▶│  Service        │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                       │
                                                       ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│ Ingress        │───▶│  Load Balance   │───▶│  Client Request │
└─────────────────┘    └──────────────────┘    └─────────────────┘

实战代码案例

1. 基础 K8s 部署脚本

python
#!/usr/bin/env python3
"""
Kubernetes 部署自动化脚本
功能:部署 Nginx 应用并监控其状态
"""
import subprocess
import time
import yaml
import sys

def run_command(cmd):
    """执行 shell 命令"""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"命令失败: {cmd}")
        print(f"错误信息: {e.stderr}")
        return None

def create_deployment_yaml():
    """创建 Deployment YAML 文件"""
    deployment = {
        'apiVersion': 'apps/v1',
        'kind': 'Deployment',
        'metadata': {
            'name': 'nginx-deployment',
            'labels': {
                'app': 'nginx',
                'version': 'v1'
            }
        },
        'spec': {
            'replicas': 3,
            'selector': {
                'matchLabels': {
                    'app': 'nginx'
                }
            },
            'template': {
                'metadata': {
                    'labels': {
                        'app': 'nginx',
                        'version': 'v1'
                    }
                },
                'spec': {
                    'containers': [
                        {
                            'name': 'nginx',
                            'image': 'nginx:1.14.2',
                            'ports': [
                                {
                                    'containerPort': 80
                                }
                            ],
                            'resources': {
                                'requests': {
                                    'cpu': '100m',
                                    'memory': '128Mi'
                                },
                                'limits': {
                                    'cpu': '200m',
                                    'memory': '256Mi'
                                }
                            },
                            'livenessProbe': {
                                'httpGet': {
                                    'path': '/health',
                                    'port': 80
                                },
                                'initialDelaySeconds': 30,
                                'periodSeconds': 10
                            },
                            'readinessProbe': {
                                'httpGet': {
                                    'path': '/ready',
                                    'port': 80
                                },
                                'initialDelaySeconds': 5,
                                'periodSeconds': 5
                            }
                        }
                    ]
                }
            }
        }
    }
    
    with open('deployment.yaml', 'w') as f:
        yaml.dump(deployment, f, default_flow_style=False)
    
    print("✅ Deployment YAML 文件已创建")

def create_service_yaml():
    """创建 Service YAML 文件"""
    service = {
        'apiVersion': 'v1',
        'kind': 'Service',
        'metadata': {
            'name': 'nginx-service',
            'labels': {
                'app': 'nginx'
            }
        },
        'spec': {
            'selector': {
                'app': 'nginx'
            },
            'ports': [
                {
                    'protocol': 'TCP',
                    'port': 80,
                    'targetPort': 80
                }
            ],
            'type': 'NodePort'
        }
    }
    
    with open('service.yaml', 'w') as f:
        yaml.dump(service, f, default_flow_style=False)
    
    print("✅ Service YAML 文件已创建")

def deploy_app():
    """部署应用到 K8s"""
    print("🚀 开始部署 Nginx 应用...")
    
    # 检查是否安装了 kubectl
    if run_command("kubectl version --client") is None:
        print("❌ kubectl 命令不可用")
        return False
    
    # 检查是否连接到 K8s 集群
    if run_command("kubectl cluster-info") is None:
        print("❌ 无法连接到 K8s 集群")
        return False
    
    # 应用 Deployment
    deploy_output = run_command("kubectl apply -f deployment.yaml")
    if deploy_output is None:
        print("❌ Deployment 应用失败")
        return False
    
    print(f"📦 Deployment 应用成功: {deploy_output}")
    
    # 应用 Service
    service_output = run_command("kubectl apply -f service.yaml")
    if service_output is None:
        print("❌ Service 应用失败")
        return False
    
    print(f"🌐 Service 应用成功: {service_output}")
    
    # 等待 Pod 就绪
    print("⏳ 等待 Pod 就绪...")
    for i in range(30):
        pods = run_command("kubectl get pods -l app=nginx")
        if pods and "Running" in pods:
            print(f"✅ Pod 就绪: {pods}")
            break
        time.sleep(5)
    else:
        print("❌ Pod 未就绪")
        return False
    
    # 获取服务地址
    service_info = run_command("kubectl get service nginx-service")
    print(f"🔗 服务信息: {service_info}")
    
    return True

def monitor_app():
    """监控应用状态"""
    print("📊 开始监控应用...")
    
    # 监控 Pod 状态
    print("\n📋 Pod 状态:")
    run_command("kubectl get pods -l app=nginx -o wide")
    
    # 监控 Deployment 状态
    print("\n📦 Deployment 状态:")
    run_command("kubectl describe deployment nginx-deployment")
    
    # 监控 Service 状态
    print("\n🌐 Service 状态:")
    run_command("kubectl describe service nginx-service")
    
    # 模拟客户端请求
    print("\n🌐 模拟客户端请求...")
    try:
        import requests
        # 获取 NodePort 地址
        nodes = run_command("kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type==\"InternalIP\")].address}'")
        node_port = run_command("kubectl get service nginx-service -o jsonpath='{.spec.ports[0].nodePort}'")
        
        if nodes and node_port:
            for node in nodes.split():
                response = requests.get(f"http://{node}:{node_port}", timeout=5)
                print(f"📡 客户端请求 {node}:{node_port} -> 状态码: {response.status_code}")
    except ImportError:
        print("⚠️ 未安装 requests 库,跳过客户端请求测试")

def cleanup():
    """清理资源"""
    print("🧹 开始清理资源...")
    
    # 删除资源
    run_command("kubectl delete -f deployment.yaml")
    run_command("kubectl delete -f service.yaml")
    
    # 删除本地文件
    import os
    for file in ['deployment.yaml', 'service.yaml']:
        if os.path.exists(file):
            os.remove(file)
            print(f"🗑️ 删除文件: {file}")
    
    print("✅ 资源清理完成")

def main():
    """主函数"""
    print("=" * 60)
    print("Kubernetes 部署自动化脚本 v1.0")
    print("=" * 60)
    
    # 导入 yaml 模块
    try:
        import yaml
    except ImportError:
        print("⚠️ 未安装 PyYAML 库,正在安装...")
        run_command("pip install pyyaml")
        import yaml
    
    # 创建 YAML 文件
    create_deployment_yaml()
    create_service_yaml()
    
    # 部署应用
    if deploy_app():
        # 监控应用
        monitor_app()
        
        # 提示用户
        print("\n🎉 应用部署成功!")
        print("📊 统计信息:")
        print("  - Pod 数量: 3")
        print("  - 服务端口: 80")
        print("  - 类型: NodePort")
        
        # 询问是否清理
        choice = input("\n是否要清理资源? (y/n): ").lower()
        if choice == 'y':
            cleanup()
    else:
        print("❌ 部署失败")
        sys.exit(1)

if __name__ == '__main__':
    main()

2. K8s 配置管理工具

python
#!/usr/bin/env python3
"""
K8s 配置管理工具
功能:管理 K8s 配置文件,验证 YAML 格式,部署应用
"""
import yaml
import json
import sys
from typing import Dict, Any

class K8sConfigManager:
    def __init__(self, config_file: str = None):
        self.config_file = config_file
        self.configs = []
    
    def load_config(self, file_path: str):
        """加载 YAML 配置文件"""
        try:
            with open(file_path, 'r') as f:
                config = yaml.safe_load(f)
                self.configs.append((file_path, config))
                print(f"✅ 成功加载配置文件: {file_path}")
                return config
        except Exception as e:
            print(f"❌ 加载配置文件失败: {file_path}, 错误: {e}")
            return None
    
    def validate_config(self, config: Dict[str, Any]) -> bool:
        """验证 K8s 配置的基本格式"""
        required_fields = ['apiVersion', 'kind']
        
        for field in required_fields:
            if field not in config:
                print(f"❌ 配置缺少必需字段: {field}")
                return False
        
        # 根据 kind 验证特定字段
        kind = config.get('kind')
        if kind == 'Deployment':
            if 'spec' not in config:
                print("❌ Deployment 类型配置缺少 spec 字段")
                return False
            if 'template' not in config['spec']:
                print("❌ Deployment 类型配置缺少 template 字段")
                return False
        
        elif kind == 'Service':
            if 'spec' not in config:
                print("❌ Service 类型配置缺少 spec 字段")
                return False
            if 'ports' not in config['spec']:
                print("❌ Service 类型配置缺少 ports 字段")
                return False
        
        elif kind == 'Pod':
            if 'spec' not in config:
                print("❌ Pod 类型配置缺少 spec 字段")
                return False
            if 'containers' not in config['spec']:
                print("❌ Pod 类型配置缺少 containers 字段")
                return False
        
        print(f"✅ {kind} 配置验证通过")
        return True
    
    def validate_all_configs(self) -> bool:
        """验证所有已加载的配置"""
        all_valid = True
        for file_path, config in self.configs:
            print(f"\n📋 验证配置文件: {file_path}")
            if not self.validate_config(config):
                all_valid = False
        
        return all_valid
    
    def generate_manifest(self) -> Dict[str, Any]:
        """生成 K8s 资源清单"""
        manifest = {
            'apiVersion': 'v1',
            'kind': 'List',
            'items': [config for _, config in self.configs]
        }
        
        with open('k8s-manifest.yaml', 'w') as f:
            yaml.dump(manifest, f, default_flow_style=False)
        
        print("✅ K8s 资源清单已生成: k8s-manifest.yaml")
        return manifest
    
    def export_to_json(self, output_file: str = None):
        """导出配置为 JSON 格式"""
        if output_file is None:
            output_file = 'k8s-config.json'
        
        json_data = []
        for file_path, config in self.configs:
            # 转换 YAML 为 JSON 可序列化的格式
            json_config = json.loads(json.dumps(config))
            json_data.append({
                'file': file_path,
                'content': json_config
            })
        
        with open(output_file, 'w') as f:
            json.dump(json_data, f, indent=2)
        
        print(f"✅ 配置已导出到 JSON 文件: {output_file}")
    
    def compare_configs(self, other_manager: 'K8sConfigManager'):
        """比较两个配置管理器中的配置"""
        print("🔍 开始比较配置...")
        
        for file_path, config in self.configs:
            # 查找相同文件名的配置
            other_config = None
            for other_path, other_c in other_manager.configs:
                if file_path == other_path:
                    other_config = other_c
                    break
            
            if other_config:
                # 比较配置内容
                if config == other_config:
                    print(f"✅ 配置相同: {file_path}")
                else:
                    print(f"❌ 配置不同: {file_path}")
                    self._print_differences(config, other_config)
            else:
                print(f"⚠️  找到新配置: {file_path}")
    
    def _print_differences(self, config1: Dict[str, Any], config2: Dict[str, Any]):
        """打印配置差异"""
        import difflib
        
        yaml1 = yaml.dump(config1, default_flow_style=False)
        yaml2 = yaml.dump(config2, default_flow_style=False)
        
        diff = difflib.unified_diff(
            yaml1.splitlines(),
            yaml2.splitlines(),
            fromfile='config1',
            tofile='config2',
            lineterm=''
        )
        
        for line in diff:
            if line.startswith('+'):
                print(f"  + {line[1:]}")
            elif line.startswith('-'):
                print(f"  - {line[1:]}")
            else:
                print(f"    {line}")

def main():
    """主函数"""
    print("=" * 60)
    print("K8s 配置管理工具 v1.0")
    print("=" * 60)
    
    # 初始化配置管理器
    manager = K8sConfigManager()
    
    # 加载配置文件
    config_files = ['deployment.yaml', 'service.yaml', 'pod.yaml']
    for file in config_files:
        manager.load_config(file)
    
    # 验证所有配置
    if manager.validate_all_configs():
        print("\n🎉 所有配置验证通过!")
        
        # 生成资源清单
        manager.generate_manifest()
        
        # 导出为 JSON
        manager.export_to_json()
        
        # 显示统计信息
        print("\n📊 配置统计:")
        print(f"  - 总配置文件数: {len(manager.configs)}")
        print(f"  - Deployment 类型: {sum(1 for _, c in manager.configs if c.get('kind') == 'Deployment')}")
        print(f"  - Service 类型: {sum(1 for _, c in manager.configs if c.get('kind') == 'Service')}")
        print(f"  - Pod 类型: {sum(1 for _, c in manager.configs if c.get('kind') == 'Pod')}")
    else:
        print("\n❌ 配置验证失败,请检查配置")

if __name__ == '__main__':
    main()