kubernetes 组件
master
k8s的master,也称为控制平面,用于控制整个k8s集群的整体运作状态。控制平面的唯一入口为API Server,它监听6443,接入api server的唯一入口就是api server主机的6443端口,为https协议。
Api server 默认情况下,需要做用户认证,而非客户端直接请求即可,而是需要双向认证:发送自己的证书给客户端验证,也要求客户端提供客户端证书,并且是api server自身信任的CA颁发的证书,才是api server认可的证书。证书在部署时自动生成的,路径为/etc/kubernetes/pki/ca.crt
而admin.conf
文件就持有CA颁发的客户端证书,因此 需要admin.conf才可以接入api server。
Kubectl config view命令查看客户端证书:
ilinux@master:/etc/kubernetes$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://k8s-api.linux.io:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
此处显示的客户端证书,客户端私钥都会隐藏起来,但真正去连接时,都必须提供证书。
客户端通过向api server提交请求,以创建pod资源,由scheduler调度,由controller创建pod,这些pod是由控制器管理的pod。
也可以直接创建pod,称为自主式pod
用于控制pod的资源类型Deployment,需要赋值后才能创建具体的控制器,这也就是资源类型实例化为资源对象的过程。比如nginx-deploy -> nginx-pod。而此时需要访问pod时,必须借助李刚一个资源类型Service,创建特定的service实例nginx-svc,关联nginx-pod以调度用户请求。
node
数据平面,它负责两项工作:
(1) 运行pod
(2) 为pod设定service转换为当前节点的iptables/ipvs规则 ,依靠kube-proxy组件完成
kubernetes的基本操作
获取pod时,可以指定名称空间,它是名称空间级别的资源,默认为default。
- 获取名称空间:
ilinux@master:~$ kubectl get ns
NAME STATUS AGE
default Active 30d
kube-node-lease Active 30d
kube-public Active 30d
kube-system Active 30d
-
创建资源:kubectl create
ilinux@master:~$ kubectl create namespace develop namespace/develop created
-
删除资源:kubectl delete 资源类型 资源名 -n 名称空间
ilinux@master:~$ kubectl delete ns/testing ns/develop namespace "testing" deleted namespace "develop" deleted
可删除多个资源,删除名称空间很危险!
-
查找资源
ilinux@master:~$ kubectl get ns/default -o wide # 输出长格式 NAME STATUS AGE default Active 30d ilinux@master:~$ kubectl get ns/default -o yaml # 输出yaml格式 apiVersion: v1 kind: Namespace ... ilinux@master:~$ kubectl get ns/default -o json # 输出json格式 { "apiVersion": "v1", "kind": "Namespace", ... }
一般每个资源,都由5个一级字段,这是标准格式。这些字段往往都一样,分别为:apiVersion,Kind,metadata,spec,status
-
输出描述信息,一般是当前状态信息
ilinux@master:~$ kubectl describe ns/default Name: default ...
-
创建deployment控制器控制的pod
ilinux@master:~$ kubectl create deploy nginx-dep --image=nginx:1.14-alpine deployment.apps/nginx-dep created
此时创建了default名称空间的的nginx deploy控制器
-
显示当前名称空间所有资源
ilinux@master:~$ kubectl get all NAME READY STATUS RESTARTS AGE pod/nginx-dep-6fcd89c765-hk5j5 1/1 Running 0 14s # pod对象,由控制器创建 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 30d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-dep 1/1 1 1 3m1s # deployment对象 NAME DESIRED CURRENT READY AGE replicaset.apps/nginx-dep-6fcd89c765 1 1 1 14s # rs对象
直接访问pod IP:
ilinux@master:~$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-dep-6fcd89c765-hk5j5 1/1 Running 0 3m26s 10.244.2.163 node2 <none> <none> ilinux@master:~$ curl 10.244.2.163 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style>
-
删除pod
ilinux@master:~$ kubectl delete pods/nginx-dep-6fcd89c765-hk5j5 pod "nginx-dep-6fcd89c765-hk5j5" deleted
删除后,控制器会自动重新创建一个pod
此时客户端访问的pod IP,会变动。应该对用户透明,此时需要中间层:service
客户端访问调度器,调度器把请求调度到后端。此时,调度器必须有IP+port,后端也需要IP+port。因此service必须明确指定IP+port。它实际创建了iptables规则,类似dnat规则:
比如:DNAT -d 10.96.1.7 --dport 80 -j DNAT --to-destination 10.244.3.2:80
Service就是生成此规则,每个节点上都会存在此规则。因此客户端访问时,请求会被自动拦截
-
创建 service
ilinux@master:~$ kubectl create service clusterip nginx-dep --tcp=80:80 service/nginx-dep created ilinux@master:~$ kubectl describe svc/nginx-dep Name: nginx-dep Namespace: default Labels: app=nginx-dep Annotations: <none> Selector: app=nginx-dep Type: ClusterIP IP: 10.111.132.206 Port: 80-80 80/TCP TargetPort: 80/TCP Endpoints: 10.244.2.164:80 # 关联的后端pod Session Affinity: None Events: <none>
此时会自动关联,encpoints为后端port的地址,然后访问service IP即可访问到podIP
此时删除pod,
kubectl delete pods --all
然后控制器重建,地址和调度主机可能都会变化。service关联的是新地址,因为所有变动会反应在api-server,它会通知一切的客户端并发生相应改变。
Service地址如果变化了,客户端访问ip就出问题,因此可以使用service名称,比如curl nginx-dep,会提示解析失败。本地默认dns地址需要变化。必须让coreDNS的解析,因此kubectl get svc -n kube-system,获取它的dns地址,然后临时把nameserver修改为coreDNS IP
ilinux@master:~$ kubectl get pods -n kube-system -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-7ff77c879f-lxrhv 1/1 Running 37 30d 10.244.0.76 master <none> <none> ... root@master:~# cat /etc/resolv.conf nameserver 10.244.0.76 search default.svc.cluster.local ## 解析成功 ilinux@master:~$ curl nginx-dep <!DOCTYPE html> <html>
注意:搜索域默认为:default.svc.cluster.local. 它可再kubeadm init时,通过选项 --service-dns-domain指定,格式为 svc名称.namesapce.svc.默认域名cluster.local
此时就算svc重建,也可通过名称访问成功。
deployment控制器可以按需伸缩pod的规模,看以下例子:
ilinux@master:~$ kubectl create deployment myapp --image=ikubernetes/myapp:v1
deployment.apps/myapp created
ilinux@master:~$ curl 10.244.2.166/hostname.html
myapp-5d587c4d45-8xzvt
ilinux@master:~$ kubectl create service clusterip myapp --tcp=80:80
service/myapp created
ilinux@master:~$ curl myapp
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
此时扩容:
ilinux@master:~$ kubectl scale --replicas=3 deployment/myapp
deployment.apps/myapp scaled
而且service自动关联到三个pod:
ilinux@master:~$ kubectl describe svc myapp
Name: myapp
Namespace: default
Labels: app=myapp
Annotations: <none>
Selector: app=myapp
Type: ClusterIP
IP: 10.105.212.160
Port: 80-80 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.163:80,10.244.2.166:80,10.244.2.167:80
Session Affinity: None
Events: <none>
实际上,curl访问时,pod会默认随机调度,并非轮询。因为它使用iptables调度。如果是ipvs还能指定调度算法。
如果把replicas=2
,则缩容。因为会精确符合用户定义的需求。
在同一组pod时,如果不指定clusterIP,而是指定nodePort,还能在集群外部访问。
ilinux@master:~$ kubectl create service nodeport myapp --tcp=80:80
service/myapp created
ilinux@master:~$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp NodePort 10.111.65.91 <none> 80:30778/TCP 5s
访问集群任何一个宿主机的端口,即可访问到service
http://192.168.2.10:30778/hostname.html
此时,实际上通过iptables的自定义链,作为目标跳转链,实现dnat转发
API Server总结
- 对kubernetes集群的所有操作,必须经过API Server ,整个k8s的hub。API server自身作为集群的gateway,可以使用kubectl,UI dashboard等客户端工具与之交互
- 客户端提交给k8s请求的格式为json。如果提交yaml,k8s会自动转化为json,这是唯一接受的序列化方案
- 额外也支持grpc协议:google研发的分布式rpc协议。据说比http协议性能更好,用于取代restful api的设计协议
- Etcd:保存集群的所有状态,需要把它做成高可用,如此集群才是可用的
api-version
显示api 版本
ilinux@master:~$ kubectl api-versions
admissionregistration.k8s.io/v1
admissionregistration.k8s.io/v1beta1
apiextensions.k8s.io/v1
apiextensions.k8s.io/v1beta1
apiregistration.k8s.io/v1
...
支持很多类型的资源,pod,controller,service… 如果service改变,API只有一个版本,则所有资源都需要从新打包一次,演进一次。
因此,k8s把API分类,分解到不同的群组当中。api server 把api 接口的资源分为了多个逻辑组合。其中每个组合都是相关的类型放置在一起,每个组合成称为api group。
每个组的版本,可以独立演进和迭代。而且每个组还可以多版本并存,因此每个资源必然是某个群组下的某个类型,并且可以存在多个版本如apps/v1和apps/v1beta1 。而同一种资源类型在不同版本下,可能支持的属性不同。
资源对象的配置格式
- API server接受和返回的所有JSON对象都遵循同一个模式,它们都具有“kind”和“apiVersion”字段,用于标识对象所属的资源类型、API群组及相关的版本
- 大多数的对象或列表类型的资源还需要具有三个嵌套型的字段:metadata、spec和status
- metadata字段为资源提供元数据信息,例如名称、隶属的名称空间和标签等;
- spec用于定义用户期望的状态,不同的资源类型,其状态的意义各有不同,例
如Pod资源最为核心的功能在于运行容器; - status则记录着活动对象的当前状态信息,它由Kubernetes系统自行维护,对
用户来说为只读字段;
- “kubectl api-resources”命令可以获取集群支持使用的所有资源类型
和解循环
和解循环(Reconciliation Loop)
-
客户端向API server提交POST请求以创建对象
-
通过JSON格式的body提交
-
YAML格式需要事先完成向JSON的转换
-
对象配置信息保存于etcd中,其定义出的状态也称为“期望的状态(spec)”
-
-
控制器负责将其创建为Kubernetes集群上的具体对象(活动对象),并确保其当前状态(status)与用户定义的期望状态相同;
- status由控制器自行维护,而spec则由用户进行提交
- 活动对象在运行过程中因节点故障等原因可能会在某一时刻导致其status不再吻合于spec
- 控制器通过和解循环(loop)不间断地监控着相关对象的当前状态,在对象的当前状态
发生改变时运行合适的操作让其当前状态无限接近期望的状态
使用配置文件yaml的好处:更加精细和详细定义资源格式,而且能够复用,可借助版本控制工具git,此时还能追踪内容变动,每次版本变动发生了哪些修改。比如:本地编写yaml,push到git,然后pull到api -server,然后应用于k8s。
RESTFUL WEB Services:表征状态转移
它是一种架构范式,能够设计用于分布式组件相互通信和调用的方式,调用很简洁。每个被操作数据对象都被操作为如下格式:
Protocol Host(domain name) port appkication context version parameter
比如:http://localhost:9999/restfulserveices/v1/users【资源类型】/{id}【资源id】
因此,操作k8s的资源类型也能直接使用restful风格的api的方式来引用,并且put,get,delete等。当然也有更简单的命令如kubectl delete,kubectl get等。