赞
踩
1. 预留和限制的概念
requestsCpu
和requestsMemory
;limitsCpu
和limitsMemory
;注:这里提一下个人理解,对于上述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. 单位的含义
MiB
和Gi
,和常规单位的关系如下为:1 MiB = 2^20 bytes = 1024 kibibytes = 1048576 bytes(以2为底数)
1 MB = 1000KB(以10为底数)
m
或milli
),或者直接是核(没有单位,如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 }}
K8S中的探针应该是必不可少的一部分,参照文档,理了理,探针主要分为2类:
实际观测,只有当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
实际开发中,发现后端的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
2.具体指令
探针检测的具体行为可以自己定义,比如:
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
上述是将探针的行为定义为Linux中的cat
命令
3.TCP
比如一些ES的连接可能会用到TCP
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
在rancher中,权限一般给的很严,一般不会使用docker
命令直接进入容器,而是要使用kubectl
命令才能进入:
kubectl exec -it <podName> -c <containerName> -n <namespace> -- -- /bin/bash
# 退出容器
exit
如果Pod中只有1个容器,上述命令可以直接简写为kubectl exec -it <podName> -n <namespace> -- -- /bin/bash
,进入容器后即可为所欲为了(●ˇ∀ˇ●)。
【注】上述通过kubectl exec
可以拓展为任意容器中的命令,形如kubectl exec <pod-name> -n <name-space> -- <command> xxx
,只要容器中可以执行<command> xxx
命令,那就可以通过上述的方式在不进入容器的情况下对容器执行一些命令。
Pod日志可以直接通过logs
或describe
查看,具体示例如下:
# 1. logs方式
kubectl logs <pod_name> -c <container_name> -n <namespace>
# 2. describe方式
kubectl describe pod <pod_name> -n <namespace>
得到的结果也是2个不同的,logs
方式得到的日志是我们是部署应用时指定挂载目录位置中的日志,如deployment中在指定容器规约时:
spec:
containers:
...
volumeMounts:
- mountPath: /data/logs/paas
name: log-dir
那此时看到的日志就是/data/logs/paas/xxx.log
的内容。
describe
方式得到的日志是关于pod行为的日志,最典型的就是Pod节点的分配Scheduled
、镜像的拉取Pulling
/Pulled
、容器的创建Created
及运行Started
、探针检测Unhealthy
/Healthy
。
和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"}
我们需要的是服务端版本,指的是GitVersion:"v1.11.5"
,所以kubeVersion: 1.11.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
注:OSS还需要额外绑定k8s-www.hhu.com
域名,否则无法进行代理。
默认情况下,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=""
# 正常删除
kubectl delete pod xxx
# 强删
kubectl delete pod <pod name> --force --grace-period=0 -n <namespace>
删除命名空间的准确步骤如下:
kubectl get all -n xxx
,然后清空资源(务必确认这个步骤);kubectl delete ns xxx
;kubectl get ns
;【注】:
kubectl delete pod <pod name> --force --grace-period=0 -n <namespace>
;finalizers
属性:# 编辑命名空间的配置文件
kubectl edit ns xxx
# 删除如下属性保存退出即可
finalizers:
- kubernetes
# 创建凭证
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>
在控制器的规约中指定拉取镜像的密码名字即可,在spec中直接使用:
spec:
template:
spec:
imagePullSecrets:
- name: <secret-name>
【注】上述的--docker-xxx
选项中的值可能包含了一些特殊的值(比如#
或者@
等符号),此时需要使用英文的引号'xxx'
来包裹,这种写法也是比较推荐的,不会出现问题。
比如升级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...
测试环境中,由于少了域名解析的这一层,通常需要额外绑定一些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"
通过名命令kubectl exec server-746ddf6747-9kmx7 -n <name-space> -- cat /etc/hosts
可以检验。
在k8s中为了可以进行remote debug,可以做如下配置:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=<remote-debug-port>
;nodePort
类型)映射到remote-debug-port
;<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
K8S在创建Service时,将会给其分配IP地址(默认是cluster IP
,即只能在集群中访问),Service可以将任意请求到port
的端口映射到targetPort
端口,完成最终请求,默认情况下,为了便利,targetPort
设置为和port
一样的值。
ServiceType,即在定义Service时,spec.type
字段,默认为ClusterIP
,相关释义如下:
ClusterIP
:使用集群内部的IP暴露服务,只能在集群内部访问服务,默认值;NodePort
:将服务暴露在集群中每个节点的一个指定静态端口上(即port.NodePort
),将会自动创建一个ClusterIp
的Service,然后将NodePort
的服务路由到上面,此时集群外部可以访问集群内的服务;LoadBalancer
:使用云提供商(如阿里云)提供的负载均衡暴露服务,指向集群外部负载均衡的NodePort
和ClusterIp
Service会被自动创建;ExternalName
(CoreDNS1.7+):externalName
字段通过返回CNAME记录的方式,将Service映射到上面。有些场景需要将Pod调度到指定节点上,步骤如下:
kubectl label node <node_name> <your_label>
;NodeSelector: <your_label>
即可; 由于使用使用的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
问题出现场景:在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
在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
注:关于resolve中的配置作如下说明:
nameserver
:用于指定DNS服务器;search
:用于指定若用户域名中的点号.
的数量小于ndots
参数指定的值,那它将会尝试带上这些search
指定的前缀去解析用户域名,若执行ping test
(该域名中没有点号),实际它会帮我们同时去ping test
、ping hhu-test.svc.cluster.local.test
、ping svc.cluster.local.test
、ping cluster.local.test
,系统会尝试都去解析这些域名;ndots
:用于指定用户域名中的点号.
小于这个指定值,那系统会自动帮我们加上search
中的各个前缀作域名解析;single-request-reopen
和single-request
:为了解决A记录和AAAA记录解析冲突或超时使用的;single-request-reopen
和single-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配置:
Default
:默认继承所在Node的DNS策略,但它并不是Pod的默认DNS策略,如果pod的DNS配置缺省,默认是ClusterFirst;ClusterFirst
:不符合已配置的domain的后缀的域名,将会转交给从所在Node继承的nameserver DNS去作解析;ClusterFirstWithHostNet
:使用hostNetwork的Pod必须显式的设置ClusterFirstWithHostNet=true
;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
一般集群中的DNS出现解析异常或解析超时等问题,可以尝试加上如下的dns的配置:
dnsConfig:
options:
- name: timeout
value: "5"
- name: ndots
value: "2"
- name: single-request-reopen
dnsPolicy: ClusterFirst
核心思想是将超时时间变长、减少不必要的域名解析(将增加域名前缀的域名条件变成点号为2)、以及减少冲突。
在基于alpine镜像中,配置基本的时区:
apk add tzdata
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 可选,删除可以最小化镜像
apk del tzdata
# 使用 -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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。