17 10月, 2023

01 – Kubernetes 原理和安装

内容纲要

[TOC]

K8S 的角色及组件

管理节点

  • Master(control plane):Kubernetes集群的控制节点,主要职责是调度,即决定应用放在哪里运行。为了实现高可用,可以运行多个Master。Master节点由三个组件组成:
    • apiserver:提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制。
    • scheduler:集群调度器,负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上。
    • controller-manager:集群控制器,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户和令牌控制器。每一个控制器都独立工作以维护其所需的状态。简单来讲,就是负责维护集群的状态,比如:故障检测、自动扩展、滚动更新等。

数据库节点

  • Etcd:key/value 数据库,用于存储Kubernetes集群相关信息,包括节点信息、pod信息、service信息等,只与apiserver交互。

工作节点

  • Node(Data plane):Kubernetes集群的工作节点,负责容器应用的运行,由Master管理,Node负责监控并汇报容器状态,同时根据Master的要求管理容器的生命周期。由两个组件组成:
    • kubelet:负责管控docker容器,如启动、停止、监控运行状态等。它会定期从apiserver获取分配到本机的pod,并根据pod信息启动或停止相应的容器。同时,它也会接收apiserver的http请求,汇报pod的运行状态。也负责Voleme(CVI)和网络(CNI)的管理。
    • kube-proxy:负责为pod提供代理。它会定期从apiserver获取所有的service,并根据service信息创建代理。当某个客户pod要访问其它pod时,访问请求会经过本机的proxy做转发。同时负责为Service提供cluster内部的服务发现和负载均衡。

K8S 的整体工作流程

以 deployment 为例,分析向 k8s 提交一个 yaml 文件后的工作流程:

  1. 准备好一个 Deployment 的 YAML 文件,通过 kubectl 发送到 api-server;
  2. api-server 接收到客户端的请求,将资源内容存储到数据库 etcd 中;
  3. Controller 组件(包括 Scheduler、replication、endpoint )监控资源变化并做出反应;
  4. ReplicaSet 检查数据库变化,创建期望数量的 Pod 实例;
  5. Scheduler 再次检查数据库变化,发现尚未被分配到具体节点的 Pod,然后根据一组相关规则,将 Pod 分配到可以运行它们的节点上,并更新数据库记录,记录 Pod 分配情况;
  6. Kubelet 监控数据库变化,管理后续 Pod 生命周期,发现被分配到它所在节点上运行的那些 Pod。如果发现新 Pod,则会在该节点上运行这个新 Pod;
  7. Kube-proxy 运行在集群的各个主机上,管理网络通信、服务发现、负载均衡。当有数据发送到主机时,其将路由到正确的 Pod 或容器,对于主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮询调度算法(RR)将请求发送到集群中的多个实例。

Kube-apiserver 的工作

当 YAML 文件被提交到 kube-apiserver 后:

  1. 过滤这个请求,完成一些前置性工作(比如:授权、超时、处理、审计等);
  2. 进入 MUX 和 Routes 流程。

    MUX 和 Routes 要做的就是完成 URL 和 Handler 绑定的场所。
    Handler 则是按照 /api(apis)/Groups/Version/Resource,对于 Deployment 来说则是 /api/v1/deployments(node、pid、deployment是 "")找到 Deployment 类型的定义;

  3. kube-apiserver 会根据这个 Deployment 类型的定义,使用用户提交的 YAML 文件里的字段,创建一个 CronJob 对象。

    在这个过程中,API Server 会进行一个 Convert工作,即:把用户提交的 YAML 文件转换成一个叫做 Super Version 的对象,它正式该 API 资源类型所有版本的字段全集。这样用户提交的不同版本的 YAML 文件,就都可以用这个 Super Version 对象来进行处理了。

  4. kube-apiserver 先后进行 Admission() 和 Validation() 操作。

    Validation 负责验证这个对象的各个字段是否合法。这个被验证过的API对象,都保存在 kube-apiserver 中一个叫做 Registry 的数据结构中。

  5. kube-apiserver 把验证过的 API 对象转换为用户最初提交的版本,进行序列化操作,并调用 etcd 的 API 保存起来。

Controller 的工作


从 apiserver 中获取它所关心的对象。这个操作是依靠 Informer 完成的,Informer 和 API 对象一一对应。在创建 Informer 的时候,需要传递一个 networkClient,Informer 通过 networkClient 和 apiserver 建立连接。真正负责维护这个连接的,是 Informer 的 Reflector,Reflector 使用 ListAndWatch 方法获取并监听 API 对象实例的变化。
在 ListAndWatch 机制下,一旦 apiserver 有新的 API 实例被创建、删除或者更新,Reflector 都会收到"事件通知"。
这时,该事件和它所对应的 API 对象这个组合,就被称为增量(Delta),它被放进一个 Delta FIFO Queue 中。
另一方面,Informer 会从这个队列中读取增量。每拿到一个增量,Informer 就会判断这个增量里的事件类型,然后创建或者更新本地对象缓存(Store);并且 Informer 会根据这个事件类型,触发事先注册号的 ResourceEventHandler。这些 Handler 需要在创建控制器的时候注册给它对应的 Informer。
在控制器中,还有一个工作队列(WorkQueue),这个工作队列的作用就是负责同步 Informer 和控制循环之间的数据。

比如在 Handler 中注册了三个 Handler(AddFunc、UpdateFunc、DeleteFunc),然后将该事件对应的 API 对象的 key 加入到工作队列中。

后面的控制循环,则会不断从工作队列中拿到 API 对象的 key,然后到缓存中去查询对应的 API 对象,执行真正的逻辑控制。

各节点需安装的组件

  • master
    • kube-apiserver
    • kube-controller-manager
    • kube-scheduler
    • etcd
    • kubelet
    • kube-proxy
    • docker
  • node
    • kubelet
    • kube-proxy
    • docker

各组件示意图:

单 master K8S 集群部署

环境说明

部署环境: 主机名 IP地址 节点类型 配置
cka01 192.168.6.171 master、etcd 最低2C、4G
cka02 192.168.6.172 worker 最低2C、4G
cka03 192.168.6.173 worker 最低2C、4G
相关组件版本说明: 组件 版本 说明
kubernetes 1.20.9 主程序
docker 19.03.15 容器运行时
calico 3.18.1 网络插件
etcd 3.4.13 数据库
coredns 1.7.0 dns组件
kubernetes-dashboard v2.2.0 web界面

集群安装步骤

  1. 基础环境准备,需要在所有节点上操作

    • 配置主机名
      hostnamectl set-hostname cka01 --static
    • 添加 /etc/hosts (每个节点添加对自己的解析就可以了)
      echo "192.168.6.171 cka01" >> /etc/hosts
    • 清空防火墙
      iptables -F
      iptables -t nat -F
    • 关闭 selinux
      setenforce 0
      sed -i 's/SELINUX=/SELINUX=disabled/g' /etc/selinux/config
    • 设置 yum 源
      yum install -y wget
      wget -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo
      yum install -y epel-release
      sed -i "s/#baseurl/baseurl/g" /etc/yum.repos.d/epel.repo
      sed -i "s/metalink/#metalink/g" /etc/yum.repos.d/epel.repo
      sed -i "s@https\?://download.fedoraproject.org/pub@https://repo.huaweicloud.com@g" /etc/yum.repos.d/epel.repo
    • 关闭 swap

      swapoff -a
      sed -ri 's/(.*swap.*)/#\1/' /etc/fstab
      
      // 为什么要关闭swap?
      // 1. 支持swap很复杂;2. 使用swap的话,性能会很大影响。 所以在 k8s 1.8 版本强制要求必须关闭swap。
    • 配置内核参数

      cat << EOF | sudo tee /etc/sysctl.d/k8s.conf
      net.ipv4.ip_forward = 1
      vm.swappiness = 0
      net.bridge.bridge-nf-call-ip6tables = 1    
      net.bridge.bridge-nf-call-iptables = 1
      EOF
      // 如果两个 net.bridge 报错,可忽略
      // vm.swappiness 的作用是禁止将内存中的数据交换到swap,但是并不会关掉swap。
      
      sysctl -p /etc/sysctl.d/k8s.conf
    • 加载相关内核模块

      cat > /etc/sysconfig/modules/ipvs.modules << EOF
      #! /bin/bash
      modprobe -- br_netfilter
      modprobe -- ip_vs
      modprobe -- ip_vs_rr
      modprobe -- ip_vs_wrr
      modprobe -- ip_vs_sh
      modprobe -- nf_conntrack_ipv4
      EOF
      
      chmod 755 /etc/sysconfig/modules/ipvs.modules && \
      bash /etc/sysconfig/modules/ipvs.modules && \
      lsmod | grep -E 'ip_vs|nf_conntrack_ipv4'                
    • 安装 docker

      yum install -y yum-utils device-mapper-persistent-data lvm2
      wget -O /etc/yum.repos.d/docker-ce.repo https://repo.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo
      sed -i 's+download.docker.com+repo.huaweicloud.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
      yum install -y docker-ce-19.03.15
      mkdir /etc/docker
      
      cat > /etc/docker/daemon.json << EOF
      {
          "log-driver": "json-file",
          "log-opts": {
              "max-size": "100m",
              "max-file": "10"
          },
          "live-restore": true,
          "registry-mirrors": ["https://o9x1boqy.mirror.aliyuncs.com"]
      }
      EOF
      
      systemctl enable docker --now
    • 安装 kubelet、kubectl、kubeadm

      cat < /etc/yum.repos.d/kubernetes.repo
      [kubernetes]
      name=Kubernetes
      baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
      enabled=1
      gpgcheck=1
      repo_gpgcheck=1
      gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
      EOF
      
      yum install -y kubelet-1.20.9 kubeadm-1.20.9 kubectl-1.20.9
      systemctl enable kubelet --now        # kubelet作为单独服务,需要启动
  2. 配置 master 节点

    • 生成 kubeadm-config.yaml 文件
      kubeadm config print init-defaults > kubeadm-config.yaml
    • 编辑 kubeadm-config.yaml 文件
      apiVersion: kubeadm.k8s.io/v1beta2
      bootstrapTokens:
      - groups:
        - system:bootstrappers:kubeadm:default-node-token
        token: abcdef.0123456789abcdef
        ttl: 24h0m0s
        usages:
        - signing
        - authentication
      kind: InitConfiguration
      localAPIEndpoint:
        advertiseAddress: 192.168.6.171        #修改ip地址
        bindPort: 6443
      nodeRegistration:
        criSocket: /var/run/dockershim.sock
        name: cka01        #修改主机名
        taints:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
      ---
      apiServer:
        timeoutForControlPlane: 4m0s
      apiVersion: kubeadm.k8s.io/v1beta2
      certificatesDir: /etc/kubernetes/pki
      clusterName: kubernetes
      controllerManager: {}
      dns:
        type: CoreDNS
      etcd:
        local:
          dataDir: /var/lib/etcd
      imageRepository: registry.aliyuncs.com/google_containers    #修改拉取k8s镜像的仓库,默认的 k8s.gcr.io 是 google 的仓库,需要翻墙
      kind: ClusterConfiguration
      kubernetesVersion: v1.20.9    #修改版本号
      networking:
        dnsDomain: cluster.local
        serviceSubnet: 10.96.0.0/12
        podSubnet: 10.244.0.0/16        #添加Pod使用的子网,calico插件默认使用 192.168.0.0/16 的网络,如果需要自定义网络,则calico会自动从cubeadm工具部署的已运行的k8s配置中读取pod子网。如果是其它工具部署,就需要修改 CALICO_IPV4POOL_CIDR 参数。
      scheduler: {}
      ---
      apiVersion: kubeproxy.config.k8s.io/v1alpha1
      kind: KubeProxyConfiguration
      mode: "ipvs"
    • 根据配置的 kubeadmin-config.yaml 文件部署 master
      kubeadm init --config kubeadm-config.yaml
    • 配置访问集群(kubelet命令需要读取此配置文件)

      • 普通用户:
        mkdir -p $HOME/.kube
        cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
        chown $(id -u):$(id -u) $HOME/.kube/config
      • root 用户:
        export KUBECONFIG=/etc/kubernetes/admin.conf

      部署完成后,发现两个问题:

      1. 使用kubectl get nodes命令发现:master 节点一直处于 NotReady
      2. 使用kubectl get pods -n kube-system命令发现:coredns pod 一直处于 pending

      这两个问题都是因为还没有安装网络插件导致的。

    • 安装网络插件
      curl https://docs.projectcalico.org/manifests/calico.yaml -O
      kubectl apply -f calico.yaml
      //安装完成后,等一会,master的状态就会转变成 Ready

      参考:https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises#install-calico-with-kubernetes-api-datastore-50-nodes-or-less

    • 验证集群状态
      • kubectl get nodes:查看节点状态
      • kubectl get pods -n kube-system:查看所有控制面组件的运行状态
      • kubectl get componentstatuses|cs:查看各组件健康状态
  3. 配置 worker 节点
    在master节点上,当部署master成功时,会返回类似如下信息:

    Then you can join any number of worker nodes by running the following on each as root:
    
    kubeadm join 192.168.6.171:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:e275f28377f62b2884ee501a77fce2a6f9340aed5cc69c2453aee931203f94b0
    // 在worker节点上执行上面命令,就可以将节点加入到K8S集群
    • 将节点加入集群

      kubeadm join 192.168.6.171:6443 --token abcdef.0123456789abcdef \
      --discovery-token-ca-cert-hash sha256:e275f28377f62b2884ee501a77fce2a6f9340aed5cc69c2453aee931203f94b0
      • 注意:以上指令中的token有效期只有24小时,当token失效以后,可以使用 kubeadm token create --print-join-command 生成新的添加节点指令。
    • 从集群中删除节点

      kubectl delete nodes cka03
      
      //如果需要重新加入集群,还需要删除下面两个文件
      // rm -f /etc/kubernetes/kubelet.conf /etc/kubernetes/pki/ca.crt && systemctl restart kubelet

扩展插件安装

在集群安装完成之后,正式投产还需要安装另外一些插件。
安装扩展插件,只需要在 master 节点上操作。

安装 helm

helm的github仓库地址:https://github.com/helm/helm
安装:

wget https://breezey-public.oss-cn-zhangjiakou.aliyuncs.com/softwares/linux/helm-v3.0.3-linux-amd64.tar.gz

#添加官方chart
helm repo add stable https://kubernetes-charts.storage.googleapis.com/

安装 ingress

ingress的github地址:https://github.com/kubernetes/ingress-nginx
安装:

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml

# 修改内容如下: 
...
          image: registry.cn-zhangjiakou.aliyuncs.com/breezey/ingress-nginx:v0.41.2
          ...
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
              hostPort: 80
            - name: https
              containerPort: 443
              protocol: TCP
              hostPort: 443
            - name: webhook
              containerPort: 8443
              protocol: TCP          
...

安装 metrics-server

metrics-server 用于在集群内部向 kube-apiserver 暴露集群指标。
metrics-server 的github地址:https://github.com/kubernetes-sigs/metrics-server
安装:

# 下载安装文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.1/components.yaml

# 修改 metrics-server-deployment.yaml 文件相关配置:
// 1. 修改镜像地址,默认镜像来自k8s.grc.io
image: registry.cn-zhangjiakou.aliyuncs.com/breezey/metrics-server:v0.4.1
// 2. 修改 metrics-server 启动参数
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-insecure-tls
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port

# 部署
kubectl apply -f components.yaml

# 验证
[root@cka-master metrics-server]# kubectl top nodes
NAME           CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
192.168.0.93   169m         8%     1467Mi          39%
cka-node1      138m         6%     899Mi           24%

安装 dashboard

dashboard 的gitbub仓库地址:https://github.com/kubernetes/dashboard
代码仓库当中,有给出安装示例的相关部署文件,我们可以直接获取之后,直接部署即可:

wget https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended.yaml
kubectl apply -f ./recommended.yaml

默认这个部署文件当中,会单独创建一个名为 kubernetes-dashboard 的命名空间,并将 kubernetes-dashboard 部署在该命名空间下。dashboard 的镜像来自 docker hub 官方,所以可不用修改镜像地址,直接从官方获取即可。
但是在默认情况下,dashboard并不对外开放访问端口,这里简化了操作,直接使用 nodePort 的方式将其端口暴露出来,修改 service 部分的定义:

kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 32443
  type: NodePort
  selector:
    k8s-app: kubernetes-dashboard

重新创建 service:

kubectl delete svc kubernetes-dashboard -n kubernetes-dashboard
kubectl apply -f ./recommended.yaml

此时,即可通过浏览器访问web端,端口为 32443。

可以看到出现如上画面,需要输入一个 kubeconfig 文件或者一个 token。事实上在安装 dashboard 时,已为我们默认创建好了一个 serviceaccout,为 "kubernetes-dashboard",并为其生成好了 token,可以通过如下指令获取该 sa 的 token:

kubectl describe secret -n kubernetes-dashboard $(kubectl get secret -n kubernetes-dashboard |grep  kubernetes-dashboard-token | awk '{print $1}') |grep token | awk '{print $2}'

通过该 token 登录集群后,发现很多 namespace 包括一些其他的资源都没有足够的权限查看,这是因为默认我们使用的这个账户权限有限。可以通过对该 sa 授予 cluster-admin 权限来解决这个问题:

# 修改 ClusterRoleBinding 资源内容如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: kubernetes-dashboard
    namespace: kubernetes-dashboard

重新创建 clusterrolebinding:

kubectl delete clusterrolebinding  kubernetes-dashboard -n kubernetes-dashboard
kubectl apply -f ./recommended.yaml

此时,kubernetes-dashboard相关配置都已完成。


重置集群

在安装过程中,如果遇到问题,可以将集群重置,重新开始部署:

# 重置集群
kubeadm reset

# 停止 kubelet
systemctl stop kebelet

# 删除已经部署的容器
docker ps -qa | xargs docker rm -f

# 清理所有目录
rm -rf /etc/kubernetes /var/lib/kubelet /var/lib/etcd /var/lib/cni/

升级集群

注意事项:

  1. 使用 kubeadm 升级集群,不支持跨版本升级;
  2. swap 必须关闭;
  3. 注意数据备份。虽然 kubeadm upgrade 操作不会触碰工作负载,只会更新 kubernetes 组件,但任何时候,备份都是最佳实践;
  4. 节点更新完成后,其上的所有容器都会被重启;

操作升级

参考:https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/

升级 master 节点

升级 kubeadm

  • 检查可用 kubeadm 版本

    # ubuntu
    apt update
    apt-cache madison kubeadm
    
    # centos
    yum list --showduplicates kubeadm --disableexcludes=kubernetes
  • 更新 kubeadm 软件包

    # ubuntu
    apt update 
    apt upgrade -y kubeadm=1.21.0-00
    
    # centos
    yum update -y kubeadm-1.21.0-0
  • 排干要更新的节点

    # 这里以 master 节点为例
    kubectl drain 192.168.6.171 --ignore-daemonsets
  • 创建升级计划

    kubeadm upgrade plan
  • 按照提示执行升级

    kubeadm upgrade apply v1.21.0
  • 将节点重新设置为可调度

    kubectl uncordon cka01

如果有多个 master 节点,升级其它 master 节点,直接执行如下操作即可:

kubeadm upgrade node

升级 kubelet 和 kubectl

# ubuntu
apt update
apt upgrade -y kubelet=1.20.4-00 kubectl=1.21.0-00

# centos
yum update -y kubelet-1.20.4-0 kubectl-1.21.0-0

重启kubelet:

systemctl daemon-reload
systemctl restart kubelet

升级 worker 节点

# 在woker节点上升级kubeadm
yum upgrade kubeadm-1.21.0-0 -y

# 在master节点上排干要升级的worker节点
kubectl drain cka02

# 在worker节点上执行升级操作
kubectl upgrade node 

# 在worker节点上更新kubelet和kubectl
yum upgrade kubelet-1.21.0-0 kubectl-1.21.0-0

# 重启worker节点上的kubelet
systemctl daemon_reload
systemctl restart kubelet

# 在master节点上取消worker节点的不可调度设置
kubectl uncordon cka02

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注