1. 预留和限制的概念
requestsCpu
和requestsMemory
;limitsCpu
和limitsMemory
;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
,进入容器后即可为所欲为了(●ˇ∀ˇ●)。
【注】上述通过kubectl exec
可以拓展为任意容器中的命令,形如kubectl exec
,只要容器中可以执行
命令,那就可以通过上述的方式在不进入容器的情况下对容器执行一些命令。
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 --force --grace-period=0 -n
;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=[email protected]
# 删除密码
kubectl delete secret <secret-name>
在控制器的规约中指定拉取镜像的密码名字即可,在spec中直接使用:
spec:
template:
spec:
imagePullSecrets:
- name: -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
可以检验。
在k8s中为了可以进行remote debug,可以做如下配置:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=
;nodePort
类型)映射到remote-debug-port
;:
),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
;NodeSelector:
即可;# 使用 -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