Kubernetes系统核心组件之再回首及安全策略

K8s系统核心组件: 

  我们学习了关于K8s常用的组件,也可以基于K8s搭建我们的环境及部署CICD的服务。接下去我们有必要来学习一下关于K8s的核心组件部分。本文主要是介绍一个基本的认知。如果小伙伴们感兴趣也可以相互探讨。

Master和Node

  官网 :https://kubernetes.io/zh/docs/concepts/architecture/master-node-communication/

  K8S集群中的控制节点,负责整个集群的管理和控制,可以做成高可用,防止一台Master打竞技或者不可用。其中有一些关键的组件:比如API Server,Controller Manager,Scheduler等。Node会被Master分配一些工作负载,当某个Node不可用时,会将工作负载转移到其他Node节点上。Node上有一些关键的进程:kubelet,kube-proxy,docker等

  查看集群中的Node :kubectl get nodes     kubectl describe node node-name

kubeadm:

  kubeadm init:集群的基本环境安装好以后我们需要对 master进行初始化,这是初始化的时候的日志信息。

   我们根据上图来简单的看一下在初始化的时候都做了哪些事。

01-进行一系列检查[init之前的检查],以确定这台机器可以部署kubernetes. kubeadm init pre-flight check:
  (1)kubeadm版本与要安装的kubernetes版本的检查
  (2)kubernetes安装的系统需求检查[centos版本、cgroup、docker等]
  (3)用户、主机、端口、swap等
02-生成kubernetes对外提供服务所需要的各种证书可对应目录,也就是生成私钥和数字证书 /etc/kubernetes/pki/*   (1)自建ca,生成ca.key和ca.crt   (2)apiserver的私钥与公钥证书   (3)apiserver访问kubelet使用的客户端私钥与证书   (4)sa.key和sa.pub   (5)etcd相关私钥和数字证书
03-为其他组件生成访问kube-ApiServer所需的配置文件xxx.conf . ls /etc/kubernetes/
  admin.conf controller-manager.conf kubelet.conf scheduler.conf
  (1)# 有了$HOME/.kube/config就可以使用kubectl和K8s集群打交道了,这个文件是来自于admin.config     mkdir -p $HOME/.kube     sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config     sudo chown $(id -u):$(id -g) $HOME/.kube/config   (2)kubeconfig中包含了cluster、user和context信息:kubectl config view   (3)允许kubectl快速切换context,管理多集群
04-为master生成Pod配置文件,这些组件会被master节点上的kubelet读取到,并且创建对应资源 ls /etc/kubernetes/manifests/*.yaml   kube-apiserver.yaml   kube-controller-manager.yaml   kube-scheduler.yaml   这些pod由kubelet直接管理,是静态pod,直接使用主机网络kubelet读取manifests目录并管理各控制平台组件pod的启动与停止要想修改这些pod,直接修改manifests下的yaml文件即可
05-下载镜像[这里的镜像我们已经提前准备好了],等待控制平面启动k8s.gcr.io下载不了,所以我们先提前下载并且tag好了
06-一旦这些 YAML 文件出现在被 kubelet 监视的/etc/kubernetes/manifests/目录下,kubelet就会自动创建这些yaml文件定义的pod,即master组件的容器。master容器启动后,
  kubeadm会通过检查localhost:6443/healthz这个master组件的健康状态检查URL,等待master组件完全运行起来【cat kube-apiserver.yaml里面有健康检查的配置】
07-为集群生成一个bootstrap token,设定当前node为master,master节点将不承担工作负载
08-将ca.crt等 Master节点的重要信息,通过ConfigMap的方式保存在etcd中,工后续部署node节点使用
09-安装默认插件,kubernetes默认kube-proxy和DNS两个插件是必须安装的,dns插件安装了会出于pending状态,要等网络插件安装完成,比如calico
  kubectl get daemonset -n kube-system可以看到kube-proxy和calico[或者其他网络插件]

kubeadm join:

  kubeadm join 192.168.0.51:6443 --token yu1ak0.2dcecvmpozsy8loh \ --discovery-token-ca-cert-hash sha256:5c4a69b3bb05b81b675db5559b0e4d7972f1d0a61195f217161522f464c307b0

  这个命令时主节点初始化的时候生成的,那么在节点加入集群的时候都做了什么呢?

01 join前检查

02 discovery-token-ca-cert-hash用于验证master身份
  可以计算出来,在w节点上执行
  openssl x509 -in /etc/kubernetes/pki/ca.crt -noout -pubkey | openssl rsa -pubin -outform DER 2>/dev/null | sha256sum | cut -d' ' -f1
  最终hash的值
  909adc03d6e4bd676cfc4e04b864530dd19927d8ed9d84e224101ef38ed0bb96

03 token用于master验证node
  在master上节点上,可以查看对应的token
  kubectl get secret -n kube-system | grep bootstrap-token
  得到token的值
  kubectl get secret/bootstrap-token-kggzhc -n kube-system -o yaml
  对token的值进行解码
  echo NHRzZHp0Y2RidDRmd2U5dw==|base64 -d   --->4tsdztcdbt4fwe9w
  最终token的值
  kggzhc.4tsdztcdbt4fwe9w
04 实在忘了怎么办?有些小伙伴可能没有及时保存最后的join信息,或者24小时之后过期了,这时候可以重新生成
  (1)重新生成token
    kubeadm token create

  (2)获取ca证书sha256编码hash值
    openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

  (3)重新生成join信息
    kubeadm join 主节点ip地址:6443 --token token填这里 --discovery-token-ca-cert-hash sha256:哈希值填这里

   主节点的初始化完成,以及node节点的加入也完成了,那么在此过程中涉及的内部组件的创建,以及后续我们需要通过某些核心组件同集群打交道,那么这些组件都扮演着什么样的角色呢?先把核心组件总体过一遍,不妨查看一下之前的K8s架构图,勾起回忆

 

  对之前理解的优化,先是整体

1 kubectl:总得要有一个操作集群的客户端,也就是和集群打交道

2 kube-apiserver:整个集群的中枢纽带,负责的事情很多

(1)/etc/kubernetes/manifests/kube-apiserver.yaml  # kubelet管理的静态pod
(2)--insecure-port=0  # 默认使用http非安全协议访问
(3)安全验证的一些文件
(4)准入策略的拦截器
(5)--authorization-mode=Node,RBAC
(6)--etcd   # 配置apiserver与etcd通信

3 kube-scheduler:单纯地调度pod,按照特定的调度算法和策略,将待调度Pod绑定到集群中某个适合的Node,并写入绑定信息,由对应节点的kubelet服务创建pod。

(1)/etc/kubernetes/manifests/kube-scheduler.yaml  # kubelet管理的静态pod
(2)--address表示只在master节点上提供服务,不对外
(3)kubeconfig表示

4 kube-controller-manager:负责集群中Node、Pod副本、服务的endpoint、命名空间、Service Account、资源配合等管理会划分成不同类型的controller,每个controller都是一个死循环,在循环中controller通过apiserver监视自己控制资源的状态,一旦状态发生变化就会努力改变状态,直到变成期望状态

(1)/etc/kubernetes/manifests/kube-controller-manager.yaml  # kubelet管理的静态pod
(2)参数设置ca-file
(3)多个manager,是否需要进行leader选举

5 kubelet 集群中的所有节点都有运行,用于管理pod和container,每个kubelet会向apiserver注册本节点的信息,并向master节点上报本节点资源使用的情况

(1)kubelet由操作系统init[systemd]进行启动
(2)ls /lib/systemd/system/kubelet.service
(3)systemctl daemon-reload & systemctl restart kubelet

6 kube-proxy:集群中的所有节点都有运行,像service的操作都是由kube-proxy代理的,对于客户端是透明的

(1)kube-proxy由daemonset控制器在各个节点上启动唯一实例
(2)配置参数:/var/lib/kube-proxy/config.conf(pod内)  # 不是静态pod
(3)kubectl get pods -n kube-system
(4)kubectl exec kube-proxy-jt9n4 -n kube-system -- cat /var/lib/kube-proxy/config.conf
(5)mode:""  ---># iptables

7 DNS:域名解析的问题

8 dashboard:需要有监控面板能够监测整个集群的状态

9 etcd:整个集群的配置中心,所有集群的状态数据,对象数据都存储在etcd中,kubeadm引导启动的K8s集群,默认只启动一个etcd节点

(1)/etc/kubernetes/manifests/etcd.yaml  # kubelet管理的静态pod
(2)etcd所使用的相关秘钥在/etc/kubernetes/pki/etcd里面
(3)etcd挂载master节点本地路径/var/lib/etcd用于运行时数据存储,tree

 Kubernetes源码查看方式:

  源码地址 :https://github.com/kubernetes/kubernetes

  https://github.com/kubernetes/kubernetes/tree/release-1.14

 kubectl:

  官网 :https://kubernetes.io/docs/reference/kubectl/overview/

  Kubectl 是一个命令行接口,用于对 Kubernetes 集群运行命令。kubectl 在 $HOME/.kube 目录中寻找一个名为 config 的文件。语法 :kubectl [command] [TYPE] [NAME] [flag]

  command:用于操作k8s集资源对象的命令,比如apply、delete、describe、get等

  TYPE:要操作资源对象的类型,区分大小写,比如pod[pods/po]、deployment

  NAME:要操作对象的具体名称,若不指定,则返回该资源类型的全部对象[是默认命名空间下的]

  flags:可选

 API Server:

  官网 :https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kube-apiserver/

  APIServer提供了K8S各类资源对象的操作,是集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。Kubernetes API server 为 api 对象验证并配置数据,包括 pods、 services、 replicationcontrollers 和其它 api 对象。API Server 提供 REST 操作和到集群共享状态的前端,所有其他组件通过它进行交互。

 (1)查看yaml文件中的apiVersion 。grep -r "apiVersion" .

(2)REST API设计

  api官网 :https://kubernetes.io/docs/concepts/overview/kubernetes-api/

  v1.14 :https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/

 

 (3)想要写Pod的yaml文件,但是我们又不知道具体的标签,这个时候可以查阅如下网址:

  https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#pod-v1-core

(4)kube-apiserver:

lsof -i tcp:8080
vi /etc/kubernetes/manifests/kube-apiserver.yaml  [kubeadm安装方式]
# 查询insecure-port,并将修改端口为8080
insecure-port=8080
# kubect apply生效,需要等待一会
kubectl apply -f kube-apiserver.yaml

(5)查看端口以及访问测试。可以发现结果和kubectl使用一样

lsof -i tcp:8080
curl localhost:8080
curl localhost:8080/api
curl localhost:8080/api/v1
curl localhost:8080/api/v1/pods
curl localhost:8080/api/v1/services

(6)设计一个Pod的url请求

  https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#-strong-write-operations-pod-v1-core-strong-

  curl localhost:8080/api/v1/namespaces/default/pods 通过这个就可以看到默认的命名空间下的 pods的信息。

(7)这种操作还是相对比较麻烦的,哪怕使用kubectl,怎么办?已有先驱为我们做好了这一切https://github.com/kubernetes-client

  Java :https://github.com/kubernetes-client/java

  Go :https://github.com/kubernetes/client-go

集群安全机制之API Server:

  官网 :https://v1-12.docs.kubernetes.io/docs/reference/access-authn-authz/controlling-access/

  对于k8s集群的访问操作,都是通过api server的rest api来实现的,难道所有的操作都允许吗?当然不行,这里就涉及到认证授权准入等操作。

API Server认证(Authentication):

  好比你是一个公司的员工,你有工卡,就能得到该公司的认证。

  官网 :https://v1-12.docs.kubernetes.io/docs/reference/access-authn-authz/controlling-access/#authentication

  说白了,就是如何来识别客户端的身份,K8s集群提供了3种识别客户端身份的方式

  1. HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式
  2. HTTP Token认证:通过一个Token来识别合法用户
  3. HTTP Base认证:通过用户名+密码的方式认证

API Server授权(Authorization):

  官网 :https://v1-12.docs.kubernetes.io/docs/reference/access-authn-authz/controlling-access/#authorization

  • ABAC授权模式:基于属性的权限验证(ABAC: Attribute-Based Access Control)已经不大用了
  • Webhook授权模式
  • RBRC授权模式:基于角色的权限验证。

  Role、ClusterRole、RoleBinding和ClusterRoleBinding。用户可以使用kubectl或者API调用等方式操作这些资源对象。

  • Role对象只能用于授予对某一namespace中资源的访问权限。
  • ClusterRole对象可以授予整个集群范围内资源访问权限
  • RoleBinding可以将同一namespace中的subject(用户)绑定到某个具有特定权限的Role下,则此subject即具有该Role定义的权限。
  • ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限。

  如下图,我们可以看到当有人访问我们的k8s集群中的某些资源的时候,需要进行一些操作,那么需要通过RoleBinding或者ClusterRoleBinding找到他对应的Role或者是ClusterRole。也就是对应的权限。进而决定它可以又哪些操作

Admission Control(准入控制):

  类似我们程序中的拦截器,过滤器

  官网 :https://v1-12.docs.kubernetes.io/docs/reference/access-authn-authz/controlling-access/#admission-control

  • Always:允许所有请求
  • AlwaysPullImages:在启动容器之前总是尝试重新下载镜像
  • AlwaysDeny:禁止所有请求

Scheduler:

  官网 :https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/

  通过调度算法,为待调度Pod列表的每个Pod,从Node列表中选择一个最合适的Node。然后,目标节点上的kubelet通过API Server监听到Kubernetes Scheduler产生的Pod绑定事件,获取对应的Pod清单,下载Image镜像,并启动容器。

  我们通过 kubectl apply 命令创建pod的时候,api server会将该命令持久化进etcd,然后pod Queue会去监听是否有新的请求,然后有序的进行pods的操作,还有一个事当node节点join到集群的时候,kubelet会汇报节点信息,这个时候Node queue就可以拿到节点信息,Scheduler通过这两个信息进行分配。架构图

  流程描述:叙述了scheduler在分配过程中做的事。

  https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/#kube-scheduler-implementation

  1. 预选调度策略:遍历所有目标Node,刷选出符合Pod要求的候选节点
  2. 优选调度策略:在(1)的基础上,采用优选策略算法计算出每个候选节点的积分,积分最高者胜出

预选策略:

  https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/#filtering

  • PodFitsHostPorts:如果 Pod 中定义了 hostPort 属性,那么需要先检查这个指定端口是否 已经被 Node 上其他服务占用了。
  • PodFitsHost:若 pod 对象拥有 hostname 属性,则检查 Node 名称字符串与此属性是否匹配。
  • PodFitsResources:检查 Node 上是否有足够的资源(如,cpu 和内存)来满足 pod 的资源请求。
  • PodMatchNodeSelector:检查 Node 的 标签 是否能匹配 Pod 属性上 Node 的 标签 值。
  • NoVolumeZoneConflict:检测 pod 请求的 Volumes 在 Node 上是否可用,因为某些存储卷存在区域调度约束。
  • NoDiskConflict:检查 Pod 对象请求的存储卷在 Node 上是否可用,若不存在冲突则通过检查。
  • MaxCSIVolumeCount:检查 Node 上已经挂载的 CSI 存储卷数量是否超过了指定的最大值。
  • CheckNodeMemoryPressure:如果 Node 上报了内存资源压力过大,而且没有配置异常,那么 Pod 将不会被调度到这个 Node 上。
  • CheckNodePIDPressure:如果 Node 上报了 PID 资源压力过大,而且没有配置异常,那么 Pod 将不会被调度到这个 Node 上。
  • CheckNodeDiskPressure:如果 Node 上报了磁盘资源压力过大(文件系统满了或者将近满了), 而且配置异常,那么 Pod 将不会被调度到这个 Node 上。
  • CheckNodeCondition:Node 可以上报其自身的状态,如磁盘、网络不可用,表明 kubelet 未准备好运行 pod。 如果 Node 被设置成这种状态,那么 pod 将不会被调度到这个 Node 上。
  • PodToleratesNodeTaints:检查 pod 属性上的 tolerations 能否容忍 Node 的 taints。
  • CheckVolumeBinding:检查 Node 上已经绑定的和未绑定的 PVCs 能否满足 Pod 对象的存储卷需求。

优选策略:

  https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/#scoring

  • SelectorSpreadPriority:尽量将归属于同一个 Service、StatefulSet 或 ReplicaSet 的 Pod 资源分散到不同的 Node 上。
  • InterPodAffinityPriority:遍历 Pod 对象的亲和性条目,并将那些能够匹配到给定 Node 的条目的权重相加,结果值越大的 Node 得分越高。
  • LeastRequestedPriority:空闲资源比例越高的 Node 得分越高。换句话说,Node 上的 Pod 越多,并且资源被占用的越多,那么这个 Node 的得分就会越少。
  • MostRequestedPriority:空闲资源比例越低的 Node 得分越高。这个调度策略将会把你所有的工作负载(Pod)调度到尽量少的 Node 上。
  • RequestedToCapacityRatioPriority:为 Node 上每个资源占用比例设定得分值,给资源打分函数在打分时使用。
  • BalancedResourceAllocation:优选那些使得资源利用率更为均衡的节点。
  • NodePreferAvoidPodsPriority:这个策略将根据 Node 的注解信息中是否含有 scheduler.alpha.kubernetes.io/preferAvoidPods 来 计算其优先级。使用这个策略可以将两个不同 Pod 运行在不同的 Node 上。
  • NodeAffinityPriority:基于 Pod 属性中 PreferredDuringSchedulingIgnoredDuringExecution 来进行 Node 亲和性调度。你可以通过这篇文章 Pods 到 Nodes 的分派 来了解到更详细的内容。
  • TaintTolerationPriority:基于 Pod 中对每个 Node 上污点容忍程度进行优先级评估,这个策略能够调整待选 Node 的排名。
  • ImageLocalityPriority:Node 上已经拥有 Pod 需要的 容器镜像 的 Node 会有较高的优先级。
  • ServiceSpreadingPriority:这个调度策略的主要目的是确保将归属于同一个 Service 的 Pod 调度到不同的 Node 上。如果 Node 上 没有归属于同一个 Service 的 Pod,这个策略更倾向于将 Pod 调度到这类 Node 上。最终的目的:即使在一个 Node 宕机之后 Service 也具有很强容灾能力。
  • CalculateAntiAffinityPriorityMap:这个策略主要是用来实现pod反亲和。
  • EqualPriorityMap:将所有的 Node 设置成相同的权重为 1。

Node实战

(1)创建 scheduler-/scheduler-node.yaml。主要是体现node的调度

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scheduler-node
spec:
  selector:
    matchLabels:
      app: scheduler-node
  replicas: 1
  template:
    metadata:
      labels:
        app: scheduler-node
    spec:
      containers:
      - name: scheduler-node
        image: registry.cn-hangzhou.aliyuncs.com/itcrazy2016/test-docker-image:v1.0
        ports:
        - containerPort: 8080
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: beta.kubernetes.io/arch
                operator: In
                values:
                - amd641
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: disktype
                operator: NotIn
                values:
                - ssd

  kubectl get nodes w1 -o yaml 。找到labels,可以发现这里有6个标签。而我们的yaml里要求的是标签的key是 beta.kubernetes.io/arch ,而value是amd641他是不存在的,那么创建这个pod的时候会是怎么样的呢?

  kubectl apply -f scheduler-node.yaml

  kubectl get pods

  kubectl describe pod pod-name

   会发现整个pod会处于一个 Pending的状态,同时会报错,信息是两个节点是不可用的。没有匹配到选择。这个时候我们只要把value改成amd64就可以启动成功。

Pod的配置:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution: #这个是必须要满足的
      nodeSelectorTerms:
        - matchExpressions:
          - key: beta.kubernetes.io/arch
          operator: In
          values:
          - amd641
    preferredDuringSchedulingIgnoredDuringExecution: #这个是最好满足
    - weight: 1
      preference:
        matchExpressions:
        - key: disktype
          operator: NotIn
          values:
          - ssd

kubelet:

  官网 :https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/

  在k8s集群中,每个Node上都会启动一个kubelet服务进程,用于处理master节点下发到本节点的任务。管理Pod及Pod中的容器,每个kubelet进程会在API Server上注册节点自身信息,定期向Master节点汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。

kube-proxy:

  官网 :https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/

  在k8s集群中,每个Node上都会运行一个kube-proxy进行,它是Service的透明代理兼负载均衡器,核心功能是将某个Service的访问请求转发到后端的多个Pod实例上。

posted @ 2020-01-07 20:04  吴振照  阅读(571)  评论(0编辑  收藏  举报