K8S知识点记录

文章目录

一.K8s组件

整体调用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

各个组件调用关系

在这里插入图片描述

二.K8s常用知识点

1.Namespace

Namespace用于对k8s中资源对象的分组。namespace之间没有嵌套或层级关系。一个资源对象只能属于一个namespace。不同组之间的对象是隔离的,互相不可见。

常见的pods, services, replication controllers和deployments等都是属于某一个namespace的(默认是default),而node, persistentVolumes等则不属于任何namespace。

Namespace 适合用于隔离不同用户创建的资源。

注意:namespace无法保证网络的隔离性,比如说service可以跨namespace访问。

默认来说Kubernetes具有如下3个namespace:

  • default: k8s默认的namespace,如果操作如果不指明namespace,默认会操作名为default的namespace。
  • kube-system: k8s系统自己运行所需的资源对象所在的namespace。
  • kube-public: k8s自动创建的namespace,对所有用户可见。适合放置集群范围都可见的服务。

创建namespace

#test.yaml:

kind: Namespace
apiVersion: v1
metadata:
  name: test
labels:
  name: test

╰─➤  kubectl apply -f test.yaml --validate=false                                                                                                          1 ↵
namespace/test created

查看namespace

kubectl get namespace

在这里插入图片描述

在namespace中创建资源

Pod YAML文件

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  namespace: test
  labels:
    name: mypod
spec:
  containers:
  - name: mypod
    image: nginx
╰─➤  kubectl apply -f  nginx-pod.yaml                                                                                                                        1 ↵
pod/mypod created

查看namespace pods

╰─➤  kubectl get pods --namespace=test
NAME    READY   STATUS    RESTARTS   AGE
mypod   1/1     Running   0          5m4s```

2.Pod

k8s 创建的pod会被分配到不同的 Node 上

一般是使用deployment创建 与管理 pod

创建nginx pod,port-forward 实现本地访问

  • 创建nginx pod

    k create -f nginx-pod.yaml
    

    nginx-pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        name: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
    
  • 查看pod状态
    在这里插入图片描述

  • 进入pod容器中查看nginx

    k exec -it nginx /bin/bash
    

    在这里插入图片描述

  • 本地端口映射
    k port-forward nginx 8080:80

    在这里插入图片描述
    在这里插入图片描述

创建一个redis pod,挂载conf文件

在这里插入图片描述
redis.conf

#daemonize yes
pidfile /data/redis.pid
port 6379
tcp-backlog 30000
timeout 0
tcp-keepalive 10
loglevel notice
logfile /data/redis.log
databases 16
#save 900 1
#save 300 10
#save 60 10000
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
requirepass ibalife
maxclients 30000
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events KEA
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 1000
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
# 在 qa-ibaboss-elk namespace 中创建 configmap
kubectl create configmap qa-ibaboss-elk-redis-conf --from-file=redis.conf -n spacename
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: qa-ibaboss-elk-redis
  namespace: test
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: qa-ibaboss-elk-redis
    spec:
      containers:
        - name: qa-ibaboss-elk-redis
          image: redis
          volumeMounts:
            - name: foo
              mountPath: "/usr/local/etc"
          command:
            - "redis-server"
          args:
            - "/usr/local/etc/redis/redis.conf"
      volumes:
        - name: foo
          configMap:
            name: qa-ibaboss-elk-redis-conf
            items:
              - key: redis.conf
                path: redis/redis.conf

3.Service

service是什么?

在说明Service是什么之前先了解下Service的使用场景:

当客户端想要访问K8S集群中的pod时,需要知道pod的ip以及端口,那K8S中如何在不知道pod的地址信息的情况下进行pod服务的快速连接?

若某一node上的pod发生故障,K8S最大的特点就是能够给感知和重启该pod,但是pod重启后ip会发生变化,那么客户端如何感知并保持对pod的访问?

如果多个pod组合在一起形成pod组,如何在被访问时达到负载均衡的效果?

针对上面三种需求,K8S提出了Service的概念,意在解决上述三个问题和场景,下面俩看看Service的定义:

Kubernetes Service是为了管理具有相同功能的一组Pod而定义的一种对象,Service具体的作用和场景如下:

  • 通过Pod的Label Selector访问Pod组。

  • Service的IP保持不变(Headless Servcie除外,下面会单独讲),保证了访问接口的稳定性,屏蔽了Pod的IP地址变化带来的影响,进而实现解耦合。虽然这样,还是建议使用ServiceName进行访问。

  • Service通过kube-proxy借助iptables/ipvs提供负载均衡的能力,实现反向代理,将请求转发到合适的Pod上。

Service可以看作是一组提供相同服务的Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。

Service的类型

在Serive定义时,我们需要指定spec.type字段,这个字段拥有四个选项:

  • ClusterIP。默认值。给这个Service分配一个Cluster IP,它是Kubernetes系统自动分配的虚拟IP,因此只能在集群内部访问。
  • NodePort。将Service通过指定的Node上的端口暴露给外部。通过此方法,访问任意一个NodeIP:nodePort都将路由到ClusterIP,从而成功获得该服务。
  • LoadBalancer。在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort。此模式只能在云服务器(AWS等)上使用。
  • ExternalName。将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。

在这里插入图片描述

2.1 ClusterIp

ClusterIP主要在每个node节点使用iptables,将发向ClusterIP对应端口的数据,转发到kube-proxy中。然后kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的pod的地址和端口。

在这里插入图片描述

2.2 NodePort类型

在定义Service时指定spec.type=NodePort,并指定spec.ports.nodePort的值,Kubernetes就会在集群中的每一个Node上打开你定义的这个端口,这样,就能够从外部通过任意一个NodeIP:nodePort访问到这个Service了。

NodePort的原理在于在 Node上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy进一步到给对应的 pod。

这种类型的service工作流程为:
Client----->NodeIP:NodePort----->ClusterIP:ServicePort----->PodIP:ContainerPort
https://www.jianshu.com/p/72e0d3033ab8

在这里插入图片描述

在这里插入图片描述

下面是一个简单的例子:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    run: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    nodePort: 30001
    targetPort: 5003
  type: NodePort

假如有3个app: nginx Pod运行在3个不同的Node中,那么此时客户端访问任意一个Node的30001端口都能访问到这个nginx服务。
NodeIP: 30001=> ClusterIP:80 => PodIP:5003

2.3 LoadBalancer

LoadBalancer和NodePort其实是同一种方式。区别在于LoadBalancer比NodePort多了一步,就是可以调用Cloud provider去创建LB来向节点导流。

使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。

在这里插入图片描述
在这里插入图片描述

service的三种端口

port
service暴露在cluster ip上的端口,:port 是提供给集群内部客户访问service的入口。

nodePort
nodePort是k8s提供给集群外部客户访问service入口的一种方式,:nodePort 是提供给集群外部客户访问service的入口。

targetPort
targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。

port、nodePort总结
总的来说,port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。

kubectl expose

将资源暴露为新的Kubernetes Service。

指定deployment、service、replica set、replication controller或pod ,并使用该资源的选择器作为指定端口上新服务的选择器。deployment 或 replica set只有当其选择器可转换为service支持的选择器时,即当选择器仅包含matchLabels组件时才会作为暴露新的Service。

为RC的nginx创建service,并通过Service的80端口转发至容器的8000端口上。

kubectl expose rc nginx --port=80 --target-port=8000

集群内访问 Service

在集群里面,其他 pod 要怎么访问到我们所创建的这个 service 呢?有三种方式:

首先我们可以通过 service 的虚拟 IP 去访问,比如说刚创建的 my-service 这个服务,通过 kubectl get svc 或者 kubectl discribe service 都可以看到它的虚拟 IP 地址是 172.29.3.27,端口是 80,然后就可以通过这个虚拟 IP 及端口在 pod 里面直接访问到这个 service 的地址。

第二种方式直接访问服务名,依靠 DNS 解析,就是同一个 namespace 里 pod 可以直接通过 service 的名字去访问到刚才所声明的这个 service。不同的 namespace 里面,我们可以通过 service 名字加“.”,然后加 service 所在的哪个 namespace 去访问这个 service,例如我们直接用 curl 去访问,就是 my-service:80 就可以访问到这个 service。

第三种是通过环境变量访问,在同一个 namespace 里的 pod 启动时,K8s 会把 service 的一些 IP 地址、端口,以及一些简单的配置,通过环境变量的方式放到 K8s 的 pod 里面。在 K8s pod 的容器启动之后,通过读取系统的环境变量比读取到 namespace 里面其他 service 配置的一个地址,或者是它的端口号等等。比如在集群的某一个 pod 里面,可以直接通过 curl $ 取到一个环境变量的值,比如取到 MY_SERVICE_SERVICE_HOST 就是它的一个 IP 地址,MY_SERVICE 就是刚才我们声明的 MY_SERVICE,SERVICE_PORT 就是它的端口号,这样也可以请求到集群里面的 MY_SERVICE 这个 service。

4.Ingress

Ingress 包含两大组件:Ingress Controller 和 Ingress。

在这里插入图片描述

Ingress 简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;

Ingress Controoler

Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,并按照规则模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图:

在这里插入图片描述

当然在实际应用中,最新版本 Kubernetes 已经将 Nginx 与 Ingress Controller 合并为一个组件,所以 Nginx 无需单独部署,只需要部署 Ingress Controller 即可

(注意:写入 nginx.conf 的不是service的地址,而是service backend 的 pod 的地址,避免在 service 在增加一层负载均衡转发)

https://www.jianshu.com/p/3574ecda6417
部署ingress及使用

高可靠部署架构

在Kubernetes集群中,Ingress作为集群流量接入层,Ingress的高可靠性显得尤为重要

当您成功创建一个ACK集群后,默认情况下,集群内部已经部署了一套拥有2个Pod副本的Nginx Ingress Controller服务,其前端挂载在一个公网SLB实例上。
https://help.aliyun.com/document_detail/151524.html

高可靠性首先要解决的就是单点故障问题,一般常用的是采用多副本部署的方式,我们在Kubernetes集群中部署高可靠Ingress接入层同样采用多节点部署架构,同时由于Ingress作为集群流量接入口,建议采用独占Ingress节点的方式,以避免业务应用与Ingress服务发生资源争抢。

在这里插入图片描述

如上述部署架构图,由多个独占Ingress实例组成统一接入层承载集群入口流量,同时可依据后端业务流量水平扩缩容Ingress节点。当然如果您前期的集群规模并不大,也可以采用将Ingress服务与业务应用混部的方式,但建议进行资源限制和隔离

我们可以通过修改deployment(nginx-ingress-controller)的副本数来实现高可用,但是由于ingress承载着整个集群流量的接入,所以生产环境中,建议把ingress通过DaemonSet的方式部署集群中,而且该节点打上污点不允许业务pod进行调度,以避免业务应用与Ingress服务发生资源争抢然后通过SLB把ingress节点主机添为后端服务器,进行流量转发。

Ingress高可用

有哪些部署方案 ?

Nginx Ingress on TKE 部署最佳实践

方案一:Deployment + LB

在 TKE 上部署 Nginx Ingress 最简单的方式就是将 Nginx Ingress Controller 以 Deployment 的方式部署,并且为其创建 LoadBalancer 类型的 Service(可以是自动创建 CLB 也可以是绑定已有 CLB),这样就可以让 CLB 接收外部流量,然后转发到 Nginx Ingress 内部:

在这里插入图片描述

前 TKE 上 LoadBalancer 类型的 Service 默认实现是基于 NodePort,CLB 会绑定各节点的 NodePort 作为后端 rs,将流量转发到节点的 NodePort,然后节点上再通过 Iptables 或 IPVS 将请求路由到 Service 对应的后端 Pod,这里的 Pod 就是 Nginx Ingress Controller 的 Pod。后续如果有节点的增删,CLB 也会自动更新节点 NodePort 的绑定。

方案二:Daemonset + HostNetwork + LB

方案一虽然简单,但是流量会经过一层 NodePort,会多一层转发。这种方式有一些缺点:

转发路径较长,流量到了 NodePort 还会再经过 Kubernetes 内部负载均衡,通过 Iptables 或 IPVS 转发到 Nginx,会增加一点网络耗时。
经过 NodePort 必然发生 SNAT,如果流量过于集中容易导致源端口耗尽或者 conntrack 插入冲突导致丢包,引发部分流量异常。
每个节点的 NodePort 也充当一个负载均衡器,CLB 如果绑定大量节点的 NodePort,负载均衡的状态就分散在每个节点上,容易导致全局负载不均。
CLB 会对 NodePort 进行健康探测,探测包最终会被转发到 Nginx Ingress 的 Pod,如果 CLB 绑定的节点多,Nginx Ingress 的 Pod 少,会导致探测包对 Nginx Ingress 造成较大的压力。
我们可以让 Nginx Ingress 使用 hostNetwork,CLB 直接绑节点 IP + 端口(80,443), 这样就不用走 NodePort;由于使用 hostNetwork,Nginx Ingress 的 pod 就不能被调度到同一节点避免端口监听冲突。通常做法是提前规划好,选取部分节点作为边缘节点,专门用于部署 Nginx Ingress,为这些节点打上 label,然后 Nginx Ingress 以 DaemonSet 方式部署在这些节点上。下面是架构图:

在这里插入图片描述

方案三:Deployment + LB 直通 Pod

方案二虽然相比方案一有一些优势,但同时也引入了手动维护 CLB 和 Nginx Ingress 节点的运维成本,需要提前规划好 Nginx Ingress 的节点,增删 Nginx Ingress 节点时需要手动在 CLB 控制台绑定和解绑节点,也无法支持自动扩缩容。如果你的网络模式是 VPC-CNI,那么所有的 Pod 都使用的弹性网卡,弹性网卡的 Pod 是支持 CLB 直接绑 Pod 的,可以绕过 NodePort,并且不用手动管理 CLB,支持自动扩缩容:
在这里插入图片描述

5.Deployment

在 k8s 中编排应用可以更好地做弹性扩容,负载均衡。既然要均衡,一个 Pod 肯定不能均衡,自然要部署多个 Pod

docker-compose 可以简单地通过 docker-compose scale 来扩容,现在用k8s扩容

在k8s中管理 Pod 的称作 Controller,我们可以使用 Deployment 这种 Controller 来为 Pod 进行扩容,当然它还可以滚动升级,回滚,金丝雀等等关于部署的事情

Deployment同样为Kubernetes的一个核心内容,主要职责同样是为了保证pod的数量和健康,90%的功能与Replication Controller完全一样,可以看做新一代的Replication Controller。但是,它又具备了Replication Controller之外的新特性:

Replication Controller全部功能:Deployment继承了上面描述的Replication Controller全部功能。

  • 事件和状态查看:可以查看Deployment的升级详细进度和状态。

  • 回滚:当升级pod镜像或者相关参数的时候发现问题,可以使用回滚操作回滚到上一个稳定的版本或者指定的版本。

  • 版本记录: 每一次对Deployment的操作,都能保存下来,给予后续可能的回滚使用。

  • 暂停和启动:对于每一次升级,都能够随时暂停和启动。

  • 多种升级方案:Recreate:删除所有已存在的pod,重新创建新的; - RollingUpdate:滚动升级,逐步替换的策略,同时滚动升级时,支持更多的附加参数,例如设置最大不可用pod数量,最小升级间隔时间等等。

我们编写一个 Deployment 的资源配置文件

  • spec.template: 指定要部署的 Pod
  • spec.replicas: 指定要部署的个数
  • spec.selector: 定位需要管理的 Pod

kubectl get deplouyment命令可以查看 httpd-ken 的状态,输出显示一个副本正常运行。
kubectl describe deployment 了解更详细的信息

kubectl  describe deployment qa-ibaboss-elk-redis

在这里插入图片描述
创建了一个 ReplicaSet qa-ibaboss-elk-redis-6446fd4484,
Events 是 Deployment 的日志,记录了 ReplicaSet 的启动过程。

Deployment 通过 ReplicaSet 来管理 Pod

接着我们将注意力切换到 qa-ibaboss-elk-redis-6446fd4484,执行

╰─➤  kubectl get replicaset
NAME                              DESIRED   CURRENT   READY   AGE
qa-ibaboss-elk-redis-6446fd4484   1         1         1       23m

查看副本详细信息:

kubectl describe replicaset 

在这里插入图片描述
Controlled By 指明此 ReplicaSet 是由 Deployment httpd-ken 创建。
Events 记录了两个副本 Pod 的创建。

rollout history

k rollout history deployment deploymentName

回到上一个版本

k rollout undo deployment deploymentName

在这里插入图片描述

StatefulSet 无状态服务

在这里插入图片描述

6 kube-dns发现服务

kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。

● 在k8s集群中,服务是运行在Pod中的,Pod的发现和副本间负载均衡是我们面临的问题。

● 通过Service可以解决这两个问题,但访问Service也需要对应的IP,因此又引入了Service发现的问题。

● 得益于kube-dns插件,我们可以通过域名来访问集群内的Service,解决了Service发现的问题。

● 为了让Pod中的容器可以使用kube-dns来解析域名,k8s会修改容器的/etc/resolv.conf配置。

部分 DNS 查询延迟的问题

经过搜索发现这是一个普遍问题。
根本原因是内核conntrack模块的bug。

https://tencentcloudcontainerteam.github.io/2018/10/26/DNS-5-seconds-delay/

7.k8s Endpoints

endpoint 是k8s集群中一个资源对象,存储在etcd里面,用来记录一个service对应的所有pod的访问地址。
简述: endpoints: 实际上servce服务后端的pod端点集合

k8s集群中创建一个名为test的service,就h会生成一个同名的endpoint 对象,endpoint对象就是关联pod的ip 地址和端口 (使用kubectl describe svc mongodb -n namespace-name, 查看当前的service 下面有一个pod 的)

注意的点:

如果在应用程序中直接使用存储应用的ip 地址,考虑如果后期的ip变化了,我们要手动修改应用的配置。

当然使用configmap也可以解决我说的上述问题,只需要将端点存储在Configmap里面,并将其作为环境变量用于代码中读取,但是如果端点发生变化,我们可能要重新所有的应用的容器

我们需要能够在k8s里面像使用同一个命名空间下面的服务那种直接使用service name 名称,我们可以使用k8s的静态服务来解决,如果后期需要将有状态服务添加到k8s里面,则代码不需要任何修改。

8.externalTrafficPolicy

把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持【当我们的集群服务需要访问k8s之外的集群时,可以选择这种类型,然后把外部服务的IP及端口写入到k8s服务中来,k8s的代理将会帮助我们访问到外部的集群服务】

1 什么是external-traffic-policy
在k8s的Service对象(申明一条访问通道)中,有一个“externalTrafficPolicy”字段可以设置。有2个值可以设置:Cluster或者Local。

1)Cluster表示:流量可以转发到其他节点上的Pod。

2)Local表示:流量只发给本机的Pod。

在这里插入图片描述

externalTrafficPolicy=Local
为了解决这个问题, k8s 提供了一个功能,通过设置 externalTrafficPolicy=Local 可以保留源IP地址

  • client sends packet to node2:nodePort, which doesn’t have any endpoints
    客户端发送tcp包到 node2:nodePort, 但是 node2 并没有 这个pod
  • packet is dropped
    tcp包被丢弃
  • client sends packet to node1:nodePort, which does have endpoints
    客户端发送数据包到 node1:nodePort, node1有pod
  • node1 routes packet to endpoint with the correct source IP
    node1 把包路由到对应的pod,那么pod 就可以拿到正确的客户端源IP地址

缺点是负载均衡可能不是很好,因为一旦容器实例分布在多个节点上,它只转发给本机,不跨节点转发流量。当然,少了一次转发,性能会相对好一丢丢。

注:这种模式下的Service类型只能为外部流量,即:LoadBalancer 或者 NodePort 两种,否则会报错。

同时,由于本机不会跨节点转发报文,所以要想所有节点上的容器有负载均衡,就需要上一级的Loadbalancer来做了。

9.Kube-proxy

Kube-proxy 是 kubernetes 工作节点上的一个网络代理组件,运行在每个节点上。

Kube-proxy维护节点上的网络规则,实现了Kubernetes Service 概念的一部分 。它的作用是使发往 Service 的流量(通过ClusterIP和端口)负载均衡到正确的后端Pod。

kube-proxy负责为Service提供cluster内部的服务发现和负载均衡,它运行在每个Node计算节点上,负责Pod网络代理, 它会定时从etcd服务获取到service信息来做相应的策略,维护网络规则和四层负载均衡工作。在K8s集群中微服务的负载均衡是由Kube-proxy实现的,它是K8s集群内部的负载均衡器,也是一个分布式代理服务器,在K8s的每个节点上都有一个,这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。

service是一组pod的服务抽象,相当于一组pod的LB,负责将请求分发给对应的pod。service会为这个LB提供一个IP,一般称为cluster IP。kube-proxy的作用主要是负责service的实现,具体来说,就是实现了内部从pod到service和外部的从node port向service的访问。

代理模式
目前 Kube-proxy 支持4中代理模式:

userspace
iptables
ipvs
kernelspace

其中 kernelspace 专用于windows,userspace 是早期版本的实现,本文我们不作过多阐述。

iptables

iptables是一种Linux内核功能,旨在成为一种高效的防火墙,具有足够的灵活性来处理各种常见的数据包操作和过滤需求。它允许将灵活的规则序列附加到内核的数据包处理管道中的各种钩子上。

在iptables模式下,kube-proxy将规则附加到“ NAT预路由”钩子上,以实现其NAT和负载均衡功能。这种方法很简单,使用成熟的内核功能,并且可以与通过iptables实现网络策略的组件“完美配合”。

默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。

如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应, 则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。

但是,kube-proxy对iptables规则进行编程的方式是一种O(n)复杂度的算法,其中n与集群大小(或更确切地说,服务的数量和每个服务背后的后端Pod的数量)成比例地增长)。

所以综合上面的例子,对于ipable方式的k8s集群内cluster-ip类型的service总结为:

  • 流量从pod network namespace中走到host netwok namespace的docker0中。
  • 在host netwok namespace的PREROUTING chain中会经过一系列target。
  • 在这些target里根据iptable内核随机模块来实现匹配endpoint target,随机比率为均匀分配,实现均匀的负载均衡。
  • 在endpoint target里实现了DNAT,也就是将目标地址cluster ip转化为实际的pod的ip。
  • cluster ip是虚拟ip,不会和任何device绑定。
  • 负载均衡为内核实现,使用均匀负载均衡,不可以有自定义的负载均衡算法。
  • 需要host开启路由转发功能(net.ipv4.ip_forward = 1)。
  • 数据包在host netwok namespace中经过转换以及DNAT之后,由host network namespace的路由表来决定下一跳地址。
ipvs

IPVS是专门用于负载均衡的Linux内核功能。在IPVS模式下,kube-proxy可以对IPVS负载均衡器进行编程,而不是使用iptables。这非常有效,它还使用了成熟的内核功能,并且IPVS旨在均衡许多服务的负载。它具有优化的API和优化的查找例程,而不是一系列顺序规则。 结果是IPVS模式下kube-proxy的连接处理的计算复杂度为O(1)。换句话说,在大多数情况下,其连接处理性能将保持恒定,而与集群大小无关。

与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

k logs -n kube-system kube-proxy-worker-tm4nm

在这里插入图片描述

10.K8s容器资源限制

k8s Requests 和 Limits 之间的区别

  • Requests 用于在调度时通知调度器 Pod 需要多少资源才能调度
  • Limits 用来告诉 Linux 内核什么时候你的进程可以为了清理空间而被杀死或扩容

K8s的cpu、内存单位

CPU:

我们知道2核2线程的CPU,可被系统识别为4个逻辑CPU,在K8s中对CPU的分配限制是对逻辑CPU做分片限制的

也就是说分配给容器一个CPU,实际是分配一个逻辑CPU。

而且1个逻辑CPU还可被单独划分子单位,即 1个逻辑CPU,还可被划分为1000个millicore(毫核), 简单说就是1个逻辑CPU,继续逻辑分割为1000个豪核心。

豪核:
可简单理解为将CPU的时间片做逻辑分割,每一段时间片就是一个豪核心。
 
所以:500m 就是500豪核心,即0.5个逻辑CPU.

内存:

  • K,M,G,T,P,E #通常这些单位是以1000为换算标准的。
  • Ki, Mi, Gi, Ti, Pi, Ei #这些通常是以1024为换算标准的。

CPU 资源限制原理

CPU 资源限制和内存资源限制一样都是由 cgroup 控制的,上文中提到的思路和工具在这里同样适用,我们只需要关注他们的不同点就行了。首先,让我们将 CPU 资源限制添加到之前示例中的 yaml:

resources:
  requests:
    memory: 50Mi
    cpu: 50m
  limits:
    memory: 100Mi
    cpu: 100m

通过 kubectl 命令我们可以验证这个 Pod 配置了 50m 的 CPU requests:

$ kubectl get pods limit-test-5b4c495556-p2xkr -o=jsonpath='{.spec.containers[0].resources}'

map[requests:map[cpu:50m]]

我们还可以看到 Docker 为容器配置了相同的资源限制:

$ docker ps | grep busy | cut -d' ' -f1
f2321226620e

$ docker inspect f2321226620e --format '{{.HostConfig.CpuShares}}'
51

shares 用来设置 CPU 的相对值,并且是针对所有的 CPU(内核),默认值是 1024,假如系统中有两个 cgroup,分别是 A 和 B,A 的 shares 值是 1024,B 的 shares 值是 512,那么 A 将获得 1024/(1204+512)=66% 的 CPU 资源,而 B 将获得 33% 的 CPU 资源。
shares 有两个特点:如果 A 不忙,没有使用到 66% 的 CPU 时间,那么剩余的 CPU 时间将会被系统分配给 B,即 B 的 CPU 使用率可以超过 33%。
如果添加了一个新的 cgroup C,且它的 shares 值是 1024,那么 A 的限额变成了 1024/(1204+512+1024)=40%,B 的变成了 20%。

从上面两个特点可以看出: 在闲的时候,shares 基本上不起作用,只有在 CPU 忙的时候起作用,这是一个优点。 由于 shares
是一个绝对值,需要和其它 cgroup 的值进行比较才能得到自己的相对限额,而在一个部署很多容器的机器上,cgroup的数量是变化的,所以这个限额也是变化的,自己设置了一个高的值,但别人可能设置了一个更高的值,所以这个功能没法精确的控制 CPU 使用率。

cpu requests实现

Docker HostConfig.CpuShares 属性映射到 cgroup 的 cpu.shares 属性

与配置内存资源限制时 Docker 配置容器进程的内存 cgroup 的方式相同,设置 CPU 资源限制时 Docker 会配置容器进程的cpu,cpuacct cgroup:

可以验证一下

$ sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/podb5c03ddf-db10-11e8-b1e1-42010a800070/64b5f1b636dafe6635ddd321c5b36854a8add51931c7117025a694281fb11444/cpu.shares

51
k8s cpu limits实现

CPU 带宽控制组。带宽控制组定义了一个 周期,通常为 1/10 秒(即 100000 微秒)。还定义了一个 配额,表示允许进程在设置的周期长度内所能使用的 CPU 时间数,两个文件配合起来设置CPU的使用上限。两个文件的单位都是微秒(us),cfs_period_us 的取值范围为 1 毫秒(ms)到 1 秒(s),cfs_quota_us 的取值大于 1ms 即可,如果 cfs_quota_us 的值为 -1(默认值),表示不受 CPU 时间的限制。

  • 1.限制只能使用1个CPU(每250ms能使用250ms的CPU时间)
    $ echo 250000 > cpu.cfs_quota_us /* quota = 250ms /
    $ echo 250000 > cpu.cfs_period_us /
    period = 250ms */

  • 2.限制使用2个CPU(内核)(每500ms能使用1000ms的CPU时间,即使用两个内核)
    $ echo 1000000 > cpu.cfs_quota_us /* quota = 1000ms /
    $ echo 500000 > cpu.cfs_period_us /
    period = 500ms */

  • 3.限制使用1个CPU的20%(每50ms能使用10ms的CPU时间,即使用一个CPU核心的20%)
    $ echo 10000 > cpu.cfs_quota_us /* quota = 10ms /
    $ echo 50000 > cpu.cfs_period_us /
    period = 50ms */

在本例中我们将 Pod 的 cpu limits 设置为 100m,这表示 100/1000 个 CPU 核心,即 100000 微秒的 CPU 时间周期中的 10000。

所以该 limits 翻译到 cpu,cpuacct cgroup 中被设置为 cpu.cfs_period_us=100000 和 cpu.cfs_quota_us=10000。顺便说一下,其中的 cfs 代表 Completely Fair Scheduler(绝对公平调度),这是 Linux 系统中默认的 CPU 调度算法。还有一个实时调度算法,它也有自己相应的配额值。

总结

  • 在 Kubernetes 中设置的 cpu requests 最终会被 cgroup 设置为 cpu.shares 属性的值, cpu limits 会被带宽控制组设置为 cpu.cfs_period_uscpu.cfs_quota_us 属性的值。与内存一样,cpu requests 主要用于在调度时通知调度器节点上至少需要多少个 cpu shares 才可以被调度

  • 与 内存 requests 不同,设置了 cpu requests 会在 cgroup 中设置一个属性,以确保内核会将该数量的 shares 分配给进程。

  • cpu limits 与 内存 limits 也有所不同。

    • 如果容器进程使用的内存资源超过了内存使用限制,那么该进程将会成为 oom-killing 的候选者。
    • 但是容器进程基本上永远不能超过设置的 CPU 配额,所以容器永远不会因为尝试使用比分配的更多的 CPU 时间而被驱逐。系统会在调度程序中强制进行 CPU 资源限制,以确保进程不会超过这个限制。

https://icloudnative.io/posts/understanding-resource-limits-in-kubernetes-cpu-time/

hpa

为了实现 K8s 集群中资源的有效调度和充分利用, K8s 采用requests和limits两种限制类型来对资源进行容器粒度的分配。每一个容器都可以独立地设定相应的requests和limits。这 2 个参数是通过每个容器 containerSpec 的 resources 字段进行设置的。一般来说,在调度的时候requests比较重要,在运行时limits比较重要。

resources:  
    requests:    
        cpu: 50m
        memory: 50Mi
   limits:    
        cpu: 100m
        memory: 100Mi

requests定义了对应容器需要的最小资源量。这句话的含义是,举例来讲,比如对于一个 Spring Boot 业务容器,这里的requests必须是容器镜像中 JVM 虚拟机需要占用的最少资源。如果这里把 pod 的内存requests指定为 10Mi ,显然是不合理的,JVM 实际占用的内存 Xms 超出了 K8s 分配给 pod 的内存,导致 pod 内存溢出,从而 K8s 不断重启 pod 。

limits定义了这个容器最大可以消耗的资源上限,防止过量消耗资源导致资源短缺甚至宕机。特别的,设置为 0 表示对使用的资源不做限制。值得一提的是,当设置limits而没有设置requests时,Kubernetes 默认令requests等于limits。

进一步可以把requests和limits描述的资源分为 2 类:可压缩资源(例如 CPU )和不可压缩资源(例如内存)。合理地设置limits参数对于不可压缩资源来讲尤为重要。

前面我们已经知道requests参数会最终的 K8s 调度结果起到直接的显而易见的影响。借助于 Linux 内核 Cgroup 机制,limits参数实际上是被 K8s 用来约束分配给进程的资源。对于内存参数而言,实际上就是告诉 Linux 内核什么时候相关容器进程可以为了清理空间而被杀死( oom-kill )。

总结一下:

  • 对于 CPU,如果 pod 中服务使用 CPU 超过设置的limits,pod 不会被 kill 掉但会被限制。如果没有设置 limits ,pod 可以使用全部空闲的 CPU 资源。
  • 对于内存,当一个 pod 使用内存超过了设置的limits,pod 中 container 的进程会被 kernel 因 OOM kill 掉。当 container 因为 OOM 被 kill 掉时,系统倾向于在其原所在的机器上重启该 container 或本机或其他重新创建一个 pod。
  • 0 <= requests <=Node Allocatable, requests <= limits <= Infinity

https://kubesphere.io/zh/blogs/deep-dive-into-the-k8s-request-and-limit/

三.配置存储 configmap secret

pod env

apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/node-hello:1.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"

secret

Secret介绍
k8s secrets用于存储和管理一些敏感数据,比如密码,token,密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了。

Secret有三种类型

  • Opaque:base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过base64 –decode解码得到原始数据,所有加密性很弱。

    #secret.yaml
     
    apiVersion: v1
    kind: Secret
    metadata: 
      name: mysecret
    type: Opaque
    data:  
    user: YWRtaW4=  
    pass: MWYyZDFlMmU2N2Rm
    

    注:通过yaml创建Opaque类型的Secret值需要base64编码

    kubectl create -f secret.yaml

  • Service Account:用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中。

  • kubernetes.io/dockerconfigjson : 用来存储私有docker registry的认证信息。
    用来创建用户docker registry认证的Secret,直接使用kubectl create命令创建即可,如下:
    kubectl create secret docker-registry myregistry --docker-server=DOCKER_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
    imagePullSecrets 使用docker registry的认证信息

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-split-dev
  namespace: prod
spec:
  replicas: 1
  selector:
    matchLabels:
      app: product-split-dev
  revisionHistoryLimit: 10
  template:
    metadata:
      labels:
        app: product-split-dev
    spec:
      containers:
        - name: product-split-dev
          image: registry.cn-shanghai.aliyuncs.com/russell-cloud/split:latest
          imagePullPolicy: Always
          volumeMounts:
            - name: host-time
              mountPath: /etc/localtime
          ports:
            - containerPort: 8091
      restartPolicy: Always
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
      imagePullSecrets:
        - name: regcred

2.secret使用

方式一:通过Volume挂载的方式

#test-projected-volume.yaml
 
apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume 
spec:
  containers:
  - name: test-secret-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass

当 Pod 变成 Running 状态之后,我们再验证一下这些 Secret 对象是不是已经在容器里了:

$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
user
pass
$ cat /projected-volume/user
admin
$ cat /projected-volume/pass
1f2d1e2e67df

配置热更新之reloader

Configmap或Secret使用有两种方式,一种是env系统变量赋值,一种是volume挂载赋值,env写入系统的configmap是不会热更新的,而volume写入的方式支持热更新!

对于env环境的,必须要滚动更新pod才能生效,也就是删除老的pod,重新使用镜像拉起新pod加载环境变量才能生效。
对于volume的方式,虽然内容变了,但是需要我们的应用直接监控configmap的变动,或者一直去更新环境变量才能在这种情况下达到热更新的目的。

应用不支持热更新,可以在业务容器中启动一个sidercar容器,监控configmap的变动,更新配置文件,或者也滚动更新pod达到更新配置的效果。

Reloader 可以观察 ConfigMap 和 Secret 中的变化,并通过相关的 deploymentconfiggs、 deploymentconfiggs、 deploymonset 和 statefulset 对 Pods 进行滚动升级。

通过对比配置sha
https://juejin.cn/post/6897882769624727559

当 Reloader 检测到 ConfigMap 发生变化的时候,会使用 SHA1 计算 ConfigMap 的哈希值(使用 SHA1 是因为它高效且不易发生冲突),计算完哈希值之后,Reloader 获取所有的 Deployments,Daemonsets,Statefulsets 和 Rollouts 列表,并查找其 anotations 中是否配置了 Reloader 相关的注解,比如配置了如下 annotations :

metadata:
  annotations:
    reloader.stakater.com/auto: "true"

接着 Reloader 会查找配置了 Reloader 相关 annotations 的 Deployments,Daemonsets,Statefulsets 中一个特殊的环境变量。

如果找到这个环境变量,则获取其值并将其与前面计算的新 ConfigMap 哈希值进行比较,如果环境变量中的旧值与新哈希值不同,则 Reloader 会更新环境变量。

如果环境变量不存在,那么它会从 ConfigMap 创建一个具有最新哈希值的新环境变量并更新相关的deployment,daemonset或者statefulset。

k8s 检测到这个环境变量发生变化,则会触发 pod 关联的 deployment,daemonset或者statefulset 的滚动升级。

四.安全认证

在这里插入图片描述

在这里插入图片描述

secret 创建tls

在这里插入图片描述

创建https的负载均衡

在这里插入图片描述

apiVersion: v1
kind: Service
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-cert-id: 1858386724757...
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-protocol-port: 'https:443'
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
  name: ps-svc
  namespace: prod
spec:
  ports:
    - port: 443
      protocol: TCP
      targetPort: 8091
  selector:
    app: ps-dev
  type: LoadBalancer

需要通过service的annotation指定规格 service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: "slb.s1.small"
并且很多k8s service 的annotation不可用,
比如指定使用已创建的负载均衡(推荐)service.beta.kubernetes.io/alicloud-loadbalancer-id:lb-xxxx

k8s

PersistentVolume、PersistentVolumeClaim和StorageClass的概念关系

pv pvc

如果要求Pod重新调度后仍然能使用之前读写过的数据,就只能使用网络存储了,网络存储种类非常多且有不同的使用方法,通常一个云服务提供商至少有块存储、文件存储、对象存储三种,如华为云的EVS、SFS和OBS。

Kubernetes解决这个问题的方式是抽象了PV(PersistentVolume)和PVC(PersistentVolumeClaim)来解耦这个问题,从而让使用者不用关心具体的基础设施,当需要存储资源的时候,只要像CPU和内存一样,声明要多少即可。

  • PV:PV描述的是持久化存储卷,主要定义的是一个持久化存储在宿主机上的目录,比如一个NFS的挂载目录。

  • PVC:PVC描述的是Pod所希望使用的持久化存储的属性,比如,Volume存储的大小、可读写权限等等。

Kubernetes管理员设置好网络存储的类型,提供对应的PV描述符配置到Kubernetes,使用者需要存储的时候只需要创建PVC,然后在Pod中使用Volume关联PVC,即可让Pod使用到存储资源,它们之间的关系如下图所示。

在这里插入图片描述

$ kubectl get pv
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
pv-example    10Gi       RWX            Retain           Bound    default/pvc-example                            50s

可以看到状态也变成了Bound,CLAIM是default/pvc-example,表示这个PV绑定了default命名空间下的pvc-example这个PVC。

这里一个比较有意思的地方是CLAIM是default/pvc-example,为什么要显示default呢,这是因为PV是集群级别的资源,并不属于某个命名空间,而PVC是命名空间级别的资源,PV可以与任何命名空间的PVC资源绑定。

StorageClass

上节说的PV和PVC方法虽然能实现屏蔽底层存储,但是PV创建比较复杂(可以看到PV中csi字段的配置很麻烦),通常都是由集群管理员管理,这非常不方便。

Kubernetes解决这个问题的方法是提供动态配置PV的方法,可以自动创PV。管理员可以部署PV配置器(provisioner),然后定义对应的StorageClass,这样开发者在创建PVC的时候就可以选择需要创建存储的类型,PVC会把StorageClass传递给PV provisioner,由provisioner自动创建PV。

如CCE就提供csi-disk、csi-nas、csi-obs等StorageClass,在声明PVC时加上StorageClassName,就可以自动创建PV,并自动创建底层的存储资源。

使用StorageClass,不仅创建了PVC,而且创建了PV,并且将二者绑定了。

定义了StorageClass后,就可以减少创建并维护PV的工作,PV变成了自动创建,作为使用者,只需要在声明PVC时指定StorageClassName即可,这就大大减少工作量。

k8s监控

探针

在这里插入图片描述

在这里插入图片描述

https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

scale 弹性伸缩命令

http://docs.kubernetes.org.cn/664.html

k scale rc celery-controller --replicas=2
  • 动态弹性伸缩
kubectl autoscale rc foo --min=2 --max=5 --cpu-percent=80

在这里插入图片描述

弹性伸缩 监控

go-zero 给出的例子


apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-zero-demo02
  namespace: test
  labels:
    app: go-zero-demo02
spec:
  replicas: 1
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      app: go-zero-demo02
  template:
    metadata:
      labels:
        app: go-zero-demo02
    spec:
      containers:
      - name: go-zero-demo02
        image: registry.cn-shanghai.aliyuncs.com/russell-cloud/devlop-docker:latest
        lifecycle:
          preStop:
            exec:
              command: ["sh","-c","sleep 5"]
        ports:
        - containerPort: 8888
        readinessProbe:
          tcpSocket:
            port: 8888
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          tcpSocket:
            port: 8888
          initialDelaySeconds: 15
          periodSeconds: 20
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 1000m
            memory: 1024Mi
        volumeMounts:
        - name: timezone
          mountPath: /etc/localtime
      volumes:
        - name: timezone
          hostPath:
            path: /usr/share/zoneinfo/Asia/Shanghai

---

apiVersion: v1
kind: Service
metadata:
  name: go-zero-demo02-svc
  namespace: test
spec:
  ports:
    - port: 8888
  selector:
    app: go-zero-demo02
    
---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: go-zero-demo02-hpa-c
  namespace: test
  labels:
    app: go-zero-demo02-hpa-c
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: go-zero-demo02
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 80

---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: go-zero-demo02-hpa-m
  namespace: test
  labels:
    app: go-zero-demo02-hpa-m
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: go-zero-demo02
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 80

Internet到k8s的流量

https://blog.csdn.net/weixin_43746433/article/details/114290213

k8s 的一些命令

k config get-contexts 查看当前上下文以及对应使用的名称空间

在这里插入图片描述
k config view 查看配置信息

在这里插入图片描述
k cluster-info 集群信息

在这里插入图片描述

9-4 ReplicationController 与 deployment 关系

Replication Controller为Kubernetes的一个核心内容,应用托管到Kubernetes之后,需要保证应用能够持续的运行,Replication Controller就是这个保证的key,主要的功能如下:

  • 确保pod数量:它会确保Kubernetes中有指定数量的Pod在运行。如果少于指定数量的pod,Replication Controller会创建新的,反之则会删除掉多余的以保证Pod数量不变。
  • 确保pod健康:当pod不健康,运行出错或者无法提供服务时,Replication Controller也会杀死不健康的pod,重新创建新的。
  • 弹性伸缩 :在业务高峰或者低峰期的时候,可以通过Replication Controller动态的调整pod的数量来提高资源的利用率。同时,配置相应的监控功能(Hroizontal Pod Autoscaler),会定时自动从监控平台获取Replication Controller关联pod的整体资源使用情况,做到自动伸缩。
  • 滚动升级:滚动升级为一种平滑的升级方式,通过逐步替换的策略,保证整体系统的稳定,在初始化升级的时候就可以及时发现和解决问题,避免问题不断扩大。

port-forward

这是一种通过 kubectl port-forward 指令来实现数据转发的方法。kubectl port-forward 命令可以为 Pod 设置端口转发,通过在本机指定监听端口,访问这些端口的请求将会被转发到 Pod 的容器中对应的端口上。

首先,我们来看下 Kubernetes Port Forward 这种方式的工作机制:
使用 Kubectl 创建 Port Forward 后,Kubectl 会主动监听指定的本地端口。

$ kubectl port-forward pod-name local-port:container-port

当向 Local-Port 建立端口连接并向该端口发送数据时,数据流向会经过以下步骤:

1].数据发往 Kubctl 监听的 Local-Port。

2].Kubectl 通过 SPDY 协议将数据发送给 ApiServer。

3].ApiServer 与目标节点的 Kubelet 建立连接,并通过 SPDY 协议将数据发送到目标 Pod 的端口上。

4].目标节点的 Kubelet 收到数据后,通过 PIPE(STDIN、STDOUT)与 Socat 通信。

5].Socat 将 STDIN 的数据发送给 Pod 内部指定的容器端口,并将返回的数据写入到 STDOUT。

6].STDOUT 的数据由 Kubelet 接收并按照相反的路径发送回去。

注:SPDY 协议将来可能会被替换为 HTTP/2。

  • 查看pod

    kubectl get pod
    

    在这里插入图片描述

  • kubectl port-forward 端口映射

    kubectl port-forward dev-db-5959f58bd7-88zlm 5432:5432 --address=0.0.0.0
    
  • 启动Django 本地环境

    DJANGO_SETTINGS_MODULE=project_name.settings_local python3 ./manage.py runserver
    

K8S反向代理ip

externalName Service是k8s中一个特殊的service类型,它不需要指定selector去选择哪些pods实例提供服务,而是使用DNS CNAME机制把自己CNAME到你指定的另外一个域名上,你可以提供集群内的名字,比如mysql.db.svc这样的建立在db命名空间内的mysql服务,也可以指定http://mysql.example.com这样的外部真实域名

CNAME是很有用的一个功能,在不同的域名之间搭建桥梁达到明一个域名暗另一个域名,比如github就通过CNAME机制来达到为用户提供私有域名站点的功能,云服务商也都是使用CNAME为用户提供各种各样的服务。作为明域名的所有者,我可以用A云来提供服务,哪天我口味变了,我换成B云提供服务,对我的用户的来说没有任何感知。

这么好的功能,k8s当然要加以利用,那就是externalName Service。从External这个名字看,把外部服务引入集群的意味相当浓烈吧,我提供给pod一个mysql.db.svc这样一个数据库服务,至于真实的数据库是运行在同一个集群内,还是在集群外部,pod不在意也不需要关心,反正能用就成。这就是extenalName的主要用途。

how to set reverse proxy with ingress

项目中 使用yaml文件经过脱敏 后如下

apiVersion: v1
kind: Service
metadata:
  name: service_name
spec:
  externalName: "127.0.0.1"
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8433
  type: ExternalName

---


apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: service_name
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/upstream-vhost: "127.0.0.1"
spec:
  tls:
    - hosts:
        - xxx.域名.net
      secretName: service_name-oss-tls
  rules:
    - host: xxx.域名.net
      http:
        paths:
          - backend:
              serviceName: service_name
              servicePort: 80
            path: /

这样可以通过xxx.域名.net 的域名访问 通过 xxx.域名.net (举例)访问127.0 .0.1:8433

LoadBalancer

LoadBalancer 只能在service上定义。这是公有云提供的负载均衡器,如AWS、Azure、CloudStack、GCE等。

kind: Service
apiVersion: v1
metadata:
  name: influxdb
spec:
  type: LoadBalancer
  ports:
    - port: 8086
  selector:
    name: influxdb

查看服务:

$ kubectl get svc influxdb
NAME       CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
influxdb   10.97.121.42   10.13.242.236   8086:30051/TCP   39s

内部可以使用ClusterIP加端口来访问服务,如19.97.121.42:8086。

外部可以用以下两种方式访问该服务:

使用任一节点的IP加30051端口访问该服务
使用EXTERNAL-IP来访问,这是一个VIP,是云供应商提供的负载均衡器IP,如10.13.242.236:8086。

案例, 设置service 为LoadBalancer类型,公网访问

在这里插入图片描述

k get svc -o wide -n dev
NAME              TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE     SELECTOR
scheduler         LoadBalancer   172.22.5.138   8.131.97.41     80:31293/TCP                  2m31s   app=scheduler

访问 http://8.131.97.41/ 成功

nodeport(节点端口)的外部流量策略

在这里插入图片描述

k8s 好用的工具🔧

kubens命名空间切换工具

在这里插入图片描述
激活之后 直接可以操作 而不用每次都带--namespace

╰─➤  kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
mypod   1/1     Running   0          28m

k9s

github 1w+ star, 本地操作k8s神器

缺点网速不好会很难受

https://github.com/derailed/k9s

k8s 发布模式

蓝绿发布:两套环境交替升级,旧版本保留一定时间便于回滚。

项目逻辑上分为AB组(冗余),在项目系统时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。

当A组升级完毕,负载均衡重新接入A组,再把B组从负载列表中摘除,进行新版本的部署。A组重新提供服务。

灰度发布:根据比例将老版本升级,例如80%用户访问是老版本,20%用户访问是新版本。

滚动发布:按批次停止老版本实例,启动新版本实例。

三种方式均可以做到平滑式升级,在升级过程中服务仍然保持服务的连续性,升级对外界是无感知的。

那生产上选择哪种部署方法最合适呢?这取决于哪种方法最适合你的业务和技术需求。

如果你们运维自动化能力储备不够,肯定是越简单越好,建议蓝绿发布,如果业务对用户依赖很强,建议灰度发布。如果是K8S平台,滚动更新是现成的方案,建议先直接使用。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值