kubernetes 学习记录

  • Post author:
  • Post category:其他


Kubernetes 学习记录



一、

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: nginx
  template:	    #这是选择或创建的Pod的模板
    metadata:	#Pod的元数据
      labels:	#Pod的标签,上面的selector即选择包含标签app:nginx的Pod
        app: nginx
    spec:	    #期望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作为服务的访问地址,此时

    ClusterIP



    NodePort

    的访问方式仍然可用。



1.3.2 创建一个

nginx deployment



Service

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 的 Pod
  ports:
  - name: nginx-port	#端口的名字
    protocol: TCP	    #协议类型 TCP/UDP
    port: 80	        #集群内的其他容器组可通过 80 端口访问 Service
    nodePort: 32600   #通过任意节点的 32600 端口访问 Service
    targetPort: 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: value1
    key2: value2



2.2.2 标签选择器



name



UID

不同,标签不一定是唯一的。通常来讲,会有多个Kubernetes对象包含相同的标签。通过使用标签选择器(

label selector

),用户/客户端可以选择一组对象。标签选择器(

label selector

)是 Kubernetes 中最主要的分类和筛选手段。

Kubernetes api server支持两种形式的标签选择器,

equality-based

基于等式的 和

set-based

基于集合的。标签选择器可以包含多个条件,并使用逗号分隔,此时只有满足所有条件的 Kubernetes 对象才会被选中。

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

也可以使用逗号分隔的两个等式

environment=production,tier!=frontend

,此时将选中所有

environment



prod



tier

不为

front

的对象。



Pod

的节点选择器 为例,下面的

Pod

可以被调度到包含标签

accelerator=nvidia-tesla-p100

的节点上:

apiVersion: v1
kind: Pod
metadata:
  name: cuda-test
spec:
  containers:
    - name: cuda-test
      image: "k8s.gcr.io/cuda-vector-add:v0.1"
      resources:
        limits:
          nvidia.com/gpu: 1
  nodeSelector:
    accelerator: nvidia-tesla-p100

  • 基于集合的选择方式

    Set-based 标签选择器可以根据标签名的一组值进行筛选。支持的操作符有三种:

    in



    notin



    exists

    。例如:
# 选择所有的包含 `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 有些对象支持基于集合的选择方式


Job



Deployment



ReplicaSet



DaemonSet

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

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


matchLabels

是一个

{key,value}

组成的

map



map

中的一个

{key,value}

条目相当于

matchExpressions

中的一个元素,其

key



map



key



operator



In



values

数组则只包含

value

一个元素。

matchExpression

等价于基于集合的选择方式,支持的

operator



In



NotIn



Exist

s 和

DoesNotExist

。当

operator



In



NotIn

时,

values

数组不能为空。所有的选择条件都以 AND 的形式合并计算,即所有的条件都满足才可以算是匹配。



2.4 注解

annotation

metadata:
  annotations:
    key1: value1
    key2: value2



2.5 字段选择器

所有的对象都支持两个字段是

metadata.name



metadata.namespace

,在字段选择器中使用不支持的字段,将报错,可以指定多个字段选择器,用



隔开

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



2.6 容器生命周期事件处理

postStart



preStop

处理程序

  • 创建

    lifecycle-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      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

的状态,通常的原因是

master



Pod

所在节点之间的通信故障



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

  • 如果容器中的进程在碰到问题时可以自己 crash,您并不需要执行健康检查;

    kubelet

    可以自动的根据

    Pod



    restart 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: nginx
      image: nginx:latest
      ports:
        - containerPort: 80
      volumeMounts:
        - name: workdir
          mountPath: /usr/share/nginx/html
  initContainers:
    - name: install
      image: nginx
      command:
        - "/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: workdir
          mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
    - name: workdir
      emptyDir: {}


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

中,只能指定

maxUnavailable



minAvailable

中的一个。

maxUnavailable

只能应用到那些有控制器的

Pod

上。

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

使用

minAvailable

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

使用

maxUnavailable

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

查看

pdb

kubectl get pdb



2.10 控制器 –

ReplicaSet (RS)


kubernetes



ReplicaSet

用来维护一个数量稳定的

Pod

副本集合,可以保证某种定义一样的

Pod

始终有指定数量的副本数在运行。



2.10.1

ReplicaSet

的工作方式


  • selector

    : 用于指定哪些

    Pod

    属于该

    ReplicaSet

    的管辖范围

  • replicas

    : 副本数,用于指定该

    ReplicaSet

    应该维持多少个

    Pod

    副本

  • template



    Pod

    模板,在

    ReplicaSet

    使用

    Pod

    模板的定义创建新的

    Pod


ReplicaSet

控制器将通过创建或删除

Pod

,以使得当前

Pod

数量达到

replicas

指定的期望值,通过

selector

字段的定义,识别哪些

Pod

应该由其管理。如果

Pod

没有

ownerReference

字段,或者

ownerReference

字段指向的对象不是一个控制器,且该

Pod

匹配了

ReplicaSet



selector

,则该

Pod



ownerReference

将被修改为 该

ReplicaSet

的引用(包括不是由该RS创建的

Pod

)。



2.10.2 何时使用

ReplicaSet


ReplicaSet

用来维护一个数量稳定的

Pod

副本集合。

Deployment

是一个更高级别的概念,可以管理

ReplicaSet

,并提供声明式的更新,以及其他的许多有用的特性。因此,推荐用户总是使用

Deployment

,而不是直接使用

ReplicaSet

,除非您需要一些自定义的更新应用程序的方式,或者您完全不更新应用。

示例:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: nginx
        image: nginx



2.10.3

ReplicaSet

的定义

与其他

Kubernetes

对象一样,

ReplicaSet

需要的字段有:


apiVersion

:apps/v1


kind

:始终为

ReplicaSet



metadata



spec



ReplicaSet

的详细定义




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



删除

ReplicaSet



Pod

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-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

在这个例子中:

  • 将创建一个名为

    nginx-deployment



    Deployment

    (部署),名称由

    .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].image



nginx: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=nginx
	pod-template-hash=694854bbcb
  Annotations:	kubernetes.io/change-cause: image update to 1.9.1
  Containers:
   nginx:
    Image:	nginx:1.9.1
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<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 selector

    app: nginx 选择对应的 Pod,nginx 的标签为 1.7.9
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - name: nginx-port
    protocol: TCP
    port: 80
    nodePort: 32600
    targetPort: 80
  type: NodePort

  • 假如此时想发布新版本nginx 1.8.0,可以创建第二个

    Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-canary
  labels:
    app: nginx
    track: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      track: canary
  template:
    metadata:
      labels:
        app: nginx
        track: canary
    spec:
      containers:
      - name: nginx
        image: 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: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi



2.13 控制器 –

DaemonSet



2.14

Service



2.14.1

Service

概述

  • 为何需要

    Service




    Kubernetes



    Pod

    是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用

    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: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

  • Kubernetes 将为该

    Service

    分配一个 IP 地址(

    ClusterIP



    集群内 IP

    ),供 Service Proxy 使用
  • Kubernetes 将不断扫描符合该

    selector



    Pod

    ,并将最新的结果更新到与

    Service

    同名 my-service 的

    Endpoint

    对象中。

    TIP

  • Pod

    的定义中,

    Port

    可能被赋予了一个名字,您可以在

    Service



    targetPort

    字段引用这些名字,而不是直接写端口号。这种做法可以使得您在将来修改后端程序监听的端口号,而无需影响到前端程序。

  • Service

    的默认传输协议是

    TCP

    ,您也可以使用其他 支持的传输协议。

    Kubernetes Service 中,可以定义多个端口,不同的端口可以使用相同或不同的传输协议。


多端口得

Service

可以在一个

Service

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

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      nodePort: 32080
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 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-controller
    tag: "v0.41.2"
    pullPolicy: IfNotPresent
    # www-data -> uid 101
    runAsUser: 101
    allowPrivilegeEscalation: true

  # Configures the ports the nginx-controller listens on
  containerPort:
    http: 80
    https: 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-headers
  proxySetHeaders: {}

  # 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-headers
  addHeaders: {}

  # 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 apply
  reportNodeInternalIp: 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 merged
  hostNetwork: false

  ## Use host ports 80 and 443
  ## Disabled by default
  ##
  hostPort:
    enabled: false
    ports:
      http: 80
      https: 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 metadata
  podLabels: {}
  #  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: false
    namespace: ""   # 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 configmap
    annotations: {}

  ## Allows customization of the udp-services-configmap
  ##
  udp:
    configMapNamespace: ""   # defaults to .Release.Namespace
    ## Annotations to be added to the udp config configmap
    annotations: {}

  ## 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 set
  extraEnvs: []
  # 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: linux
    ingressApp: ingress
  ## Liveness and readiness probe values
  ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
  ##
  livenessProbe:
    failureThreshold: 5
    initialDelaySeconds: 10
    periodSeconds: 10
    successThreshold: 1
    timeoutSeconds: 1
    port: 10254
  readinessProbe:
    failureThreshold: 3
    initialDelaySeconds: 10
    periodSeconds: 10
    successThreshold: 1
    timeoutSeconds: 1
    port: 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: 1

  minAvailable: 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: 90Mi
    requests:
      cpu: 100m
      memory: 90Mi

  # Mutually exclusive with keda autoscaling
  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 11
    targetCPUUtilizationPercentage: 50
    targetMemoryUtilizationPercentage: 50

  autoscalingTemplate: []
  # 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 autoscaling
  keda:
    apiVersion: "keda.sh/v1alpha1"
  # apiVersion changes with keda 1.x vs 2.x
  # 2.x = keda.sh/v1alpha1
  # 1.x = keda.k8s.io/v1alpha1
    enabled: false
    minReplicas: 1
    maxReplicas: 11
    pollingInterval: 30
    cooldownPeriod: 300
    restoreToOriginalReplicaCount: false
    triggers: []
 #     - 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 template
  customTemplate:
    configMapName: ""
    configMapKey: ""

  service:
    enabled: true

    annotations: {}
    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: true
    enableHttps: 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: 0

    ports:
      http: 80
      https: 443

    targetPorts:
      http: 80
      https: 443

    type: NodePort

    # type: NodePort
    # nodePorts:
    #   http: 32080
    #   https: 32443
    #   tcp:
    #     8080: 32808
    nodePorts:
      http: "80"
      https: "443"
      tcp:
        8080: 8080
      udp: {}

    ## 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: false
      annotations: {}

      ## 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/skins

  extraVolumeMounts: []
  ## Additional volumeMounts to the controller main container.
  #  - name: copy-portal-skins
  #   mountPath: /var/lib/lemonldap-ng/portal/skins

  extraVolumes: []
  ## 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: false
    failurePolicy: Fail
    # timeoutSeconds: 10
    port: 8443
    certificate: "/usr/local/certificates/cert"
    key: "/usr/local/certificates/key"
    namespaceSelector: {}
    objectSelector: {}

    service:
      annotations: {}
      # clusterIP: ""
      externalIPs: []
      # loadBalancerIP: ""
      loadBalancerSourceRanges: []
      servicePort: 443
      type: ClusterIP

    patch:
      enabled: true
      image:
        repository: docker.io/jettech/kube-webhook-certgen
        tag: v1.5.0
        pullPolicy: IfNotPresent
      ## Provide a priority class name to the webhook patching job
      ##
      priorityClassName: ""
      podAnnotations: {}
      nodeSelector: {}
      tolerations: []
      runAsUser: 2000

  metrics:
    port: 10254
    # if this port is changed, change healthz-port: in extraArgs: accordingly
    enabled: false

    service:
      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: 9913
      type: ClusterIP
      # externalTrafficPolicy: ""
      # nodePort: ""

    serviceMonitor:
      enabled: false
      additionalLabels: {}
      namespace: ""
      namespaceSelector: {}
      # Default: scrape .Release.Namespace only
      # To scrape all, use the following:
      # namespaceSelector:
      #   any: true
      scrapeInterval: 30s
      # honorLabels: true
      targetLabels: []
      metricRelabelings: []

    prometheusRule:
      enabled: false
      additionalLabels: {}
      # 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-shutdown

  priorityClassName: ""

## 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: false

  image:
    repository: k8s.gcr.io/defaultbackend-amd64
    tag: "1.5"
    pullPolicy: IfNotPresent
    # nobody user -> uid 65534
    runAsUser: 65534
    runAsNonRoot: true
    readOnlyRootFilesystem: true
    allowPrivilegeEscalation: false

  extraArgs: {}

  serviceAccount:
    create: true
    name:
  ## Additional environment variables to set for defaultBackend pods
  extraEnvs: []

  port: 8080

  ## Readiness and liveness probes for default backend
  ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
  ##
  livenessProbe:
    failureThreshold: 3
    initialDelaySeconds: 30
    periodSeconds: 10
    successThreshold: 1
    timeoutSeconds: 5
  readinessProbe:
    failureThreshold: 6
    initialDelaySeconds: 0
    periodSeconds: 5
    successThreshold: 1
    timeoutSeconds: 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 metadata
  podLabels: {}
  #  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: 1

  minAvailable: 1

  resources: {}
  # limits:
  #   cpu: 10m
  #   memory: 20Mi
  # requests:
  #   cpu: 10m
  #   memory: 20Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 2
    targetCPUUtilizationPercentage: 50
    targetMemoryUtilizationPercentage: 50

  service:
    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: 80
    type: ClusterIP

  priorityClassName: ""

## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266
rbac:
  create: true
  scope: false

# If true, create & use Pod Security Policy resources
# https://kubernetes.io/docs/concepts/policy/pod-security-policy/
podSecurityPolicy:
  enabled: false

serviceAccount:
  create: true
  name:

## 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: 8080
      udp: {}
  • 测试部署一个应用并暴漏出去,并且配置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-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
    - name: https
      protocol: TCP
      port: 443
      targetPort: 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请求强制路由到https
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  rules:                      # Ingress 中定义 L7 路由规则
  - host: ailuoli.cn   # 根据 virtual hostname 进行路由(请使用您自己的域名)
    http:
      paths:                  # 按路径进行路由
      - path: /nginx
        backend:
          serviceName: nginx-service  # 指定后端的 Service 为之前创建的 nginx-service
          servicePort: 80
  tls:
    - hosts:
        - ailuoli.cn
      secretName: 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: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
        readOnly: false
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
        readOnly: false
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data



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

使用

volumeMounts.subPathExpr

字段,可以通过容器的环境变量指定容器内路径。使用此特性时,必须启用

VolumeSubpathEnvExpansion


如下面的例子,该 Pod 使用

subPathExpr



hostPath

数据卷

/var/log/pods

中创建了一个目录

pod1

(该参数来自于Pod的名字)。此时,宿主机目录

/var/log/pods/pod1

挂载到了容器的 /logs 路径:

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      subPathExpr: $(POD_NAME)
      readOnly: false
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      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 引入了

PersistentVolume



PersistentVolumeClaim

两个概念,将存储管理抽象成如何提供存储以及如何使用存储两个关注点。


  • 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-server

systemctl 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.cn
  path: /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-client
  accessModes:
    - ReadWriteMany
  resources:
    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-t
  service1-dev.properties: profile=dev-1.0
kind: ConfigMap
metadata:
  creationTimestamp: "2020-12-16T03:17:38Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:ailuoli-dev.properties: {}
        f:service1-dev.properties: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-16T03:17:38Z"
  name: test-config
  namespace: test
  resourceVersion: "2387780"
  selfLink: /api/v1/namespaces/test/configmaps/test-config
  uid: 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: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:from: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-16T03:33:00Z"
  name: test-env-config
  namespace: test
  resourceVersion: "2389938"
  selfLink: /api/v1/namespaces/test/configmaps/test-env-config
  uid: 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: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:testailuoli: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-16T03:43:05Z"
  name: test-config3
  namespace: test
  resourceVersion: "2391353"
  selfLink: /api/v1/namespaces/test/configmaps/test-config3
  uid: 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: world1
  hello2: world2
kind: ConfigMap
metadata:
  creationTimestamp: "2020-12-16T06:28:51Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:hello1: {}
        f:hello2: {}
    manager: kubectl-create
    operation: Update
    time: "2020-12-16T06:28:51Z"
  name: test-config4
  namespace: test
  resourceVersion: "2414579"
  selfLink: /api/v1/namespaces/test/configmaps/test-config4
  uid: d05fa08b-44ca-4bdf-aaf4-38b47477190a



基于生成器创建

ConfigMap

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

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: test-config5
  files:
  - 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: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:ailuoli-dev.properties: {}
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2020-12-16T09:40:42Z"
  name: test-config5-245dh9m2fd
  namespace: test
  resourceVersion: "2441461"
  selfLink: /api/v1/namespaces/test/configmaps/test-config5-245dh9m2fd
  uid: 0f97cda8-0c6c-47db-9599-1287e3dea642


使用

ConfigMap

定义容器环境变量
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
          - name: test-config4
            valueFrom:
              configMapKeyRef:
                key: hello1
                name: test-config4




ConfigMap

数据添加到一个

Volume


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



2.17 管理容器资源

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      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: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"



2.18.3 污点和容忍匹配

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

  • 键(

    key

    ) 相同
  • 效果(

    effect

    )相同
  • 污点

    operator

    为:


    • Exists

      (此时污点中不该指定

      value

      )
    • 或者

      Equal

      (此时容忍的

      value

      应与污点的

      value

      相同)

      如果不指定

      operator

      ,则默认为

      Equal

特殊情况:

  • 容忍中未定义

    key

    但是定义了

    operator



    Exists

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

    effect

    但是定义了

    key

    ,Kubernetes 认为此容忍匹配所有

    effect

    .
tolerations:
- key: "key"
  operator: "Exists


effect

效果有


  • NoSchedule

  • PreferNoSchedule



    NoSchedule

    更宽容一些,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=300



node.kubernetes.io/not-ready

容忍。同样的,如果 Pod 没有

node.kubernetes.io/unreachable

容忍,Kubernetes 将自动为 Pod 添加一个

tolerationSeconds=300



node.kubernetes.io/unreachable

容忍

这类自动添加的容忍确保了 Pod 在节点发生 not-ready 和 unreachable 问题时,仍然在节点上保留 5 分钟。


DaemonSet Pod

相对特殊一些,他们在创建时就添加了不带

tolerationSeconds



NoExecute

效果的容忍,适用的污点有:


  • 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: admin
  password: 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: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:password: {}
        f:username: {}
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:type: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2020-12-24T06:45:47Z"
  name: secret-test
  namespace: test
  resourceVersion: "4249564"
  selfLink: /api/v1/namespaces/test/secrets/secret-test
  uid: 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: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      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-4
    image: busybox
    command: [ "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

都绑定了一组

Secret



Secret

可以被挂载到 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-master
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: master
      tier: backend
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: redis
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    role: master
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: master
    tier: 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



版权声明:本文为qq_41896122原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。