Skip to content

k8s

K8s Schedule

CronJob计划任务

Cron表达式

# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周一;在某些系统上,7 也是星期日)
# │ │ │ │ │                          或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *

配置文件

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  concurrencyPolicy: Allow # 并发调度策略:Allow 允许并发调度,Forbid:不允许并发执行,Replace:如果之前的任务还没执行完,就直接执行新的,放弃上一个任务
  failedJobsHistoryLimit: 1 # 保留多少个失败的任务
  successfulJobsHistoryLimit: 3 # 保留多少个成功的任务
  suspend: false # 是否挂起任务,若为 true 则该任务不会执行
#  startingDeadlineSeconds: 30 # 间隔多长时间检测失败的任务并重新执行,时间不能小于 10
  schedule: "* * * * *" # 调度策略
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
查看cronjob信息:
kubectl get cronjob
kubectl get cj
查看日志信息:
kubectl logs -f <pod-name>

初始化容器InitContainer

在真正的容器启动之前,先启动 InitContainer,在初始化容器中完成真实容器所需的初始化操作,完成后再启动真实的容器。

相对于 postStart 来说,首先 InitController 能够保证一定在 EntryPoint 之前执行,而 postStart 不能,其次 postStart 更适合去执行一些命令操作,而 InitController 实际就是一个容器,可以在其他基础容器环境下执行更复杂的初始化功能。

在 pod 创建的模板中配置 initContainers 参数:
spec:
  initContainers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    command: ["sh", "-c", "echo 'inited;' >> ~/.init"]
    name: init-test

污点和容忍

k8s 集群中可能管理着非常庞大的服务器,这些服务器可能是各种各样不同类型的,比如机房、地理位置、配置等,有些是计算型节点,有些是存储型节点,此时我们希望能更好的将 pod 调度到与之需求更匹配的节点上。

此时就需要用到污点(Taint)和容忍(Toleration),这些配置都是 key: value 类型的。

污点Taint

污点:是标注在节点上的,当我们在一个节点上打上污点以后,k8s 会认为尽量不要将 pod 调度到该节点上,除非该 pod 上面表示可以容忍该污点,且一个节点可以打多个污点,此时则需要 pod 容忍所有污点才会被调度该节点。

  • 为节点打上污点 kubectl taint node k8s-master key=value:NoSchedule

  • 移除污点 kubectl taint node k8s-master key=value:NoSchedule-

  • 查看污点 kubectl describe no k8s-master

  • 污点的影响: NoSchedule:不能容忍的 pod 不能被调度到该节点,但是已经存在的节点不会被驱逐 NoExecute:不能容忍的节点会被立即清除,能容忍且没有配置 tolerationSeconds 属性,则可以一直运行,设置了 tolerationSeconds: 3600 属性,则该 pod 还能继续在该节点运行 3600 秒

NoSchedule

如果不能容忍该污点,那么 Pod 就无法调度到该节点上

NoExecute
  • 如果 Pod 不能忍受这类污点,Pod 会马上被驱逐。
  • 如果 Pod 能够忍受这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。
  • 如果 Pod 能够忍受这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。

容忍Toleration

容忍:是标注在 pod 上的,当 pod 被调度时,如果没有配置容忍,则该 pod 不会被调度到有污点的节点上,只有该 pod 上标注了满足某个节点的所有污点,则会被调度到这些节点

  • pod 的 spec 下面配置容忍
    tolerations:
    - key: "污点的 key"
      value: "污点的 value"
      offect: "NoSchedule" # 污点产生的影响
      operator: "Equal" # 表是 value 与污点的 value 要相等,也可以设置为 Exists 表示存在 key 即可,此时可以不用配置 value
    
Equal

比较操作类型为 Equal,则意味着必须与污点值做匹配,key/value都必须相同,才表示能够容忍该污点

Exists

容忍与污点的比较只比较 key,不比较 value,不关心 value 是什么东西,只要 key 存在,就表示可以容忍。

亲和力Affinity

NodeAffinity

节点亲和力:进行 pod 调度时,优先调度到符合条件的亲和力节点上

RequiredDuringSchedulingIgnoreDuringExecution
PreferredDuringSchedulingIgnoredDuringExecution

应用

匹配类型
In

部署在满足条件的节点上

NotIn

匹配不在条件中的节点,实现节点反亲和性

Exists

只要存在 key 名字就可以,不关心值是什么

DoesNotExist

匹配指定 key 名不存在的节点,实现节点反亲和性

Gt

value 为数值,且节点上的值小于指定的条件

Lt

value 为数值,且节点上的值大于指定条件

PodAffinity

Pod 亲和力:将与指定 pod 亲和力相匹配的 pod 部署在同一节点。

RequiredDuringSchedulingIgnoreDuringExecution

必须将应用部署在一块

PreferredDuringSchedulingIgnoredDuringExecution

尽量将应用部署在一块

PodAntiAffinity

Pod 反亲和力:根据策略尽量部署或不部署到一块

RequiredDuringSchedulingIgnoreDuringExecution

不要将应用与之匹配的部署到一块

podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: security
        operator: In
        values:
        - S1
    topologyKey: topology.kubernetes.io/zone

PreferredDuringSchedulingIgnoredDuringExecution

尽量不要将应用部署到一块

spec:
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2

K8s StorageClass

StorageClass

k8s 中提供了一套自动创建 PV 的机制,就是基于 StorageClass 进行的,通过 StorageClass 可以实现仅仅配置 PVC,然后交由 StorageClass 根据 PVC 的需求动态创建 PV。

Provisioner 制备器

每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。

NFS 动态制备案例

nfs-provisioner 配置

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  namespace: kube-system
  labels:
    app: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.113.121
            - name: NFS_PATH
              value: /data/nfs/rw
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.113.121
            path: /data/nfs/rw

StorageClass 配置

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
  namespace: kube-system
provisioner: fuseim.pri/ifs # 外部制备器提供者,编写为提供者的名称
parameters:
  archiveOnDelete: "false" # 是否存档,false 表示不存档,会删除 oldPath 下面的数据,true 表示存档,会重命名路径
reclaimPolicy: Retain # 回收策略,默认为 Delete 可以配置为 Retain
volumeBindingMode: Immediate # 默认为 Immediate,表示创建 PVC 立即进行绑定,只有 azuredisk 和 AWSelasticblockstore 支持其他值

RBAC 配置

markdown_extensions:
  - pymdownx.highlight:
      anchor_linenums: true
      line_spans: __span
      pygments_lang_class: true
  - pymdownx.inlinehilite
  - pymdownx.snippets
  - pymdownx.superfences
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
  namespace: kube-system
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
  namespace: kube-system
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: kube-system
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: kube-system
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

PVC处于Pending状态

在 k8s 1.20 之后,出于对性能和统一 apiserver 调用方式的初衷,移除了对 SelfLink 的支持,而默认上面指定的 provisioner 版本需要 SelfLink 功能,因此 PVC 无法进行自动制备。

修改 apiserver 配置文件

vim /etc/kubernetes/manifests/kube-apiserver.yaml

spec:
  containers:
  - command:
    - kube-apiserver
    - --feature-gates=RemoveSelfLink=false # 新增该行
    ......

修改后重新应用该配置

kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

不需要SelfLink的Provisioner

将 provisioner 修改为如下镜像之一即可

gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0

registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0

PVC 测试配置

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: auto-pv-test-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 300Mi
  storageClassName: managed-nfs-storage

K8s Storage NFS Volumes

安装nfs

安装 nfs

yum install nfs-utils -y

启动 nfs

systemctl start nfs-server

查看 nfs 版本

cat /proc/fs/nfsd/versions

创建共享目录

mkdir -p /data/nfs
cd /data/nfs
mkdir rw
mkdir ro

设置共享目录 export

vim /etc/exports
/data/nfs/rw 192.168.113.0/24(rw,sync,no_subtree_check,no_root_squash)
/data/nfs/ro 192.168.113.0/24(ro,sync,no_subtree_check,no_root_squash)

重新加载

exportfs -f
systemctl reload nfs-server

到其他测试节点安装 nfs-utils 并加载测试

mkdir -p /mnt/nfs/rw
mount -t nfs 192.168.113.121:/data/nfs/rw /mnt/nfs/rw

配置文件

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com # 网络存储服务地址
      path: /my-nfs-volume # 网络存储路径
      readOnly: true # 是否只读

K8s Configuration

ConfigMap

一般用于去存储 Pod 中应用所需的一些配置信息,或者环境变量,将配置于 Pod 分开,避免应为修改配置导致还需要重新构建 镜像与容器。

创建

使用 kubectl create configmap -h 查看示例,构建 configmap 对象 Examples:

  # Create a new config map named my-config based on folder test
  kubectl create configmap my-config --from-file=path/to/test
1. 创建一个文件夹test,里面创建一个文件:db.properties,内容如下
[root@k8s-master test]# cd ..
[root@k8s-master config]# cd test
[root@k8s-master test]# cat db.properties
username=root
password=admin
2. 创建一个名为"test-dir-config"的ConfigMap ,文件来源test/
kubectl create configmap test-dir-config --from-file=test/
查看创建的ConfigMap信息

kubectl get cm
kubectl describe test-dir-config

使用ConfigMap

创建一个pod yaml文件,进行调用名为test-dir-config的ConfigMap文件(上面创建的cm)

apiVersion: v1
kind: Pod
metadata: 
  name: test-configfile-pod
spec:
  containers:
    - name: config-test
      image: alpine
      command: ["/bin/sh","-c","sleep 3600"]
      imagePullPolicy: IfNotPresent
      env: 
      - name: JAVA_VM_OPTS
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: JAVA_OPTS_TEST #表示从name的ConfigMap中获取名字为key的value,将其赋值给本地环境变量:JAVA_VM_OPTS
      - name: APP
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: APP_NAME
      volumeMounts:
      - name: db-config
        mountPath: "/usr/local/mysql/conf" #想要将数据卷加载到容器的哪个目录
        readOnly: true
  volumes: #数据卷挂载。方式:ConfigMap、Secret
    - name: db-config #数据卷名字
      configMap:  #数据卷类型为ConfigMap
        name: test-dir-config #ConfigMap名字,必须匹配ConfigMap
        items: #对ConfigMap中的key进行映射,如果不指定,默认会将ConfigMap中所有key全部转换为一个同名的文件
          - key: "db.properties"  #ConfigMap中的key
            path: "db.properties"  #将该key的值转换为文件

进入该创建的pod中检验ConfigMap是否生效

kubectl exec -it test-configfile-pod -- sh
[root@k8s-master config]# kubectl exec -it test-configfile-pod -- sh
/ # cd /usr/local/mysql/conf
/usr/local/mysql/conf # ls
db.properties
/usr/local/mysql/conf # cat db.properties 
username=root
password=admin

加密数据配置Secret

与 ConfigMap 类似,用于存储配置信息,但是主要用于存储敏感信息、需要加密的信息,Secret 可以提供数据加密、解密功能。

  • 常见一个Secret
    kubectl create secret generic <secret-name> --from-literal=username=admin --from-literal=password=abc123
    
  • 查看Secret
    kubectl describe secret/<sercret-name>
    

在创建 Secret 时,要注意如果要加密的字符中,包含了有特殊字符,需要使用转义符转移,例如 $ 转移后为 \$,也可以对特殊字符使用单引号描述,这样就不需要转移例如 1$289-! 转换为 '1$289-!'

[root@k8s-master config]# echo 'YWRtaW4tLwo=' | base64 --decode
admin-/
[root@k8s-master config]# echo admin-/ | base64
YWRtaW4tLwo=
[root@k8s-master config]# echo admin@!-/ | base64
-bash: !-/: event not found
[root@k8s-master config]# echo 'admin@!-/' | base64
YWRtaW5AIS0vCg==
[root@k8s-master config]# echo 'YWRtaW5AIS0vCg==' |base64 --decode
admin@!-/

SubPath的使用

使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath

配置方式:

  1. 定义 volumes 时需要增加 items 属性,配置 key 和 path,且 path 的值不能从 / 开始
  2. 在容器内的 volumeMounts 中增加 subPath 属性,该值与 volumes 中 items.path 的值相同

containers: ...... volumeMounts: - mountPath: /etc/nginx/nginx.conf # 挂载到哪里 name: config-volume # 使用哪个 configmap 或 secret subPath: etc/nginx/nginx.conf # 与 volumes.[0].items.path 相同 volumes:

- configMap: name: nginx-conf # configMap 名字 items: # subPath 配置 key: nginx.conf # configMap 中的文件名 path: etc/nginx/nginx.conf # subPath 路径

配置的热更新

通过edit命令直接修改ConfigMap

通过replace替换

由于 configmap 我们创建通常都是基于文件创建,并不会编写 yaml 配置文件,因此修改时我们也是直接修改配置文件,而 replace 是没有 --from-file 参数的,因此无法实现基于源配置文件的替换,此时我们可以利用下方的命令实现

该命令的重点在于 --dry-run 参数,该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -oyaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换

kubectl create cm --from-file=nginx.conf --dry-run -oyaml | kubectl replace -f-

不可变的Secret和ConfigMap

对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true 来禁止修改

K8s HPA

开启服务指标--kubectl top

# 下载 metrics-server 组件配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml

# 修改镜像地址为国内的地址
sed -i 's/k8s.gcr.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml

# 修改容器的 tls 配置,不验证 tls,在 containers 的 args 参数中增加 --kubelet-insecure-tls 参数

# 安装组件
kubectl apply -f metrics-server-components.yaml

# 查看 pod 状态
kubectl get pods --all-namespaces | grep metrics

cpu/内存指标监控

  • 实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu 或 resources.requests.memory 才可以,可以配置当 cpu/memory 达到上述配置的百分比后进行扩容或缩容

  • 创建一个 HPA: 先准备一个好一个有做资源限制的 deployment 执行命令

    kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5
    

  • 通过 kubectl get hpa 可以获取 HPA 信息

  • 测试:找到对应服务的 service,编写循环测试脚本提升内存与 cpu 负载

    while true; do wget -q -O- http://<ip:port> > /dev/null ; done
    

  • 可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看 pods 的扩容情况

    kubectl get pods
    

  • 查看 pods 资源使用情况

    kubectl top pods
    

  • 扩容测试完成后,再关闭循环执行的指令,让 cpu 占用率降下来,然后过 5 分钟后查看自动缩容情况 ```

自定义metrics

  • 控制管理器开启–horizontal-pod-autoscaler-use-rest-clients
  • 控制管理器的–apiserver指向API Server Aggregator
  • 在API Server Aggregator中注册自定义的metrics API

K8s Persistent Volumes

  • PV: Persistent Volumes
  • PVC: Persistent Volumes Claim PV 是集群中的资源。 PVC 是对这些资源的请求,也充当对资源的声明检查。

Lifecycle of a volume and claim 卷和申领的声明周期

Provisioning 制备

Static

集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。

Dynamic

如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的。

想要实现这个操作,前提是 PVC 必须设置 StorageClass,否则会无法动态构建该 PV,可以通过启用 DefaultStorageClass 来实现 PV 的构建。

Binding 绑定

当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。

如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。

Using 使用

Pod 将 PVC 当作存储卷来使用,集群会通过 PVC 找到绑定的 PV,并为 Pod 挂载该卷。

Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。

Storage Object in Use Protection 保护使用中的存储对象

Reclaim 回收策略

Retain 保留

回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷: 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。 根据情况,手动清除所关联的存储资产上的数据。 手动删除所关联的存储资产。 如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。

Delete 删除

对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。

Recycle 回收

警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。

如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。

PV 配置文件

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  capacity:
    storage: 5Gi # pv 的容量
  volumeMode: Filesystem # 存储类型为文件系统
  accessModes: # 访问模式:ReadWriteOnce、ReadWriteMany、ReadOnlyMany
    - ReadWriteOnce # 可被单节点独写
  persistentVolumeReclaimPolicy: Recycle # 回收策略
  storageClassName: slow # 创建 PV 的存储类名,需要与 pvc 的相同
  mountOptions: # 加载配置
    - hard
    - nfsvers=4.1
  nfs: # 连接到 nfs
    path: /data/nfs/rw/test-pv # 存储路径
    server: 192.168.113.121 # nfs 服务地址

Pod 绑定 PVC

在 pod 的挂载容器配置中,增加 pvc 挂载

containers:
  ......
  volumeMounts:
    - mountPath: /tmp/pvc
      name: nfs-pvc-test
volumes:
  - name: nfs-pvc-test
    persistentVolumeClaim:
      claimName: nfs-pvc # pvc 的名称```

PVC 配置文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteOnce # 权限需要与对应的 pv 相同
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi # 资源可以小于 pv 的,但是不能大于,如果大于就会匹配不到 pv
  storageClassName: slow # 名字需要与对应的 pv 相同
#  selector: # 使用选择器选择对应的 pv
#    matchLabels:
#      release: "stable"
#    matchExpressions:
#      - {key: environment, operator: In, values: [dev]}

K8s Ingress

alt text

Ingress

安装Ingress-nginx

Ingress-nginx official github

添加helm仓库

# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

# 查看仓库列表
helm repo list

# 搜索 ingress-nginx
helm search repo ingress-nginx

下载包

# 下载安装包
helm pull ingress-nginx/ingress-nginx

配置参数

  • 将下载好的安装包解压
    tar xf ingress-nginx-xxx.tgz
    
  • 解压后,进入解压完成的目录
    cd ingress-nginx
    
  • 修改 values.yaml 镜像地址:修改为国内镜像
    registry: registry.cn-hangzhou.aliyuncs.com
    image: google_containers/nginx-ingress-controller
    image: google_containers/kube-webhook-certgen
    tag: v1.3.0
    
    hostNetwork: true
    dnsPolicy: ClusterFirstWithHostNet
    
  • 修改部署配置的 kind: DaemonSet
    nodeSelector:
      ingress: "true" # 增加选择器,如果 node 上有 ingress=true 就部署
    将 admissionWebhooks.enabled 修改为 false
    将 service 中的 type 由 LoadBalancer 修改为 ClusterIP,如果服务器是云平台才用 LoadBalancer
    

创建Namespace

  • 为 ingress 专门创建一个 namespace
    kubectl create ns ingress-nginx
    

安装Ingress

  • 为需要部署 ingress 的节点上加标签
    kubectl label node <node-name> ingress=true
    
  • 安装 ingress-nginx
    helm install ingress-nginx ./ingress-nginx -n ingress-nginx
    

基本使用

创建一个Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
  name: wolfcode-nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules: # ingress 规则配置,可以配置多个
  - host: k8s.wolfcode.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service: 
            name: nginx-svc # 代理到哪个 service
            port: 
              number: 80 # service 的端口
        path: /api # 等价于 nginx 中的 location 的路径前缀匹配

多域名配置

apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
  name: wolfcode-nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules: # ingress 规则配置,可以配置多个
  - host: k8s.wolfcode.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service: 
            name: nginx-svc # 代理到哪个 service
            port: 
              number: 80 # service 的端口
        path: /api # 等价于 nginx 中的 location 的路径前缀匹配
      - pathType: Exec # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service:
            name: nginx-svc # 代理到哪个 service
            port:
              number: 80 # service 的端口
        path: /
  - host: api.wolfcode.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service:
            name: nginx-svc # 代理到哪个 service
            port:
              number: 80 # service 的端口
        path: /

K8s service

Service 定义

Image title k8s Service
A Service is an object (the same way that a Pod or a ConfigMap is an object). You can create, view or modify Service definitions using the Kubernetes API. Usually you use a tool such as kubectl to make those API calls for you.


操作命令

配置文件
apiVersion: v1
kind: Service #资源类型:Service
metadata:
  name: nginx-svc #Service 名字
  labels:
    app: nginx  #Service自己本身的标签
spec:
  ports: #端口映射
  - port: 80  #Service自己的端口,在使用内网ip访问时候使用
    targetPort: 80  #目标po的端口
    name: web #为端口起的名字
  selector:  #匹配哪些pod会被该Service代理
    app: nginx-deploy #所有匹配到该标签的pod都可以通过该Service进行网络访问
  type: NodePort #1.随机启动一个端口(3000~32767),映射到ports中的端口,直接绑定到node上,切集群中的每个node都会绑定到这个端口。
                #2.也可以用于将服务暴露给外部访问,但这种方式实际生产环境不推荐,效率低,而且Service是四层负载,而是用ingress
                #(这个type类型有:NodePort、ClusterIP、ExternalName、LoadBalancer)

创建 service

kubectl create -f nginx-svc.yaml
查看 service 信息,通过 service 的 cluster ip 进行访问
kubectl get svc 
查看 pod 信息,通过 pod 的 ip 进行访问
kubectl get po -owide
创建其他 pod 通过 service name 进行访问(推荐)exec官方解释
kubectl exec -it busybox -- sh
curl http://nginx-svc

#创建的命令: kubectl run -it --image busbox dns-test -- /bin/sh
默认在当前 namespace 中访问,如果需要跨 namespace 访问 pod,则在 service name 后面加上 . 即可
curl http://nginx-svc.default

Endpoint

apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: nginx # 与 service 一致
  name: nginx-svc-external # 与 service 一致
  namespace: default # 与 service 一致
subsets:
- addresses:
  - ip: 120.78.159.117 # 目标 ip 地址
  ports: # 与 service 一致
  - name: web #与service一致
    port: 80
    protocol: TCP
    ```

## 代理k8s外部服务
```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wolfcode-external-domain
  name: wolfcode-external-domain
spec:
  type: ExternalName
  externalName: www.wolfcode.cn

反向代理外部域名

apiVersion: v1
kind: Service
metadata:
  labels:
    app: wolfcode-external-domain
  name: wolfcode-external-domain
spec:
  type: ExternalName
  externalName: www.wolfcode.cn

常用类型

  • ClusterIP

只能在集群内部使用,不配置类型的话默认就是 ClusterIP

  • ExternalName

返回定义的 CNAME 别名,可以配置为域名

  • NodePort

会在所有安装了 kube-proxy 的节点都绑定一个端口,此端口可以代理至对应的 Pod,集群外部可以使用任意节点 ip + NodePort 的端口号访问到集群中对应 Pod 中的服务。

当类型设置为 NodePort 后,可以在 ports 配置中增加 nodePort 配置指定端口,需要在下方的端口范围内,如果不指定会随机指定端口

端口范围:30000~32767

端口范围配置在 /usr/lib/systemd/system/kube-apiserver.service 文件中

  • LoadBalancer

使用云服务商(阿里云、腾讯云等)提供的负载均衡器服务

K8s Storage Volumes

HostPath

将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失

配置文件

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: nginx-volume
    volumeMounts:
    - mountPath: /test-pd # 挂载到容器的哪个目录
      name: test-volume # 挂载哪个 volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data # 节点中的目录
      type: Directory # 检查类型,在挂载前对挂载目录做什么检查操作,有多种选项,默认为空字符串,不做任何检查


类型:
空字符串:默认类型,不做任何检查
DirectoryOrCreate:如果给定的 path 不存在,就创建一个 755 的空目录
Directory:这个目录必须存在
FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限为 644
File:这个文件必须存在
Socket:UNIX 套接字,必须存在
CharDevice:字符设备,必须存在
BlockDevice:块设备,必须存在

EmptyDir

EmptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。

存储介质可以是任意类型,如 SSD、磁盘或网络存储。可以将 emptyDir.medium 设置为 Memory 让 k8s 使用 tmpfs(内存支持文件系统),速度比较快,但是重启 tmpfs 节点时,数据会被清除,且设置的大小会计入到 Container 的内存限制中。

配置文件

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: nginx-emptydir
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

k8s DaemonSet

alt text

指定Node节点

nodeSelector

先为 Node 打上标签

kubectl label nodes k8s-node1 svc_type=microsvc

然后再 daemonset 配置中设置 nodeSelector

spec:
  template:
    spec:
      nodeSelector:
        svc_type: microsvc

nodeAffinity

nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。

比如下面的例子代表调度到包含标签 wolfcode.cn/framework-name 并且值为 spring 或 springboot 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的Node。

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: wolfcode.cn/framework-name
            operator: In
            values:
            - spring
            - springboot
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: pauseyyf/pause

podAffinity

podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例: 如果一个 “Node 所在空间中包含至少一个带有 auth=oauth2 标签且运行中的 Pod”,那么可以调度到该 Node 不调度到 “包含至少一个带有 auth=jwt 标签且运行中 Pod”的 Node 上

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: auth
            operator: In
            values:
            - oauth2
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: auth
              operator: In
              values:
              - jwt
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: pauseyyf/pause

滚动更新

不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds

配置文件

apiVersion: apps/v1
kind: DaemonSet  #创建daemonset资源
metadata:
  name: fluentd  #daemonset名称
spec:
  selector:
    matchLabels:
      app: logging
  template:
    metadata:
      labels:
        app: logging
        id: fluentd
      name: fluentd  #pod 名称
    spec:
      containers:
      - name: fluentd-es
        image: agilestacks/fluentd-elasticsearch:v1.3.0
        env:  #配置环境变量
         - name: FLUENTD_ARGS #环境变量的key
           value: -qq  #环境变量的value
        volumeMounts:  #加载数据卷,避免数据丢失,挂载再容器内
         - name: containers  #卷名
           mountPath: /var/lib/docker/containers #把数据卷挂载到容器内的那个目录
         - name: varlog  #卷名
           mountPath: /varlog   #把数据卷挂载到容器内的那个目录
      volumes:  #定义数据卷
         - hostPath: #数据卷类型,主机路径的模式,也就是与node共享目录
             path: /var/lib/docker/containers  #node中的共享目录
           name: containers  #卷名
         - hostPath:  #数据卷类型,主机路径的模式,也就是与node共享目录
             path: /var/log  #node中的共享目录
           name: varlog  #卷名