Kubernetes 学习记录

  • 一、`kubernetes` 入门学习
    • 1.1 部署第一个 `Deployment`
    • 1.2 查看资源的命令
    • 1.3 发布应用程序
      • 1.3.1 暴漏服务的三种模式
      • 1.3.2 创建一个`nginx deployment`的`Service`
    • 1.4 设置命名空间偏好
  • 二 、`kubernetes` 的进阶学习
    • 2.1 管理 `kubernetes` 对象
      • 2.1.1 指令性的命令行
      • 2.1.2 指令性的对象配置
      • 2.1.3 声明式的对象配置
    • 2.2 标签和选择器
      • 2.2.1 标签(`Label`)
      • 2.2.2 标签选择器
    • 2.3 如何使用标签和选择器
      • 2.3.1 查询条件
      • 2.3.2 `Service `
      • 2.3.3 有些对象支持基于集合的选择方式
    • 2.4 注解`annotation`
    • 2.5 字段选择器
    • 2.6 容器生命周期事件处理`postStart` 和 `preStop` 处理程序
    • 2.7 容器组 - 生命周期
      • 2.7.1 `Pod phase`
      • 2.7.2 何时使用健康检查/就绪检查
      • 2.7.3 重启策略
    • 2.8 容器组 - 配置初始化容器
    • 2.9 容器组 - `PodDisruptionBudget(PDB)`
    • 2.10 控制器 - `ReplicaSet (RS)`
      • 2.10.1 `ReplicaSet` 的工作方式
      • 2.10.2 何时使用 `ReplicaSet `
      • 2.10.3 `ReplicaSet` 的定义
        • `PodTemplate`
        • `Pod Selector`
        • `Replicas`
      • 2.10.4 使用`ReplicaSet`
        • 删除 `ReplicaSet` 及`Pod`
        • 只删除 `ReplicaSet`
        • 将Pod 从 ReplicaSet 中隔离
    • 2.11 控制器 - `Deployment (deploy)`
      • 2.11.1 创建 `Deployment`
      • 2.11.2 更新 `Deployment`
      • 2.11.3 回滚 `Deployment`
        • 检查`Deployment` 的更新历史
        • 回滚到前一个 `revision` (版本)
      • 2.11.4 伸缩 - `Deployment`
        • 执行伸缩
      • 2.11.5 暂停和继续 - `Deployment`
      • 2.11.6 清理策略
      • 2.11.7 部署策略
      • 2.11.8 金丝雀发布(灰度发布)
    • 2.12 控制器 - `StatefulSet`
      • 2.12.1 `StatefulSet `使用场景
      • 2.12.2 `StatefulSet 的基本信息`
        • 创建StatefulSet
    • 2.13 控制器 - `DaemonSet`
    • 2.14 `Service`
      • 2.14.1 `Service` 概述
      • 2.14.2 `Service` 的详细描述
        • 创建 `Service`
        • 多端口得`Service`
      • 2.14.3 `Ingress Nginx` 暴露服务
        • `Ingress`
        • `Ingress Controller`
    • 2.15 数据卷 `Volume`
      • 2.15.1 数据卷的类型
      • 2.15.2 数据卷挂载
        • 数据卷内子路径
        • 通过环境变量指定数据卷内子路径
        • 挂载传播
      • 2.15.3 `PersistentVolume`
        • 存储卷和存储卷声明的关系
        • 为PVC 提供 PV的两种方式
        • 绑定Binding
        • 使用 Using
        • Storage Object in Use Protection
        • 回收 Reclaiming
      • 2.15.4 使用 `nfs` 做存储 创建 `StorageClass`
        • 搭建 `nfs server`
        • 客户端测试 `nfs server`
        • 创建 `Storage Class` 来自动创建 PVC
        • k8s 1.20 以上版本遇到 PVC Pending 问题
    • 2.16 `ConfigMap`
      • 基于目录创建configMap
      • 基于文件创建configMap
      • 根据字面值创建 `ConfigMap`
      • 基于生成器创建 `ConfigMap`
      • 使用 `ConfigMap` 定义容器环境变量
      • 将 `ConfigMap` 数据添加到一个 `Volume` 中
    • 2.17 管理容器资源
    • 2.18 污点和容忍
      • 2.18.1 向节点添加污点
      • 2.18.2 向`Pod`添加容忍
      • 2.18.3 污点和容忍匹配
    • 2.19 `Secret`
      • 2.19.1 手动创建 Secret
      • 2.19.2 解码和编辑 Secret
    • 2.20 Security Context
      • 2.20.1 为 Pod 设置 `Security Context`
      • 2.20.2 为容器设置`Linux Capabilities`
  • 三、`Kubernetes` 高级学习
    • 3.1 安全
      • 3.1.1 用户认证
        • 认证策略
      • 3.1.2 管理 ServiceAccount
        • User accounts 和 service accounts
        • Service account admission controller
        • Token Controller
  • 四、`Kubernetes` 实战
    • 4.1 使用 port-forward 访问集群中的应用程序

一、kubernetes 入门学习

1.1 部署第一个 Deployment

  • 创建yaml文件 nginx-deploy.yaml
apiVersion: apps/v1  #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本
kind: Deployment    #该配置的类型,我们使用的是 Deployment
metadata:           #译名为元数据,即 Deployment 的一些基本属性和信息name: nginx-deployment    #Deployment 的名称labels:      #标签,方便后面通过一些方式来选取得标记,key=value 得形式app: nginx #为该Deployment设置key为app,value为nginx的标签
spec:           #这是关于该Deployment的描述,可以理解为你期待该Deployment在k8s中如何使用replicas: 1  #使用该Deployment创建一个应用程序实例selector:       #标签选择器,与上面的标签共同作用,用来选取标签为什么得选择器matchLabels: #选择包含标签app:nginx的资源app: nginxtemplate:        #这是选择或创建的Pod的模板metadata:    #Pod的元数据labels: #Pod的标签,上面的selector即选择包含标签app:nginx的Podapp: nginxspec:       #期望Pod实现的功能(即在pod中部署)containers:  #生成container,与docker中的container是同一种- name: nginx #container的名称image: nginx:1.7.9 #使用镜像nginx:1.7.9创建container,该container默认80端口可访问
  • 执行yaml 文件
kubectl apply -f nginx-deploy.yaml
  • 查询结果
# -n 是 namespace ,命名空间
kubectl get deploy -n default

1.2 查看资源的命令

  • 查看deployment
kubectl get deploy -A
  • 显示资源详细信息
# 查看nginx deployment的详情
kubectl describe deploy nginx -n test
# 查看pod nginx 的详情
kubectl deploy  pod -n test nginx-ds-585449566-hc5jv
  • 查看pod日志(类似与docker logs -f
kubectl logs -f -n test nginx-ds-585449566-hc5jv
  • 进入pod中的容器内
kubectl exec -n test -it nginx-ds-585449566-hc5jv /bin/bash# 该命令将会在未来被替代
kubectl exec -n test nginx-ds-585449566-hc5jv -- "curl localhost"

1.3 发布应用程序

1.3.1 暴漏服务的三种模式

  • ClusterIP(默认)
    在集群中的内部IP上公布服务,这种Service只在集群内布可以访问
  • NodePort
    使用NAT在集群中每个worker节点同一个端口上公布服务。这种方式下,可以通过集群中任意节点+端口号的方式访问服务,此时ClusterIP的访问方式仍然可用。
  • LoadBalancer
    在云环境中,创建一个集群外部的负载均衡器,使用该负载均衡器的IP作为服务的访问地址,此时ClusterIPNodePort的访问方式仍然可用。

1.3.2 创建一个nginx deploymentService

apiVersion: v1
kind: Service
metadata:name: nginx-service    #Service 的名称labels:         #Service 自己的标签app: nginx    #为该 Service 设置 key 为 app,value 为 nginx 的标签
spec:       #这是关于该 Service 的定义,描述了 Service 如何选择 Pod,如何被访问selector:        #标签选择器app: nginx    #选择包含标签 app:nginx 的 Podports:- name: nginx-port #端口的名字protocol: TCP     #协议类型 TCP/UDPport: 80           #集群内的其他容器组可通过 80 端口访问 ServicenodePort: 32600   #通过任意节点的 32600 端口访问 ServicetargetPort: 80    #将请求转发到匹配 Pod 的 80 端口type: NodePort #Serive的类型,ClusterIP/NodePort/LoaderBalancer
  • 执行yaml文件
kubectl apply -f nginx-svc.yaml -n test

1.4 设置命名空间偏好

kubectl config set-context --current --namespace=test

二 、kubernetes 的进阶学习

2.1 管理 kubernetes 对象

2.1.1 指令性的命令行

当使用指令性的命令行(imperative commands)时,用户通过向kubectl 命令提供参数的方式,直接操作集群中的 Kubernetes 对象。此时,用户无需编写或修改 .yaml 文件。

kubectl create deploy nginx --image nginx:latest -n test

2.1.2 指令性的对象配置

使用指令性的对象配置(imperative object configuration)时,需要向 kubectl 命令指定具体的操作(create,replace,apply,delete等),可选参数以及至少一个配置文件的名字。配置文件中必须包括一个完整的对象的定义,可以是yaml格式,也可以是json 格式。

# 创建对象
kubectl create -f nginx.yaml
# 删除对象
kubectl delete -f nginx.yaml -f redis.yaml
# 替换对象
kubectl replace -f nginx.yaml

2.1.3 声明式的对象配置

当使用声明式的对象配置时,用户操作本地存储的Kubernetes对象配置文件,然而,在将文件传递给kubectl命令时,并不指定具体的操作,由 kubectl自动检查每一个对象的状态并自行决定是创建、更新、还是删除该对象。使用这种方法时,可以直接针对一个或多个文件目录进行操作(对不同的对象可能需要执行不同的操作)

# 查看具体的变更
kubectl diff -f configs/
kubectl apply -f configs# 递归处理目录中的内容
kubectl diff -R -f configs/
kubectl apply -R -f configs/

2.2 标签和选择器

2.2.1 标签(Label

label是附加在Kubernetes对象上的一组名值对,其意图是按照对用户有意义的方式来标识Kubernetes对象,同时,又不对Kubernetes的核心逻辑产生影响。标签可以用来组织和选择一组Kubernetes对象。您可以在创建Kubernetes对象时为其添加标签,也可以在创建以后再为其添加标签。每个Kubernetes对象可以有多个标签,同一个对象的标签的Key必须唯一,例如:

metadata:labels:key1: value1key2: value2

2.2.2 标签选择器

nameUID 不同,标签不一定是唯一的。通常来讲,会有多个Kubernetes对象包含相同的标签。通过使用标签选择器(label selector),用户/客户端可以选择一组对象。标签选择器(label selector)是 Kubernetes 中最主要的分类和筛选手段。

Kubernetes api server支持两种形式的标签选择器,equality-based基于等式的 和set-based 基于集合的。标签选择器可以包含多个条件,并使用逗号分隔,此时只有满足所有条件的 Kubernetes 对象才会被选中。

  • 基于等式的选择方式
envionment = prod
tier != frontend

也可以使用逗号分隔的两个等式 environment=production,tier!=frontend,此时将选中所有 environmentprodtier 不为front 的对象。

Pod 的节点选择器 为例,下面的Pod 可以被调度到包含标签 accelerator=nvidia-tesla-p100 的节点上:

apiVersion: v1
kind: Pod
metadata:name: cuda-test
spec:containers:- name: cuda-testimage: "k8s.gcr.io/cuda-vector-add:v0.1"resources:limits:nvidia.com/gpu: 1nodeSelector:accelerator: nvidia-tesla-p100
  • 基于集合的选择方式
    Set-based 标签选择器可以根据标签名的一组值进行筛选。支持的操作符有三种:innotinexists。例如:
# 选择所有的包含 `environment` 标签且值为 `production` 或 `qa` 的对象
environment in (production, qa)
# 选择所有的 `tier` 标签不为 `frontend` 和 `backend`的对象,或不含 `tier` 标签的对象
tier notin (frontend, backend)
# 选择所有包含 `partition` 标签的对象
partition
# 选择所有不包含 `partition` 标签的对象
!partition

基于集合的选择方式是一个更宽泛的基于等式的选择方式
例如,environment=production 等价于 environment in (production)
environment!=production 等价于 environment notin (production)

基于集合的选择方式可以和基于等式的选择方式可以混合使用,例如: partition in (customerA, customerB),environment!=qa

2.3 如何使用标签和选择器

2.3.1 查询条件

  • 基于等式的选择方式
kubectl get pods -l environment=prod,tier=front
  • 使用基于集合的选择方式
kubectl get pods -l ’environment in (prod),tier in (front)‘

2.3.2 Service

Service 中 通过spec.selector 字段来选择一组Pod,并将服务请求转发到Pod上

selector:component: redis

2.3.3 有些对象支持基于集合的选择方式

JobDeploymentReplicaSetDaemonSet 同时支持基于等式的选择方式和基于集合的选择方式。例如

selector:matchLabels:component: redismatchExpressions:- {key: tier, operator: In, values: [cache]}- {key: environment, operator: NotIn, values: [dev]}

matchLabels 是一个 {key,value} 组成的 mapmap 中的一个{key,value} 条目相当于 matchExpressions 中的一个元素,其 keymapkeyoperatorInvalues 数组则只包含 value 一个元素。matchExpression 等价于基于集合的选择方式,支持的 operatorInNotInExists 和 DoesNotExist。当 operatorInNotIn 时,values 数组不能为空。所有的选择条件都以 AND 的形式合并计算,即所有的条件都满足才可以算是匹配。

2.4 注解annotation

metadata:annotations:key1: value1key2: value2

2.5 字段选择器

所有的对象都支持两个字段是metadata.namemetadata.namespace,在字段选择器中使用不支持的字段,将报错,可以指定多个字段选择器,用 隔开

kubectl get pods --field-selector status.phase=Running -n test

2.6 容器生命周期事件处理postStartpreStop 处理程序

  • 创建 lifecycle-demo.yaml
apiVersion: v1
kind: Pod
metadata:name: lifecycle-demo
spec:containers:- name: lifecycle-demo-containerimage: nginxlifecycle:postStart:exec:command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]preStop:exec:command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

2.7 容器组 - 生命周期

2.7.1 Pod phase

pod phase代表其所处生命周期的阶段。

phase 描述
Pending Kubernetes 已经创建并确认该Pod。此时可能有两种情况:
- Pod还未完成调度(例如没有合适的节点
- 正在从 docker registry下载镜像
Running Pod 已经被绑定到一个节点,并且该 Pod所有的容器都已经成功创建。其中至少有一个容器正在运行,或者正在启动/重启
Succeded Pod 中的所有容器都已经成功终止,并且不会再被重启
Failed Pod 中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill
Unknow 因为某些未知原因,不能确定 Pod 的状态,通常的原因是 masterPod 所在节点之间的通信故障

2.7.2 何时使用健康检查/就绪检查

  • 如果容器中的进程在碰到问题时可以自己 crash,您并不需要执行健康检查;kubelet 可以自动的根据Podrestart policy(重启策略)执行对应的动作

  • 如果您希望在容器的进程无响应后,将容器 kill 掉并重启,则指定一个健康检查 liveness probe,并同时指定 restart policy(重启策略)为 Always 或者 OnFailure

  • 如果您想在探测 Pod 确实就绪之后才向其分发服务请求,请指定一个就绪检查 readiness probe。此时,就绪检查的内容可能和健康检查相同。就绪检查适合如下几类容器:

    • 初始化时需要加载大量的数据、配置文件
    • 启动时需要执行迁移任务
    • 其他

2.7.3 重启策略

定义 Pod或工作负载时,可以指定 restartPolicy,可选的值有:

  • Always (默认值)
  • OnFailure
  • Never

2.8 容器组 - 配置初始化容器

创建一个Pod,该Pod包含一个应用程序容器(工作容器)和一个初始化容器(Init Container)。初始化容器执行结束之后,应用程序容器(工作容器)才开始启动。

apiVersion: v1
kind: Pod
metadata:name: init-demo
spec:containers:- name: nginximage: nginx:latestports:- containerPort: 80volumeMounts:- name: workdirmountPath: /usr/share/nginx/htmlinitContainers:- name: installimage: nginxcommand:- "/bin/bash"- "-c"args:["echo '<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'><title>Title</title></head><body><h1>Hello World1</h1></body></html>' > /work-dir/index1.html;echo '<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'><title>Title</title></head><body><h1>Hello World2</h1></body></html>' > /work-dir/index2.html"]volumeMounts:- name: workdirmountPath: "/work-dir"dnsPolicy: Defaultvolumes:- name: workdiremptyDir: {}

Pod 中初始化容器和应用程序共享了同一个数据卷,舒适化容器将该共享数据集挂在到/work-dir 路径,应用程序容器将共享数据卷挂在到/usr/share/nginx/html 路径。初始化容器执行command命令后就退出容器。
执行该命令时,初始化容器将结果写入了应用程序nginx服务器对应的html根路径下的index.html

2.9 容器组 - PodDisruptionBudget(PDB)

应用程序管理员可以为每一个应用程序创建 PodDisruptionBudget 对象(PDB)。PDB限制了多副本应用程序在自愿的毁坏情况发生时,最多有多少个副本可以同时停止。例如,一个 web 前端的程序需要确保可用的副本数不低于总副本数的一定比例。属于一种保护机制。

PodDisruptionBudget 包含三个字段:

  • 标签选择器 .spec.selector 用于指定 PDB 适用的Pod。此字段为必填
  • .spec.minAvailable:当完成驱逐时,最少仍然要保留多少个Pod可用。该字段可以是一个整数,也可以是一个百分比
  • .spec.maxUnavailable: 当完成驱逐时,最多可以有多少个 Pod 被终止。该字段可以是一个整数,也可以是一个百分比

在一个 PodDisruptionBudget 中,只能指定maxUnavailableminAvailable 中的一个。 maxUnavailable 只能应用到那些有控制器的 Pod上。

PodDisruptionBudget 并不能真正确保指定数量(或百分比)的Pod始终保持可用。
例如,当 Pod 数量已经为 PDB 中指定的最小数时,某一个节点可能意外宕机,
导致 Pod 数量低于 PDB 中指定的数量。 PodDisruptionBudget 只能保护应用避免受到 自愿毁坏 的影响,
而不是所有原因的毁坏。

使用 minAvailable

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:name: zk-pdb
spec:minAvailable: 2selector:matchLabels:app: zookeeper

使用 maxUnavailable

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:name: zk-pdb
spec:maxUnavailable: 1selector:matchLabels:app: zookeeper

查看pdb

kubectl get pdb

2.10 控制器 - ReplicaSet (RS)

kubernetesReplicaSet 用来维护一个数量稳定的 Pod 副本集合,可以保证某种定义一样的 Pod 始终有指定数量的副本数在运行。

2.10.1 ReplicaSet 的工作方式

  • selector: 用于指定哪些Pod 属于该 ReplicaSet的管辖范围
  • replicas: 副本数,用于指定该 ReplicaSet 应该维持多少个 Pod副本
  • templatePod模板,在 ReplicaSet 使用 Pod 模板的定义创建新的 Pod

ReplicaSet 控制器将通过创建或删除 Pod,以使得当前 Pod数量达到 replicas 指定的期望值,通过selector字段的定义,识别哪些 Pod应该由其管理。如果 Pod没有 ownerReference 字段,或者 ownerReference 字段指向的对象不是一个控制器,且该 Pod匹配了 ReplicaSetselector,则该 PodownerReference 将被修改为 该 ReplicaSet的引用(包括不是由该RS创建的Pod)。

2.10.2 何时使用 ReplicaSet

ReplicaSet 用来维护一个数量稳定的 Pod 副本集合。Deployment 是一个更高级别的概念,可以管理 ReplicaSet,并提供声明式的更新,以及其他的许多有用的特性。因此,推荐用户总是使用Deployment,而不是直接使用 ReplicaSet,除非您需要一些自定义的更新应用程序的方式,或者您完全不更新应用。
示例:

apiVersion: apps/v1
kind: ReplicaSet
metadata:name: frontendlabels:app: guestbooktier: frontend
spec:# modify replicas according to your casereplicas: 3selector:matchLabels:tier: frontendtemplate:metadata:labels:tier: frontendspec:containers:- name: nginximage: nginx

2.10.3 ReplicaSet 的定义

与其他Kubernetes对象一样,ReplicaSet需要的字段有:

apiVersion:apps/v1
kind:始终为 ReplicaSet
metadata
specReplicaSet的详细定义

PodTemplate

  • .spec.template 字段是一个 Pod Template,为必填字段,且其中必须定义

  • .spec.template.metadata.labels 字段。在前面的ReplicaSet例子中,定义了 label 为 tier: frontend。请小心该字段不要与其他控制器的 selector重合,以免这些控制器尝试接管该 Pod

  • .spec.template.spec.restartPolicy 的默认值为Always

Pod Selector

  • .spec.selector 字段为一个 标签选择器,用于识别可以接管哪些 Pod。在前面的例子中,标签选择器为

Replicas

  • spec.replicas 字段用于指定同时运行的 Pod 的副本数。ReplicaSet 将创建或者删除由其管理的 Pod,以便使副本数与该字段指定的值匹配。

2.10.4 使用ReplicaSet

删除 ReplicaSetPod

kubectl delete -f  nginx-rs.yaml

只删除 ReplicaSet

kubectl delete nginx-rs --casade=false

将Pod 从 ReplicaSet 中隔离

修改Pod 的标签,可以使Pod脱离ReplicaSet的管理

2.11 控制器 - Deployment (deploy)

2.11.1 创建 Deployment

下面的 yaml 文件定义了一个 Deployment,该 Deployment 将创建一个有 3 个 nginx Pod 副本的 ReplicaSet(副本集):

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentlabels:app: nginx
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.7.9ports:- containerPort: 80

在这个例子中:

  • 将创建一个名为 nginx-deploymentDeployment(部署),名称由 .metadata.name 字段指定
  • Deployment 将创建 3 个Pod副本,副本数量由 .spec.replicas字段指定
  • .spec.selector字段指定了 Deployment 如何找到由它管理的 Pod。此案例中,我们使用了 Pod template 中定义的一个标签(app: nginx)。对于极少数的情况,这个字段也可以定义更加复杂的规则
  • .template 字段包含了如下字段:
    • .template.metadata.labels 字段,指定了 Pod的标签(app: nginx
    • .template.spec.containers[].image字段,表明该 Pod 运行一个容器 nginx:1.7.9
    • .template.spec.containers[].name 字段,表明该容器的名字是 nginx
  1. 执行命令以创建Deployment
kubectl apply -f nginx-ds.yaml

可以为该命令增加 --record选项,此时 kubectl会将kubectl apply -f nginx-ds.yaml --record 写入 Deployment 的 annotation(注解) kubernetes.io/change-cause中。这样,您在将来就可以回顾某一个 Deployment 版本变化的原因

  1. 查看 Deployment 的发布状态(rollout status),执行命令 kubectl rollout status deploy nginx-ds

  2. 查看 Pod 的标签,执行命令kubectl get pods --show-labels

2.11.2 更新 Deployment

  1. 执行以下命令,将容器镜像从 nginx:1.7.9 更新到 nginx:1.9.1
kubectl set image deploy nginx-ds nginx=nginx:1.9.1 --record

或者也可以edit 该 Deployment,并将 .spec.template.spec.containers[0].imagenginx:1.7.9修改为 nginx:1.9.1

kubectl edit deployments.apps nginx-ds
  1. 查看发布更新(rollout)的状态:
kubectl rollout status deployment nginx-ds

2.11.3 回滚 Deployment

检查Deployment 的更新历史

  1. 执行命令 kubectl rollout history deployment nginx-ds 检查 Deployment 的历史版本,输出结果如下所示:
deployment.apps/nginx-ds
REVISION  CHANGE-CAUSE
2         image update to 1.9.1
3         kubectl apply --filename=nginx-ds.yaml --record=true

CHANGE-CAUSE 是该 revision(版本)创建时从 Deployment 的 annotation kubernetes.io/change-cause 拷贝而来。
可以通过如下方式制定 CHANGE-CAUSE 信息:

  • 为 Deployment 增加注解,kubectl annotate deploy nginx-ds kubernetes.io/change-cause="image update to 1.9.1"
  • 执行 kubectl apply 命令时,增加 --record 选项
  • 手动编辑 Deployment 的 .metadata.annotation 信息
  1. 执行命令 kubectl rollout history deploy nginx-ds --revision=2 查看revision(版本)的详细信息。输出结果如下:
deployment.apps/nginx-ds with revision #2
Pod Template:Labels:    app=nginxpod-template-hash=694854bbcbAnnotations: kubernetes.io/change-cause: image update to 1.9.1Containers:nginx:Image:    nginx:1.9.1Port:    80/TCPHost Port:    0/TCPEnvironment:   <none>Mounts: <none>Volumes:    <none>

回滚到前一个 revision (版本)

  1. 执行命令 kubectl rollout undo deployment nginx-ds 将当前版本回滚到前一个版本

2.11.4 伸缩 - Deployment

执行伸缩

  • 执行命令 kubectl scale deploy nginx --replicas=3 实现手动伸缩

  • 执行命令kubectl autoscale deployment nginx --max=5 --min=3 --cpu-percent=80 cpu在百分之下最大5个 最小三个副本自动伸缩

  1. 查看horizontalpodautoscalers.autoscaling (hpa)
kubectl get hpa

2.11.5 暂停和继续 - Deployment

  • 执行命令kubectl rollout pause deployment nginx暂停 Deployment
  • 执行命令 kubectl rollout resume deployment nginx 继续改Deployment

2.11.6 清理策略

通过 Deployment 中 .spec.revisionHistoryLimit 字段,可指定为该 Deployment 保留多少个旧的 ReplicaSet。超出该数字的将被在后台进行垃圾回收。该字段的默认值是 10。如果该字段被设为0,Kubernetes 将清理掉该 Deployment 的所有历史版本(revision),因此,您将无法对该 Deployment 执行回滚操作 kubectl rollout undo

2.11.7 部署策略

通过 Deployment 中 .spec.strategy字段,可以指定使用 滚动更新 RollingUpdate 的部署策略还是使用 重新创建 Recreate的部署策略

字段名称 可选值 字段描述
类型 滚动更新,重新创建 如果选择重新创建,Deployment将先删除原有副本集中的所有 Pod,然后再创建新的副本集和新的Pod。如此,更新过程中将出现一段应用程序不可用的情况;
最大超出副本数 数字或百分比 滚动更新过程中,可以超出期望副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后向上取整的方式计算对应的绝对值;当最大超出副本数 maxUnavailable 为 0 时,此数值不能为 0;默认值为 25%。例如:假设此值被设定为 30%,当滚动更新开始时,新的副本集(ReplicaSet)可以立刻扩容,但是旧 Pod 和新 Pod 的总数不超过 Deployment 期待副本数(spec.repilcas)的 130%。一旦旧 Pod 被终止后,新的副本集可以进一步扩容,但是整个滚动更新过程中,新旧 Pod 的总数不超过 Deployment期待副本数(spec.repilcas)的 130%。
最大不可用副本数 数字或百分比 滚动更新过程中,不可用副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后向下取整的方式计算对应的绝对值;当最大超出副本数 maxSurge 为 0 时,此数值不能为 0;默认值为 25%;例如:假设此值被设定为 30%,当滚动更新开始时,旧的副本集(ReplicaSet)可以缩容到期望副本数的 70%;在新副本集扩容的过程中,一旦新的 Pod已就绪,旧的副本集可以进一步缩容,整个滚动更新过程中,确保新旧就绪副本数之和不低于期望副本数的 70%。

2.11.8 金丝雀发布(灰度发布)

如果您想使用 Deployment 将最新的应用程序版本发布给一部分用户(或服务器),您可以为每个版本创建一个 Deployment,此时,应用程序的新旧两个版本都可以同时获得生产上的流量。

  • 部署第一个版本
    第一个版本的 Deployment 包含了 3 个Pod副本,Service 通过label selectorapp: nginx 选择对应的 Pod,nginx 的标签为 1.7.9
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentlabels:app: nginx
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.7.9
---
apiVersion: v1
kind: Service
metadata:name: nginx-servicelabels:app: nginx
spec:selector:app: nginxports:- name: nginx-portprotocol: TCPport: 80nodePort: 32600targetPort: 80type: NodePort
  • 假如此时想发布新版本nginx 1.8.0,可以创建第二个Deployment
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment-canarylabels:app: nginxtrack: canary
spec:replicas: 1selector:matchLabels:app: nginxtrack: canarytemplate:metadata:labels:app: nginxtrack: canaryspec:containers:- name: nginximage: nginx:1.8.0

局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:

  • 不能根据用户注册时间、地区等请求中的内容属性进行流量分配

  • 同一个用户如果多次调用该Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod

Kubernetes中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别。如果想要更完善地实现金丝雀发布,可以考虑如下三种选择:

  • 业务代码编码实现
  • Spring Cloud 灰度发布
  • Istio 灰度发布

2.12 控制器 - StatefulSet

2.12.1 StatefulSet使用场景

StatefulSet 顾名思义,用于管理 Stateful(有状态)的应用程序。

StatefulSet 管理 Pod 时,确保其 Pod 有一个按顺序增长的 ID。

Deployment 相似,StatefulSet 基于一个 Pod 模板管理其 Pod。与 Deployment最大的不同在于 StatefulSet 始终将一系列不变的名字分配给其 Pod。这些 Pod 从同一个模板创建,但是并不能相互替换:每个 Pod都对应一个特有的持久化存储标识。

同其他所有控制器一样,StatefulSet 也使用相同的模式运作:用户在 StatefulSet中定义自己期望的结果,StatefulSet 控制器执行需要的操作,以使得该结果被达成。

场景:

  • 稳定、唯一的网络标识(dnsname)
  • 每个Pod始终对应各自的存储路径(PersistantVolumeClaimTemplate
  • 按顺序地增加副本、减少副本,并在减少副本时执行清理
  • 按顺序自动地执行滚动更新

如果一个应用程序不需要稳定的网络标识,或者不需要按顺序部署、删除、增加副本,您应该考虑使用 Deployment这类无状态(stateless)的控制器。

2.12.2 StatefulSet 的基本信息

创建StatefulSet

  • 一个名为 nginx 的 Headless Service,用于控制网络域
  • 一个名为 web 的StatefulSet,副本数为 3
  • volumeClaimTemplates 提供稳定的存储(每一个 Pod ID 对应自己的存储卷,且Pod重建后,仍然能找到对应的存储卷)
apiVersion: v1
kind: Service
metadata:name: nginxlabels:app: nginx
spec:ports:- port: 80name: webclusterIP: Noneselector:app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: web
spec:selector:matchLabels:app: nginx # has to match .spec.template.metadata.labelsserviceName: "nginx"replicas: 3 # by default is 1template:metadata:labels:app: nginx # has to match .spec.selector.matchLabelsspec:terminationGracePeriodSeconds: 10containers:- name: nginximage: nginx:1.7.9ports:- containerPort: 80name: webvolumeMounts:- name: wwwmountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: wwwspec:accessModes: [ "ReadWriteOnce" ]storageClassName: "my-storage-class"resources:requests:storage: 1Gi

2.13 控制器 - DaemonSet

2.14 Service

2.14.1 Service 概述

  • 为何需要Service
    KubernetesPod 是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用Deployment运行您的应用程序,Deployment 将会在 Pod 消亡后再创建一个新的Pod以维持所需要的副本数。每一个 Pod有自己的 IP 地址,然而,对于 Deployment而言,对应Pod 集合是动态变化的。

  • Kubernetes Service
    Kubernetes 中 Service 是一个 API 对象,通过 kubectl + YAML 或者 Kuboard,定义一个 Service,可以将符合 Service 指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。
    Service 是 Kubernetes 中的一种服务发现机制:

    • Pod 有自己的 IP 地址
    • Service 被赋予一个唯一的 dns name
    • Service 通过 label selector 选定一组 Pod
    • Service 实现负载均衡,可将请求均衡分发到选定这一组 Pod 中

2.14.2 Service 的详细描述

创建 Service

  • 每个Pod都监听9376 TCP端口
  • 每个Pod都有标签 app=MyApp
apiVersion: v1
kind: Service
metadata:name: my-service
spec:selector:app: MyAppports:- protocol: TCPport: 80targetPort: 9376
  • Kubernetes 将为该 Service 分配一个 IP 地址(ClusterIP集群内 IP),供 Service Proxy 使用
  • Kubernetes 将不断扫描符合该 selectorPod,并将最新的结果更新到与 Service同名 my-service 的 Endpoint 对象中。
    TIP
  • Pod 的定义中,Port可能被赋予了一个名字,您可以在 ServicetargetPort字段引用这些名字,而不是直接写端口号。这种做法可以使得您在将来修改后端程序监听的端口号,而无需影响到前端程序。
  • Service 的默认传输协议是 TCP,您也可以使用其他 支持的传输协议。
    Kubernetes Service 中,可以定义多个端口,不同的端口可以使用相同或不同的传输协议。

多端口得Service

可以在一个 Service 对象中定义多个端口,但必须为每个端口定义一个名字

apiVersion: v1
kind: Service
metadata:name: my-service
spec:selector:app: MyAppports:- name: httpprotocol: TCPport: 80nodePort: 32080targetPort: 9376- name: httpsprotocol: TCPport: 443targetPort: 9377# nodeport 模式type: NodePort

2.14.3 Ingress Nginx 暴露服务

Ingress

Ingress 是Kubernetes 的一种API对象,将集群内部的Service通过HTTP/HTTPS 方式暴露到集群外部,并通过规则定义HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问URL,负载均衡,SSL Termination,按域路由。

Ingress Controller

Ingress Controller (通常需要负载均衡器配合)负责实现Ingress API 对象所声明的能力。
使用 Ingress Nginx实现 Ingress Controller

  • 安装 Helm 3 (Kubernetes包管理工具)
wget https://get.helm.sh/helm-v3.4.1-linux-amd64.tar.gz
tar zxvf helm-v3.4.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
  • 使用 Helm 安装 Ingress Nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm pull ingress-nginx/ingress-nginx
tar -zxvf ingress-nginx-3.12.0.tgz
# 编辑valuse.yaml 文件
vim values.yaml
## nginx configuration
## Ref: https://github.com/kubernetes/ingress-nginx/blob/master/controllers/nginx/configuration.md
##
controller:image:repository: registry.cn-hangzhou.aliyuncs.com/k8s_gcr_io_allen/ingress-nginx-controllertag: "v0.41.2"pullPolicy: IfNotPresent# www-data -> uid 101runAsUser: 101allowPrivilegeEscalation: true# Configures the ports the nginx-controller listens oncontainerPort:http: 80https: 443# Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/config: {}## Annotations to be added to the controller config configuration configmap##configAnnotations: {}# Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headersproxySetHeaders: {}# Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headersaddHeaders: {}# Optionally customize the pod dnsConfig.dnsConfig: {}# Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'.# By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller# to keep resolving names inside the k8s network, use ClusterFirstWithHostNet.dnsPolicy: ClusterFirst# Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network# Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not applyreportNodeInternalIp: false# Required for use with CNI based kubernetes installations (such as ones set up by kubeadm),# since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920# is mergedhostNetwork: false## Use host ports 80 and 443## Disabled by default##hostPort:enabled: falseports:http: 80https: 443## Election ID to use for status update##electionID: ingress-controller-leader## Name of the ingress class to route through this controller##ingressClass: nginx# labels to add to the pod container metadatapodLabels: {}#  key: value## Security Context policies for controller pods##podSecurityContext: {}## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for## notes on enabling and using sysctls###sysctls: {}# sysctls:#   "net.core.somaxconn": "8192"## Allows customization of the source of the IP address or FQDN to report## in the ingress status field. By default, it reads the information provided## by the service. If disable, the status field reports the IP address of the## node or nodes where an ingress controller pod is running.publishService:enabled: true## Allows overriding of the publish service to bind to## Must be <namespace>/<service_name>##pathOverride: ""## Limit the scope of the controller##scope:enabled: falsenamespace: ""   # defaults to .Release.Namespace## Allows customization of the configmap / nginx-configmap namespace##configMapNamespace: ""   # defaults to .Release.Namespace## Allows customization of the tcp-services-configmap##tcp:configMapNamespace: ""   # defaults to .Release.Namespace## Annotations to be added to the tcp config configmapannotations: {}## Allows customization of the udp-services-configmap##udp:configMapNamespace: ""   # defaults to .Release.Namespace## Annotations to be added to the udp config configmapannotations: {}## Additional command line arguments to pass to nginx-ingress-controller## E.g. to specify the default SSL certificate you can use## extraArgs:
#    default-ssl-certificate: "ailuoli-ingress-nginx-secret"extraArgs: {}## Additional environment variables to setextraEnvs: []# extraEnvs:#   - name: FOO#     valueFrom:#       secretKeyRef:#         key: FOO#         name: secret-resource## DaemonSet or Deployment##kind: DaemonSet## Annotations to be added to the controller Deployment or DaemonSet##annotations: {}#  keel.sh/pollSchedule: "@every 60m"## Labels to be added to the controller Deployment or DaemonSet##labels: {}#  keel.sh/policy: patch#  keel.sh/trigger: poll# The update strategy to apply to the Deployment or DaemonSet##updateStrategy: {}#  rollingUpdate:#    maxUnavailable: 1#  type: RollingUpdate# minReadySeconds to avoid killing pods before we are ready##minReadySeconds: 0## Node tolerations for server scheduling to nodes with taints## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/##tolerations: []#  - key: "key"#    operator: "Equal|Exists"#    value: "value"#    effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"## Affinity and anti-affinity## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity##affinity: {}# # An example of preferred pod anti-affinity, weight is in the range 1-100# podAntiAffinity:#   preferredDuringSchedulingIgnoredDuringExecution:#   - weight: 100#     podAffinityTerm:#       labelSelector:#         matchExpressions:#         - key: app.kubernetes.io/name#           operator: In#           values:#           - ingress-nginx#         - key: app.kubernetes.io/instance#           operator: In#           values:#           - ingress-nginx#         - key: app.kubernetes.io/component#           operator: In#           values:#           - controller#       topologyKey: kubernetes.io/hostname# # An example of required pod anti-affinity# podAntiAffinity:#   requiredDuringSchedulingIgnoredDuringExecution:#   - labelSelector:#       matchExpressions:#       - key: app.kubernetes.io/name#         operator: In#         values:#         - ingress-nginx#       - key: app.kubernetes.io/instance#         operator: In#         values:#         - ingress-nginx#       - key: app.kubernetes.io/component#         operator: In#         values:#         - controller#     topologyKey: "kubernetes.io/hostname"## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in.## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/##topologySpreadConstraints: []# - maxSkew: 1#   topologyKey: failure-domain.beta.kubernetes.io/zone#   whenUnsatisfiable: DoNotSchedule#   labelSelector:#     matchLabels:#       app.kubernetes.io/instance: ingress-nginx-internal## terminationGracePeriodSeconds## wait up to five minutes for the drain of connections##terminationGracePeriodSeconds: 300## Node labels for controller pod assignment## Ref: https://kubernetes.io/docs/user-guide/node-selection/##nodeSelector:kubernetes.io/os: linuxingressApp: ingress## Liveness and readiness probe values## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes##livenessProbe:failureThreshold: 5initialDelaySeconds: 10periodSeconds: 10successThreshold: 1timeoutSeconds: 1port: 10254readinessProbe:failureThreshold: 3initialDelaySeconds: 10periodSeconds: 10successThreshold: 1timeoutSeconds: 1port: 10254# Path of the health check endpoint. All requests received on the port defined by# the healthz-port parameter are forwarded internally to this path.healthCheckPath: "/healthz"## Annotations to be added to controller pods##podAnnotations: {}replicaCount: 1minAvailable: 1# Define requests resources to avoid probe issues due to CPU utilization in busy nodes# ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903# Ideally, there should be no limits.# https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/resources:#  limits:#    cpu: 100m#    memory: 90Mirequests:cpu: 100mmemory: 90Mi# Mutually exclusive with keda autoscalingautoscaling:enabled: falseminReplicas: 1maxReplicas: 11targetCPUUtilizationPercentage: 50targetMemoryUtilizationPercentage: 50autoscalingTemplate: []# Custom or additional autoscaling metrics# ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics# - type: Pods#   pods:#     metric:#       name: nginx_ingress_controller_nginx_process_requests_total#     target:#       type: AverageValue#       averageValue: 10000m# Mutually exclusive with hpa autoscalingkeda:apiVersion: "keda.sh/v1alpha1"# apiVersion changes with keda 1.x vs 2.x# 2.x = keda.sh/v1alpha1# 1.x = keda.k8s.io/v1alpha1enabled: falseminReplicas: 1maxReplicas: 11pollingInterval: 30cooldownPeriod: 300restoreToOriginalReplicaCount: falsetriggers: []#     - type: prometheus#       metadata:#         serverAddress: http://<prometheus-host>:9090#         metricName: http_requests_total#         threshold: '100'#         query: sum(rate(http_requests_total{deployment="my-deployment"}[2m]))behavior: {}#     scaleDown:#       stabilizationWindowSeconds: 300#       policies:#       - type: Pods#         value: 1#         periodSeconds: 180#     scaleUp:#       stabilizationWindowSeconds: 300#       policies:#       - type: Pods#         value: 2#         periodSeconds: 60## Enable mimalloc as a drop-in replacement for malloc.## ref: https://github.com/microsoft/mimalloc##enableMimalloc: true## Override NGINX templatecustomTemplate:configMapName: ""configMapKey: ""service:enabled: trueannotations: {}labels: {}# clusterIP: ""## List of IP addresses at which the controller services are available## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips##externalIPs: []# loadBalancerIP: ""loadBalancerSourceRanges: []enableHttp: trueenableHttps: true## Set external traffic policy to: "Local" to preserve source IP on## providers supporting it## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer# externalTrafficPolicy: ""# Must be either "None" or "ClientIP" if set. Kubernetes will default to "None".# Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies# sessionAffinity: ""# specifies the health check node port (numeric port number) for the service. If healthCheckNodePort isn’t specified,# the service controller allocates a port from your cluster’s NodePort range.# Ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip# healthCheckNodePort: 0ports:http: 80https: 443targetPorts:http: 80https: 443type: NodePort# type: NodePort# nodePorts:#   http: 32080#   https: 32443#   tcp:#     8080: 32808nodePorts:http: "80"https: "443"tcp:8080: 8080udp: {}## Enables an additional internal load balancer (besides the external one).## Annotations are mandatory for the load balancer to come up. Varies with the cloud service.internal:enabled: falseannotations: {}## Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0.loadBalancerSourceRanges: []## Set external traffic policy to: "Local" to preserve source IP on## providers supporting it## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer# externalTrafficPolicy: ""extraContainers: []## Additional containers to be added to the controller pod.## See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example.#  - name: my-sidecar#    image: nginx:latest#  - name: lemonldap-ng-controller#    image: lemonldapng/lemonldap-ng-controller:0.2.0#    args:#      - /lemonldap-ng-controller#      - --alsologtostderr#      - --configmap=$(POD_NAMESPACE)/lemonldap-ng-configuration#    env:#      - name: POD_NAME#        valueFrom:#          fieldRef:#            fieldPath: metadata.name#      - name: POD_NAMESPACE#        valueFrom:#          fieldRef:#            fieldPath: metadata.namespace#    volumeMounts:#    - name: copy-portal-skins#      mountPath: /srv/var/lib/lemonldap-ng/portal/skinsextraVolumeMounts: []## Additional volumeMounts to the controller main container.#  - name: copy-portal-skins#   mountPath: /var/lib/lemonldap-ng/portal/skinsextraVolumes: []## Additional volumes to the controller pod.#  - name: copy-portal-skins#    emptyDir: {}extraInitContainers: []## Containers, which are run before the app containers are started.# - name: init-myservice#   image: busybox#   command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']admissionWebhooks:annotations: {}enabled: falsefailurePolicy: Fail# timeoutSeconds: 10port: 8443certificate: "/usr/local/certificates/cert"key: "/usr/local/certificates/key"namespaceSelector: {}objectSelector: {}service:annotations: {}# clusterIP: ""externalIPs: []# loadBalancerIP: ""loadBalancerSourceRanges: []servicePort: 443type: ClusterIPpatch:enabled: trueimage:repository: docker.io/jettech/kube-webhook-certgentag: v1.5.0pullPolicy: IfNotPresent## Provide a priority class name to the webhook patching job##priorityClassName: ""podAnnotations: {}nodeSelector: {}tolerations: []runAsUser: 2000metrics:port: 10254# if this port is changed, change healthz-port: in extraArgs: accordinglyenabled: falseservice:annotations: {}# prometheus.io/scrape: "true"# prometheus.io/port: "10254"# clusterIP: ""## List of IP addresses at which the stats-exporter service is available## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips##externalIPs: []# loadBalancerIP: ""loadBalancerSourceRanges: []servicePort: 9913type: ClusterIP# externalTrafficPolicy: ""# nodePort: ""serviceMonitor:enabled: falseadditionalLabels: {}namespace: ""namespaceSelector: {}# Default: scrape .Release.Namespace only# To scrape all, use the following:# namespaceSelector:#   any: truescrapeInterval: 30s# honorLabels: truetargetLabels: []metricRelabelings: []prometheusRule:enabled: falseadditionalLabels: {}# namespace: ""rules: []# # These are just examples rules, please adapt them to your needs# - alert: NGINXConfigFailed#   expr: count(nginx_ingress_controller_config_last_reload_successful == 0) > 0#   for: 1s#   labels:#     severity: critical#   annotations:#     description: bad ingress config - nginx config test failed#     summary: uninstall the latest ingress changes to allow config reloads to resume# - alert: NGINXCertificateExpiry#   expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800#   for: 1s#   labels:#     severity: critical#   annotations:#     description: ssl certificate(s) will expire in less then a week#     summary: renew expiring certificates to avoid downtime# - alert: NGINXTooMany500s#   expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"5.+"} ) / sum(nginx_ingress_controller_requests) ) > 5#   for: 1m#   labels:#     severity: warning#   annotations:#     description: Too many 5XXs#     summary: More than 5% of all requests returned 5XX, this requires your attention# - alert: NGINXTooMany400s#   expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"4.+"} ) / sum(nginx_ingress_controller_requests) ) > 5#   for: 1m#   labels:#     severity: warning#   annotations:#     description: Too many 4XXs#     summary: More than 5% of all requests returned 4XX, this requires your attention## Improve connection draining when ingress controller pod is deleted using a lifecycle hook:## With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds## to 300, allowing the draining of connections up to five minutes.## If the active connections end before that, the pod will terminate gracefully at that time.## To effectively take advantage of this feature, the Configmap feature## worker-shutdown-timeout new value is 240s instead of 10s.##lifecycle:preStop:exec:command:- /wait-shutdownpriorityClassName: ""## Rollback limit
##
revisionHistoryLimit: 10# Maxmind license key to download GeoLite2 Databases
# https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases
maxmindLicenseKey: ""## Default 404 backend
##
defaultBackend:##enabled: falseimage:repository: k8s.gcr.io/defaultbackend-amd64tag: "1.5"pullPolicy: IfNotPresent# nobody user -> uid 65534runAsUser: 65534runAsNonRoot: truereadOnlyRootFilesystem: trueallowPrivilegeEscalation: falseextraArgs: {}serviceAccount:create: truename:## Additional environment variables to set for defaultBackend podsextraEnvs: []port: 8080## Readiness and liveness probes for default backend## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/##livenessProbe:failureThreshold: 3initialDelaySeconds: 30periodSeconds: 10successThreshold: 1timeoutSeconds: 5readinessProbe:failureThreshold: 6initialDelaySeconds: 0periodSeconds: 5successThreshold: 1timeoutSeconds: 5## Node tolerations for server scheduling to nodes with taints## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/##tolerations: []#  - key: "key"#    operator: "Equal|Exists"#    value: "value"#    effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"affinity: {}## Security Context policies for controller pods## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for## notes on enabling and using sysctls##podSecurityContext: {}# labels to add to the pod container metadatapodLabels: {}#  key: value## Node labels for default backend pod assignment## Ref: https://kubernetes.io/docs/user-guide/node-selection/##nodeSelector: {}## Annotations to be added to default backend pods##podAnnotations: {}replicaCount: 1minAvailable: 1resources: {}# limits:#   cpu: 10m#   memory: 20Mi# requests:#   cpu: 10m#   memory: 20Miautoscaling:enabled: falseminReplicas: 1maxReplicas: 2targetCPUUtilizationPercentage: 50targetMemoryUtilizationPercentage: 50service:annotations: {}# clusterIP: ""## List of IP addresses at which the default backend service is available## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips##externalIPs: []# loadBalancerIP: ""loadBalancerSourceRanges: []servicePort: 80type: ClusterIPpriorityClassName: ""## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266
rbac:create: truescope: false# If true, create & use Pod Security Policy resources
# https://kubernetes.io/docs/concepts/policy/pod-security-policy/
podSecurityPolicy:enabled: falseserviceAccount:create: truename:## Optional array of imagePullSecrets containing private registry credentials
## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
imagePullSecrets: []
# - name: secretName# TCP service key:value pairs
# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tcp
##
tcp: {}
#  8080: "default/example-tcp-svc:9000"# UDP service key:value pairs
# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/udp
##
udp: {}
#  53: "kube-system/kube-dns:53"

这里选择是要NodePort得方式部署这个ingress nginx,默认方式是使用 LoadBlancer 得,所以需要修改一些配置

  1. controller.image.repository : registry.cn-hangzhou.aliyuncs.com/k8s_gcr_io_allen/ingress-nginx-controller image 得仓库要是可用得,默认的是谷歌得镜像,无法访问。
  2. controller.kind:DaemonSet 这个apiVersion建议使用 DaemonSet
  3. controller.nodeSelector.ingressApp: ingress 这里新增个label 选择部署得节点
  4. controller.service.type:NodePort 这里得类型改为NodePort,并且将端口号暴漏出去,注意这里需要把集群得端口范围改为1-65535
    nodePorts:http: "80"https: "443"tcp:8080: 8080udp: {}
  • 测试部署一个应用并暴漏出去,并且配置https,云服务器要配置80 和 443 端口得开放
    先创建个secret 用于配置 https。
kubectl create secret tls ailuoli-nginx-secret --cert=ailuoli.cn.crt --key=ailuoli.cn.key -n test
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentlabels:app: nginx
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:latest
---
apiVersion: v1
kind: Service
metadata:name: nginx-servicelabels:app: nginx
spec:selector:app: nginxports:- name: httpprotocol: TCPport: 80targetPort: 80- name: httpsprotocol: TCPport: 443targetPort: 443---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:name: my-ingress-for-nginx  # Ingress 的名字,仅用于标识annotations:# 路径匹配规则,匹配后路径rewrite 为 / ,例如访问 https://ailuoli.cn/nginx,实际得路径匹配到了/nginx规则,路由到真正得服务则是https://ailuoli.cn/nginx.ingress.kubernetes.io/rewrite-target: /kubernetes.io/ingress.class: nginx# 设置http请求强制路由到httpsnginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:rules:                      # Ingress 中定义 L7 路由规则- host: ailuoli.cn   # 根据 virtual hostname 进行路由(请使用您自己的域名)http:paths:                  # 按路径进行路由- path: /nginxbackend:serviceName: nginx-service  # 指定后端的 Service 为之前创建的 nginx-serviceservicePort: 80tls:- hosts:- ailuoli.cnsecretName: ailuoli-ingress-nginx-secret

执行创建 Deployment,Service,Ingress

kubectl apply -f nginx-ingress.yaml

关于 Annotations 注解得作用

2.15 数据卷 Volume

kubernetes volume主要解决得问题

  • 数据持久性:通常情况下,容器运行起来之后,写入到其文件系统的文件暂时性的。当容器崩溃后,kubelet 将会重启该容器,此时原容器运行后写入的文件将丢失,因为容器将重新从镜像创建。
  • 数据共享:同一个 Pod(容器组)中运行的容器之间,经常会存在共享文件/文件夹的需求

2.15.1 数据卷的类型

  • emptyDir
    emptyDir 类型的数据卷在容器组被创建时分配给该容器组,并且直到容器组被移除,该数据卷才被释放。该数据卷初始分配时,始终是个空目录。同一容器组中的不通容器都可以对该目录执行读写操作,并且共享其中的数据,当容器组被删除时,emptyDir数据卷中的数据将被永久删除。
  • nfs
    nfs 类型的数据卷可以加载NFS 到容器组/容器。容器组被移除时,将仅仅umount(写在)NFS数据卷,NFS中的数据仍将被保留。
  • cephfs
    cephfs 数据卷可以挂载一个外部CephFS卷到容器组中。
  • hostPath
    hostPath 类型的数据卷将Pod所在的节点文件系统上某一个文件或文件夹挂载进容器组。
    除了为 hostPath 指定 path 字段以外,您还可以为其指定 type字段,可选的type字段描述如下:
Type 字段取值 描述
空字符串(default)用于向后兼容,此时,kubernetes 在挂载 hostPath 数据卷前不会执行任何检查
DirectoryOrCreate 如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空文件夹,权限设置为 0755,与 kubelet 进程具备相同的 group 和 ownership
Directory 指定 hostPath 路径必须存在,且是一个文件夹
FileOrCreate 如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空的文件,权限设置为 0644,与 kubelet 进程具备相同的 group 和 ownership
File 指定 hostPath 路径必须存在,且是一个文件
Socket 指定 hostPath 路径必须存在,且是一个 Unix Socket
CharDevice 指定 hostPath 路径必须存在,且是一个 character device
BlockDevice 指定 hostPath 路径必须存在,且是一个 block device
  • configMap
    ConfigMap 提供了一种向容器组注入配置信息的途径。ConfigMap中的数据可以被Pod中的容器作为一个数据卷挂载。
    在数据卷中引用ConfigMap时

    • 可以引用整个ConfigMap到数据卷,此时 ConfigMap 中的每一个 key 对应一个文件名,value 对应该文件的内容
    • 只引用 ConfigMap 中的某一个名值对,此时可以将 key 映射成一个新的文件名
  • secret
    secret 数据卷可以用来注入敏感信息(例如密码)到容器组。您可以将敏感信息存入 kubernetes secret 对象,并通过 Volume(数据卷)以文件的形式挂载到容器组(或容器)。secret 数据卷使用 tmpfs(基于 RAM 的文件系统)挂载。
  • persistentVolumeClaim
    persistentVolumeClaim数据卷用来挂载 PersistentVolume 存储卷。PersistentVolume 存储卷为用户提供了一种在无需关心具体所在云环境的情况下”声明“ 所需持久化存储的方式。

2.15.2 数据卷挂载

数据卷内子路径

我们需要在同一个 Pod 的不同容器间共享数据卷。使用 volumeMounts.subPath 属性,可以使容器在挂载数据卷时指向数据卷内部的一个子路径,而不是直接指向数据卷的根路径。
下面的例子中,一个 LAMP(Linux Apache Mysql PHP)应用的 Pod 使用了一个共享数据卷,HTML 内容映射到数据卷的 html 目录,数据库的内容映射到了 mysql目录

apiVersion: v1
kind: Pod
metadata:name: my-lamp-site
spec:containers:- name: mysqlimage: mysqlenv:- name: MYSQL_ROOT_PASSWORDvalue: "rootpasswd"volumeMounts:- mountPath: /var/lib/mysqlname: site-datasubPath: mysqlreadOnly: false- name: phpimage: php:7.0-apachevolumeMounts:- mountPath: /var/www/htmlname: site-datasubPath: htmlreadOnly: falsevolumes:- name: site-datapersistentVolumeClaim:claimName: my-lamp-site-data

通过环境变量指定数据卷内子路径

使用 volumeMounts.subPathExpr 字段,可以通过容器的环境变量指定容器内路径。使用此特性时,必须启用 VolumeSubpathEnvExpansion
如下面的例子,该 Pod 使用 subPathExprhostPath 数据卷 /var/log/pods 中创建了一个目录 pod1(该参数来自于Pod的名字)。此时,宿主机目录 /var/log/pods/pod1 挂载到了容器的 /logs 路径:

apiVersion: v1
kind: Pod
metadata:name: pod1
spec:containers:- name: container1env:- name: POD_NAMEvalueFrom:fieldRef:apiVersion: v1fieldPath: metadata.nameimage: busyboxcommand: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]volumeMounts:- name: workdir1mountPath: /logssubPathExpr: $(POD_NAME)readOnly: falserestartPolicy: Nevervolumes:- name: workdir1hostPath:path: /var/log/pods

挂载传播

数据卷的挂载传播(Mount Propagation)由 Pod 的 spec.containers[*].volumeMounts.mountPropagation 字段控制。可选的取值有:

  • None: 默认值。在数据卷被挂载到容器之后,此数据卷不会再接受任何后续宿主机或其他容器挂载到该数据卷对应目录下的子目录的挂载。同样的,在容器中向该数据卷对应目录挂载新目录时,宿主机也不能看到。对应 Linux 的 private mount propagation 选项 。
  • HostToContainer: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的。对应 Linux 的 rslave mount propagation 选项。
  • Bidirectional: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的;同时,从容器中向该数据卷创建挂载,同样也对宿主机可见。对应 Linux 的 rshared mount propagation 选项

2.15.3 PersistentVolume

与管理计算资源相比,管理存储资源是一个完全不同的问题。为了更好的管理存储,Kubernetes 引入了 PersistentVolumePersistentVolumeClaim 两个概念,将存储管理抽象成如何提供存储以及如何使用存储两个关注点。

  • PersistentVolume(PV 存储卷)是集群中的一块存储空间,由集群管理员管理、或者由 Storage Class(存储类)自动管理。PV(存储卷)和 node(节点)一样,是集群中的资源(kubernetes 集群由存储资源和计算资源组成)。PersistentVolumeClaim(存储卷声明)是一种类型的 Volume(数据卷),PersistentVolumeClaim(存储卷声明)引用的 PersistentVolume(存储卷)有自己的生命周期,该生命周期独立于任何使用它的容器组。PersistentVolume(存储卷)描述了如何提供存储的细节信息(NFS、cephfs等存储的具体参数)。
  • PersistentVolumeClaim(PVC 存储卷声明)代表用户使用存储的请求。Pod 容器组消耗 node 计算资源,PVC 存储卷声明消耗 PersistentVolume 存储资源。Pod 容器组可以请求特定数量的计算资源(CPU / 内存);PersistentVolumeClaim 可以请求特定大小/特定访问模式(只能被单节点读写/可被多节点只读/可被多节点读写)的存储资源。

存储卷和存储卷声明的关系

  • PersistentVolume 是集群中的存储资源,通常由集群管理员创建和管理
  • StorageClass 用于对PersistentVolume进行分类,如果正确配置,StorageClass 也可以根据 PersistentVolumeClaim的请求动态创建 Persistent Volume
  • PersistentVolumeClaim 是使用该资源的请求,通常由应用程序提出请求,并指定对应的 StorageClass 和需求的空间大小
  • PersistentVolumeClaim 可以做为数据卷的一种,被挂载到容器组/容器中使用

为PVC 提供 PV的两种方式

  • 静态提供static
    集群管理员实现创建好一系列 PersistentVolume,它们包含了可供集群中应用程序使用的关于实际存储的具体信息。
  • 动态提供Dynamic
    在配置有合适的 StorageClass(存储类)且 PersistentVolumeClaim 关联了该 StorageClass的情况下,kubernetes 集群可以为应用程序动态创建 PersistentVolume

绑定Binding

如果一个 PersistentVolume 是动态提供给一个新的 PersistentVolumeClaim,Kubernetes master 会始终将其绑定到该PersistentVolumeClaim。除此之外,应用程序将被绑定一个不小于(可能大于)其 PersistentVolumeClaim中请求的存储空间大小的 PersistentVolume。一旦绑定,PersistentVolumeClaim将拒绝其他PersistentVolume的绑定关系。PVC 与 PV 之间的绑定关系是一对一的映射。

使用 Using

对于 Pod 容器组来说,PersistentVolumeClaim 存储卷声明是一种类型的 Volume 数据卷。Kubernetes 集群将 PersistentVolumeClaim 所绑定的 PersistentVolume 挂载到容器组供其使用。

Storage Object in Use Protection

  • 使用中保护(Storage Object in Use Protection)的目的是确保正在被容器组使用的 PersistentVolumeClaim 以及其绑定的 PersistentVolume 不能被系统删除,以避免可能的数据丢失。
  • 如果用户删除一个正在使用中的 PersistentVolumeClaim,则该 PVC 不会立即被移除掉,而是推迟到该 PVC 不在被任何容器组使用时才移除;同样的如果管理员删除了一个已经绑定到 PVC 的 PersistentVolume,则该 PV 也不会立刻被移除掉,而是推迟到其绑定的 PVC 被删除后才移除掉。

回收 Reclaiming

当用户不在需要其数据卷时,可以删除掉其 PersistentVolumeClaim,此时其对应的 PersistentVolume将被集群回收并再利用。Kubernetes 集群根据 PersistentVolume 中的 reclaim policy(回收策略)决定在其被回收时做对应的处理。当前支持的回收策略有:Retained(保留)、Recycled(重复利用)、Deleted(删除)

  • 保留 Retain
    保留策略需要集群管理员手工回收该资源。当绑定的 PersistentVolumeClaim 被删除后,PersistentVolume 仍然存在,并被认为是”已释放“。但是此时该存储卷仍然不能被其他 PersistentVolumeClaim 绑定,因为前一个绑定的 PersistentVolumeClaim 对应容器组的数据还在其中。集群管理员可以通过如下步骤回收该 PersistentVolume:

    • 删除该 PersistentVolume。PV 删除后,其数据仍然存在于对应的外部存储介质中(nfs、cefpfs、glusterfs 等)
    • 手工删除对应存储介质上的数据
    • 手工删除对应的存储介质,您也可以创建一个新的 PersistentVolume 并再次使用该存储介质
  • 删除 Delete
    删除策略将从 kubernete 集群移除 PersistentVolume 以及其关联的外部存储介质(云环境中的 AWA EBS、GCE PD、Azure Disk 或 Cinder volume)。

  • 再利用 Recycle
    再利用策略将在 PersistentVolume回收时,执行一个基本的清除操作(rm -rf /thevolume/*),并使其可以再次被新的 PersistentVolumeClaim 绑定。
    集群管理员也可以自定义一个 recycler pod template,用于执行清除操作

2.15.4 使用 nfs 做存储 创建 StorageClass

搭建 nfs server

yum install -y nfs-utils
mkdir /data/nfsdata/
chmod 777 /data/nfsdata/

编辑 /etc/exports 文件,内容如下

/data/nfsdata/ *(rw,sync,insecure,no_subtree_check,no_root_squash)

其中 *可以指定那些 ip 可以共享该文件夹

systemctl enable rpcbind
systemctl enable nfs-serversystemctl start rpcbind
systemctl start nfs-server

添加公网 ip 作为网卡

cat > /etc/sysconfig/network-scripts/ifcfg-eth0:1 <<EOF
BOOTPROTO=static
DEVICE=eth0:1
IPADDR=120.53.234.127
PREFIX=32
TYPE=Ethernet
USERCTL=no
ONBOOT=yes
EOF

客户端测试 nfs server

yum install -y nfs-utils
# 检查服务端是否设有共享目录
showmount -e  ailuoli.cn

执行命令挂载nfs服务器上的共享目录到本机路径

mkdir /data/share
mount -t nfs ailuoli.cn:/data/nfsdata /data/share
#写一个测试文件
echo "hello nfs server" > /data/share/test.txt

验证nfs server 有没有

cat /data/nfsdata/test.txt

取消挂载

umount /data/share

创建 Storage Class 来自动创建 PVC

使用 helm部署 nfs-client-provisioner

helm repo add stable https://charts.helm.sh/stable
helm pull stable/nfs-client-provisioner
tar -zxvf nfs-client-provisioner-1.2.11.tgz

修改 values.yaml

nfs:server: ailuoli.cnpath: /data/nfsdata
... 删除策略reclaimPolicy: Retain

执行创建 nfs-client

helm install nfs-client .
# 查看创建好的 Storage Class
kubectl get sc

测试下是否可以正常创建 PVC,创建 pvc-test.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc1
spec:storageClassName: nfs-clientaccessModes:- ReadWriteManyresources:requests:storage: 1Gi

执行文件

kubectl apply -f pvc-test.yaml

查看 pvc

kubectl get pvc

结果如下,status 为 Bound 就为正常

NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-mysql-0   Bound    pvc-be7fdb16-e579-4857-b2ef-e5015b56e6f8   8Gi        RWO            nfs-client     9h

k8s 1.20 以上版本遇到 PVC Pending 问题

编辑该文件

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

添加此配置

- --feature-gates=RemoveSelfLink=false

2.16 ConfigMap

可以使用 kubectl create configmap 命令基于目录,文件,字面值来创建

kubectl create configmap <map-name> <data-source>

基于目录创建configMap

kubectl create configmap test-config --from-file=./dev/

查看configmaps

kubectl get configmaps test-config -o yaml
apiVersion: v1
data:ailuoli-dev.properties: from=git-dev-1.0-tservice1-dev.properties: profile=dev-1.0
kind: ConfigMap
metadata:creationTimestamp: "2020-12-16T03:17:38Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:ailuoli-dev.properties: {}f:service1-dev.properties: {}manager: kubectl-createoperation: Updatetime: "2020-12-16T03:17:38Z"name: test-confignamespace: testresourceVersion: "2387780"selfLink: /api/v1/namespaces/test/configmaps/test-configuid: 87f7843a-8230-4bb8-ad22-4085c0b108a9

基于文件创建configMap

kubectl create configmap test-config2 --from-file=./ailuoli-dev.properties

可以使用多个 --from-file 来创建 configMap

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties --from-file=configure-pod-container/configmap/ui.properties

使用 --from-env-file 来创建 configMap

  • Env 文件中的每一行必须为 VAR=VAL 格式。
  • 以#开头的行(即注释)将被忽略。
  • 空行将被忽略。
  • 引号不会被特殊处理(即它们将成为 ConfigMap 值的一部分)。
kubectl create configmap test-env-config --from-env-file=./ailuoli-test.properties
apiVersion: v1
data:from: git-test-1.0
kind: ConfigMap
metadata:creationTimestamp: "2020-12-16T03:33:00Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:from: {}manager: kubectl-createoperation: Updatetime: "2020-12-16T03:33:00Z"name: test-env-confignamespace: testresourceVersion: "2389938"selfLink: /api/v1/namespaces/test/configmaps/test-env-configuid: 5bfba06b-7073-487f-80e3-a1e6384eeaeb

同样 --from-env-file 也可以使用多个

kubectl create configmap config-multi-env-files \--from-env-file=configure-pod-container/configmap/game-env-file.properties \--from-env-file=configure-pod-container/configmap/ui-env-file.properties

定义从文件创建 ConfigMap时要使用的键

kubectl create configmap test-config3 --from-file=testailuoli=./ailuoli-dev.properties
apiVersion: v1
data:testailuoli: from=Ailuoli-Ailuoli-Ailuoli
kind: ConfigMap
metadata:creationTimestamp: "2020-12-16T03:43:05Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:testailuoli: {}manager: kubectl-createoperation: Updatetime: "2020-12-16T03:43:05Z"name: test-config3namespace: testresourceVersion: "2391353"selfLink: /api/v1/namespaces/test/configmaps/test-config3uid: 9dafdda6-3085-451c-9e41-b02ffe81d367

根据字面值创建 ConfigMap

--from-literal 根据命令行定义文字值:

kubectl create configmap test-config4 --from-literal=hello1=world1 --from-literal=hello2=world2
apiVersion: v1
data:hello1: world1hello2: world2
kind: ConfigMap
metadata:creationTimestamp: "2020-12-16T06:28:51Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:hello1: {}f:hello2: {}manager: kubectl-createoperation: Updatetime: "2020-12-16T06:28:51Z"name: test-config4namespace: testresourceVersion: "2414579"selfLink: /api/v1/namespaces/test/configmaps/test-config4uid: d05fa08b-44ca-4bdf-aaf4-38b47477190a

基于生成器创建 ConfigMap

自1.14 开始,kubectl 开始支持 kustomization.yaml。

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: test-config5files:- configmap/config-repos/ailuoli-dev.properties
# - test=configmap/config-repos/ailuoli-dev.properties
# 自定义key
EOF
kubectl apply -k .
apiVersion: v1
data:ailuoli-dev.properties: from=Ailuoli-Ailuoli-Ailuoli
kind: ConfigMap
metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"v1","data":{"ailuoli-dev.properties":"from=Ailuoli-Ailuoli-Ailuoli"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test-config5-245dh9m2fd","namespace":"test"}}creationTimestamp: "2020-12-16T09:40:42Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:ailuoli-dev.properties: {}f:metadata:f:annotations:.: {}f:kubectl.kubernetes.io/last-applied-configuration: {}manager: kubectl-client-side-applyoperation: Updatetime: "2020-12-16T09:40:42Z"name: test-config5-245dh9m2fdnamespace: testresourceVersion: "2441461"selfLink: /api/v1/namespaces/test/configmaps/test-config5-245dh9m2fduid: 0f97cda8-0c6c-47db-9599-1287e3dea642

使用 ConfigMap 定义容器环境变量

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deploymentlabels:app: nginx
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:latestenv:- name: test-config4valueFrom:configMapKeyRef:key: hello1name: test-config4

ConfigMap 数据添加到一个 Volume

apiVersion: v1
kind: Pod
metadata:name: dapi-test-pod
spec:containers:- name: test-containerimage: k8s.gcr.io/busyboxcommand: [ "/bin/sh", "-c", "ls /etc/config/" ]volumeMounts:- name: config-volumemountPath: /etc/configvolumes:- name: config-volumeconfigMap:# Provide the name of the ConfigMap containing the files you want# to add to the containername: special-configitems:#使用 path 字段为特定的 ConfigMap 项目指定预期的文件路径。 在这里,SPECIAL_LEVEL 将挂载在 config-volume 数据卷中 /etc/config/keys 目录下。- key: SPECIAL_LEVELpath: keys       restartPolicy: Never

2.17 管理容器资源

apiVersion: v1
kind: Pod
metadata:name: frontend
spec:containers:- name: appimage: images.my-company.example/app:v4env:- name: MYSQL_ROOT_PASSWORDvalue: "password"resources:requests:memory: "64Mi"cpu: "250m"limits:memory: "128Mi"cpu: "500m"- name: log-aggregatorimage: images.my-company.example/log-aggregator:v6resources:requests:memory: "64Mi"cpu: "250m"limits:memory: "128Mi"cpu: "500m"

2.18 污点和容忍

Pod 中存在属性 Node selector / Node affinity 用于将Pod指定到合适的节点。相对的,节点中存在 污点属性 taints,使得节点可以排斥某些 Pod

污点和容忍(taints and )成对工作,以确保Pod不会被调度到不合适的节点上。

  • 可以为节点增加污点(taints, 一个节点可有 多个污点)
  • 可以为Pod增加容忍 (toleration, 一个节点可以有多个容忍)
  • 如果节点上存在污点,则该节点不会接受任何不能容忍该污点得Pod

2.18.1 向节点添加污点

  • 执行 kubectl taint 命令,可以向节点添加污点
kubectl taint nodes node1 key=value:NoSchedule

该命令向节点 node1 添加了一个污点。污点是一个键值对,污点得键为key,值为value ,污点效果为 NoSchedule。此污点意味着kubernetes 不会向该节点调度任何Pod,除非该 Pod 有一个匹配得容忍(toleration

  • 执行以下命令去除污点
kubectl taint nodes node1 key:NoSchedule-

2.18.2 向Pod添加容忍

PodSpec 中有一个tolerations 字段,可用于向Pod添加容忍。

  • 容忍1:
tolerations:
- key: "key"operator: "Equal"value: "value"effect: "NoSchedule"
  • 容忍2:
tolerations:
- key: "key"operator: "Exists"effect: "NoSchedule"
apiVersion: v1
kind: Pod
metadata:name: nginxlabels:env: test
spec:containers:- name: nginximage: nginximagePullPolicy: IfNotPresenttolerations:- key: "example-key"operator: "Exists"effect: "NoSchedule"

2.18.3 污点和容忍匹配

当满足如下条件时,k8s 认为容忍和污点匹配:

  • 键(key) 相同
  • 效果(effect)相同
  • 污点 operator 为:
    • Exists (此时污点中不该指定 value)
    • 或者 Equal (此时容忍的 value 应与污点的 value 相同)
      如果不指定 operator ,则默认为 Equal

特殊情况:

  • 容忍中未定义 key 但是定义了 operatorExists,Kubernetes 认为此容忍匹配所有的污点.
tolerations:
- operator: "Exists"
  • 容忍中未定义 effect但是定义了key,Kubernetes 认为此容忍匹配所有effect.
tolerations:
- key: "key"operator: "Exists

effect 效果有

  • NoSchedule
  • PreferNoScheduleNoSchedule 更宽容一些,Kubernetes将尽量避免将没有匹配容忍的Pod调度该节点上,但是并不是不可以
  • NoExecute 不能在节点上运行(如果已经运行,将被驱逐 )
    示例:
    给一个Node 添加三个污点
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

同时有一个Pod带有两个容忍:

tolerations:
- key: "key1"operator: "Equal"value: "value1"effect: "NoSchedule"
- key: "key1"operator: "Equal"value: "value1"effect: "NoExecute"

在这个案例中,Pod 上有两个容忍,匹配了节点的前两个污点,只有节点的第三个污点对该 Pod 来说不可忽略,该污点的效果为 NoSchedule

  • Kubernetes 不会将此 Pod 调度到该节点上
  • 如果 Kubernetes 先将 Pod 调度到了该节点,后向该节点添加了第三个污点,则 Pod 将继续在该节点上运行而不会被驱逐(节点上带有 NoExecute 效果的污点已被 Pod 上的第二个容忍匹配,因此被忽略)

可以设置 Pod在多久后被驱逐tolerationSeconds

tolerations:
- key: "key1"operator: "Equal"value: "value1"effect: "NoExecute"tolerationSeconds: 3600

kubernetes 的节点控制器在碰到某些特定的条件时,将自动为节点添加污点,默认启用。

  • node.kubernetes.io/not-ready: 节点未就绪。对应着 NodeCondition Ready 为 False 的情况
  • node.kubernetes.io/unreachable: 节点不可触达。对应着 NodeCondition Ready 为 Unknown 的情况
  • node.kubernetes.io/out-of-disk:节点磁盘空间已满
  • node.kubernetes.io/memory-pressure:节点内存吃紧
  • node.kubernetes.io/disk-pressure:节点磁盘吃紧
  • node.kubernetes.io/network-unavailable:节点网络不可用
  • node.kubernetes.io/unschedulable:节点不可调度
  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 是由 “外部” 云服务商启动的,该污点用来标识某个节点当前为不可用的状态。在“云控制器”(cloud-controller-manager)初始化这个节点以后,kubelet将此污点移除
    示例:
    某一个包含了大量本地状态的应用,在网络断开时,可能仍然想要在节点上停留比较长的时间,以等待网络能够恢复,而避免从节点上驱逐。此时,该 Pod 的容忍可能如下所示:
tolerations:
- key: "node.kubernetes.io/unreachable"operator: "Exists"effect: "NoExecute"tolerationSeconds: 6000

如果 Pod 没有 node.kubernetes.io/not-ready 容忍, Kubernetes 将自动为 Pod 添加一个 tolerationSeconds=300node.kubernetes.io/not-ready 容忍。同样的,如果 Pod 没有 node.kubernetes.io/unreachable 容忍,Kubernetes 将自动为 Pod 添加一个 tolerationSeconds=300node.kubernetes.io/unreachable 容忍
这类自动添加的容忍确保了 Pod 在节点发生 not-ready 和 unreachable 问题时,仍然在节点上保留 5 分钟。

DaemonSet Pod 相对特殊一些,他们在创建时就添加了不带 tolerationSecondsNoExecute 效果的容忍,适用的污点有:

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

这将确保 DaemonSet Pod 始终不会被驱逐

2.19 Secret

2.19.1 手动创建 Secret

和创建其他类型 API对象一样,可以现在yaml 文件定义好Secret,然后通过 kubectl apply -f 命令创建。此时可以通过两种方式在yaml中定义Secret

  • data: 使用 字段 时,取值的欸容必须是 base64 编码
  • stringDate: 使用stringData时,更为方便,可以直接将取值以明文方式写在yaml文件中
  1. yaml中定义data
echo -n 'admin' | base64
echo -n '123456' | base64
apiVersion: v1
kind: Secret
metadata:name: mysecret
type: Opaque
data:username: YWRtaW4=password: MTIzNDU2
  1. 在yaml中定义stringData
apiVersion: v1
kind: Secret
metadata:name: mysecret
type: Opaque
stringData:username: adminpassword: a1234567890

但是这种写法在yaml文件annotation中仍然可以看见铭文

apiVersion: v1
data:password: YTEyMzQ1Njc4OTA=username: YWRtaW4=
kind: Secret
metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"secret-test","namespace":"test"},"stringData":{"password":"a1234567890","username":"admin"},"type":"Opaque"}creationTimestamp: "2020-12-24T06:45:47Z"managedFields:- apiVersion: v1fieldsType: FieldsV1fieldsV1:f:data:.: {}f:password: {}f:username: {}f:metadata:f:annotations:.: {}f:kubectl.kubernetes.io/last-applied-configuration: {}f:type: {}manager: kubectl-client-side-applyoperation: Updatetime: "2020-12-24T06:45:47Z"name: secret-testnamespace: testresourceVersion: "4249564"selfLink: /api/v1/namespaces/test/secrets/secret-testuid: 6228d000-976f-49ca-8122-4940425b1c35
type: Opaque
  1. 同时定义了 data 和 stringData
    如果同事定义了data 和 stringData,对于对象中key重复的字段,最终采纳 stringData 中的value
apiVersion: v1
kind: Secret
metadata:name: mysecret
type: Opaque
data:username: YWRtaW4=
stringData:username: administrator

2.19.2 解码和编辑 Secret

  1. 解码
echo 'YTEyMzQ1Njc4OTA=' | base64 --decode
  1. 编辑
kubectl edit secrets mysecret

2.20 Security Context

2.20.1 为 Pod 设置 Security Context

在 Pod 的定义中增加 securityContext 字段,即可为 Pod 执行 Security 相关的设定。securityContext 字段是一个 PodSecurityContext 对象。通过该字段指定的内容将对该 Pod 中所有的容器生效。

apiVersion: v1
kind: Pod
metadata:name: security-context-demo
spec:securityContext:runAsUser: 1000runAsGroup: 3000fsGroup: 2000volumes:- name: sec-ctx-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /data/demosecurityContext:allowPrivilegeEscalation: false
  • spec.securityContext.runAsUser 字段指定了该 Pod 中所有容器的进程都以UserID 1000 的身份运行,spec.securityContext.runAsGroup 字段指定了该 Pod 中所有容器的进程都以GroupID 3000 的身份运行

    • 如果该字段被省略,容器进程的GroupID为 root(0)
    • 容器中创建的文件,其所有者为 userID 1000,groupID 3000
  • spec.securityContext.fsGroup 字段指定了该 Pod 的 fsGroup 为 2000
    数据卷 (本例中,对应挂载点 /data/demo 的数据卷为 sec-ctx-demo) 的所有者以及在该数据卷下创建的任何文件,其 GroupID 为 2000

2.20.2 为容器设置Linux Capabilities

使用 Linux Capabilities可以为容器内的进程授予某些特定的权限(而不是 root 用户的所有权限)。在容器定义的 securityContext中添加 capabilities 字段,可以向容器添加或删除Linux Capability

apiVersion: v1
kind: Pod
metadata:name: security-context-demo-4
spec:containers:- name: sec-ctx-demo-4image: busyboxcommand: [ "sh", "-c", "sleep 1h" ]securityContext:capabilities:add: ["NET_ADMIN", "SYS_TIME"]

三、Kubernetes 高级学习

3.1 安全

3.1.1 用户认证

所有Kubernetes 集群都有两类用户,Kubernetes管理的Service Account 和 普通用户

与普通用户相对,Service Account是通过 Kubernetes API 管理的用户。Service Account 是名称空间级别的对象,可能由 ApiServer 自动创建,或者通过调用 API 接口创建。Service Account 都绑定了一组 SecretSecret 可以被挂载到 Pod 中,以便 Pod 中的进程可以获得调用 Kubernetes API 的权限。

集群内外的任何一个进程,在调用 API Server 的接口时,都必须认证其身份,或者被当做一个匿名用户。可能的场景有:

  • 集群中某一个 Pod 调用 API Server 的接口查询集群的信息
  • 用户通过 kubectl 执行指令,kubectl 调用 API Server 的接口完成用户的指令
  • 用户通过 Kuboard 界面管理集群,Kuboard 调用 API Server 的接口实现界面上的功能

认证策略

Kubernetes 的认证策略(Authentication Strategies)是:通过 authentication plugin 认证发起 API 请求的用户身份,认证方式有 client certificates、bearer tokens、authenticating proxy、HTTP basic auth。当 API Server 接收到 HTTP 请求时,authentication plugin 会尝试将如下属性附加到请求:

  • Username:唯一标识用户的一个字符串。例如 kube-admin 或者 jane@example.com
  • UID:唯一标识用户的一个字符串,相较于 username,提供更强的一致性和唯一性。(某些 Identity Provider 可能允许用户更改 username)
  • Groups:一组字符串,标识用户所属的用户组
  • 额外字段:Key,Value 都是 String 的 Map,包含一些 对 authorizer 可能有用的信息\

上述所有的字段对认证系统来说都是透明的,且只对 authorizer 有意义(authentication plugin 将认证结果中的某些字段赋值到上述字段中,认证系统只是按照自己的方式正常工作,并不知道上述这些字段的存在)。这使得 Kubernetes 可以同时支持多种认证方式,在实际工作中,您通常应该至少配置两种认证方式:

  • Service account tokens 用于认证 Service Account,
  • 至少另外一种认证方式用于认证普通用户。

3.1.2 管理 ServiceAccount

User accounts 和 service accounts

Kubernetes 明确地区分了 user account 和 service account 的概念,原因如下:

  • User account 的使用者是用户(人),service account 的使用者是运行在 Pod 中的进程。
  • User account 应该是全局的,用户名在集群范围内(跨名称空间)必须唯一。Service account 的名称在名称空间内唯一即可
  • 通常,集群的 user account 可能是从企业的数据库同步过来,在那里,创建新的 user account 需要特殊的权限,并且受复杂的业务流程管控。Service account 的创建则更加轻量级,允许集群的用户为特定的任务创建 service account,(最小权限的原则)
  • 对用户(人)和 service account 的审计过程可能会不一样
  • 一个复杂系统中,可能为不同的组件配置不同的 service account。由于 service account 可以临时创建,并且在名称空间内唯一,这种配置信息是可以移植的

Service account admission controller

Admission Controller (opens new window)是 apiserver 的一部分,它在 Pod 创建或者更新时,对 Pod 执行一些修改。此控制器激活时(默认处于激活状态),当 Pod 被创建或修改时,该控制器将执行如下动作:

  • 如果 Pod 未设置 ServiceAccount,将 ServiceAccount 设置为 default
  • 确保 Pod 引用的 ServiceAccount 存在,否则拒绝创建或者修改 Pod
  • 如果 Pod 不包含任何ImagePullSecrets,则 ServiceAccount中的 ImagePullSecrets 将被添加到 Pod 上
  • 为 Pod 添加一个 volume(其中包含了访问 APIServer 的 token)
  • 为 Pod 中的每一个容器添加一个 volumeSource,并挂载到路径/var/run/secrets/kubernetes.io/serviceaccount

Token Controller

TokenController 作为 controller-manager 的一部分运行。以异步的方式执行如下动作:

  • 监听 ServiceAccount的创建,并创建一个对应的 Secret 以允许访问 APIServer
  • 监听 ServiceAccount 的删除,并删除所有对应的 ServiceAccountToken Secrets
  • 监听 Secret 的添加,确保其引用的 ServiceAccount 以存在,并在需要时向 Secret 添加 Token
  • 监听 Secret 的删除,并在需要的情况下将对应 ServiceAccount 中对 Secret 的引用也删除掉

。。。。 未完待续

四、Kubernetes 实战

4.1 使用 port-forward 访问集群中的应用程序

使用kubectl port-forward 访问kubernetes集群中的Redis Server ,Debug 时很有效

  1. 创建 Redis 服务
apiVersion: apps/v1
kind: Deployment
metadata:name: redis-masterlabels:app: redis
spec:selector:matchLabels:app: redisrole: mastertier: backendreplicas: 1template:metadata:labels:app: redisrole: mastertier: backendspec:containers:- name: masterimage: redisresources:requests:cpu: 100mmemory: 100Miports:- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:name: redis-masterlabels:app: redisrole: mastertier: backend
spec:ports:- port: 6379targetPort: 6379selector:app: redisrole: mastertier: backend

这时redis 的pod就创建好了,Service使用的ClusterIP

  1. 转发本地端口到Pod的端口
# 这几个命令执行任意一个即可
kubectl port-forward redis-master-765d459796-258hz 7000:6379
kubectl port-forward pods/redis-master-765d459796-258hz 7000:6379
kubectl port-forward deployment/redis-master 7000:6379
kubectl port-forward rs/redis-master 7000:6379
kubectl port-forward svc/redis-master 7000:6379

kubernetes 学习记录相关推荐

  1. Kubernetes学习笔记(一)

    2019独角兽企业重金招聘Python工程师标准>>> Kubernetes学习笔记(一) 博客分类: Kubernetes 导语 2015年4月,传闻已久的Borg论文伴随Kube ...

  2. Kubernetes学习笔记之Calico CNI Plugin源码解析(二)

    女主宣言 今天小编继续为大家分享Kubernetes Calico CNI Plugin学习笔记,希望能对大家有所帮助. PS:丰富的一线技术.多元化的表现形式,尽在"360云计算" ...

  3. Kubernetes 学习总结(19)—— Kubernetes 集群管理平台如何选择?Rancher vs KubeSphere

    前言 Kubernetes(K8s)集群管理平台都是基于 Kubernetes 提供功能,可以说他们是在 K8s 的基础上封装了一层更为友好的操作方式.他们都是为了降低 k8s 集群运维复杂度,降低运 ...

  4. Kubernetes学习-K8S安装篇-Kubeadm安装高可用K8S集群

    Kubernetes学习-K8S安装篇-Kubeadm高可用安装K8S集群 1. Kubernetes 高可用安装 1.1 kubeadm高可用安装k8s集群1.23.1 1.1.1 基本环境配置 1 ...

  5. Kubernetes学习笔记-未整理

    Kubernetes学习笔记 标签:Kubernetes 学习笔记 原文:https://github.com/wtysos11/NoteBook/blob/master/微服务/Kubernetes ...

  6. Kubernetes学习笔记

    Kubernetes学习笔记 1.简介 用于自动部署.扩缩和管理容器化应用程序的开源系统,支持自动化部署.大规模可伸缩. 2.架构 2.1.Control Plane 对集群做出全局决策 Contro ...

  7. Pytorch学习记录-torchtext和Pytorch的实例( 使用神经网络训练Seq2Seq代码)

    Pytorch学习记录-torchtext和Pytorch的实例1 0. PyTorch Seq2Seq项目介绍 1. 使用神经网络训练Seq2Seq 1.1 简介,对论文中公式的解读 1.2 数据预 ...

  8. HTML5与CSS3权威指南之CSS3学习记录

    title: HTML5与CSS3权威指南之CSS3学习记录 toc: true date: 2018-10-14 00:06:09 学习资料--<HTML5与CSS3权威指南>(第3版) ...

  9. springboot @cacheable不起作用_Springboot学习记录13 使用缓存:整合redis

    本学习记录的代码,部分参考自gitee码云的如下工程.这个工程有详尽的Spingboot1.x教程.鸣谢! https://gitee.com/didispace/SpringBoot-Learnin ...

最新文章

  1. usaco Healthy Holsteins
  2. 二叉树:二叉搜索树实现 逆序数问题
  3. 【首轮官宣】中国肠道大会姊妹盛会,GUT 2022正式启航!
  4. 业务工作流平台设计(六)
  5. spring集成kafka
  6. 免费下载!《九年双11:互联网技术超级工程》,300页干货精华
  7. 分享十二本经典电子书,涉及java, OO design, spring, hibernate,struts2, agile
  8. spark.mllib:回归算法
  9. mfc如何将一个数组中的字节数据用串口发送出去_RS232串口多机通信
  10. Easy UI中dategrid的getSelections方法只能获取一个id的解决办法
  11. 年会尽头是闲鱼!超11万人在闲鱼转卖年会奖品
  12. 基于HT for Web的Web SCADA工控移动应用
  13. sql依据单个字段去重_Java面试之常用SQL
  14. 通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
  15. 夏普(SHARP) LS050T1SX01 液晶屏接口定义
  16. iOS屏幕自动旋转 以及横屏模式打开APP出现的问题
  17. ajax请求后台下载文件
  18. phpfpm怎么连接mysql_配置nginx、mysql、php-fpm的方法
  19. 神经网络和图神经网络,神经网络的图怎么画
  20. 计算机应用基础设施实用教程孙新德,刘国基

热门文章

  1. “抖音”式的酷炫短视频开发进阶
  2. 最实用的vue刷新当前页面,provide / inject 组合 方式实现vue页面刷新
  3. STM32矩阵按键扫描冲突问题
  4. golang iris mysql_golang iris使用
  5. 华为 系统升级 服务器,服务器系统升级
  6. opencv/openmv识别三角形思路(识别多边形)
  7. 游戏原画角色设计基本功有哪些?
  8. 2020年正则表达式匹配三大运营商手机号码,附各运营商号段
  9. 《aelf经济和治理白皮书》重磅发布:为DAPP提供治理高效、价值驱动的生态环境
  10. 【mysql学习】2.创建数据库和表,学会使用 select,delete,update ,insert,where等指令