1. 为什么选择Ansible部署Kubernetes集群在云原生时代Kubernetes已经成为容器编排的事实标准。但手动部署一个Kubernetes集群仍然是个复杂的过程特别是在多节点环境下。这就是为什么我们需要Ansible这样的自动化工具来简化部署流程。我最近在openEuler 24.03上部署Kubernetes 1.28集群时深刻体会到Ansible带来的便利。传统的手动部署方式需要我们在每个节点上重复执行相同的命令不仅耗时耗力还容易出错。而使用Ansible我们只需要编写一次Playbook就能在所有节点上自动执行部署任务。Ansible的优势在于它的简单性和幂等性。你不需要在被管理节点上安装任何代理程序只需要SSH连接即可。而且Ansible Playbook可以反复执行不会因为重复运行而导致系统状态异常。这对于Kubernetes集群部署来说简直是完美匹配。2. 环境准备与基础配置2.1 系统要求与节点规划在开始之前我们需要准备至少三台运行openEuler 24.03的服务器。我建议的配置如下1个Master节点4核CPU8GB内存50GB磁盘2个Worker节点2核CPU4GB内存50GB磁盘在实际项目中我遇到过因为资源不足导致的各种奇怪问题。比如kubelet因为内存不足频繁重启或者etcd因为磁盘空间不足而崩溃。所以宁可配置高一些也不要为了节省资源而埋下隐患。2.2 系统初始化配置所有节点都需要进行一些基础配置。首先是关闭防火墙和SELinux这可以避免很多网络和权限问题# 关闭并禁用firewalld sudo systemctl stop firewalld sudo systemctl disable firewalld # 临时关闭SELinux sudo setenforce 0 # 永久关闭SELinux需要重启生效 sudo sed -i s/^SELINUXenforcing$/SELINUXdisabled/ /etc/selinux/config接下来是配置主机名和hosts文件。这一步很重要因为Kubernetes组件之间需要通过主机名互相通信。我在一个项目中曾经因为主机名配置错误导致kubelet无法注册到API Server排查了好久才发现问题。# 在Master节点上 sudo hostnamectl set-hostname k8s-master # 在Worker节点上 sudo hostnamectl set-hostname k8s-node1 sudo hostnamectl set-hostname k8s-node2 # 所有节点都需要编辑/etc/hosts文件 sudo vim /etc/hosts # 添加以下内容 192.168.48.161 k8s-master 192.168.48.162 k8s-node1 192.168.48.163 k8s-node22.3 安装Python和基础工具Ansible是用Python编写的所以所有节点都需要安装Python。openEuler 24.03默认可能没有安装pip我们需要一并安装sudo dnf install -y python3 python3-pip此外我还建议安装一些常用的工具比如curl、wget、vim等方便后续调试sudo dnf install -y curl wget vim git conntrack socat ebtables ethtool ipset iptables rsync3. Ansible控制节点配置3.1 安装Ansible我们选择Master节点作为Ansible控制节点。安装Ansible非常简单sudo dnf update -y sudo dnf install -y ansible安装完成后可以检查版本确认安装成功ansible --version3.2 配置SSH免密登录Ansible通过SSH连接到各个节点为了避免每次执行命令都输入密码我们需要配置SSH密钥认证# 生成SSH密钥对 ssh-keygen -t rsa # 将公钥复制到所有节点 ssh-copy-id rootk8s-master ssh-copy-id rootk8s-node1 ssh-copy-id rootk8s-node2这里有个小技巧如果你管理的节点很多可以使用Ansible的authorized_key模块批量部署公钥比手动操作高效得多。3.3 配置Ansible InventoryAnsible通过Inventory文件知道它要管理哪些机器。我们需要创建一个清晰的Inventory文件sudo vim /etc/ansible/hosts文件内容如下[k8s-masters] k8s-master ansible_host192.168.48.161 [k8s-nodes] k8s-node1 ansible_host192.168.48.162 k8s-node2 ansible_host192.168.48.163 [k8s-cluster:children] k8s-masters k8s-nodes这种分组方式在后续部署Kubernetes时非常有用我们可以针对不同角色的节点执行不同的任务。4. 验证Ansible环境4.1 测试Ansible连通性配置完成后我们可以用ping模块测试Ansible是否能成功连接到所有节点ansible all -m ping如果一切正常你会看到每个节点都返回pong的响应。如果遇到连接问题可以检查以下几点SSH服务是否在所有节点上运行防火墙是否完全关闭/etc/hosts文件中的主机名和IP映射是否正确4.2 执行远程命令为了进一步验证我们可以尝试在所有节点上执行一个简单的命令ansible all -a cat /etc/os-release这个命令会在所有节点上显示openEuler的版本信息。如果能看到正确的输出说明Ansible环境已经准备就绪。5. 编写Kubernetes部署Playbook5.1 Playbook结构设计一个好的Playbook应该结构清晰易于维护。我通常会将Playbook分为几个部分基础环境准备在所有节点上执行Master节点专属任务集群初始化Worker节点专属任务加入集群下面是一个基本的Playbook框架--- - name: Prepare all nodes hosts: k8s-cluster become: yes tasks: - name: Disable swap command: swapoff -a ignore_errors: yes - name: Initialize master node hosts: k8s-masters become: yes tasks: - name: Initialize Kubernetes cluster command: kubeadm init ... - name: Join worker nodes hosts: k8s-nodes become: yes tasks: - name: Join to cluster command: kubeadm join ...5.2 基础环境准备所有节点都需要安装容器运行时和Kubernetes组件。我推荐使用containerd作为容器运行时它比Docker更轻量也是Kubernetes官方推荐的运行时。- name: Install containerd dnf: name: containerd state: present - name: Configure containerd copy: dest: /etc/containerd/config.toml content: | version 2 [plugins] [plugins.io.containerd.grpc.v1.cri] sandbox_image registry.aliyuncs.com/google_containers/pause:3.9 [plugins.io.containerd.grpc.v1.cri.containerd] discard_unpacked_layers true [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] runtime_type io.containerd.runc.v2 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true - name: Start containerd systemd: name: containerd state: started enabled: yes5.3 内核参数配置Kubernetes对Linux内核有一些特殊要求我们需要配置相应的参数- name: Load kernel modules modprobe: name: {{ item }} state: present loop: - br_netfilter - ip_vs - ip_vs_rr - ip_vs_wrr - ip_vs_sh - nf_conntrack - name: Configure sysctl copy: dest: /etc/sysctl.d/k8s.conf content: | net.bridge.bridge-nf-call-ip6tables 1 net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 - name: Apply sysctl command: sysctl --system我曾经因为忘记加载br_netfilter模块导致Flannel网络插件无法正常工作花了很长时间才找到原因。所以这些内核配置千万不能忽略。5.4 安装Kubernetes组件我们可以从阿里云的镜像源安装Kubernetes组件速度会快很多- name: Add Kubernetes repo copy: dest: /etc/yum.repos.d/kubernetes.repo content: | [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck1 repo_gpgcheck1 gpgkeyhttps://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg - name: Install Kubernetes components dnf: name: - kubeadm-1.28.0 - kubelet-1.28.0 - kubectl-1.28.0 state: present - name: Start kubelet systemd: name: kubelet state: started enabled: yes6. 集群初始化与节点加入6.1 初始化Master节点Master节点的初始化是整个部署过程中最关键的一步。我们需要使用kubeadm init命令- name: Initialize Kubernetes cluster command: kubeadm init --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr10.244.0.0/16 --ignore-preflight-errorsswap register: kubeadm_init ignore_errors: yes - name: Setup kubeconfig block: - name: Create .kube directory file: path: /root/.kube state: directory mode: 0700 - name: Copy admin.conf copy: src: /etc/kubernetes/admin.conf dest: /root/.kube/config remote_src: yes owner: root group: root mode: 0600 when: Your Kubernetes control-plane has initialized successfully! in kubeadm_init.stdout初始化成功后kubeadm会输出一个join命令我们需要保存这个命令供Worker节点使用- name: Save join command copy: content: {{ kubeadm_init.stdout_lines[-2] }} --ignore-preflight-errorsswap dest: /root/join-command.sh mode: 0700 when: Your Kubernetes control-plane has initialized successfully! in kubeadm_init.stdout6.2 部署网络插件Kubernetes需要网络插件来实现Pod之间的通信。我推荐使用Flannel它简单易用- name: Deploy Flannel command: kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml when: Your Kubernetes control-plane has initialized successfully! in kubeadm_init.stdout有时候Flannel Pod可能会因为网络问题无法启动。如果遇到这种情况可以尝试删除Flannel Pod让它重新创建- name: Restart Flannel pods command: kubectl delete pods -n kube-flannel --all ignore_errors: yes when: Your Kubernetes control-plane has initialized successfully! in kubeadm_init.stdout6.3 Worker节点加入集群Worker节点加入集群的过程相对简单只需要执行之前保存的join命令- name: Join worker nodes hosts: k8s-nodes become: yes tasks: - name: Get join command from master slurp: src: /root/join-command.sh register: join_command_content delegate_to: {{ groups[k8s-masters][0] }} - name: Save join command locally copy: content: {{ join_command_content.content | b64decode }} dest: /tmp/join-command.sh mode: 0700 - name: Join to cluster command: sh /tmp/join-command.sh7. 验证集群状态部署完成后我们需要验证集群是否正常工作。在Master节点上执行以下命令kubectl get nodes所有节点的状态应该是Ready。如果看到节点状态为NotReady可能是以下原因网络插件没有正确安装kubelet服务没有正常运行节点资源不足还可以检查核心组件状态kubectl get pods -n kube-system确保所有系统Pod都处于Running状态。特别是coredns和flannel相关的Pod它们对集群功能至关重要。8. 常见问题排查8.1 Flannel网络问题Flannel是最容易出问题的组件之一。如果发现Pod无法跨节点通信可以检查以下几点br_netfilter模块是否加载防火墙是否完全关闭/etc/sysctl.d/k8s.conf中的参数是否正确我曾经遇到一个案例Flannel Pod不断重启日志显示Failed to check br_netfilter。解决方法就是确保所有节点都加载了br_netfilter模块modprobe br_netfilter echo br_netfilter /etc/modules-load.d/br_netfilter.conf8.2 CNI插件缺失另一个常见问题是CNI插件缺失表现为Pod一直处于ContainerCreating状态。可以通过以下命令检查kubectl describe pod pod-name如果看到CNI network not ready之类的错误需要安装CNI插件wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz mkdir -p /opt/cni/bin tar -C /opt/cni/bin -xzf cni-plugins-linux-amd64-v1.3.0.tgz systemctl restart kubelet8.3 节点NotReady状态如果节点状态显示NotReady首先检查kubelet服务是否运行systemctl status kubelet如果kubelet正常运行可以查看它的日志journalctl -u kubelet -f常见的问题包括证书过期、资源不足、网络配置错误等。根据具体错误信息进行排查。9. 集群维护与升级9.1 日常维护命令掌握一些常用的kubectl命令对维护集群很有帮助# 查看节点资源使用情况 kubectl top nodes # 查看Pod资源使用情况 kubectl top pods -A # 查看事件 kubectl get events -A --sort-by.metadata.creationTimestamp # 查看组件状态 kubectl get cs9.2 集群升级策略Kubernetes版本升级需要谨慎操作。我建议的升级策略是先升级kubeadm然后升级控制平面节点最后升级Worker节点具体步骤可以参考官方文档。在升级前一定要做好备份# 备份etcd kubectl exec -n kube-system etcd-$(hostname) -- etcdctl snapshot save /var/lib/etcd/snapshot.db # 备份kubeadm配置 kubeadm config view kubeadm-config.yaml10. 扩展功能部署10.1 部署DashboardKubernetes Dashboard提供了Web界面来管理集群。部署步骤如下kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml然后创建ServiceAccount和ClusterRoleBindingapiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard获取访问令牌kubectl -n kubernetes-dashboard create token admin-user10.2 部署监控系统Prometheus和Grafana是监控Kubernetes集群的黄金组合。可以使用kube-prometheus-stack快速部署helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack部署完成后可以通过Grafana Dashboard查看集群各项指标。11. 安全加固建议11.1 RBAC配置Kubernetes的RBAC基于角色的访问控制非常重要。我建议为每个用户或应用创建单独的ServiceAccount遵循最小权限原则只授予必要的权限定期审计RBAC配置11.2 网络策略使用NetworkPolicy可以限制Pod之间的网络流量apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny spec: podSelector: {} policyTypes: - Ingress - Egress这个策略会默认拒绝所有进出Pod的流量然后你可以根据需要添加更细粒度的规则。11.3 Pod安全策略虽然PodSecurityPolicy在Kubernetes 1.25之后被移除但可以使用Pod Security Admission替代apiVersion: v1 kind: Namespace metadata: name: my-namespace labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/warn: restricted这样可以确保在命名空间中创建的Pod符合安全基准。12. 性能优化技巧12.1 资源请求与限制为Pod设置合理的资源请求和限制可以避免资源争用resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi12.2 节点亲和性使用节点亲和性可以将Pod调度到合适的节点上affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: - amd6412.3 Pod拓扑分布对于高可用部署可以使用Pod拓扑分布约束topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: my-app这样可以确保Pod均匀分布在不同的节点上。13. 备份与恢复13.1 etcd备份etcd存储了Kubernetes的所有关键数据定期备份非常重要ETCDCTL_API3 etcdctl --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save snapshot.db13.2 资源备份可以使用velero工具备份整个集群资源velero install \ --provider aws \ --bucket my-backup \ --secret-file ./credentials \ --use-volume-snapshotsfalse \ --plugins velero/velero-plugin-for-aws:v1.0.0 \ --backup-location-config regionminio,s3ForcePathStyletrue,s3Urlhttp://minio:900014. 自动化运维14.1 GitOps实践使用Argo CD可以实现GitOps工作流kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml然后你可以将Kubernetes清单存储在Git仓库中Argo CD会自动同步到集群。14.2 自动扩缩容Kubernetes支持水平和垂直Pod自动扩缩容apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: my-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 5015. 结语通过Ansible自动化部署Kubernetes集群我们不仅提高了效率还确保了部署的一致性和可重复性。openEuler作为国产操作系统的优秀代表与Kubernetes的结合为企业提供了安全可靠的云原生基础平台。在实际生产环境中除了掌握部署技巧外还需要深入理解Kubernetes的各个组件工作原理。只有这样才能在遇到问题时快速定位和解决。希望这篇指南能帮助你在openEuler上顺利部署和管理Kubernetes集群。