当前位置:   article > 正文

k8s实用知识点总结_资源限制与资源预留的区别

资源限制与资源预留的区别

1. 资源配额

1. 预留和限制的概念

  • 预留:即至少需要的资源,对应于requestsCpurequestsMemory
  • 资源限制:最大资源限制,对应于limitsCpulimitsMemory

注:这里提一下个人理解,对于上述2个参数刚接触的时候感觉理解了又不理解,不知道新人是不是也是这种感觉,后来接触多了,感觉这样理解会好一点:requestsXxx表示必须的资源,在K8S对Pod进行调度时,只有节点上的资源至少满足requestsXxx的值,那么这个Pod才有可能调度到这些满足需求的节点上,所以很多相关译文上将requestsXxx翻译为“预留XXX”很到位;而当Pod已经运行在某个节点上时,就没requestsXxx参数什么事了,最终该Pod可以获得宿主机多少资源取决自身,但不会超过limitsXxx限制的值。所以可以粗略地理解为requestsXxx是为了Pod调度,而limitsXxx是为了遏制Pod对宿主机资源的索取最大限度。K8S对于CPU资源管理的策略分为none(默认,通过CFS配额 cgroups-control groups)和static(为节点上具有某些特征的 pod 赋予增强的 CPU 亲和性和独占性,针对配置资源为整数型的POD)

2. 单位的含义

  • 内存:常用的单位为MiBGi,和常规单位的关系如下为:
1 MiB = 2^20 bytes = 1024 kibibytes = 1048576 bytes(以2为底数)
1 MB = 1000KB(以10为底数)
  • 1
  • 2
  • CPU:常用单位为毫核(mmilli),或者直接是核(没有单位,如1核直接表示为1),换算关系为:1个核=1000m,当然也可以使用占比来表示,如:1/4个核=0.25,半个核=0.5,1个整核=1。

注:在rancher中部署应用时,经常遇到deployment does not have minimum availability异常,此时适当调大资源限额可以解决大部分的问题,下面是Chart模板中常用的配置:

resources:
  limits:
    memory: {{ .Values.resources.limits.memory }}
    cpu: {{ .Values.resources.limits.cpu }}
  requests:
    memory: {{ .Values.resources.requests.memory }}
    cpu: {{ .Values.resources.requests.cpu }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2. 关于探针

 K8S中的探针应该是必不可少的一部分,参照文档,理了理,探针主要分为2类:

  • liveness probe:存活探针,它可以决定何时重启容器,比如存活探针在应用运行过程中遇到死锁(应用确实是正在运行的,但无法获取结果),这种情况往往是由于代码中存在bug,尽管如此,但存活探针的存在可以让应用更加具有可用性;
  • readiness probe:就绪探针,它可以决定一个容器何时就绪,准备接受外部的流量。如果说某个Pod已经就绪表示该Pod中的所有容器都已经就绪,当一个Pod处于未就绪的状态,他将会从负载均衡中移除;

实际观测,只有当2类探针同时检测通过,Pod才算启动成功,下面是探针的配置方式:

  readinessProbe:
    enabled: true
    httpGet:
      path: "/healthcheck"
      port: 8080
    periodSeconds: 20
    initialDelaySeconds: 60
    failureThreshold: 3
  
  livenessProbe:
    enabled: true
    httpGet:
      path: "/healthcheck"
      port: 8080
    initialDelaySeconds: 180
    periodSeconds: 30
    failureThreshold: 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

实际开发中,发现后端的Server启动用这套探针没啥问题,但前端的Server(使用node)启动的过程非常不稳定,build和拉取依赖的过程耗时特别长,基本都会重启好几遍容器才能成功,我曾经试图去调节存活探针的initialDelaySeconds参数,但如果这个过程快的话我岂不是要浪费一些时间,最终发现调节失败的阈值failureThreshold会更加方便。

 具体探针的行为可以有如下的一些:

1.HTTP
java中探针往往都是通过调用类似于go的HTTP接口,示例:

    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.具体指令
探针检测的具体行为可以自己定义,比如:

    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上述是将探针的行为定义为Linux中的cat命令

3.TCP
比如一些ES的连接可能会用到TCP

    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. Pod日志查看

3.1 进入指定Pod中的指定容器

 在rancher中,权限一般给的很严,一般不会使用docker命令直接进入容器,而是要使用kubectl命令才能进入:

kubectl exec -it <podName> -c <containerName> -n <namespace> -- -- /bin/bash

# 退出容器
exit
  • 1
  • 2
  • 3
  • 4

如果Pod中只有1个容器,上述命令可以直接简写为kubectl exec -it <podName> -n <namespace> -- -- /bin/bash,进入容器后即可为所欲为了(●ˇ∀ˇ●)。

【注】上述通过kubectl exec可以拓展为任意容器中的命令,形如kubectl exec <pod-name> -n <name-space> -- <command> xxx,只要容器中可以执行<command> xxx命令,那就可以通过上述的方式在不进入容器的情况下对容器执行一些命令。

3.2 查看Pod日志

 Pod日志可以直接通过logsdescribe查看,具体示例如下:

# 1. logs方式
kubectl logs <pod_name> -c <container_name> -n <namespace>
# 2. describe方式
kubectl describe pod <pod_name> -n <namespace>
  • 1
  • 2
  • 3
  • 4

得到的结果也是2个不同的,logs方式得到的日志是我们是部署应用时指定挂载目录位置中的日志,如deployment中在指定容器规约时:

spec:
  containers:
  	...
    volumeMounts:
    - mountPath: /data/logs/paas
      name: log-dir
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

那此时看到的日志就是/data/logs/paas/xxx.log的内容。

describe方式得到的日志是关于pod行为的日志,最典型的就是Pod节点的分配Scheduled、镜像的拉取Pulling/Pulled、容器的创建Created及运行Started、探针检测Unhealthy/Healthy

4. 版本

 和Helm类似的,K8S集群和K8S集群客户端是两回事,在写Chart时需要一个属性kubeVersion,查看版本信息:

Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.7", GitCommit:"6f482974b76db3f1e0f5d24605a9d1d38fad9a2b", GitTreeState:"clean", BuildDate:"2019-03-25T02:52:13Z", GoVersion:"go1.10.8", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.5", GitCommit:"753b2dbc622f5cc417845f0ff8a77f539a4213ea", GitTreeState:"clean", BuildDate:"2018-11-26T14:31:35Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
  • 1
  • 2

我们需要的是服务端版本,指的是GitVersion:"v1.11.5",所以kubeVersion: 1.11.5

5. 使用 ExternalName Service 代理OSS静态

 通常使用Service去发现Server,对于正常应用都可以应对,但当代理OSS静态资源时,有点尴尬,集群内是没有明确的Server对应,此时必须使用ExternalName类型的Service去解决,示例如下:

# 额外的ExternalName Service用于OSS静态代理
kind: Service
apiVersion: v1
metadata:
  name: hhu-static
spec:
  type: ExternalName
  # 此处为OSS访问域名,取回CNAME与之映射
  externalName: hhu-prod-static.oss-cn-beijing-internal.aliyuncs.com

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: server-hhu
  annotations:
    kubernetes.io/ingress.class: nginx-ingress
spec:
  rules:
  - host: k8s-www.hhu.com
    http:
      paths:
      - path: /*
        backend:
          serviceName: hhu-static
          servicePort: 80
  • 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

注:OSS还需要额外绑定k8s-www.hhu.com域名,否则无法进行代理。

6. 让Master节点参与调度

 默认情况下,k8s集群是不让master节点参与Pod的调度,通过如下的命令可以改变这种行为:

# 让master节点参与调度
kubectl taint node <k8s-master-hostname> node-role.kubernetes.io/master-

# 恢复master节点的Master Only状态
kubectl taint node <k8s-master-hostname> node-role.kubernetes.io/master=""
  • 1
  • 2
  • 3
  • 4
  • 5

7. 删除Pod

# 正常删除
kubectl delete pod xxx

# 强删
kubectl delete pod <pod name> --force --grace-period=0 -n <namespace>
  • 1
  • 2
  • 3
  • 4
  • 5

8. 删除命名空间

 删除命名空间的准确步骤如下:

  1. 查看待删除的命名空间中是否存在资源:kubectl get all -n xxx,然后清空资源(务必确认这个步骤);
  2. 删除namespace:kubectl delete ns xxx
  3. 确认命名空间已删除:kubectl get ns

【注】:

  • 如果上述第一步没有完全删除待删除命名空间下的资源,那第三步可能出现该命名空间一直处于 Terminating 的状态;
  • 无法删除 Pod 时可采用强制手段:kubectl delete pod <pod name> --force --grace-period=0 -n <namespace>
  • 如果上述手段无效解决 Terminating 状态的namespace,可以直接升级namespace,去除yaml文件中的finalizers属性:
# 编辑命名空间的配置文件
kubectl edit ns xxx

# 删除如下属性保存退出即可
  finalizers:
  - kubernetes
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9. 创建拉取镜像的凭证

# 创建凭证
kubectl -n <name-space> create secret docker-registry <secret-name> \
--docker-server=registry.xxx.com \
--docker-username=<user-name> \
--docker-password=<password> \
--docker-email=xxx@xx.com

# 删除密码
kubectl delete secret <secret-name>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在控制器的规约中指定拉取镜像的密码名字即可,在spec中直接使用:

spec: 
  template: 
    spec: 
      imagePullSecrets:
      - name: <secret-name>
  • 1
  • 2
  • 3
  • 4
  • 5

【注】上述的--docker-xxx选项中的值可能包含了一些特殊的值(比如#或者@等符号),此时需要使用英文的引号'xxx'来包裹,这种写法也是比较推荐的,不会出现问题。

10. 升级deployment/svc等(通用)

 比如升级deployment:kubectl edit deployment apollo-portal修改保存后就会自动升级,然后通过kubectl rollout status deployment apollo-portal即可看到更新的状态(不动不是卡住,是正在执行一个阶段),直到... rolled out即表示deployment升级成功,其对应的Pod也会重新创建并运行,下面是升级apollo-portal的日志:

# 升级 apollo-portal 的deployment
kubectl edit deployment apollo-portal
# 修改保存后退出出现下面的打印结果
deployment.extensions/apollo-portal edited

# 查看升级过程
kubectl rollout status deployment apollo-portal
# 下面是升级日志
Waiting for deployment "apollo-portal" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "apollo-portal" rollout to finish: 1 old replicas are pending termination...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

11. 在Pod中添加host

 测试环境中,由于少了域名解析的这一层,通常需要额外绑定一些host,此时可以通过hostAliases向Pod中添加指定的host,形如:

spec:
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

通过名命令kubectl exec server-746ddf6747-9kmx7 -n <name-space> -- cat /etc/hosts可以检验。

12. 开启远程debug

 在k8s中为了可以进行remote debug,可以做如下配置:

  1. java的启动参数增加:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=<remote-debug-port>
  2. 在Service中指定一个端口(使用nodePort类型)映射到remote-debug-port
  3. 直接使用集群中任意节点的IP加nodePort形式即可(<nodeIp>:<nodePort>),nodeIp可以是任意节点IP;

【注】
在上述第2步中,由于remote-debug使用的端口使用的是NodePort,所以在定义Service时需要额外将type也指定为NodePort,否者默认是ClusterIP类型,在该类型不允许使用nodePort来做映射,即类似于:

apiVersion: v1
kind: Service
# 省略部分配置
spec:
  ports:
  - port: 80
    targetPort: {{ .Values.service.port }}
    name: http
  - port: {{.Values.remoteDebugPort}}
    targetPort: {{.Values.remoteDebugPort}}
    nodePort: {{.Values.remoteDebugNodePort}}
    name: remote-debug
  selector:
    service-cluster: {{ .Values.deploy.serviceCluster }}
    app: {{ .Values.deploy.app }}
    project-type: java
  type: NodePort
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

K8S在创建Service时,将会给其分配IP地址(默认是cluster IP,即只能在集群中访问),Service可以将任意请求到port的端口映射到targetPort端口,完成最终请求,默认情况下,为了便利,targetPort设置为和port一样的值。

ServiceType,即在定义Service时,spec.type字段,默认为ClusterIP,相关释义如下:

  • ClusterIP:使用集群内部的IP暴露服务,只能在集群内部访问服务,默认值;
  • NodePort:将服务暴露在集群中每个节点的一个指定静态端口上(即port.NodePort),将会自动创建一个ClusterIp的Service,然后将NodePort的服务路由到上面,此时集群外部可以访问集群内的服务;
  • LoadBalancer:使用云提供商(如阿里云)提供的负载均衡暴露服务,指向集群外部负载均衡的NodePortClusterIpService会被自动创建;
  • ExternalName(CoreDNS1.7+):externalName字段通过返回CNAME记录的方式,将Service映射到上面。

13. 定向调度

 有些场景需要将Pod调度到指定节点上,步骤如下:

  1. 给节点添加标签:kubectl label node <node_name> <your_label>
  2. 在定义Pod时,直接通过NodeSelector: <your_label>即可;

14. 预览chart包生成对应的K8S资源

 由于使用使用的go模板方式,写完之后最好验证一下最终会生成什么样的K8S资源,在排查chart包问题时十分管用,使用helm install --dry-run --debug命令:

# 查看渲染后的文件, nginx-ingress 是chart包工程名,不是文件夹名,然后 nginx-ingress.yaml 是具体资源配置使用的yaml文件,如tiller在远程,通过 --host=x.x.x.x:xxxx 来指定
helm install --dry-run --debug nginx-ingress -f nginx-ingress.yaml
  • 1
  • 2

15. 集群中DNS解析异常或超时

 问题出现场景:在k8s集群中,项目中使用OKHttpClient发送请求时出现DNS无法解析的异常(在频繁发送时),异常日志如下:

20:03:20.907 [http-nio-8080-exec-3] ERROR [OKHttpClient.java:201] - gosspublic.alicdn.com: Try again
java.net.UnknownHostException: gosspublic.alicdn.com: Try again
  • 1
  • 2

在Pod上查看内 DNS 策略 cat /etc/resolv.conf

# nameserver 用于指定DNS服务器的IP
nameserver 10.247.3.10
# search 用于指定解析域名时可能会带上的前缀
search hhu-test.svc.cluster.local svc.cluster.local cluster.local
# 如果域名中的点号 . 少于5个,将会尝试将 search 中的那些前缀加到用户ping的域名前面来尝试解析
options ndots:5 single-request-reopen timeout:2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注:关于resolve中的配置作如下说明:

  • nameserver:用于指定DNS服务器;
  • search:用于指定若用户域名中的点号.的数量小于ndots参数指定的值,那它将会尝试带上这些search指定的前缀去解析用户域名,若执行ping test(该域名中没有点号),实际它会帮我们同时去ping testping hhu-test.svc.cluster.local.testping svc.cluster.local.testping cluster.local.test,系统会尝试都去解析这些域名;
  • ndots:用于指定用户域名中的点号.小于这个指定值,那系统会自动帮我们加上search中的各个前缀作域名解析;
  • single-request-reopensingle-request:为了解决A记录和AAAA记录解析冲突或超时使用的;

single-request-reopensingle-request区别:

  • single-request-reopen (since glibc 2.9): A 和 AAAA 请求使用不同的 socket 来发送,这样它们的源 Port 就不同,五元组也就不同,避免了使用同一个 conntrack 表项;
  • single-request (since glibc 2.10): A 和 AAAA 请求改成串行,没有并发,从而也避免了冲突;

single-request-reopen (since glibc 2.9)
Sets RES_SNGLKUPREOP in _res.options. The resolver
uses the same socket for the A and AAAA requests. Some
hardware mistakenly sends back only one reply. When
that happens the client system will sit and wait for
the second reply. Turning this option on changes this
behavior so that if two requests from the same port are
not handled correctly it will close the socket and open
a new one before sending the second request.

single-request (since glibc 2.10)
Sets RES_SNGLKUP in _res.options. By default, glibc
performs IPv4 and IPv6 lookups in parallel since
version 2.9. Some appliance DNS servers cannot handle
these queries properly and make the requests time out.
This option disables the behavior and makes glibc
perform the IPv6 and IPv4 requests sequentially (at the
cost of some slowdown of the resolving process).

DNS 区域采用资源记录的形式存储信息。每条资源记录均具有一个类型 , 表明其保留的数据类型。
– A : 名称至 IPv4 地址
– AAAA : 名称至 IPv6 地址
– CNAME : 名称至 ”规范名称 “ ( 包含 A/AAAA 记录的另一个名称 )
– PTR : IPv4/IPv6 地址至名称
– MX : 用于名称的邮件交换器 ( 向何处发送其电子邮件 )
– NS : 域名的名称服务器
– SOA :” 授权起始 “ , DNS 区域的信息 ( 管理信息 )

K8S中DNS配置:

  1. Default:默认继承所在Node的DNS策略,但它并不是Pod的默认DNS策略,如果pod的DNS配置缺省,默认是ClusterFirst
  2. ClusterFirst:不符合已配置的domain的后缀的域名,将会转交给从所在Node继承的nameserver DNS去作解析;
  3. ClusterFirstWithHostNet:使用hostNetwork的Pod必须显式的设置ClusterFirstWithHostNet=true
  4. None:Pod会直接忽略K8S环境中的DNS设置,所有的DNS配置应该在Pod规约中(pod->spec)使用dnsConfig指定;

示例:

service/networking/custom-dns.yaml 

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "ClusterFirst"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
   - name: timeout
     value: "5"
     # 这里没有语法错误,注意空格和索引
     - name: single-request-reopen
  • 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

一般集群中的DNS出现解析异常或解析超时等问题,可以尝试加上如下的dns的配置:

      dnsConfig:
        options:
        - name: timeout
          value: "5"
        - name: ndots
          value: "2"
        - name: single-request-reopen
      dnsPolicy: ClusterFirst
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

核心思想是将超时时间变长、减少不必要的域名解析(将增加域名前缀的域名条件变成点号为2)、以及减少冲突。

16. 关于时区

 在基于alpine镜像中,配置基本的时区:

apk add tzdata
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 可选,删除可以最小化镜像
apk del tzdata
  • 1
  • 2
  • 3
  • 4

附:一些常用的命令

# 使用 -o yaml 选项查看 deployment 的yaml文件
kubectl get deployment my-nginx -n hhu-pro -o yaml

# 使用 -o wide 选项查看 pod 的详细信息,可查看到分配的worker节点
kubectl get pods -n hhu-pro -o wide

# 运行时动态修改Pod副本数量,controller-type可能为deployment或者rc,controller-name为controller的名字
kubectl scale <controller-type> <controller-name> --replicas=3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/247214
推荐阅读
相关标签
  

闽ICP备14008679号