K8S备忘录

K8S的概念:

  • Node(节点),节点机器,可以是物理机或虚拟机,比如ECS,运行了各种进程和docker等。
  • Pod(容器组),应用的执行单元,创建和部署的单元;包含一个或多个容器,存储和网络资源,以及配置项。
  • Controller(应用),管理Pods、复制和自愈,例如Node失败时自动迁移Pods到其他节点。
  • Deployment(无状态),无状态Controller(应用)。
  • Container(容器),一个Pod可以跑N>=1个Container,这些Container可以共享一些东西。

K8S命令

在macOS下可以直接用brew安装:

brew install kubectl

常用的K8S命令:

kubectl cluster-info
kubectl get cm --all-namespaces
kubectl get cm -n kube-system
kubectl get cm/srs3-config
kubectl get cm/cluster-info -n kube-public -o yaml
kubectl get cm/cluster-info -n kube-public -o json
kubectl get deploy
kubectl get deploy/nginx-deployment -o yaml
kubectl get deploy/nginx-deployment -o json
kubectl get rs
kubectl get rs -w
kubectl get pods
kubectl get pods --show-labels
kubectl edit deployment.v1.apps/nginx-deployment
kubectl api-resources # 可以查询到缩写。
kubectl get services
kubectl get svc/nginx-service -o yaml
kubectl exec srs-deployment-f4cd6b6cc-c74bm -c nginx env
kubectl get pods -o jsonpath="{.items[*].spec.containers[*].name}"
kubectl delete po/srs-origin-deployment-577cf4c7b7-zp7bs --force --grace-period=0
kubectl get po --show-labels
kubectl label deploy/srs-origin-deployment app=srs --overwrite
kubectl label po/srs-origin-deployment-6b4fcf6674-qphdz app=srs --overwrite
kubectl exec srs-edge-deploy-58d9999b7c-f4rtr -- ./objs/srs -v
kubectl set image deploy/srs-edge-deploy srs=ossrs/srs:v4.0.6
kubectl set image deploy/srs-edge-deploy srs=ossrs/srs:v4.0.6 --record
kubectl rollout history deploy/srs-edge-deploy
kubectl get rs
kubectl get rs -w # 可以Watch变化。
kubectl scale --replicas=3 deploy/srs-edge-deploy
kubectl logs srs-edge-deploy-d4cfc9d8b-5wnw7
kubectl describe po/srs-edge-deploy-d4cfc9d8b-85nkf |grep -A 3 Events
kubectl get nodes
kubectl describe nodes/cn-beijing.172.17.232.153
kubectl config get-contexts

切换集群

查看集群:

kubectl config get-contexts

设置默认集群:

kubectl config use-context kubernetes-admin-xxx

ConfigMap保存证书

ConfigMap可以配置多个文件,比如证书的私钥(*.key)和公钥(*.pem),后台添加配置,或者用yaml:

cat <

挂到容器作为配置文件:

cat <

可以查看etc目录,就有了证书文件:

$ kubectl get pods -l app=srs
NAME                   READY   STATUS    RESTARTS   AGE
srs-77b446fbfc-ftz7c   1/1     Running   0          4m50s

$ kubectl exec srs-77b446fbfc-ftz7c -- ls -lh etc
lrwxrwxrwx 1 root root 16 Feb  1 07:06 https.key -> ..data/https.key
lrwxrwxrwx 1 root root 16 Feb  1 07:06 https.pem -> ..data/https.pem

Volume

Volumes,docker有这个概念但比较简单且无生命周期管理,k8s提供各种复杂的volume,而且volume是随pod的生命周期(大于container)。

emptyDir,临时的空目录,在pod删除时会情况,container crash后目录数据还在,可用于crash后的恢复。

比如SRS写到nginx目录后,nginx可以分发切片文件。

  • nginx默认目录是:/usr/share/nginx/html
  • SRS的默认目录是:/usr/local/srs/objs/nginx/html

起一个Pod运行这两个容器,共享这个目录,这样就可以实现SRS写Nginx读了。

Container Args

设置container的命令和参数。

使用command和args,command是命令,args是它的参数,一般需要一起设置。可以command指定为/bin/sh,用shell执行脚本,参数就是:

command: ["/bin/sh"]
args: ["-c", "cp -R ./objs/nginx/html/* /tmp/html/; sleep infinity"]

也可以在里面写for循环:

command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]

还可以写成数组的方式,字符串用长字符串换行:

command: ["/bin/sh"]
args:
- "-c"
- >
  while true; do
    ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
      -c copy -f flv rtmp://srs/live/livestream
  done

可以用脚本做初始化的事情。也可以只用args,会从args中解析出command:

args:
- /bin/sh
- -c
- echo hello

Resource

管理容器使用的计算资源,Resource。

可以创建Pod时指定需要的CPU和内存,这样调度时知道调度到哪个节点;还可以指定资源的限额。资源限额定义,Quota。

资源的要求和限制,指定在:

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory

CPU,比如cpu: 0.1就是100m,m就是千分之,millicpu,millicores。
Memory,比如memory: 100Mi就是100MiB,还有Ki,Gi,Ti,Pi。

limits是最多使用的限制;requests是需要使用的,也就是要求的资源。

0<=Request<=Limit<=Infinity (如果Limit为0表示不对资源进行限制,这时可以小于Request)

关于调度:

  • 调度时会根据request的资源,保障总的request资源小于Node容量。所以request的资源,是比实际运行的要小的。
  • 或者可以认为,低峰期时空闲时CPU是1%,高峰时可能request到30%,limit到50%。调度时就根据各个Pod的30%作为上限。

关于limit:

  • K8S启动容器时,会把request cpu传递到容器runtime中,docker run的-c参数:
    • 默认是1024,至少是2。如果某个容器是空闲的,其他容器可以使用剩下的全部的CPU。某个容器能使用的最多CPU是变化的。
    • 例如如果一个是1024,两个是512,如果容器内的CPU都跑到100%了,那么实际上第一个是50%,剩下两个是25%。
    • 如果再加第四个容器也是1024,那么第一个是33%,第二和三是16.5%,第四是33%。
    • 虽然每个容器的CPU份额是小于100%的,但在多核的机器上可能让多个CPU跑100%。
    • 比如有3个CPU,第一个容器是512,另外是1024,那么可能CPU0是100%跑第一个容器,CPU1和CPU2也是100%跑第二个容器。从系统总体看CPU是300%,100%给了第一个容器,200%给了第二个容器。
  • 而limit cpu,则会传递到docker的cpu-quota参数。
  • 而limit memory,则会传递到docker的memory参数:
    • 如果容器超过内存限制limits.memory,很可能重启。内存和磁盘是不可压缩资源。
    • 如果容器超过要求的内存requests.memory,在节点超过能力时会被迁移。
    • 容器可能会被允许超过CPU的限制,但不会因为CPU过高而被KILL。CPU是可压缩资源。
  • 如果容器因为资源限制被终止:
    • 查看容器的事件:kubectl describe po/srs-edge-deploy-d4cfc9d8b-85nkf |grep -A 3 Events
  • 查看节点调度的资源情况:kubectl describe nodes/cn-beijing.172.17.232.153

改进:

  • 打算支持e自定义资源:
  • 打算支持分级QoS来复用

关于QoS:

  • Guranteed,最高保障,request和limit相等,只指定了limit(就默认request等于limit)。
    *Burstable,Strong wish,强烈要求,request和limit有设置,但不相等。
  • Best-Effort,最好能支持,完全不设置。

OOM时KILL的顺序:最先KILL Best-Effort,然后是Burstable,最后是Guranteed。

Config Reload

ConfigMap修改后,文件会变。

注意:fsnotify signal可能因为符号链接收不到。

Linux是使用inotify机制,用inotify_init返回fd,watch后如果有变化可以read出来。

可以侦听所有事件:IN_ALL_EVENTS,但主要是IN_MODIFY和IN_CREATE。

返回的内容转换成结构体:inotify_event

YAML格式

-表示列表,比如ports可以指定多个,所以就以-开头。

管道命令行apply:

cat <

可以把多个yaml写在一起,用三个横杠分割,比如stateful:

cat <

配置镜像拉取,如果存在就不拉取images:

spec.containers[*].imagePullPolicy: IfNotPresent

如果总是拉取,则设置为Always

Service

Service就是如何让Pods提供服务,或微服务,涉及服务发现和负载均衡。

一般服务发现使用的是label selector,有时候也需要无selector的Service(必须后端是另外一个K8S或非Pod)。标签(label)就是分组的一种方式了,或者一种ID。

定义服务时,需要指定selector比如app=nginx,指定服务端口port,以及后端端口targetPort(默认等于port)。

注意targetPort可以是字符串,会解析成对应的端口,这样后端服务会比较灵活。

下面是一个服务的描述。

cat <

查询该服务:

$ kubectl get svc/nginx-service
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   172.21.7.190           80/TCP    3m13s

服务是由kube-proxy分配的VIP并进行代理,没有采用DNS因为有缓存等问题:

kube-proxy有几种不同的代理方式:

  • user space proxy mode,用户空间代理模式,proxy为每个service侦听一个端口,通过iptables将服务的请求转发到这个端口然后转给pod。
  • iptables proxy mode,iptables代理,proxy为每个service新增一条转发规则,负载高应该是内核netfilter转发,但出错后不会重试。
  • IPVS proxy mode,1.11后支持了IPVS代理,使用的是内核的netlink,比iptables性能更高吞吐率也更高。

如果杀掉nginx的pod,如果只有一个pod会服务不可用,但过一会儿K8S会重新启动pod,服务就可以了。

Service Type

ServiceTypes服务类型,一般ClusterIP就是内部服务,还有其他的:

  • ClusterIP,默认就是这种,集群内部可以访问。分配的是Service网段IP(172.21.*),有内部端点无外部端点。
  • NodePort,绑定到Node上,也就是Node(ECS)网段(172.17.*),会自动创建ClusterIP服务。外部可以通过Node的IP访问服务。
  • LoadBalancer,通过云服务的负载均衡实现,会自动创建NodePort和ClusterIP。外部访问的是负载均衡。
  • ExternalName,绑定到CNAME,比如foo.example.com,不会创建代理,coredns1.7及以上才支持。

默认是ClusterIP,看到内网的IP(ClusterIP),但无外部IP(ExternalIP),ClusterIP 172.21.89.226 80/TCP,可访问性如下:

  • nginx的realserver是在172.17.232.153
  • nginx的POD的IP,可在跳板机访问:curl http://172.20.0.24
  • nginx-service的IP是172.21.89.226,无法在跳板机访问,但可以在node上访问:curl http://172.21.89.226

LoadBalancer负载均衡,一般阿里云标准用法是EIP绑定到SLB上,通过SLB对外提供服务。SLB创建时选内部SLB,交换机选k8s-node使用的,比如IP是172.17.232.159。然后再买EIP绑定到SLB。不用创建SLB侦听,k8s会自动做这事,创建后是这样:

$ kubectl get svc/nginx-service
NAME            TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
nginx-service   LoadBalancer   172.21.113.176   172.17.232.159   80:30589/TCP   6s

可以发现,类型是LoadBalancer,设置了ExternalIP。

Service Discovery

服务发现可以用ENV环境变量,或者DNS。

当注册Service后,K8S会在POD启动时,增加服务对应的环境变量,那么POD就可以用这个环境变量来发现服务了。

CoreDNS组件开启后,创建了Service就会创建对应ServiceName的DNS记录,比如nginx-service。

$ kubectl get service/nginx-service
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   172.21.7.190           80/TCP    16m

$ kubectl exec srs3-demo-deploy-55df684cbb-5dzsh -- ping nginx-service
PING nginx-service.default.svc.cluster.local (172.21.7.190) 56(84) bytes of data.
64 bytes from nginx-service.default.svc.cluster.local (172.21.7.190): icmp_seq=1 ttl=64 time=0.033 ms

StatefulSets时,需要给每个Pod分配可达的地址,这就是HeadlessServices。通过配置:ClusterIP: None 来指定的。

声明式更新

Declarative updates,声明式更新:

However, a Deployment is a higher-level concept that manages 
ReplicaSets and provides declarative updates to Pods along with 
a lot of other useful features.

这文章说declarative updates:

  1. kubectl apply一般是declarative。参考#1和 #2
  2. kubectl run会转成declaratively Deployment:kubectl translates your imperative command into a declarative Kubernetes Deployment object. A Deployment is a higher-level API that allows rolling updates (see below).
  3. 意味着保持期望状态和实际状态一致:The Kubernetes API is fundamentally declarative, which means that the controllers always work to reconcile the observed state with the desired state. Therefore, if we delete a Pod, the ReplicaSet controller will create a new one to replace it, to maintain the desired replica count.
  4. 配置你想要的,K8S知道怎么实现:However, the power of Kubernetes is in its declarative API and controllers. You can just tell Kubernetes what you want, and it will know what to do.
  5. apply是幂等操作,可以执行多次:The kubectl apply command is idempotent. We can reuse it after modifying the manifests.

官方也有几个文章说Imperative命令式和Declarative声明式:

  • 命令式,Managing Kubernetes Objects Using Imperative Commands
  • 声明式,Declarative Management of Kubernetes Objects Using Configuration Files
  • Kubernetes Object Management,对比了命令式和声明式的差异。

Aliyun ACK

ACK托管集群,需要的基本资源:

  • 至少1个worker,2CPU2GB。包括,Replicas:10个,内存:850m,CPU:1030Mi。
  • 虽然coredns是两个replicas,但是也可以运行在一个worker上面。
  • 至少3个IP,1个是kubectl访问集群用,1个是SLB对外提供服务用,1个是绑定到worker访问外网用(比如下载更新docker镜像)。

你可能感兴趣的:(K8S备忘录)