K8S(1)

K8S简介

K8S是负责自动化运维管理多个Docker程序的集群

传统的后端部署方式当请求量上来,已部署的服务会响应不过来。传统的做法往往是,如果请求量、内存、CPU超过阈值做了告警,运维马上再加几台服务器,部署好服务之后,接入负载均衡来分担已有服务的压力。

  • K8S自动化运维管理Docker(容器化)程序

K8S是属于主从设备模型(Master-Slave架构),即有Master节点负责核心的调度、管理和运维,Slave节点则在执行用户的程序。在K8S中,主节点: Master Node或者Head Node(本文采用Master Node称呼方式),而从节点则被称为Worker Node或者Node**(本文采用Worker Node称呼方式)。

要注意一点:Master Node和Worker Node是分别安装了K8S的Master和Woker组件的实体服务器,每个Node都对应了一台实体服务器(虽然Master Node可以和其中一个Worker Node安装在同一台服务器,但是建议Master Node单独部署),所有Master Node和Worker Node组成了K8S集群,同一个集群可能存在多个Master Node和Worker Node。

master node组件

  • API ServerK8S的请求入口服务。API Server负责接收K8S所有请求(来自UI界面或者CLI命令行工具),然后,API Server根据用户的具体请求,去通知其他组件干活。
  • SchedulerK8S所有Worker Node的调度器。当用户要部署服务时,Scheduler会选择最合适的Worker Node(服务器)来部署。
  • Controller ManagerK8S所有Worker Node的监控器。Controller Manager有很多具体的Controller,在文章Components of Kubernetes Architecture中提到的有Node Controller、Service Controller、Volume Controller等。Controller负责监控和调整在Worker Node上部署的服务的状态,比如用户要求A服务部署2个副本,那么当其中一个服务挂了的时候,Controller会马上调整,让Scheduler再选择一个Worker Node重新部署服务。
  • etcdK8S的存储服务。etcd存储了K8S的关键配置和用户配置,K8S中仅API Server才具备读写权限,其他组件必须通过API Server的接口才能读写数据(见Kubernetes Works Like an Operating System)。

Worker Node

  • KubeletWorker Node的监视器,以及与Master Node的通讯器。Kubelet是Master Node安插在Worker Node上的“眼线”,它会定期向Worker Node汇报自己Node上运行的服务的状态,并接受来自Master Node的指示采取调整措施。
  • Kube-ProxyK8S的网络代理。Kube-Proxy负责Node在K8S的网络通讯、以及对外部网络流量的负载均衡。

总结来看,K8S的Master Node具备:请求入口管理(API Server),Worker Node调度(Scheduler),监控和自动调节(Controller Manager),以及存储功能(etcd);而K8S的Worker Node具备:状态和监控收集(Kubelet),网络和负载均衡(Kube-Proxy)、保障容器化运行环境(Container Runtime)、以及定制化功能(Add-Ons)。

K8S重要概念

Pod实例

Pod是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。

Pod可以被理解成一群可以共享网络、存储和计算资源的容器化服务的集合。再打个形象的比喻,在同一个Pod里的几个Docker服务/程序,好像被部署在同一台机器上,可以通过localhost互相访问,并且可以共用Pod里的存储资源(这里是指Docker可以挂载Pod内的数据卷,数据卷的概念,后文会详细讲述,暂时理解为“需要手动mount的磁盘”)。笔者总结Pod如下图,可以看到:同一个Pod之间的Container可以通过localhost互相访问,并且可以挂载Pod内所有的数据卷;但是不同的Pod之间的Container不能用localhost访问,也不能挂载其他Pod的数据卷

apiVersion: v1
kind: Pod  
#kind记录该yaml的对象,比如这是一份Pod的yaml配置文件,那么值内容就是`Pod`,即他是什么的配置文件
metadata:
  name: memory-demo # pod名字
  namespace: mem-example # 该pod的命名空间
spec:  #spec记录了Pod内部所有的资源的详细信息
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    ports:
    - containerPort: 30001
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
  • apiVersion记录K8S的API Server版本,现在看到的都是v1,用户不用管。

  • kind记录该yaml的对象,比如这是一份Pod的yaml配置文件,那么值内容就是Pod

  • metadata记录了Pod自身的元数据,比如这个Pod的名字、这个Pod属于哪个namespace(命名空间的概念,后文会详述,暂时理解为“同一个命名空间内的对象互相可见”)。

  • spec记录了Pod内部所有的资源的详细信息,看懂这个很重要:

    • containers记录了Pod内的容器信息,containers包括了:name容器名,image容器的镜像地址,resources容器需要的CPU、内存、GPU等资源,command容器的入口命令,args容器的入口参数,volumeMounts容器要挂载的Pod数据卷等。可以看到,上述这些信息都是启动容器的必要和必需的信息

    • volumes记录了Pod内的数据卷信息

数据卷volume是Pod内部的磁盘资源, 注意volumeMounts是通过它将主机路径卷挂载到容器中对应的位置

volume制定主机卷,而volumeMounts指定挂载到容器内的卷的名称和挂载路径

Container 容器

本文中提到的镜像Image、容器Container,都指代了Pod下的一个container一个Pod内可以有多个容器container。一般来说,我们部署的大多是标准容器( Application Container)注意镜像需要从dockerhub上拉取,也就意味着你要将自己镜像发布。

Deployment 和 ReplicaSet(简称RS)

Deployment的作用是管理和控制Pod和ReplicaSet,管控它们运行在用户期望的状态中。哎,打个形象的比喻,Deployment就是包工头,主要负责监督底下的工人Pod干活,确保每时每刻有用户要求数量的Pod在工作。如果一旦发现某个工人Pod不行了,就赶紧新拉一个Pod过来替换它。

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。

再来翻译下:ReplicaSet的作用就是管理和控制Pod,管控他们好好干活。但是,ReplicaSet受控于Deployment。形象来说,ReplicaSet就是总包工头手下的小包工头

具体点的例子:

apiVersion: apps/v1
kind: Deployment
metadata:  #:包含有关 Deployment 对象的元数据,例如名称和命名空间等
  name: weblens
  namespace: default
spec: # 定义pod
  replicas: 3
  selector:  # 定义匹配 Pod 副本的标签选择器。
    matchLabels:
      app: weblens
  template: # 定义要使用的 Pod 模板。
    metadata: # pod的名称
      labels:  #定义 Pod 对象的标签
        app: weblens 
    spec: #定义容器
      containers:
      - name: weblens
        image: guardianlux/iweblens-server:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 30001
       # 是 Kubernetes 中容器定义中的一个属性,用于指定容器将监听的端口号。每当创建一个容器时,Kubernetes 将在该容器内部打开指定的端口,并允许其他容器或外部流量通过该端口与该容器进行通信。
        resources:
            limits:
              cpu: "0.5"
              memory: "512Mi"
            requests:
              cpu: "0.5"
              memory: "512Mi"    

Service and ingress

Service和Ingress则负责管控Pod网络服务。 Kubernetes 为 Pods 提供自己的 IP 地址,并为一组 Pod 提供相同的 DNS 名, 并且可以在它们之间进行负载均衡。K8S中的服务(Service)并不是我们常说的“服务”的含义,而更像是网关层,是若干个Pod的流量入口、流量均衡器。

Service是K8S服务的核心,屏蔽了服务细节,统一对外暴露服务接口,真正做到了“微服务”。举个例子,我们的一个服务A,部署了3个备份,也就是3个Pod;对于用户来说,只需要关注一个Service的入口就可以,而不需要操心究竟应该请求哪一个Pod。优势非常明显:一方面外部用户不需要感知因为Pod上服务的意外崩溃、K8S重新拉起Pod而造成的IP变更,外部用户也不需要感知因升级、变更服务带来的Pod替换而造成的IP变化,另一方面,Service还可以做流量负载均衡

通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求进行负载分发到后端的各个容器应用上。至于如何负载分发,我们不用去担心,k8s自己搞定。

简单来说Service就是一个把所有Pod统一成一个组,然后对外提供固定一个IP,具体是哪些Pod,可以通过之前介绍到的Label标签来进行设置,假设一个pod死掉,副本控制器在生成一个pod,这是pod ip肯定会变,但是我们不去关心你pod ip是多少,我们只知道service ip 没变就好了,因为新的pod 早就加入到我的service中了,各个服务之间通信是通过service 唯一ip来通信的。

但是,Service主要负责K8S集群内部的网络拓扑。那么集群外部怎么访问集群内部呢?这个时候就需要Ingress了,官方文档中的解释是:

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。
Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

翻译一下:Ingress是整个K8S集群的接入层,复杂集群内外通讯。

案例 k8s外网访问业务应用

一套简单的架构,nginx做反向代理,后端web为8个tomcat实例,在k8s集群中怎么实现呢,我只需要跑8个pod,每个pod运行tomcat容器,service池化这8个pod,而且副本控制会自动动态控制pod数量,少于8个他会创建到8个,多余8个会自动删除到8个。而且我们访问service ip,k8s中的kube-proxy会自动负载均衡后端的8个pod,这一套服务集群内部访问,只需要一个service ip 和端口号就可以;同理,redis集群,同样可以这样实现,每个service相当于微服务,各个服务之间灵活通信。

  • 创建副本控制资源名为nginx-deployment,运行两个pod,每个pod运行一个nginx容器,容器端口开放80.

[root@master yaml]# cat nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx  
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx  #就是说哪些Pod被Service池化是根据Label标签来的,此行nginx字样,后面我们创建  						Service会用到
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80


kubectl create -f nginx.yaml 
deployment.apps/nginx-deployment created

创建Service 池化刚才的两个nginx pod

[root@master yaml]# cat nginx-svc.yml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 8080  这个资源(svc)开放的端口
    targetPort: 80

# selector选择之前Label标签为nginx的Pod作为Service池化的对象,
# 最后说的是把Service的8080端口映射到Pod的80端口。
# nodeport是 Kubernetes 中一种服务类型,它允许将pod端口映射到节点上的某个端口。每当创建 NodePort 类型的服务时,Kubernetes 将在每个节点上打开一个相同的端口,以便外部流量可以访问该端口,并通过负载均衡将请求路由到 Pod 中运行的容器。
kubectl apply -f nginx-svc.yml 
service/nginx-svc created


2388015.webp)

创建完成之后nginx-svc会分配到一个cluster-ip,可以通过该ip访问后端nginx业务。
那它是怎么实现的呢?答案是通过iptables实现的地址转换和端口转换,自己去研究

外网地址访问设置。在实际生产环境中,对Service的访问可能会有两种来源:Kubernetes集群内部的程序(Pod)和Kubernetes集群外部,为了满足上述的场景,Kubernetes service有以下三种类型:

1.ClusterIP:提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。
2.NodePort:在每个Node上打开一个随机端口并且每个Node的端口都是一样的,通过:NodePort的方式Kubernetes集群外部的程序可以访问Service。
3.LoadBalancer:利用Cloud Provider特有的Load Balancer对外提供服务,Cloud Provider负责将Load Balancer的流量导向Service

本篇文章我着重讲下第二种方式,也就是NodePort方式

如果刚开始你没有设置NodePort这个type的时候在端口那只会显示一个8080端口,而设置了之后会看到多了一个端口也就是37884,那8080就是是cluster-ip监听的端口,那37844就是在node节点上新起的一个端口(K8s会从30000~32767中分配一个可用的端口),只用于端口转发,转发到service的端口。
<用户访问服务,每个节点都会监听这个端口,并转发给Service,service负载均衡调度后端pod,也就是防止说一个节点挂了影响访问>。
说的再通俗一点,集群内部可以直接通过service的ip+端口访问,而nodeport就是为了外网访问服务,给node开了一个端口,转发到service的ip+端口。
可能有人会问了,说这里的Service可不可以固定?当时可以了,可以在Service nginx-svc.yml文件里面添加一个nodeport。


从上到下依次为 nodePort sevicePort 容器port 且访问依然是负载均衡的

参考链接:https://www.jianshu.com/p/50b930fa7ca3

https://blog.csdn.net/weixin_44267608/article/details/102728050

  • 对于第一种情况即cluster-ip的情况, 我们可以指定代领proxy

启动kubernetes proxy模式
kubectl proxy --port=8080

  • 对于LoadBalancer来说。必须在云服务器使用,服务允许将容器端口映射到一个公共 IP 地址,并使用外部负载均衡器将请求路由到 Pod 中运行的容器。在公共云环境中,Kubernetes 可以与云提供商的负载均衡器集成,从而自动创建和配置外部负载均衡器。这使得外部客户端可以通过唯一的 IP 地址访问该服务,并通过负载均衡将请求路由到 Pod 中运行的容器。需要注意的是,在本地或私有数据中心运行 Kubernetes 集群时,需要自己提供外部负载均衡器,并将其配置为与 Kubernetes 集群相关联。

  • 因此,LoadBalancer 类型的服务适用于需要将服务暴露到公共网络中的场景,而 NodePort 类型的服务适用于在集群内部不同命名空间或节点之间进行服务发现和负载均衡的场景。

    具体做法见https://zhuanlan.zhihu.com/p/266422557

  • NodePort 服务是引导外部流量到你的服务的最原始方式。NodePort,正如这个名字所示,在所有节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。

  • Ingress
    有别于以上所有例子,Ingress 事实上不是一种服务类型。相反,它处于多个服务的前端,扮演着“智能路由”或者集群入口的角色。

    你可以用 Ingress 来做许多不同的事情,各种不同类型的 Ingress 控制器也有不同的能力。

    GKE 上的默认 ingress 控制器是启动一个 HTTP(S) Load Balancer。它允许你基于路径或者子域名来路由流量到后端服务。例如,你可以将任何发往域名 foo.yourdomain.com 的流量转到 foo 服务,将路径 yourdomain.com/bar/path 的流量转到 bar 服务。

namespace

而namespace则是为了服务整个K8S集群的与pod没什么关系。namespace是为了把一个K8S集群划分为若干个资源不可共享的虚拟集群而诞生的。也就是说,可以通过在K8S集群内创建namespace来分隔资源和对象

比如我有2个业务A和B,那么我可以创建ns-a和ns-b分别部署业务A和B的服务,如在ns-a中部署了一个deployment,名字是hello,返回用户的是“hello a”;在ns-b中也部署了一个deployment,名字恰巧也是hello,返回用户的是“hello b”(要知道,在同一个namespace下deployment不能同名;但是不同namespace之间没有影响)

kind

未完待续

posted @ 2023-04-10 09:50  ZZX11  阅读(80)  评论(0编辑  收藏  举报