Kubernetes笔记

K8S介绍

资料链接

K8S中文教程:https://www.kuboard.cn/learning/

K8S中文文档:< >

GO语言编写

高可用集群副本最好大于等于3(2可能产生脑裂,1会单节点故障),且为奇数(偶数投票数目相等可能会产生问题)

特点

  • 轻量级:消耗资源少
  • 开源
  • 弹性伸缩
  • 负载均衡:IPVS

服务分类

  • 有状态服务:DBMS
  • 无状态服务:Apache、LVS

K8S想要实现有状态,所以需要持久化

组件

master节点

  • APISERVER:所有服务统一访问入口,虚拟IP,相同服务相同标签的pod组的管理入口
  • CrontrollerManager:维持副本期望数目
  • Scheduler:通过算法选择合适的节点分配任务
  • ETCD:键值对数据库,存储K8S集群所有重要信息(持久化)

node节点

  • Kubelet:直接和容器引擎进行交互,对容器生命周期进行管理
  • Kube-proxy:负责写入规则到iptables、IPVS实现服务映射访问 ,借此实现Service的负载均衡
  • CROEDNS:为集群的SVC创建虚拟ip

控制器

  • ReplicationController:用来确保应用的副本数始终保持在用户定义的副本数,如果有容器异常退出,会自动创建新的pod来代替,异常多出的pod也会自动回收,新版本中建议使用ReplicaSet来取代
  • ReplicaSet和上面一样,但支持集合式的selector(标签选择器)
  • Deployment:一般建议用这个自动管理RS,支持滚动更新,RS不支持;Deployment创建RS,再通过RS创建pod
  • Horizontal Pod Autoscaling:HPA基于RS创建,可以根据CPU状况设定MAX和MIN自动创建或删除pod,实现自动弹性部署
  • StatefulSet:
    • 解决有状态存储的问题,有稳定的持久化存储,pod死亡后重建之间连接,不会数据丢失,基于PVC实现;
    • 稳定的网络标志,pod重新调度podname和hostname不变,基于Headless Service实现;
    • 有序部署,部署或拓展要依据定义依次进行(在下一个pod运行前保证之前的pod必须是running和ready状态),基于init containers来实现;
    • 有序收缩,有序删除
  • DaemonSet:确保全部(或一些,node可以打污点,这样就不会创建pod)Node上运行一个Pod,当有node加入集群或移除,pod会创建或删除,删除DaemonSet将删除其创建的所有Pod;可以用来运行集群存储daemon、每个node上运行日志收集daemon、每个node上运行监控daemon;如果有需求在每一个node上运行一个守护进程,就可以使用DaemonSet
  • Job:负责批处理任务
  • Cron Job:管理基于时间的job

组件介绍

Pod

开启pod时默认开启第一个pause容器,其余容器默认共享pause网络栈,需要注意pod内容器端口不能冲突;其余容器同时共享pause的存储卷

网络

k8s网络三层.png

网络解决方案

实现Pod间IP通信

相同主机Flannel

直接由Docker0网桥实现转发

不同主机Flannel

在node上开启一个Flannel守护进程,将本机pod的请求二次封装发送到指定node节点,在目标节点上解包发送到指定pod上,实现不同主机的pod通信。

ETCD之Flannel:存储Flannel可分配的ip地址段资源(避免ip冲突)、监控每个pod的实际地址,并在内存中建立维护Pod节点路由表,以此实现pod通信获取到目标pod的node节点的ip地址

Pod至Service的网络

之前是基于iptables维护转发,现在走LVS,性能更强

K8S集群

K8S集群搭建

最好使用私有化docker仓库--harbor

搭建单Master多Node

  • 设计好服务器数量,统计好ip地址,写入所有hosts文件,关闭SELinus、swap、firewalld,一台机器下载配置ansible,分配公钥

  • 使用写好的ansible配置安装,先安装依赖包进行初始化配置,升级内核,最后重启
    升级内核如果有问题,yum卸载后重新安装一次即可

    # 查看可使用的内核列表
    awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
    # 查看当前默认内核启动项
    grub2-editenv list
    # 修改内核启动项
    grub2-set-default "CentOS Linux (4.4.242-1.el7.elrepo.x86_64) 7 (Core)"
    # 或者根据上面list的序号修改
    grub2-set-default 0
    # 修改好之后重启
    reboot
    # 查看内核是否修改正确
    uname -r
    
  • 按照写好的ansible下载docker和k8s,下载镜像

  • ansible的配置完成后,先配置主节点:

    # 配置master节点
    # 先新建个目录用来存储安装信息install-k8s/core
    # 打印kubeadm默认配置,在此基础上进行修改
    kubeadm config print init-defaults > kubeadm-config.yaml
    # 以/kubeadm-config.yaml 文件进行初始化,在此之前修改配置
    localAPIEndpoint:  
      advertiseAddress: 172.27.0.127  # 这里修改成对应的本机ip,必须是ipv4
    # 修改安装的对应版本,这里是1.15.1
    kubernetesVersion: v1.15.1  # 把后面版本号修改
    # 添加的pod网段
    networking:
      dnsDomain: cluster.local
      podSubnet: "10.244.0.0/16"  # 这里是添加行,添加的pod网段配合Flanneld
      serviceSubnet: 10.96.0.0/12
    
    # 在最后添加以下内容,把默认调度方式改为IPVS
    ---
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    kind: KubeProxyConfiguration
    featureGates:
      SupportIPVSProxyMode: true
    mode: ipvs
    #################################################################
    # 执行初始化,后一个参数是配置高可用的,并将初始化信息收集到当前目录日志中
    kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs | tee kubeadm-init.log
    # 根据日志描述,初始化之后需要做几件事
    mkdir -p $HOME/.kube  &&
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config  &&
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    #################################################################
    # 安装插件Flanneld,在install-k8s目录下创建plugin/flannel目录
    # kube-clannel.yml在配套的文件夹里,移动到指定目录即可
    # 下载完成后执行
    kubectl  create -f kube-flannel.yml
    # 剧本执行完查看是否安装成功
    kubectl get pod -n kube-system
    kubectl get node
    
    # 配置work节点
    # 在master节点输入命令
    kubeadm token create --print-join-command
    # 将命令的输出复制到work节点运行,就可以连接上master节点了,这个token两小时过期
    

集群资源分类

  • 命名空间级别:kube-system(系统组件pod)
  • 集群级别:role
  • 元数据型级别

NameSpace

# 命名空间创建格式,通过kubectl get namespace获取
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx

Pod

pod文件常用字段

pod文件常用字段1.pngpod文件常用字段2.pngpod文件常用字段3.png

# 如果有不清楚的可以用以下命令
kubectl explain pod  # 后面跟参数,嵌套用.

pod生命周期

Pod生命周期.png

Init C

Inin C容器总是运行到成功为止,每个Init容器必须在下一个Init启动前成功完成,如果pod的Init容器失败,该pod会不断被重启直到成功(除非restartPolicy设置为Never)

在文件中体现为initContainers关键字,一般用busybox镜像多一些

探针

探针是kubelet对容器执行的定期判断。要执行判断,kubelet调用容器实现的Handler。有三种类型的处理程序:

  • ExecAction:在容器内执行指定命令。如果退出时返回码为0,则认为诊断成功
  • TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查。如果端口打开,则诊断成功
  • HTTPGetAction:对指定端口和路径上的容器IP地址执行HTTP Get请求。如果响应的状态码大于等于200且小于400,则诊断成功

每次探测都会获得以下三种结果之一:

  • 成功:容器通过了诊断
  • 失败:容器未通过诊断
  • 未知:诊断失败,因此不会采取任何行动

探测方式:

  • livenessProbe:只是容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器将受到重启策略的影响。如果容器不提供存活探针,则默认状态为Success

    # 该探针贯穿整个pod存活周期,一旦诊断失败直接杀死pod
    spec:  # 指定该资源内容
      containers:
      - name: app  # 容器名字
        image:  # 容器使用的镜像地址
        livenessProbe:  # 就绪探针
          exec: 
            command: ["test","-e","/tmp/live"]	
            initialDelaySeconds: 1  # 容器启动1s后开始检测
            periodSeconds: 3  # 3s后再次检测
    
  • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制将从与pod匹配的所有Service的端点中删除该pod的IP地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success

    # 就绪检测如果失败只是状态不是ready,不像存活检测直接杀死pod重启
    spec:  # 指定该资源内容
      containers:
      - name: app  # 容器名字
        image:  # 容器使用的镜像地址
        readinessProbe:  # 就绪探针
          httpGet:  # 发送HTTP Get请求来探测
            port: 80  # 端口
            path: /index.html  # 请求地址
            initialDelaySeconds: 1  # 容器启动1s后开始检测
            periodSeconds: 3  # 3s后再次检测
    

启动、退出

spec:  # 指定该资源内容
  containers:
  - name: app  # 容器名字
    image:  # 容器使用的镜像地址
    lifecycle:  
      postStart:  # 容器创建成功后,运行前的任务,用于资源部署、环境准备等
        exec:
          command: []
      preStop:  # 在容器被终止前的任务,用于优雅关闭应用程序、通知其他系统等等
        exec:
          command: []

yaml文件模板

# pod.yaml文件模板
apiVersion: v1  # 指定api版本,此值必须在kubectl api-versions中
kind: Pod  # P必须大写
metadata:  # 资源元数据/属性
  name: [自定义名称]
  namespace: [命名空间不写默认default]
  labels:  
    app: [自定义标签名]
    version: v1
spec:  # 指定该资源内容
  restartPolicy: Always  # 表明该容器一直运行,默认k8s策略在此容器退出后会立即创建一个相同的容器
  containers:
  - name: app  # 容器名字
    image:  # 容器使用的镜像地址
    imagePullPolicy: Never # 三个选择Always、Never、IfNotPresent,每次启动时检查和更新(从                                  registery)images的策略,
                           # Always,每次都检查
                           # Never,每次都不检查(不管本地是否有)
                           # IfNotPresent,如果本地有就不检查,如果没有就拉取
    command: ['sh']  # 启动容器的运行命令,将覆盖容器中的Entrypoint,对应Dockefile中的ENTRYPOINT 
    args: ["$(str)"]  # 启动容器的命令参数,对应Dockerfile中CMD参数
    readinessProbe:  # 就绪探针
      httpGet:  # 发送HTTP Get请求来探测
        port: 80  # 端口
        path: /index.html  # 请求地址
        initialDelaySeconds: 1  # 容器启动1s后开始检测
        periodSeconds: 3  # 3s后再次检测
  initContainers:  # initC容器,负责前置处理
  - name: init-app
    image: busybox  # 用busybox实现服务检测
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting; sleep 2; done;']

Pod状态

kubectl get pod  # 查看pod状态,RESTARTS重启次数
NAME    READY   STATUS    RESTARTS   AGE
myapp   1/1     Running   0          4s

STATUS几种状态

  • 挂起(Pending):Pod已经被K8S系统接受,但有1或多个镜像尚未创建,等待时间包括调度pod的时间和通过网络下载镜像的时间,可能会花费些时间
  • 运行中(Running):该pod已经绑定到一个节点上,pod中所有容器都已被创建,至少有一个容器正在运行,或正处于启动或重启状态
  • 成功(Successed):pod中多有容器都被成功终止,并且不会再重启
  • 失败(Failed):pod所有容器都已经终止了,并至少有一个容器因为失败而终止,也就是说,容器以非0状态退出或者被系统终止
  • 未知(Unknown):因为某些原因无法取得pod的状态,通常是因为与pod所在的主机通信失败

控制器

控制器类型

  • ReplicationController和ReplicaSet
  • Deployment
  • DaemonSet
  • StateFulSet
  • Job/CronJob
  • Horizontal Pod Autoscaling

ReplicaSet

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: [name]
spec:
  replicas: 3  # 副本数量,如果pod少了或多了会自动创建删除
  selector:  # 选择标签
    matchLabels:
      tier: fs  # 键值对
  template:  # 理解为下面嵌套了对pod的定义
    metadata:
      labels:
        tier: fs
    spec:
      containers:
      - name: 
        image:
        env:  # 环境变量
        - name: 
          key: values
        ports:
        - containerPort: 80  # 开放端口     

Deployment

# 执行kubectl apply -f *.yaml --record最后加上record记录详细信息
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: [name]
spec:
  replicas: 3  # 副本数量,如果pod少了或多了会自动创建删除
  template:  # 理解为下面嵌套了对pod的定义
    metadata:
      labels:
        tier: fs
    spec:
      containers:
      - name: 
        image:
        env:  # 环境变量
        - name: 
          key: values
        ports:
        - containerPort: 80  # 开放端口     
# 扩容命令
kubectl scale deployment [name] --replicas=10  # 扩容到10个pod
# 滚动更新镜像
kubectl set image deployment/[name] [container]=[image]:1.1
# 回滚,通过RS交替创建和删除pod实现,可以指定版本,不指定默认上一个版本
kubectl rollout undo deployment/[name] --to-reversion=2  # 回滚回老版本
# 查看更新的历史版本
kubectl rollout history deployment/[name]
# 查看更新状态
kubectl rollout status deployment/[name]
# 暂停更新
kubectl rollout pause deployment/[name]

可以通过设置==.spec.revisionHistoryLimit==指定最多保存多少历史版本,默认全部保存,改为0就无法回滚了

Rollover(多个Rollout并行)

如果创建10个pod,只创建到3个的时候就进行更新操作,Deployment会直接杀死3个老版本pod再创建10个新pod。

在新版本中,pod的热更新从1-1变为25%-25%,一次更新1/4

DaemonSet

# 命令式,最好用create创建
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: daemonset
  labels:
    app: daemonset
spec: 
  selector:
    matchLabels:
      name: daemonset123
    template:
      metadata:
        labels:
          name: daemonset123
      spec:
        containers:
        - name: 
          image:    

即使删除某个node上的pod,也会立刻重启,DaemonSet保证每个节点上都有该pod,但是master默认不参与pod的运行

Job

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      name: pi
    spec: 
      containers:
      - name: pi
        image: 
        command: []
      restartPolicy: Never

CronJob

  • spec.template格式同pod
  • restartPolicy仅支持Never和OnFailure
  • 单个pod时,默认pod成功运行后Job结束
  • .spec.completions标志Job结束需要成功运行的Pod个数,默认为1
  • .spec.parallelism标志并行运行的Pod个数,默认为1
  • spec.activeDeadlineSeconds标志Pod的重试最大时间,超过这个时间不会继续重试

Service

默认四层负载均衡能力,只有rr轮询一个算法,可以通过Ingress七层负载。新版的ipvs代理模式支持更多的负载算法,效率也更高,可以通过ipvsadm -Ln查看ipvs代理路由

Service在k8s中有以下4种类型:

  • ClusterIP:默认类型,自动分配一个仅Cluster内部可访问的虚拟VIP,有特殊类型HeadLess无头域名,不用ip而用域名依然可以访问,把spec.clusterIP设置为None就行了
  • NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样可以通过 :NodePort 来访问该服务,外部可以访问,可以在部署该Service的node前加nginx实现完全的负载均衡
  • LoadBalancer:在NodePort基础上借助cloud provider创建一个外部负载均衡器,并将请求转发到 :NodePort ,需要花钱
  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有高版本k8s才支持
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: ClusterIP  # 四种类型
  selector:  # 标签必须和deployment一致,否则匹配不到
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
# 查看ipvs路由观察svc的作用
ipvsadm -Ln
TCP  10.111.64.40:80 rr  # VIP和算法
  -> 10.244.1.8:80                Masq    1      0          0
  -> 10.244.1.9:80                Masq    1      0          0
  -> 10.244.1.10:80               Masq    1      0          0
  -> 10.244.2.8:80                Masq    1      0          0
  -> 10.244.2.9:80                Masq    1      0          0

Ingress

在svc基础上再加一层Nginx对外提供服务,但是配置自动生成,可以通过label自动发现服务所以是Ingress-Nginx,当然也有其他负载类型的Ingress-controller

部署参考:https://www.cnblogs.com/panwenbin-logs/p/9915927.html

default-http-backend的pod:
如果外界访问的域名不存在的话,则默认转发到default-http-backend这个Service,其会直接返回404

配置文件放在ingress目录下

  1. 首先实例化ingress-controller的pod,还有对应的service,这里从官网下载的,镜像也提前拉好,并在配置文件内替换镜像地址

    kubectl apply -f mandatory.yaml
    # 在所有节点拉取镜像
    docker pull registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/defaultbackend-amd64:1.5
    docker pull registry.cn-qingdao.aliyuncs.com/kubernetes_xingej/nginx-ingress-controller:0.20.0  # 注意版本!
    # 然后实例化NodePort服务提供对外访问端口
    # 最后编写实际运行的deployment和service
    

    有两种建议,第一种是最终的服务用HeadLess服务,将ingress-controller的replicas数增加作为高可用;第二种可以将ingress-controller写为daemonSet,然后直接和pod对接,这样效率最高。

    # Ingress配置格式
    apiVersion: extensions/v1beta1
    kind: Ingress  # 首字母必须大写
    metadata:
      name: nginx-test  
    spec:
      rules:
      - host: www1.test.com  # 七层代理
        http:
          paths:
          - path: /
            backend:
              serviceName: nginx-svc  # 配合需要对应的Service
              servicePort: 80  
      - host: www2.test.com  # 新的规则在下面继续写,一个ingress可以配置多个规则
    

ConfigMap

让镜像和配置文件解耦,以便实现镜像的可移植性和可复用性,因为一个configMap其实就是一系列配置信息的集合,将来可直接注入到Pod中的容器使用

三种创建方式:

  1. 使用目录创建,先创建一个目录,下面放配置文件,这个最好

    # --from-file指定目录下所有文件会创建键值对,文件名是key,文件内容是value
    kubectl create configmap [name] --from-file=[path]
    # 可以用kubectl get cm查看,缩写为cm
    
  2. 根据文件创建,和上面一样,只是目录换为文件名

  3. 使用字面值创建

    # 可以创建多个,但是一次只能一对键值对
    kubectl create configmap [name] --from-literal=[key]=[value] --from-literal...  # 重复添加
    # 查看创建的configmap
    kubectl get cm
    
  4. 根据yaml配置文件创建

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: [name]
      namespace: default
    data:
      log_level: INFO  # 数据键值对
    

导入使用configMap作为环境变量

# 第一种方式,针对性取值重命名导入
...
  containers:
    # 可以在内部使用这个参数
    command: ["/bin/bash","-c","echo $(define.key1) $(define.key2)"]
    env:
    - name: [define.key1]  # 自定义键名
      valueFrom:  # 来源
        configMapKeyRef:
          name: [configMap.name]  # configMap名字
          key: [configMap.key1]  # configMap的key名
    - name: [define.key2]
      valueFrom:
        configMapKeyRef:
          name: [configMap.name]
          key: [configMap.key2]
      
# 第二种方式,全部导入
...  # 前略
  containers:
    envFrom:
    - configMapRef:
        name: [name]  # 导入的configMap的名称
# 第三种方式,挂载为pod中的配置文件
...
  containers:
    ...
    volumeMounts:
    - name: configMap-volume
      mountPath: /etc/config  # 将配置文件名和内容挂载到这个目录
  volumes:
  - name: configMap-volume
    configMap:
      name: [configMap.name]  # 将对应configMap进行挂载
# 配置中心热更新,类似nginx配置热更新,但是配置修改后并不会读取,需要重启pod或配置热更新设置!
kubectl edit configmap [configMap.name]  # 进入修改

Secret

用于解决密码、密钥、token等敏感数据的配置问题,可以以volume或环境变量的方式使用

  • Service Account:用来访问Kubernetes API,由K8S自动创建并且会自动挂载到Pod的/run/secrets/kubenetes.io/serviceaccount目录中,不需要我们操作

  • Opaque:base64编码格式的Secret,用来存储密码、密钥等,只是编码方式不是加密用的所以容易破解,但不明文显示

    echo -n "admin" | base64  # 得出后的结果就是base64编码后的
    
    # 创建Opaque
    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      password: [base64]  # 这里是上面base64编码过后的值
      username: [base64]  # 
    # 将Secret挂载到Volume中,使用时会自动解码base64
    apiVersion: v1
    kind: Pod
    metadata:
      laels:
        name: seret-test
      name: seret-test
    spec:
      volumes:
      - name: secrets
        secret:
          secretName: mysecret  # 引用上面创建的名字
        containers:
        - image: [images]
          name: db
          volumeMounts:
          - name: secrets
            mountPath: "/etc/secrets"  # 按键值对生成文件
            readOnly: true
    # 将Secret导出到环境变量
    ...
      containers: 
      - name: pod
        image: [image]
        ports:
        - containerPort: 80
        env:
        - name: [define.name]
          valueFrom:
            secretKeyRef:
              name: mysecret  # 引用上面的名称
              key: username  # 键的值
        - name: [define.name]  # 新的环境变量参数
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
    
  • kubenetes.io/dockerconfigjson:用来存储私有docker register的认证信息

Volume

当容器崩溃时k8s会将其重启,但容器内文件会丢失,容器会以干净(最初)的状态重新启动,所以需要volume卷,volume和pod同级,创建pod时volume挂在pause上,所以容器挂了换新的也不影响其中的数据。

Pod中所有容器共享Volume,可以指定各自的mount路径

Volume类型

emptyDir

首先创建空卷和pause绑定,再去创建容器,创建后为空目录,一般用于测试,或者缓存场景,因为emptyDir Volume 的生命周期与 Pod 一致

emptyDir是host上定义的一块临时存储,通过bind mount的形式挂载到容器中使用,容器重启数据会保留,容器删除则volume会随之删除。

apiVersion: v1
kind: pod  # 先要创建pod
metadata:
  name: test-pd
spec:
  containers:
  - images: [image]
    name: [define.name]
    volumeMounts:
    - mountPath: /path  # 挂载路径
      name: [volume.name]  # 下面定义的volume名
  volumes:  # 和containers同级
  - name: [volume.name]
    emptyDir: {}  # 定义类型

hostPath

将主机节点的文件系统中的文件或目录挂载到集群中

用途:

  • 运行需要访问Docker内部的容器;使用 /var/lib/docker 的 hostPath
  • 在容器中迅鹰cAdvisor;使用 /dev/cgroups 的 hostPath

除了所需的 path 属性之外,用户还可以为hostPath卷指定type

行为
空字符串(默认)用于向后兼容,这意味着在挂载hostPath卷之前不会执行任何检查
DirectoryOrCreate如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为0755,与Kubelet具有相同的组合所有权
Directory给定的路径下必须存在目录
FileOrCreate如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为0644,与Kubelet具有相同的组合所有权
File给定的路径下必须存在文件
Socket给定的路径下必须存在UNIX套接字
CharDevice给定的路径下必须存在字符设备
BlockDevice给定的路径下必须存在块设备

==注意==:

  • 由于每个节点上文件都不同,具有相同配置的pod在不同节点上的行为可能会有所不同!
  • 当Kubernetes按照计划添加自愿感知调度时,将无法考虑hostPath使用的资源
  • 在底层主机上创建的文件或目录只能由root写入
apiVersion: v1
kind: pod  # 先要创建pod
metadata:
  name: test-pd
spec:
  containers:
  - images: [image]
    name: [define.name]
    volumeMounts:
    - mountPath: /path  # 挂载路径
      name: [volume.name]  # 下面定义的volume名
  volumes:  # 和containers同级
  - name: [volume.name]
    hostPath: 
    path: /data
    type: Directory

PV-PVC

PersistentVolume(PV)

管理员设置的存储,是集群的一部分。pv独立于pod的生命周期之外

PersistentVolumeClaim(PVC)

pvc是pod对pv的请求方式,声明可以请求特定的大小和访问模式

静态pv

集群管理员创建的一些pv,带有可供集群用户使用的实际存储的细节,存在于k8s的API中,可用于消费

动态pv

暂不用

绑定

master中控制环路监视新的pvc,寻找匹配的pv,并将它们绑定在一起。如果为新的pvc动态调配pv,则该环路将始终将该pv绑定到pvc。否则,用户总会得到他们所请求的存储,但容量可能超出要求的数量。一旦pv和pvc绑定后,绑定是排他性的,不管是如何绑定的,pvc跟pv绑定是一对一的映射

持久卷演示代码

apiVersion: v1
kind: PersistentVolume  # 先要创建pv
metadata:
  name: [define.pv.name]
spec:
  capacity: 
    storage: 5Gi  # 声明卷大小
  volumeMode: Filesystem  # 文件类型
  accessModes:  # 访问策略
    - ReadWriteOnce  # 单独用户读写
  persistentVolumeReclaimPolicy: Recycle  # 回收策略
  storageClassName: slow  # 存储类名称自定义,以此为依据划分存储给pvc
  mountOptions
    - hard
    - nfsvers=4.1
  nfs: 
    path: /tmp  # 挂载位置
    server: 172.17.0.2  # 从哪里挂载

accessModes-访问策略:

  • ReadWriteOnce(RWO):该卷可被单节点以读/写模式挂载
  • ReadOnlyMany(ROX):该卷可被多节点以只读模式挂载
  • ReadWriterMany(RWX):该卷可被多节点以读/写模式挂载

persistentVolumeReclaimPolicy-回收策略:

  • Retain(保留):手动回收
  • Recycle(回收):基本擦出(rm -rf /thevolume/*)
  • Delete(删除):关联的存储资产将被删除

当前值与NFS和HostPath支持回收策略。AWS EBS、GCE PD、Azure Disk和Cinder卷支持删除策略

scheduler集群调度

Scheduler是k8s的predicate调度器,作为单独的程序运行

Predicate有一系列算法,不满足排除该node:

  • PodFitsResources:节点剩余资源是否发育pod请求资源
  • PodFitsHost:如果pod指定了NodeName,检查是否匹配
  • PodFitsHostPorts:节点上已经使用的port是否和pod申请的port冲突
  • PodSelectorMatches:过滤和pod指定的label不匹配的节点
  • NoDiskConflict:已经mount的volume和pod指定的volume不冲突,除非都是只读

如果predicate过程中没有合适的节点,pod会一直在pending状态,不断重试调度,直到有节点满足;如果有多个node满足,就继续priorities过程:按照优先级大小对节点排序

  • LeastRequestedPriority:通过计算CPU和Memory使用率决定权重,倾向于资源使用比例更低的节点
  • BalancedResourceAllocation:节点CPU和Memory使用率越接近权重越高,和上面的一起使用,倾向于资源使用更稳定的节点
  • ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大权重越高

最后通过算法对所有优先级项目和权重进行计算得出最终结果,也可以自定义调度器进行使用

节点亲和性

pod.spec.nodeAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略

    # 软策略期望符合,不符合也可以
    apiVersion: v1
    kind: Pod
    metadata: 
      name: [name]
      labels:
        app: node-affinity-pod
    spec: 
      containers:
      - name: [name]
        image: [image]
      affinity:
        nodeAffinity:  # 节点亲和性为软性
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1  # 如果有多个软策略按权重比较
            preference:
              matchExpressions:
              - key: source  # 键名
                operator: In  # 键值运算关系
                values:  
                - qikqiak
    
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

    # 硬策略必须符合,不符合则不运行
    apiVersion: v1
    kind: Pod
    metadata: 
      name: [name]
      labels:
        app: node-affinity-pod
    spec: 
      containers:
      - name: [name]
        image: [image]
      affinity:
        nodeAffinity:  # 节点亲和性为硬性
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname  # 键名hostname
                operator: NotIn  # 键值运算关系
                        # In:在某个列表中
                        # Gt:label的值大于某个值
                        # Lt:label的值小于某个值
                        # Exists:某个label存在
                        # DoesNotExist:某个label不存在
                values:  # 节点hostname不是这个值即可
                - k8s-node02
    

    软硬可以放一起用,硬优先更高

Pod亲和性

pod.spec.affinity.podAffinity/podAntiAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略

    ...
      affinity:
        podAntiAffinity:  # ;两个pod不运行在一个node,反亲和性
          preferredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - pod-1
            topologyKey: kubernetes.io/hostname
    
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

    ...
      affinity:
        podAffinity:  # 类型为pod亲和性,两个pod运行在一个node上
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬
          - labelSelector:  # 标签选择
            matchExpressions:
            - key: app  
              operator: In
              values:
              - pod-1
            topologyKey: kubernetes.io/hostname  # 判断两个pod在一个节点
    

污点和容忍

污点(Taint)

使用 kubectl taint 命令可以给node节点设置污点,node被设置上污点之后就和pod之间存在了一种相斥的关系,可以让node拒绝pod的调度执行,甚至将node已经存在的pod驱逐出去

master节点天生有污点,所以默认不会调度到这个节点

key=value:effect  # 每个污点的组成

key和value作为污点的标签,value可以为空,effect描述污点的作用,当前taint effect支持如下三个选项:

  • NoSchedule:k8s不会将pod调度到具有该污点的node上
  • PreferNoSchedule:k8s尽量避免将pod调度到具有该污点的node上
  • NoExecute:k8s不会将pod调度到具有该污点的node上,并将该node已存在的pod驱逐出去

污点的设置、查看和去除

# 设置污点
kubectl taint nodes ndoe1 key1=value1:NoSchedule

# 节点说明里查找 Taints 字段进行查看
kubectl describe pod pod-name

# 去除污点
kubectl taint nodes node1 key1:Noschedule-  # 最后加个-表示去除

容忍(Tolerations)

可以在pod上设置容忍,设置了容忍的pod可以容忍node的污点,可以被调度到存在污点的node上

pod.spec.tolerations

tolerations:
- key: "key1"  # 不指定key时容忍所有key
  operator: "Equal"  # Exists忽略
  vaue: "value1"
  effect: "NoSchedule"  # 不指定时容忍所有类型
  tolerationSeconds: 3600
  • key、value、effect要与node上设置的taint保持一致
  • operator的值为Exists将会忽略value值
  • tolerationSeconds用于描述当pod需要被驱逐时可以在pod上继续保留运行的时间

固定节点

指定节点运行pod

资源限制

这里的资源限制是针对Namespace的,有两种方式:

  • ResourceQuota:用来限制 namespace 中所有的 Pod 占用的总的资源 request 和 limit
  • LimitRange:用来限制 namespace 中 单个Pod 默认资源 request 和 limit
# 创建ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
spec:
  hard:
    persistentvolumeclaims: "2" # 持久存储卷
    services.loadbalancers: "2" # 负载均衡器
    services.nodeports: "0" # NodePort
    
# 创建LimitRange
apiVersion: v1
kind: LimitRange
metadata:
  name: limits
spec:
  limits:
  - default:
      cpu: 200m
      memory: 512Mi
    defaultRequest:
      cpu: 100m
      memory: 256Mi
    type: Container

K8S命令

命令文档:http://docs.kubernetes.org.cn/683.html

# 查看日志,f选项代表持续输出,c选项指定容器
kubectl logs -f [pod名称] -c [docker名称]  # pod里有多个docker可以指定-c
# 删除pod
kubectl delete pod [pod名称]
# 交互式进入pod,类似docker
kubectl exec -it [pod名称] /bin/bash
# 显示标签
kubectl get pod --show-labels
# 修改标签覆写
kubectl label pod [pod名称] [label] --overwrite=True

二进制部署

Q.E.D.


刚毕业走上社会被毒打的搬砖狗