k8s_day02_04
node 介绍

查看所有节点

[root@master01 ~]# kubectl  get node
NAME       STATUS   ROLES    AGE     VERSION
master01   Ready    master   7d      v1.19.4
node01     Ready    <none>   6d23h   v1.19.4
node02     Ready    <none>   6d23h   v1.19.4
[root@master01 ~]# 

​ name 节点名称

​ status 显示节点状态

​ role 显示是否为主节点 角色 none 表示的就是node 工作节点

​ AGE: 存活的时长

查看指定节点

[root@master01 ~]# kubectl   get node/node01
NAME     STATUS   ROLES    AGE     VERSION
node01   Ready    <none>   6d23h   v1.19.4

查看指定节点 以yaml 格式查看详细信息

[root@master01 ~]# kubectl   get node/node01 -o yaml

查看分配给pod 所属的子网段

节点控制器 可以节点对象声明周期的管理 , 比如节点加入集群时给他分配一个子网、与服务器列表交互,以维护可用的节点列表信息,监控节点的健康状态,一旦节点不健康可以拒绝调度器把pod调度到这台机器上去,这些都是基操。对于每个节点对象,节点控制器都会根据它的元数据中name执行健康状态检测,以随时验证节点是否可用。k8s 上的资源必须有相应的资源控制器管理,否则所有创建的数据也仅仅是能保存在apiserver上的数据项,并不能真正意义上发挥作用,一个控制器可能控制多个类型,所以二者不是一一对应的

​ kubelet是运行在节点上的代理程序, 它负载从apiserver 接收并且执行 自身pod 相关的管理任务,并且向master 上报自身的状态==【kubelet心跳信息】== 以维持集群正常运行》

心跳检查是这样做的:

kubelet 每隔10秒钟,向apiserver报告自己的状态 【发送一次心跳信息】,告诉它自己还活着,仍然能接收pod的运行任务。一旦连续4个周期apisever没有 接收到特定节点的心跳信息,apiserver 就会把这个节点置为 NotReady , 之前在该节点创建的pod 就会被调度到其他server

在k8s -1.13版本之前,向apiserver报告自己的状态 同时会发送node status 信息,node status中有个image字段,非常非常大(存储了这个节点的所有image 可用信息),带来的缺点就是 如歌节点过多,每10s发一次会占用很大的流量带宽资源。

在k8s -1.13版本之后,为了解决这个问题 引入给节点专门使用的租约概念: lease , 资源类型就叫leases , 都放在kube-node-lease 名称空间之下

[root@master01 ~]#   kubectl  get  leases   -n kube-node-lease
NAME       HOLDER     AGE
master01   master01   7d10h
node01     node01     7d9h
node02     node02     7d9h
[root@master01 ~]# 

有了租约之后,可以把节点的状态以租约的形式保存在节点之上

[root@master01 ~]#   kubectl  get  leases/node02   -n kube-node-lease -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
creationTimestamp: "2021-11-24T17:09:25Z"
name: node02
namespace: kube-node-lease
ownerReferences:
  - apiVersion: v1
    kind: Node
    name: node02
    uid: 7e330895-413f-4347-b967-2e7e9704d2bc
  resourceVersion: "228716"
  selfLink: /apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/node02
  uid: 50eedbd7-152f-491d-af93-79b1c78ed212
spec:
  holderIdentity: node02
  leaseDurationSeconds: 40
  renewTime: "2021-12-02T03:02:42.745544Z"
[root@master01 ~]# 

大体报告了租约的更新时间 leaseDurationSeconds:40 ,如歌40秒没有发生变化, 就会报告故障

之后的心跳只更新租约,而不是更新status 了,nodestatu只有真正发生变化 Transition 了才上告,要不就等到5五分钟才上报一次

查看node status 信息

[root@master01 ~]# kubectl  get   no/node01 -o yaml
status:
  addresses:
  - address: 192.168.2.2
    type: InternalIP
  - address: node01
    type: Hostname
  allocatable:   #可分配的资源
    cpu: "2"
    ephemeral-storage: "48294789041"    
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 3759112Ki
    pods: "110"
  capacity:  #实际容量
    cpu: "2"
    ephemeral-storage: 51175Mi
    hugepages-1Gi: "0"
    hugepages-2Mi: "0"
    memory: 3861512Ki
    pods: "110"

ephemeral-storage : 临时存储 节点本地 可被被容器作为存储卷使用的空间

pods: “110” 默认最多节点可分配的最多数量 ,可以调整

conditions 表示 当前节点所处的处境,会汇报 NetworkUnavailable、MemoryPressure、DiskPressure、PIDPressure 类型的心跳检测, 如果status取值为false 就表示为正常。类型 Ready为True 表示正常

  conditions:
  - lastHeartbeatTime: "2021-12-01T11:27:25Z"
    lastTransitionTime: "2021-12-01T11:27:25Z"
    message: Flannel is running on this node
    reason: FlannelIsUp
    status: "False"
    type: NetworkUnavailable
  - lastHeartbeatTime: "2021-12-02T03:31:56Z"
    lastTransitionTime: "2021-12-01T10:00:40Z"
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: "2021-12-02T03:31:56Z"
    lastTransitionTime: "2021-12-01T10:00:40Z"
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: "2021-12-02T03:31:56Z"
    lastTransitionTime: "2021-12-01T10:00:40Z"
    message: kubelet has sufficient PID available
    reason: KubeletHasSufficientPID
    status: "False"
    type: PIDPressure
  - lastHeartbeatTime: "2021-12-02T03:31:56Z"
    lastTransitionTime: "2021-12-01T11:26:59Z"
    message: kubelet is posting ready status
    reason: KubeletReady
    status: "True"
    type: Ready

单单靠一个配置文件是无法创建一个节点的, 因为控制器无法随便给变一个物理节点存在,也不会部署kubelet存在

[root@master01 ~]# cat  no3.yml 
apiVersion: v1
kind: Node
metadata:
  name: node3
spec: 
  podCIDR: 10.244.20.0/24
  podCIDRs: [10.244.20.0/24]

[root@master01 ~]# 

[root@master01 ~]# kubectl  create -f no3.yml 
node/node3 created
[root@master01 ~]# kubectl  get   no
NAME       STATUS    ROLES    AGE     VERSION
master01   Ready     master   7d12h   v1.19.4
node01     Ready     <none>   7d12h   v1.19.4
node02     Ready     <none>   7d12h   v1.19.4
node3      Unknown   <none>   14s     

所以node3 的状态是notready, unkonw是因为未解析

验证: 在master 上添加解析 就会成为notReady

[root@master01 ~]# echo  "192.168.2.4  node3 " >>  /etc/hosts
[root@master01 ~]# kubectl  get   no
NAME       STATUS     ROLES    AGE     VERSION
master01   Ready      master   7d13h   v1.19.4
node01     Ready      <none>   7d12h   v1.19.4
node02     Ready      <none>   7d12h   v1.19.4
node3      NotReady   <none>   5m15s   
[root@master01 ~]# 

查看节点描述的信息

[root@master01 ~]# kubectl describe  no/node01


PodCIDR:                      10.244.5.0/24
PodCIDRs:                     10.244.5.0/24
Non-terminated Pods:          (5 in total)
  Namespace                   Name                        CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                        ------------  ----------  ---------------  -------------  ---
  default                     demoapp-5f7d8f9847-lkwxw    0 (0%)        0 (0%)      0 (0%)           0 (0%)         14h
  default                     mypod                       0 (0%)        0 (0%)      0 (0%)           0 (0%)         14h
  kube-system                 coredns-6d56c8448f-7lwtf    100m (5%)     0 (0%)      70Mi (1%)        170Mi (4%)     18h
  kube-system                 kube-flannel-ds-w62zd       100m (5%)     100m (5%)   50Mi (1%)        50Mi (1%)      18h
  kube-system                 kube-proxy-k9jmf            0 (0%)        0 (0%)      0 (0%)           0 (0%)         18h
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                200m (10%)  100m (5%)
  memory             120Mi (3%)  220Mi (5%)
  ephemeral-storage  0 (0%)      0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)
Events:              <none>

可以看到Non-terminated Pods 运行状态pod个数 非系统空间

Allocated resources 已经分配出去的资源 100m 表示0.1 个cpu

删除节点

[root@master01 ~]# kubectl delete  no/node3
node "node3" deleted
[root@master01 ~]# kubectl  get  no
NAME       STATUS   ROLES    AGE     VERSION
master01   Ready    master   7d13h   v1.19.4
node01     Ready    <none>   7d12h   v1.19.4
node02     Ready    <none>   7d12h   v1.19.4
[root@master01 ~]# 

pod的 status 叫做 pod 的phase ,相位 ,一个pod 有5个常见的相位

1、Running: 运行中

2、Pending: 挂起
主要原因是未调度成功,或者仍然处于下载镜像的过程当中

3、Succeed: 表示pod 中所有容器已经成功终止

​ job类型的pod 一旦运行完成了就处于succeed状态,如果有一个未能终止就显示为Failed

4、Failed

5、Unknown:

​ 如果apiserver 无法获取 pod 的运行状态, 通常是无法与kubelet 建立联系通信导致的,如节点网络拥塞,kubelet 故障等等

​ 当然除了以上五种状态 还有CrashLoopBackOff,Error。 这些状态通常来说不能称为POD的状态,而是pod内容器的状态。 比如 容器内应用没有放在前台运行 ,所监听的端口被占用等等所导致的pod 异常退出

pod内容器的组织方式有如下几种

1、sidecar: 用于为主容器提供辅助功能的 如日志采集器

2、Adapter: 一般来说, 一个pod 内的应用数据输出格式 与我们规范的格式不兼容,会加一些代码转换让他们兼容起来, 比如监控系统的agent 就是专门用来做格式转换的 。nginx 的status 无法与Prometheus指标格式相兼容,如果使用Prometheus去监视nginx内建的start staus几个数据,这时候就可以额外部署一个转换器,让nginx 指标兼容于Prometheus格式

3、Ambassador 如果pod内的主容器一些功能不方便去实现对外联络,这时候可以专门部署一个容器实现这种功能,这容器就可以称为大使容器。redis-cluster 用于构建其他节点时可能用到

image-20211202152010859

​ 在启动pod 的时候,最先启动的容器叫 初始化容器 ,初始化容器可能会有一个或者多个 ,且串行运行(一个初始化容器运行完成以后再运行另外一个初始化容器容器),初始化容器存在生命周期,最终肯定是要终止的。

​ 一旦初始化容器 运行完 才开始运行 主容器,其他的sidecar、 适配器、或者大使类的辅助类容器同时运行,默认情况无法人为控制

​ 一个pod的运行过程中存在2个非常重要的钩子 hook ,还有3个探针。钩子的作用是运行人为运行一些命令或程序 做一些自定义的功能。

post start hook: 容器启动后执行 单次执行

​ 刚启动完一个主容器就有一个 钩子: post start hook. 主要做的是一些初始化操作,类似于linux 系统中的/etc/profile 文件

pre stop hook:容器结束前执行 单次执行

​ 主容器结束之前也有个钩子 pre stop hook ,用于容器结束前执行一些清理类工作,比如清缓存

​ 探针可以理解为传感器, 用于做健康状态检测的,自定义的健康状态检查机制. 默认情况下 kubelet 就可以做健康状态检测,如果pod 处于运行状态就是健康的,但是pod 处于运行状态而容器不一定是健康的,因为容器内的进程虽然在运行状态,但是可能会假死 无法回应正常客户端的请求,但是kubelet又探测不到。 所以我们可以自定义 更精确的探测机制

​ livness probe: 存活探针: 判定容器健康与否 周期性执行

​ readyness probe: 就绪探针,存活但是不一定就绪。 周期性执行

生产环境中,有很大的war 包, tomcat 展开到运行能正常访问,可能需要10~20秒都不止,容器一旦运行起来就存活了,但是容器一运行起来不意味着就能马上 接收用户访问了,所以当容器内的程序可以正常提供访问时,我们才认为它是就绪的。

startup probe: 启动探针 容器启动之后执行一定的时长 单次执行

pod 内有这样一种功能, 在pod 中内置一个liveness probe健康探针 ,pod 一启动就会周期性检测,比如2秒检测一次,如果容器内的进程(如java)花非的时间比较长 ,在检测周期内没有启动 pod就会无限重启这个容器,就GG。 所以第一次检测 就应该延后一点,另外还可能需要额外的特殊条件,所以需要startup探针额外构建来判断是否启动成功

在其他容器 无论是主容器还是sidecar adaptor 之类的 容器内部都可以构建2个钩子,3个探针

pod 需要关注的关键信息

[root@master01 ~]# kubectl  get  po/demoapp-5f7d8f9847-4t7sk -o  yaml

priority: 0  # 容器的优先级


spec:
  containers:
 - image: ikubernetes/demoapp:v1.0
    name: demoapp
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount   # 存储器挂载点
    
qosClass: BestEffort  # 服务质量级别 尽量满足
Tolerations   污点容忍度

容器如何配置?

可以在容器中传递环境变量,通过环境变量向容器传递信息和数据,向容器传值

dockerfile entrypoint cmd 区别

​ 如果只有cmd ,最后一个生效,如果 entrypoint cmd 都有则 entrypoint 生效,而且cmd 要作为参数传给 entrypoint

​ 这种设计的好处是:

​ entrypoint 的作用是让你在运行主程序之前,先运行一个初始化脚本,这脚本可以用来让那些 并非设计直接在容器中运行的应用,给他传一些环境变量替换到应用的配置文件中去的。所以entrypoint 一般是一个特殊化脚本 ,在这脚本之后去运行容器的主进程

什么叫云原生应用? 本身就运行在容器中 可以直接使用环境变量配置的 ,非原生只能借助 entrypoint 脚本了

例子: 向pod 容器传入变量

[root@master01 ~]# kubectl  explain po.spec.containers.env
[root@master01 ~]# mkdir  manifests
[root@master01 ~]# cd  manifests/
[root@master01 manifests]# cat  mypod-with-env-var.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod-with-env-var
  labels:
    app: mypod
    release: canary
spec:
  containers:
  - name: demoapp
    image: ikubernetes/demoapp:v1.0
    env:
    - name: HOST
      value: 127.0.0.1
    - name: PORT
      value: "8080"


[root@master01 manifests]# kubectl   apply -f mypod-with-env-var.yaml 

验证

[root@master01 manifests]# kubectl  exec  mypod-with-env-var -- netstat  -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      
[root@master01 manifests]# 

前提是容器支持这变量 ,否则除了在容器中看见。没啥用

[root@master01 manifests]# kubectl  exec  mypod-with-env-var  -- printenv |head -5
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=mypod-with-env-var
HOST=127.0.0.1
PORT=8080
KUBERNETES_SERVICE_HOST=10.96.0.1
[root@master01 manifests]# 

image-20211202170452405

pod 创建的过程:

1 、用户发送create Pod的过程会发送给 APIserver 【create pod】

2、APIserver 先存储在etcd 中 【write】

3、scheduler 会看到etcd 中这个pod 处于未调度状态 【watch new pod】

4、scheduler会挑一个node 执行pod,【 bind pod 】

5、scheduler再存回apiserver 中 【write】

6、kubelet 会监视到和自己有关pod 的创建 【watch new pod】 ,然后拿到配置 开始启动容器【Start Container through CRI】 ,并且运行完以后再存回apiserver 【 update pod status.】

7. 由apiserver 存回到 etcd 中

删除的过程与之向反

能与etcd打交道的只有 apiserver

如何暴露POD端口?

pod 上暴露端口好像没什么用? 因为 pod 到pod 的通信 跨主机也是可以直接通信的,也就是说 po.spec.containers.ports.containerPort 没必要设置

如果与集群外l流量 信.POD 做不到 ,要么使用 node port 类型的sevice, 要么共享宿主机网络名称空间. 设置po.spec.hostNetwork 字段

或者 在po.spec.containers.ports.hostPort 设置字段就行,可以实现类似docker expose效果但是这种的暴露方式缺点很大

[root@master01 manifests]# kubectl explain  po.spec.containers.ports
FIELDS:
   containerPort	<integer> -required-
     Number of port to expose on the pod's IP address. This must be a valid port
     number, 0 < x < 65536.

   hostIP	<string>
     What host IP to bind the external port to.

   hostPort	<integer>
     Number of port to expose on the host. If specified, this must be a valid
     port number, 0 < x < 65536. If HostNetwork is specified, this must match
     ContainerPort. Most containers do not need this.

   name	<string>
     If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
     named port in a pod must have a unique name. Name for the port that can be
     referred to by services.

   protocol	<string>
     Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP".

例子:

[root@master01 manifests]# cat  mypod-with-ports.yml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod-with-ports
  labels:
    app: mypod
    release: canary
spec:
  containers:
  - name: demoapp
    image: ikubernetes/demoapp:v1.0
    ports:
    - containerPort: 80
      name: http
      protocol: TCP
      hostPort: 8080
[root@master01 manifests]# 
[root@master01 manifests]# kubectl  apply  -f  mypod-with-ports.yml 

验证结果

image-20211202190408254

缺点是 只能通过被调度到的节点访问, 因为pod 的调度是不确定的 ,作为外部用户是不知道pod 所在主机地址的

Pod直接共享宿主机的网络名称空间,无论调度到哪个节点都是

[root@master01 manifests]# kubectl explain  po.spec.hostNetwork
KIND:     Pod
VERSION:  v1

FIELD:    hostNetwork <boolean>

DESCRIPTION:
     Host networking requested for this pod. Use the host's network namespace.
     If this option is set, the ports that will be used must be specified.
     Default to false.

例子

[root@master01 manifests]# kubectl  apply  -f  mypod-with-hostnetwork.yml 
pod/mypod-with-hostnetwork created
[root@master01 manifests]# cat mypod-with-hostnetwork.yml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod-with-hostnetwork
  labels:
    app: mypod
    release: canary
spec:
  containers:
  - name: demoapp
    image: ikubernetes/demoapp:v1.0
    env:
    - name: PORT
      value: "8080"
  hostNetwork: True
[root@master01 manifests]# 

发生诡异的一幕来了,因为直接共享的宿主机的名称地址 , 所以看到的 servername 和serverip 都不对!

image-20211202190540282在容器内执行ip a 看到的是物理机的信息 ,所以特别危险 ,生产环境要禁止使用

[root@master01 ~]# kubectl exec  mypod-with-hostnetwork -- ip a
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐