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 <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: https-config
data:
  https.key: |-
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA8mEyACKNJcxAfPWvAYr0uy/wVtd16LPEpy7w1l9WD9+SBsOj
    n0QZSc2+y/mEpwXA65irqUply42NabB6rgfsfbcLra8SA5CbyHtrIP4fT6jnBA85
    Jzmznp/q/1n4nlfaqawFyOB2IzPjPiwzexN/gv2wWC+5wSQ9yq2k
    -----END RSA PRIVATE KEY-----
  https.pem: |-
    -----BEGIN CERTIFICATE-----
    MIIFfjCCBGagAwIBAgIQD8reYBHHOc99faMYlJ0lazANBgkqhkiG9w0BAQsFADBu
    MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
    d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg
    -----END CERTIFICATE-----
EOF

挂到容器作为配置文件:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: srs
  labels:
    app: srs
spec:
  selector:
    matchLabels:
      app: srs
  template:
    metadata:
      labels:
        app: srs
    spec:
      volumes:
        - name: ssl-volume
          configMap:
            name: https-config
      containers:
      - name: srs
        image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:3
        imagePullPolicy: IfNotPresent
        volumeMounts:
          - name: ssl-volume
            mountPath: /usr/local/srs/etc
EOF

可以查看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

改进:

关于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 <<EOF | kubectl apply -f -
apiVersion: v1
EOF

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

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
---
apiVersion: apps/v1
kind: StatefulSet
EOF

配置镜像拉取,如果存在就不拉取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 <<EOF | kubectl apply -f -
kind: Service
apiVersion: v1
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
EOF

查询该服务:

$ kubectl get svc/nginx-service
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   172.21.7.190   <none>        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 <none> 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   <none>        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声明式:

Aliyun ACK

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

  • 至少1个worker,2CPU2GB。包括,Replicas:10个,内存:850m,CPU:1030Mi。
  • 虽然coredns是两个replicas,但是也可以运行在一个worker上面。
  • 至少3个IP,1个是kubectl访问集群用,1个是SLB对外提供服务用,1个是绑定到worker访问外网用(比如下载更新docker镜像)。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容