kubernetes入门
Pod有两种类型:普通pod和静态pod (static pod)
静态pod没有存储到etcd中,而是被存放在具体node的一个文件夹下,具只在此node上启动运行
selector中同时设置了matchLabels和matchExpressions,则两者之间是AND关系,必须都满足
kubectl get deployments xxx
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
UP-TO-DATE:最新版本的Pod的副本数量,用于指示在滚动升级过程中,有多少个Pod副本已经成功升级
HPA
cpu指标是所有pod的算数平均值,其中每个pod的计算方法为:cpu当前用量/request的量
快速创建命令
kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10
service多端口问题
当一个service有多个端口时,即拥有多个ep,要求每个ep都必须定义一个名称来区分,如
1 2 3 4 5 6 7 8 |
kind: Service spec: ports: - port: 8080 name: servic-port - port: 8005 name: shutdown-port ... |
kubernetes安装配置指南
kubeadm会将配置文件以configmap的方式存保存到kube-system命令空间
kubectl get configmaps -n kube-system kubeadm-config -o yaml
部署时,一般是先导出默认配置文件,然后修改为适合使用的
kubeadm config print init-defaults > init.defaults.yaml # 输出kubeadm init默认参数文件
kubeadm config print join-defaults > join.defaults.yaml # 输出kubeadm join默认参数文件
kubeadm init --config=init.defaults.yaml
kubeadm join --config=join.defaults.yaml
# 默认使用kubeadm部署的集群没有网络插件,需要单独部署
hyperkube 包含了所有服务的程序,可以启动任一服务
获取多种对象的信息
kubectl get pod/mypod deploy/mypod
kubectl使用命令行插件
kubectl <plugin-name>
插件的可执行文件名需要有前缀 kubectl- 且放于$PATH目录中
深入掌握pod
Pod编排文件
spec.hostNetwork bool 是否使用主机网络模式,值为true时,使用宿主机网络,不再使用docker网桥,一台宿主机只能启动pod的一个副本
静态Pod
由kubelet管理的仅存在于特定node上的pod,不能通过apiserver进行管理,且kubelet无法对它们进行健康检查
静态pod不能使用configmap
若执行kubectl delete pod xxxx 将会使pod进入pending状态且不能被删除
两种创建方式:
- 配置文件方式
需设置kubelet启动参数中有 --config 用于指定需要监控的目录,kubelet会定期扫描该目录,并根据目录下的.yaml或.json文件执行创建动作
- HTTP方式
设置kubelet启动参数中设置 --manifest-url 会定期从该url地址下载pod定义文件,并以.yaml或.json文件格式进行解析,然后创建pod
查看已停止的pod
kubectl get pods --show-all #这会显示如completed状态的pod
使用configmap的限制条件
- 必须在pod前创建
- 受namespace限制
- configmap中配额管理未实现
- 静态pod不能使用configmap
- 在pod对configmap进行volume挂载操作时,只能挂载为目录,无法挂载为文件。目录下会包含configmap的项目,若目录下原来还有文件,将会被覆盖 (解决方法,添加subPath参数)
1 2 3 |
- mountPath: /usr/local/tomcat/conf/server.xml name: tomcat-config subPath: server.xml |
initalDelaySeconds,启动容器后进行首次健康检查的等待时间
timeoutSeconds,健康检查发送请求后等待响应的超时时间
Readiness探测机制的扩展
kubectl explain pod.spec.readinessGates
1.14版达到GA稳定,Pod Readiness Gates
用户可以将自定义的ReadinessProbe探测方式设置在pod上,辅助k8s设置pod何时达到可用状态(Ready)
需部署一个外部控制器Controller设置相应的Condition状态,k8s会在判断全部readinessGates条件都为True时,才设置Pod服务可用壮态为Ready
删除deploy时,不删除对应的pod
kubectl delete deploy my-deploy --cascade=false
NodeAffinity
如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个匹配成功即可
如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该pod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: beta.kubernetes.io/arch operator: In values: - amd64 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: disk-type operator: In values: - ssd containers: ... |
pod亲和性调度
如果在具有标签X的node上运行一个或多个符合条件Y的pod,那么pod应该/拒绝运行在这个node上
其中X是一个集群中的节点、机架、区域等概念,通过内置标签中的key来进行声明,即topologyKey(注意,不是value)
pod亲和性调度对topologyKey的限制
- 在Pod亲和性和RequiredDuringScheduling的pod互斥性定义中,不允许使用空的topologyKey
- 若Admission controller包含LimitPodHardAntiAffinityTopology,那么针对Required DuringScheduling的pod互斥会被限制为kubernetes.io/hostname,此时不能使用自定义的topologyKey
- 在PreferredDuringScheduling类型的Pod互斥性定义中,空的topologyKey会被解释为kubernetes.io/hostname failure-domain.beta.kubernetes.io/zone 及 failure-domain.beta.kubernetes.io/region的组合
- 若不是上述情况,就可采用任意合法的topologyKey
- 还可指定namespace列表进行限制,使用Label Selector对namespace进行选择(namespacep定义与topologyKey同级),省略namespace表示同定义文件所在ns相同;为空表示所有namespace
- 在所有关联requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全满足后,pod才能被调度
# 为node添加污点
kubectl taint nodes xxxx key=value:NoSchedule
## taint的键为key,值为value,效果为NoSchedule
pod的toleration
key和effect需要与taint的设置保持一致,并满足如下条件之一
- operator的值为Exists(无需指定value)
- operator的值为Equal且value相等
若不指定operator,默认为Equal
特例
- 空的key配合Exists能够匹配所有键和值
- 空的effect匹配所有的effect
PreferNoSchedule 软限制调度
NoExecute 驱逐 tolerationSeconds 允许在该node上保留的时长
1 2 3 4 5 6 |
tolerations: - key: "key1" operator: "Equal" value: "value1" effect: "NoExecute" tolerationSeconds: 3600 |
pod驱逐行为
NoExecute这个taint效果可用于pod驱逐
- 没有配置toleration的pod会被立刻驱逐
- 配置了对应toleration,但没有tolerationSeconds赋值,会一直留在节点上
- 配置了对应toleration,且定义了tolerationSeconds,在指定时间后驱逐
- 若节点发生故障,会自动标记taint(只针对node unreachable和node not ready)。该功能依赖TaintBasedEvictions=controller
daemonset
可以在pod定义中使用nodeSeceltor或nodeAffinity指定满足条件的node范围进行调度
daemonset的默认更新策略为RollingUpdate 可选OnDelete
spec.updateStrategy.type=RollingUpdate
--watch
kubectl get pods --watch
使用自定义调度器
若提供自定义调度器,则会由该自定义调度器调度,忽略默认调度器;若自定义调度器不可用,pod将一直处理pending状态
1 2 |
spec: schedulerName: my-scheduler |
自定义调度器示例
使用bash实现,调度策略为随机选择一个node (该调度器需通过kubectl proxy运行)
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/bash SERVER='localhost:8001' while true; do for PODNAME in (kubectl --server $SERVER get pods -o json | jq '.items[] | select (.spec.schedulerName == "my-scheduler") | select (.spec.nodeName == null) | .metadata.name' |tr -d '"'); do NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"')) NUMNODES=${#NODES[@]} CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]} curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1","kind":"Binding","metadata":{"name":"'$PODNAME'"},"target":{"apiVersion":"v1","kind":"Node","name":"'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/ echo "Assigned $PODNAME to $CHOSEN" done sleep 1 done |
init container容器
若多个init容器都定义了资源请求/限制,则取最大值作为所有init容器的资源请求/限制
pod的有效资源请求/限制取以下二者中较大值
- 所有应用容器资源请求/限制值之和
- init容器有效资源请求/限制值
在pod重启时,init容器会重新运行
pod重启的几种场景
- init容器镜像被更新时,init容器会重新运行,导致pod重启;仅更新应用容器镜像只会使应用容器被重启
- pod的infrastructure容器更新时,pod会重启
- 若pod中所有应用容器都终止,且RestartPolicy=Always,则pod会重启
更新镜像版本
kubectl set image deployment/nginx-deploy nginx=nginx:1.9.1
# 其中nginx= 这个是容器的名称
查看更新状态
kubectl rollout status deployment/nginx-deploy
滚动更新
需要注意多重更新的情况 Rollover
上一次更新还在进行的时候,用户又发起新的更新,即多重更新
需要注意的是,多重更新时,不会等上一次更新安成才进行新的更新,而是直接进行最新版本的更新
关于更新deploy标签选择器
新增或更新标签,原来的rs和pod将会脱离deploy的管控,也不会被删除
删除标签选择器,即删除一个或多个标签,不会有什么影响,被删除的标签仍会存在于现有pod和rs中
deploy回滚
kubectl rollout status deployments nginx
# 查看历史版本 (--record)
kubectl rollout history deployment/nginx
# 查看某个版本的配置详情
kubectl rollout history deployment/nginx --revision=3
# 撤销本次发布 回滚到上个部署版本
kubectl rollout undo deployment/nginx
# 回滚到指定版本
kubectl rollout undo deployment/nginx --ro-revision=2
# 暂停deploy部署操作
kubectl rollout pause deployment/nginx
-- 此时对配置的多次更新均不会触发部署
# 恢复
kubectl rollout resume deploy nginx
设置资源限制
kubectl set resources deployments nginx -c=nginx --limits=cpu=200m,memory=512Mi
其他管理对像的更新策略
DaemonSet
- OnDelete ,需手动删除旧pod才会触发更新
- RollingUpdate
StatefulSet
- RollingUpdate
- Paritioned
- OnDelete
deploy的扩缩容
kubectl scale deployment nginx --replicas 5
hpa自动扩缩容
hpa探测周期由kube-controller-manager启动参数 --horizontal-pod-autoscaler-sync-period 定义,默认15s
指标类型
- Resource,基于资源的指标值,如cpu/mem
- Pods,基于pod的指标,对全部pod进行平均值计算
- Object,基于某种资源对象,如ingress或应用系统的任意自定义指标
API版本
- autoscaling/v1 ,仅支持cpu使用率
- autoscaling/v2beta2 ,支持所有指标
k8s推荐尽量使用type为Object的HPA配置方式,这可以通过使用Operator模式,将外部指标通过CRD定义为API资源对象来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: php-apache namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: AverageUtilization AverageUtilization: 50 - type: Pods pods: metric: name: packets-per-second targetAverageValue: 1k - type: Object object: metric: name: requests-per-second describedObject: apiVersion: extensions/v1beta1 kind: Ingress name: main-route target: kind: Value value: 10k - type: External external: metric: name: queue_messages_ready selector: "queue=worker_tasks" target: type: AverageValue averageValue: 30 |
通过k8s的Metrics Aggregation 层将自定义指标API注册到API Server中,以 /api/custom.metrics.k8s.io 路径提供指标数据
prometheus-operator
书上有一个使用prometheus作为自定义hpa的完整示例
参考:基于自定义指标的HPA实践 page 249-263
# 裸查询
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests?selector=app%3Dsample-app"
# 查看hpa状态
kubectl describe -n dev hpa.v2beta2.autoscaling sample-app
kubeprod describe -n dev horizontalpodautoscalers.autoscaling sample-app
Headless Service
spec.clusterIP: None
深入掌握 Service
多端口时,每个端口需要命名
外部服务service
没有 Label Selector 的svc,再手动创建一个同名的endpoint,用于指向实际后端服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
kind: Service apiVersion: v1 metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 80 --- kind: Endpoints apiVersion: v1 metadata: name: my-service subsets: - addresses: - IP: 1.2.3.4 ports: - port: 80 |
Headless Service
不设置 ClusterIP 即 clusterIP: None
对其进行访问会获得全部pod列表,然后由客户端自行决定如何处理这个ip列表
如 statefulset 就使用到了headless service
示例:搭建 cassandra集群 page285
从集群外部访问pod/svc
- 容器级别的 hostPort
- pod级别的 spec.hostNetwork=true
- svc级别的 设置nodePort 同时设置type类型为 NodePort
- svc级别的 LoadBalancer模式 loadBalancerIP 同时设置type类型为 LoadBalancer
coredns服务
前置条件
- kubelet启动参数添加 --cluster=<dns-svc-clusterip> --cluster-domain=cluster.local
- 创建coredns应用,需要configmap/deployment/service三种资源
- 若启用了brac,还需要设置 ServiceAccount/ClusterRole/ClusterRoleBinding
ConfigMap主要设置CoreDNS的配置文件 Corefile ,可定义各种域名解析方式和使用的插件
CoreDNS配置说明
CoreDNS主要功能是通过插件系统实现的,将DNS逻辑抽象成一个个插件,能灵活组合
插件会以从上到下的顺序依次执行
常用插件:
loadbalance,提供基于dns的负载均衡功能
loop,检测dns解析中出现的简单循环问题
cache,提供前端缓存功能
health,对endpoint进行健康检查
kubernetes,从集群中读取zone数据
etcd,从etcd中读取zone数据,可用于自定义域名记录
file,从RFC1035格式文件中读取zone数据
hosts,从/etc/hosts文件或者其他文件读取zone数据,可用于自定义域名记录
auto,从磁盘中自动加载区域文件
reload,定时自动重新加载corefile配置文件
forward,转发域名查询到上游dns服务器
proxy,转发特定的域名查询到多个其他dns服务器,同时提供到多个dns服务器的负载均衡功能
prometheus,为prometheus提供采集性能指标数据的url
pprof,在url路径/debug/pprof下提供运行时的性能数据
log,对dns查询进行日志记录
errors,对错误信息进行日志记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
cluster.local { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 cache 30 loop reload loadbalance } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 将以 .com 结尾的域名记录配置为从etcd中获取,并将记录保存在/skydns路径下 { etcd com { path /skydns endpoint http://192.168.10.10:2379 upstream /etc/resolv.conf } cache 160 com loadbalance proxy . /etc/resolv.conf } # 添加域名 etcdctl put /skydns/com/mycompany '{"host":"10.1.1.1","ttl":60}' |
Pod级别的dns配置
spec.dnsPolicy: Default
- Default 继承Pod所有宿主机的dns设置
- ClusterFirst 优先使用集群的dns服务,并将无法解析的域名转发到宿主机继承的dns服务器
- ClusterFirstWithHostNet 与ClusterFirst相同,对以hostNetwork模式运行的pod,应明确指定使用该策略
- None 忽略k8s环境的dns配置,通过spec.dnsConfig自定义DNS配置
-- nameservers dns服务器列表,最多可设置3个
-- searches 用于域名搜索的dns域名后缀,最多可设置6个
-- options 可选的dns参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
apiVersion: v1 kind: Pod metadata: namespace: default name: dns-example spec: containers: - name: test image: nginx dnsPolicy: "None" dnsConfig: nameservers: - 1.2.3.4 searches: - ns1.svc.cluster.local - my.dns.search.suffix options: - name: ndots value: "2" - name: edns0 # 生成的/etc/resolv.conf文件内容为: nameservers 1.2.3.4 searches ns1.svc.cluster.local my.dns.search.suffix options ndots:2 edns0 |
ingress原理
ingress会将请求直接转发到后端 endpoint 上,ingress需要一个默认后端 (kube-system/default-http-backend一种方式)
基本逻辑
1、监听API Server,获取全部Ingress定义 (api server的 /ingress端口)
2、基于ingress定义,生成配置文件 /etc/nginx/nginx.conf
3、执行 nginx -s reload 命令,重新加载配置文件
kubectl geet ingress -o wide
可通过 ADDRESS 列看是否有pod的ip而判断该ingress规则是否生效
curl带域名访问方法
#注:部分curl不支持该参数 --resolve <host:port:address>
curl --resolve mywebsite.com:80:192.168.18.3 http://mywebsite.com/demo/
curl -H 'Host:mywebsite.com' http://192.168.18.3/demo/
无域名ingress转发
<ingress-controller-ip>/demo
默认会强制启用HTTPS,http请求会301到https
要阻止此默认形为,添加 annotation: ingress.kubernetes.io/ssl-redirect=false
1 2 3 4 5 6 7 8 |
spec: rules: - http: paths: - path: /demo backend: serviceName: webapp servicePort: 8080 |
核心组件运行机制
API Server 原理
API Server本身也是一个Service,名称为kubernetes,它的Cluster IP地址是ClusterIP地址池里的第一个地址
在常见公有云环境中,一个3节点etcd集群在轻负载环境中处理一个请求的时间低于1ms;在重负载环境中可以每秒处理超过3w个请求
etcd有Watch API接口;API Server也提供自己的Watch接口
客户端调用API Server的List接口获取相关资源的全量数据并存放在内存中,然后启用对应资源的Watch协程
在接收到Watch事件后,对内存中的对象进行增删改操作,从而可以实现增量式、高性能、近乎实时的数据同步
Proxy API 接口
直接从node取数据,而不是从etcd (数据可能会和etcd中有短暂不一致)
API Server把收到的请求转发到某个node的kubelet守护进程的REST端口,由该kubelet进程负责响应
/api/v1/proxy/nodes/{节点名称或IP}/pods/ #列出指定节点所有pod信息
/api/v1/proxy/nodes/{节点名称或IP}/stats/ #列出指定节点物理资源统计信息
/api/v1/proxy/nodes/{节点名称或IP}/spec/ #列出指定节点概要信息
/api/v1/proxy/namespaces/{namespace}/pods/{name}/{path:*} #访问Pod的某个子接口(不需要端口号)
/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path:*} #访问Pod的某个子接口(不需要端口号)
/api/v1/proxy/namespaces/{namespace}/pods/{name} #访问Pod的首页(不需要端口号)
/api/v1/namespaces/{namespace}/pods/{name}/proxy #访问Pod的首页(不需要端口号)
也有service的接口,和pod的类似
为缓解集群名模块对API Server的访问压力,各功能模块都采用缓存机制缓存数据
定时从API Server获取指定资源对象信息(List-Watch方法)并缓存到内存
然后通过访问缓存数据间接访问API Server
Controller Manager 原理
controller manager 是自动化功能的核心,里面有各种控制器
Node Controller
若controller manager设置了--cluster-cidr参数,则控制器负责为每个Node配置spec.PodCIDR
ResourceQuota Controller
三个层次的资源配额管理
- 容器级别 对cpu和mem进行限制
- Pod级别
- Namespace级别 pod数量、rc数量、svc数量、ResourceQuota数量、secret数量、pv数量
配额管理是通过Admission Control控制
LimitRanger作用于pod和container;ResourceQuota作用于namespace
Endpoints Controller
监测Sevice,若svc被删除,则删除和该svc同名的Endpoints对象
Endpoints对象是由每个node上的kube-proxy使用的
Scheduler 原理
Scheduler 将待调度的pod通过计算找到最佳目标节点,然后绑定到该节点上
包含预选过程(xxx Predicates) 和 优选过程(xxx Priority)
一个调度算法就是一组预选策略和一组优选策略的组合
预选策略
- NoDiskConflict 判断是否有gce、aws存储卷冲突
- PodFitsResources 判断节点cpu/mem资源是否充足
- PodSelectorMatchs 判断spec.nodeSelector是否满足
- PodFitsHost 判断spec.nodeName是否满足
- CheckNodeLabelPresence 判断策略列出的标签是否存在
- CheckServiceAffinity 判断备选节点是否包含策略指定的标签,或包含和备选pod在相同service和namespace下的pod所在节点的标签
- PodFitsPorts 判断端口是否被占用
优选策略
- LeastRequestedPriority 选择资源消耗最小的节点
- CalculateNodeLabelPriority 检查标签
- BalancedResourceAllocation 选择资源使用率最均衡的节点
kubelet 运行机制
kubelet 处理master下发到本节点的任务,管理pod及pod中的容器
启动参数 --register-node true 启用向api server注册自己
--node-status-update-frequency 多长时间报告一次自己状态 默认为10s
--config 指定目录下的配置文件是 static pod;默认为 /etc/kubernetes/manifests/ --file-check-frequency 检查频率,默认20s
--manifest-url --http-check-frequency 默认也是20s
api server会为static pod创建一个Mirror Pod与之匹配,Mirror Pod的状态将真实反映Static Pod的状态;当static pod删除后,与之对应的mirror pod也将删除
cAdvisor
默认,cAdvisor通过所在Node节点4194端口暴露一个简单UI (k8s 1.12版本以前)
kube-proxy 运行机制
Service只是一个概念,而将Service落实的是kube-proxy
kube-proxy 在运行中动态创建与Service相关的iptables规则
IPVS(IP Virtual Server) 稳定的推荐的方式
iptables与ipvs虽都是基于Netfilter实现,但因定位不同,二者有本质区别
iptables是为防火墙设计;ipvs专门用于高性能负载均衡,使用更高效的数据结构(Hash表),允许几乎无限规模扩张
ipvs使用了ipset功能,可动态修改这个集合
转载请注明:轻风博客 » kubernetes权威指南笔记(一)