k8s

文章目录

  • pod
    • 容器初始化
      • InitC
      • Init容器的作用:
      • 容器探针
        • 检测探针-就绪检测
        • 检测探针-存活检测
      • 启动退出动作
      • pod phase(相位):状态
  • 资源控制器
    • Pod控制器
      • 什么是控制器
      • 控制器类型
      • ReplicationController 和 ReplicaSet
      • Deployment
      • DaemonSet
      • Job
      • CronJob
      • StatefulSet
      • Horizontal Pod Autoscaling
    • Deployment控制器
      • RS 与 RC 与 Deployment 关联
      • RS 与 Deployment 的关联
      • Deployment
      • 更新 Deployment
      • Deployment 更新策略
      • Rollover(多个rollout并行)
      • 回退 Deployment
      • 清理 Policy
    • DaemonSet
    • Job
    • CronJob
  • service
    • Service 的类型
    • VIP 和 Service 代理
    • 代理模式的分类
      • Ⅰ、userspace 代理模式
      • Ⅱ、iptables 代理模式
      • Ⅲ、ipvs 代理模式
    • ClusterIP
    • Headless Service
    • NodePort
    • LoadBalancer
    • ExternalName
  • kubernetes lngress
    • 部署 Ingress-Nginx
    • Ingress HTTP 代理访问
    • Ingress HTTPS 代理访问
    • Nginx 进行 BasicAuth
    • Nginx 进行重写
  • 存储
    • configmap
      • ConfigMap 描述信息
      • ConfigMap 的创建
        • Ⅰ、使用目录创建
        • Ⅱ、使用文件创建
        • Ⅲ、使用字面值创建
        • Ⅳ、yaml文件创建
      • Pod 中使用 ConfigMap
        • Ⅰ、使用 ConfigMap 来替代环境变量
        • Ⅱ、用 ConfigMap 设置命令行参数
        • Ⅲ、通过数据卷插件使用ConfigMap
          • Ⅰ、挂载configmap中所有key/value
          • Ⅱ、挂载指定文件,自定义文件名
          • Ⅲ、文件分别挂载到指定目录,自定义文件名
          • 小结
      • ConfigMap 的热更新
    • Secret
      • Secret 存在意义
      • Service Account
      • Opaque Secret
        • Ⅰ、创建说明
        • Ⅱ、使用方式
      • kubernetes.io/dockerconfigjson
    • volume
      • 背景
      • 卷的类型
      • emptyDir
      • hostPath
    • PV
      • 概念
      • PV 访问模式
      • 回收策略
      • 状态
      • 持久化演示说明 - NFS
        • Ⅰ、安装 NFS 服务器
        • Ⅱ、部署 PV
        • Ⅲ、创建服务并使用 PVC
      • 关于 StatefulSet
  • k8s命令
  • DTR(镜像库)
  • 问题处理
  • k8s命令
  • DTR(镜像库)
  • 问题处理

pod

  • 资源清单
    • 明称空间级别
      • pod
    • 集群级别
      • role、namespace、node、role、clusterRole、roleBinding、ClusterRoleBinding
    • 元数据
      • HPA

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L33TYpAW-1576315971384)(E:\OneDrive\笔记\markdown\picture\image-20191118220148390.png)]

容器初始化

  • pod能有多个容器,应用运行在容器里面,但是他也有可能有一个或多个先于容器启动的Init容器。

  • init容器与普通容器非常像,除了以下两点:

    • 1.inti容器总是运行到成功完成为止
    • 2.每个init容器都必须在下一个init容器启动之前成功完成

如果pod的init容器启动失败,kubernetes会不断重启该pod,直到init容器成功为止。然而,如果pod对应的restartPolicy为Never,他不会重新启动

InitC

Pod能够具有多个容器,应用运行在容器里面,但是他也有一个或多个先于应用容器启动的Init容器。

Init容器与普通容器非常像,除了以下两点:

  • Init容器总是运行到成功完成为止
  • 每个Init容器都必须在下一个Init容器启动之前成功完成

如果Pod的Init容器容器失败,Kubernetes会不断重启该Pod,直到Init容器启动成功为止。然而,如果Pod对应的restartPolicy为Never,他不会重新启动。

Init容器的作用:

因为Init容器具有与应用程序容器分离的单独镜像,所以他们的启动相关代码具有如下优势:

  • 他们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用容器镜像中包含这些实用工具的
  • 他们可以包含实用工具或定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要FROM另一个镜像,只需要在安装过程中实用类似sed、awk、python、dig这样的工具
  • 应用程序镜像可以分离出创建和部署的角色,而没有必要联合他们构件一个单独的镜像
  • Init容器使用Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,他们能够具有访问Secrit的权限,而应用程序容器则不能。
  • 他们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以Init容器能够提供一种简单的阻塞或延迟应用容器的启动的方法,知道满足了一组先决条件。

特殊说明:

  • Init容器具有应用容器的所有字段。除了readinessProbe,因为Init容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行
  • 在Pod中的每个app和Init容器的名称必须唯一;与任何其他容器共享同一个名称,会在验证时抛出错误。

init模板:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh','-c','echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh','-c','until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh','-c','until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
apiVersion: v1
kind: Service
metadata:
 name: myservice
spec:
 ports:
   - protocol: TCP
     port: 80
     targetPort: 1111
---
apiVersion: v1
kind: Service
metadata:
 name: mydb
spec:
 ports:
   - protocol: TCP
     port: 80
     targetPort: 1112

容器探针

探针是由kubelet对容器执行的定期诊断。要执行诊断,kubelet调用由容器实现的Handler。

有三种类型的处理程序:

  1. ExecAction:在容器内执行指定命令。如果命令退出时返回码为0,则认为诊断成功。

  2. TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查。如果端口打开,则诊断被认为是成功的

  3. HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTP Get请求。如果(200<=状态码<400),则诊断被认为是成功的

    HTTP probe中可以给httpGet设置以下配置项:

    • host: 连接的主机名,默认连接到pod的ip
    • scheme: 支持的协议,http、https,默认http
    • path:访问的HTTP server的path
    • httpHeaders:自定义请求的header
    • port:访问的容器的端口名字或者端口号,端口号必须介于1和65525之间

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

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

???Probe中的精切配置,可以更精确的配置liveness和readiness检测:

  • periodSeconds: 3每3秒执行一次探测。执行探测的频率,默认是10秒,最小1秒
  • initialDelaySeconds: 3容器初始化完成后延迟3秒执行探测
  • timeoutSeconds:探测超时时间,默认1秒,最小1秒
  • successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认1,最小值是1。对于liveness必须是1
  • failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3,最小值是1

探测方式

  • 以下就绪检测和存活检测可以同时设置
  • 就绪检测和存货检测均支持以上三种检测方式
  • livenessProbe存活探针:指示容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器会受其重启策略的影响。如果容器不提供存活探针,则默认状态为Success
  • readinessProbe就绪探针:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为Success

检测探针-就绪检测

readinessProbe-httpget

apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-pod
  namespace: default
spec:
  containers:
  - name: readiness-httpget-container
    image: tomcat:v1 #镜像名称
    imagePullPolicy: Never #镜像拉去策略,IfNotPresent:本地docker没有时从仓库拉取
    readinessProbe:
      httpGet: #httpget方式的探针诊断类型
        port: 8080 #访问的端口
        path: /docs/index.html #httpget请求的路径
      initialDelaySeconds: 1 #容器初始化完成后延时几秒再探测
      periodSeconds: 3 #每3秒检测一次

检测探针-存活检测

如果不符合存活条件就销毁pod再创建新的pod,与上面的就绪检测有一定差别

livenessProbe-exec

说明:以下容器启动时会先创建一个live文件,然后过60秒再删除这个文件。存活检测每三秒检测一次这个文件是否存在,如果不存在会直接销毁容器,重新创建。最后的现象就是pod会隔段时间不断重启

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox #不写镜像tag默认是latest
    imagePullPolicy: IfNotPresent #镜像拉去策略,IfNotPresent:本地docker没有时从仓库拉取
    command: ["bin/sh","-c","touch /tmp/live;sleep 60;rm -rf /tmp/live;sleep 60"] #容器创建完后先创建一个live文件,睡60秒,删除文件再睡60秒
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/live"] #测试文件是否存在,不存在就重启
      initialDelaySeconds: 1 #容器初始化完成后延时几秒再探测
      periodSeconds: 3 #每3秒检测一次

livenessProbe-httpget

说明:启动是正常的,进到容器内删除tomcat中的webapps/docs/index.html后,就会探活失败,pod会重启。

apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: tomcat
    imagePullPolicy: IfNotPresent
    ports:
    - name: haha
      containerPort: 8080 #pod暴露的端口,此端口仅是额外的信息,对端口是否被暴露没有影响,不太理解
    livenessProbe:
      httpGet:
        port: haha #试探时发现要与上面的ports中的haha相对应才行,否则要直接写一个具体的端口号。
        path: /docs/index.html #访问的HTTP server的path
      initialDelaySeconds: 1 #容器初始化完成后延时几秒再探测
      periodSeconds: 3 #每3秒检测一次
      timeoutSeconds: 10 #超时时间

livenessProbe-tcp

说明:tcp探活探测端口9090是否有监听,本处是没有的,探测9090端口时没返回,而且超时时间设置为1秒,直接超时导致认为pod是挂的,会不断重启pod

apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp-pod
  namespace: default
spec:
  containers:
  - name: liveness-tcp-container
    image: tomcat
    imagePullPolicy: IfNotPresent
    livenessProbe:
      initialDelaySeconds: 5 #容器初始化完成后延时几秒再探测
      periodSeconds: 3 #每3秒检测一次
      timeoutSeconds: 1 #超时时间
      tcpSocket: #tcp探测端口是否开启,如果没有监听则认为容器是挂的,就会重启pod
        port: 9090

启动退出动作

也即当容器启动时要做的事情,与容器停止时要做的事情。

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-pod #pod名称
  namespace: default
spec:
  containers:
  - name: lifecycle-demo-container #容器名称
    image: tomcat
    lifecycle:
      postStart: #启动容器后的操作
        exec:
          command: ["/bin/sh","-c","echo Hello postStart handler > /home/msg.txt"]
      preStop: #停止容器前的操作
        exec:
          command: ["/bin/sh","-c","echo bye~  postStop handler > /home/msg.txt"]

pod phase(相位):状态

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

资源控制器

Pod控制器

什么是控制器

Kubernetes 中内建了很多 controller(控制器),这些相当于一个状态机,用来控制 Pod 的具体状态和行为

控制器类型

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

ReplicationController 和 ReplicaSet

ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收;

在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。ReplicaSet 跟ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector;RC已经被逐渐废弃!

Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括;

  • 定义 Deployment 来创建 Pod 和 ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续 Deployment

DaemonSet

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterdceph
  • 在每个 Node 上运行日志收集 daemon,例如fluentdlogstash
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd 、Datadog 代理、[[New Relic 代理,或 Ganglia gmond
  • New Relic 代理,或 Ganglia gmond

Job

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

CronJob

Cron Job 管理基于时间的 Job,即:

  • 在给定时间点只运行一次
  • 周期性地在给定时间点运行

使用前提条件:

当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)。对于先前版本的集群,版本 <1.8,启动 API Server时,通过传递选项--runtime-config=batch/v2alpha1=true可以开启 batch/v2alpha1API

典型的用法如下所示:

  • 在给定的时间点调度 Job 运行
  • 创建周期性运行的 Job,例如:数据库备份、发送邮件

StatefulSet

StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序

StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
  • 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
  • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
  • 有序收缩,有序删除(即从N-1到0)

Horizontal Pod Autoscaling

应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?这就有赖于Horizontal Pod Autoscaling了,顾名思义,使Pod水平自动缩放

Deployment控制器

RS 与 RC 与 Deployment 关联

RC (ReplicationController )主要的作用就是用来确保容器应用的副本数始终保持在用户定义的副本数 。即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收

Kubernetes 官方建议使用 RS(ReplicaSet ) 替代 RC (ReplicationController ) 进行部署,RS 跟 RC 没有本质的不同,只是名字不一样,并且 RS 支持集合式的 selector

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: replicaset-demo #注意名字不能有大写字母
spec:
  replicas: 3
  selector:
    matchLabels: #标签选择器
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: my-tomcat
        image: tomcat
        env:
        - name: yc #环境变量key
          value: yangche #环境变量value
        ports:
        - containerPort: 80 #容器暴露的端口

RS 与 Deployment 的关联

image-20191124193014656

Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义(declarative)方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括:

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment

Ⅰ、部署一个简单的 Nginx 应用

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
    labels:
      app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record
## --record参数可以记录命令,我们可以很方便的查看每次 revision 的变化

Ⅱ、扩容

kubectl scale deployment nginx-deployment --replicas 10

Ⅲ、如果集群支持 horizontal pod autoscaling 的话,还可以为Deployment设置自动扩展

kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80

Ⅳ、更新镜像也比较简单

#更新镜像版本,在deployment下有个名称为nginx-deployment的deployment
#为nginx的更改镜像为nginx:1.9.1
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

Ⅴ、回滚

kubectl rollout undo deployment/nginx-deployment

更新 Deployment

假如我们现在想要让 nginx pod 使用nginx:1.9.1 的镜像来代替原来的nginx:1.7.9 的镜像

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

可以使用edit 命令来编辑 Deployment

$ kubectl edit deployment/nginx-deployment
deployment "nginx-deployment" edited

查看 rollout 的状态

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out

查看历史 RS

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1564180365 3 3 0 6s
nginx-deployment-2035384211 0 0 0 36s

Deployment 更新策略

Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)

Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )

未来的 Kuberentes 版本中,将从1-1变成25%-25%

$ kubectl describe deployments

Rollover(多个rollout并行)

假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9 的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9 的 Pod,并开始创建nginx:1.9.1 的 Pod。它不会等到所有的5个nginx:1.7.9的Pod 都创建完成后才开始改变航道

回退 Deployment

kubectl set image deployment/nginx-deployment nginx=nginx:1.91
kubectl rollout status deployments nginx-deployment
kubectl get pods
kubectl rollout history deployment/nginx-deployment
kubectl rollout undo deployment/nginx-deployment
kubectl rollout undo deployment/nginx-deployment --to-revision=2 ## 可以使用 --revision参数指定
某个历史版本
kubectl rollout pause deployment/nginx-deployment ## 暂停 deployment 的更新

您可以用kubectl rollout status 命令查看 Deployment 是否完成。如果 rollout 成功完成, kubectl rollout status 将返回一个0值的 Exit Code

$ kubectl rollout status deploy/nginx
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx" successfully rolled out
$ echo $?
0

清理 Policy

您可以通过设置.spec.revisonHistoryLimit 项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了

DaemonSet

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

一般主节点不会创建,有一个污点的概念,存在污点的不会创建,具体请看解释。

  • 使用 DaemonSet 的一些典型用法:
    • 运行集群存储 daemon,例如在每个 Node 上运行 glusterdceph
    • 在每个 Node 上运行日志收集 daemon,例如fluentdlogstash
    • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: deamonset-example
  labels:
    app: daemonset
spec:
  selector:
    matchLabels:
      name: deamonset-example #此处特别注意,必须和上面的标签对应
  template:
    metadata:
      labels:
        name: deamonset-example
    spec:
      containers:
      - name: daemonset-example
        image: tomcat

Job

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

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

Example:查看日志可以查看答应的π小数点后2000位

查看日志的命令kubectl logs [pod名称] -n [namespace]

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
      - name: pi
        image: perl #通过这个镜像,运行下列命令,得到圆周率小数点后2000位
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

CronJob

  • Cron Job 管理基于时间的 Job,即:

    • 在给定时间点只运行一次
    • 周期性地在给定时间点运行
  • 使用条件

  • 当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)

  • CrondJob 本身的一些限制

  • 创建 Job 操作应该是 幂等的

  • 典型的用法

    • 在给定的时间点调度 Job 运行
    • 创建周期性运行的 Job,例如:数据库备份、发送邮件
  • CronJob Spec

    • .spec.schedule :调度,必需字段,指定任务运行周期,格式同 Cron

    • .spec.jobTemplate :Job 模板,必需字段,指定需要运行的任务,格式同 Job

    • .spec.startingDeadlineSeconds :启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限

    • .spec.concurrencyPolicy :并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:

      • Allow (默认):允许并发运行 Job
      • Forbid :禁止并发运行,如果前一个还没有完成,则直接跳过下一个
      • Replace :取消当前正在运行的 Job,用一个新的来替换

      注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。

    • .spec.suspend :挂起,该字段也是可选的。如果设置为 true ,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false

    • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit :历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 31 。设置限制的值为 0 ,相关类型的 Job 完成后将不会被保留。

Example

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cronjob-hello
            image: busybox
            args: 
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST-SCHEDULE
hello */1 * * * * False 0 <none>
$ kubectl get jobs
NAME DESIRED SUCCESSFUL AGE
hello-1202039034 1 1 49s
$ pods=$(kubectl get pods --selector=job-name=hello-1202039034 --output=jsonpath={.items..metadata.name})
$ kubectl logs $pods
Mon Aug 29 21:34:09 UTC 2016
Hello from the Kubernetes cluster
# 注意,删除 cronjob 的时候不会自动删除 job,这些 job 可以用 kubectl delete job 来删除
$ kubectl delete cronjob hello
cronjob "hello" deleted

service

Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector

image-20191201112844638
  • Service能够提供负载均衡的能力,但是在使用上有以下限制
    • 只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的

Service 的类型

Service 在 K8s 中有以下四种类型

  • ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访问该服务
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort
  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlDeWVCk-1576315971388)(E:\OneDrive\笔记\markdown\picture\image-20191201113413524.png)]

VIP 和 Service 代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是iptables 代理。 在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理

在 Kubernetes 1.14 版本开始默认使用 ipvs 代理

在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务

!为何不使用 round-robin DNS?:因为大部分客户端会对dns进行缓存,而不能自动清除缓存。

代理模式的分类

Ⅰ、userspace 代理模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOC1ABB4-1576315971389)(E:\OneDrive\笔记\markdown\picture\image-20191201114510393.png)]

Ⅱ、iptables 代理模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTuSrNPG-1576315971390)(E:\OneDrive\笔记\markdown\picture\image-20191201114449014.png)]

Ⅲ、ipvs 代理模式

这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod

与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:

  • rr :轮询调度
  • lc :最小连接数dh :目标哈希
  • sh :源哈希
  • sed :最短期望延迟nq : 不排队调度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3q9wgmn-1576315971391)(E:\OneDrive\笔记\markdown\picture\image-20191201114349614.png)]

ClusterIP

clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmWOlGQa-1576315971392)(E:\OneDrive\笔记\markdown\picture\image-20191201114635338.png)]

为了实现图上的功能,主要需要以下几个组件的协同工作:

  • apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
  • kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中
  • iptables 使用NAT等技术将virtualIP的流量转至endpoint中

Example

创建 myapp-deploy.yaml 文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: stabel
  template:
    metadata:
      labels:
        app: myapp
        release: stabel
        env: test
    spec:
      containers:
      - name: myapp
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 8080

创建 Service 信息

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80 #集群端口
    targetPort: 8080 #后端服务真实代理的是哪些端口

查询svc代理的ip端口命令:ipvsadm -Ln,必须是ipvs才行

Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kubeproxy不会处理它们,而且平台也不会为它们进行负载均衡和路由

apiVersion: v1
kind: Service
metadata:
  name: myapp-headless
  namespace: default
spec:
  selector:
    app: myapp
  clusterIP: "None"
  ports:
  - port: 80
    targetPort: 8080

别名会被写入core dns,他也是一个pod可以通过kubectl get pod -n kube-system查看,通过这个coredns可以解析myapp-headless.default.svc.cluster.local

dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10

NodePort

对外提供服务,service和pod是多对多的关系,是通过标签选择器代理到对应的pod。

nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: NodePort
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    nodePort: 9000 #开启对外服务的端口,如果不写,会随机分配一个端口,端口范围在30000-32762
    targetPort: 8080

查询流程

iptables -t nat -nvL KUBE-NODEPORTS
ipvsadm -Ln

LoadBalancer

声明:此种方式收费,看看就好

loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用cloud provider 去创建 LB 来向节点导流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeVHQVNo-1576315971393)(E:\OneDrive\笔记\markdown\picture\image-20191201162024625.png)]

ExternalName

这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.atguigu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务

apiVersion: v1
kind: Service
metadata:
  name: service-external
  namespace: default
spec:
  type: ExternalName
  externalName: www.baidu.com

当查询主机 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的DNS 服务将返回一个值 my.database.example.com 的 CNAME 记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发

如果没有dig命令可以用命令安装一个: yum install bind-utils

通过kubectl get pod -n kube-system可以查看core dns,通过corn

查询服务,注意@前有一个空格

dig -t A service-external.default.svc.cluster.local. @10.244.0.14

查询结果

;; ANSWER SECTION:
service-external.default.svc.cluster.local. 5 IN CNAME www.baidu.com.
www.baidu.com.		5	IN	CNAME	www.a.shifen.com.
www.a.shifen.com.	5	IN	A	61.135.169.121
www.a.shifen.com.	5	IN	A	61.135.169.125

kubernetes lngress

Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx

Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqZXvXL5-1576315971395)(E:\OneDrive\笔记\markdown\picture\image-20191208105917049.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3PhgWUTj-1576315971396)(E:\OneDrive\笔记\markdown\picture\image-20191208105931937.png)]

部署 Ingress-Nginx

 kubectl  apply -f mandatory.yaml
 kubectl  apply -f service-nodeport.yaml

Ingress HTTP 代理访问

deployment、Service、Ingress Yaml 文件

kind: Deployment
metadata:
  name: nginx-dm
spec:
  replicas: 2
  template:
    metadata:
      labels:
        name: nginx
      spec:
        containers:
        - name: nginx
          image: wangyanglinux/myapp:v1
          imagePullPolicy: IfNotPresent
          ports:
          - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    name: nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-test
spec:
  rules:
  - host: www1.atguigu.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80

Ingress HTTPS 代理访问

创建证书,以及 cert 存储方式

 openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
kubectl create secret tls tls-secret --key tls.key --cert tls.crt

deployment、Service、Ingress Yaml 文件

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-test
spec:
  tls:
  - hosts:
    - foo.bar.com
    secretName: tls-secret
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80

Nginx 进行 BasicAuth

yum -y install httpd
htpasswd -c auth foo
kubectl create secret generic basic-auth --from-file=auth
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
  rules:
  - host: foo2.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80

Nginx 进行重写

名称 描述
nginx.ingress.kubernetes.io/rewrite-target 必须重定向流量的目标URI
nginx.ingress.kubernetes.io/ssl-redirect 指示位置部分是否仅可访问SSL(当Ingress包含证书时默认为True) 布尔
nginx.ingress.kubernetes.io/force-ssl-redirect 即使Ingress未启用TLS,也强制重定向到HTTPS 布尔
nginx.ingress.kubernetes.io/app-root 定义Controller必须重定向的应用程序根,如果它在’/'上下文中
nginx.ingress.kubernetes.io/use-regex 指示Ingress上定义的路径是否使用正则表达式 布尔
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-test
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: http://foo.bar.com:31795/hostname.html
spec:
  rules:
  - host: foo10.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80

存储

configmap

ConfigMap 描述信息

ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象

ConfigMap 的创建

Ⅰ、使用目录创建

$ ls docs/user-guide/configmap/kubectl/
game.properties
ui.properties

$ cat docs/user-guide/configmap/kubectl/game.properties
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

$ cat docs/user-guide/configmap/kubectl/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

$ kubectl create configmap game-config --from-file=docs/user-guide/configmap/kubectl

--from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容

Ⅱ、使用文件创建

只要指定为一个文件就可以从单个文件中创建 ConfigMap

$ kubectl create configmap game-config-2 --from-file=docs/user-
guide/configmap/kubectl/game.properties

$ kubectl get configmaps game-config-2 -o yaml

--from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的

Ⅲ、使用字面值创建

使用文字值创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次,格式如下

$ kubectl create configmap special-config --from-literal=special.how=very --from-
literal=special.type=charm

$ kubectl get configmaps special-config -o yaml

Ⅳ、yaml文件创建

事先写好标准的configMap的yaml文件,然后kubectl apply -f xxx.yaml创建

Pod 中使用 ConfigMap

Ⅰ、使用 ConfigMap 来替代环境变量

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  #如果key: |,类似这样有个竖杠,则后面的内容会作为一个文件
  special.how: very
  special.type: charm
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
  - name: test-container
    image: hub.atguigu.com/library/myapp:v1
    command: [ "/bin/sh", "-c", "env" ]
    env:
    - name: SPECIAL_LEVEL_KEY #环境变量的key
      valueFrom: #环境变量的值来源
        configMapKeyRef: #指向configmap
          name: special-config #configmap的名称
          key: special.how #configmap中data中的key值
    - name: SPECIAL_TYPE_KEY
      valueFrom:
        configMapKeyRef:
          name: special-config
          key: special.type
    envFrom:
    - configMapRef:
        #这种方式会将所有configmap中data中的key作为环境变量的key
        #,value做为pod中环境变量的value
        name: env-config
  restartPolicy: Never

注意envFrom这种方式引用的环境变量的key值都来自configmap中的key值,如果key值设置为mysql.config时,进到容器内打印环境变量echo $mysql.config时会提示“bad substitution”,将configmap中的mysql.config改成mysql_config,再打印就能正常显示,所以,用此种方式引用环境变量时,必须注意变量的命名规范

Ⅱ、用 ConfigMap 设置命令行参数

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
namespace: default
data:
  special.how: very
  special.type: charm
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
  - name: test-container
    image: hub.atguigu.com/library/myapp:v1
    command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
    env:
    - name: SPECIAL_LEVEL_KEY
      valueFrom:
        configMapKeyRef:
          name: special-config
          key: special.how
    - name: SPECIAL_TYPE_KEY
      valueFrom:
        configMapKeyRef:
          name: special-config
          key: special.type
  restartPolicy: Never

Ⅲ、通过数据卷插件使用ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
  - name: test-container
    image: hub.atguigu.com/library/myapp:v1
    command: [ "/bin/sh", "-c", "cat /etc/config/special.how" ]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
    volumes:
    - name: config-volume
      configMap:
        name: special-config
  restartPolicy: Never

以下都是通过volume使用configmap,重要

创建一个configmap,基本涵盖所有方式,接下来的volume都用到此configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap-demo
  namespace: default
data:
  config1: yangche
  config2:
    username:yangche
    sex:man
    iphone:18713161789
  user.config: |
    nimeide,
    nidayede,
    ╭(╯^╰)╮

以上configmap生成后的json格式如下

[root@k8s-master01 configmap]# kubectl get cm configmap-demo -o json
{
    "apiVersion": "v1",
    "data": {
        "config1": "yangche",
        "config2": "username:yangche sex:man iphone:18713161789",
        "user.config": "nimeide,\nnidayede,\n╭(╯^╰)╮\n"
    },
    "kind": "ConfigMap",
    "metadata": {
        "annotations": {
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"config1\":\"yangche\",\"config2\":\"username:yangche sex:man iphone:18713161789\",\"user.config\":\"nimeide,\\nnidayede,\\n╭(╯^╰)╮\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"configmap-demo\",\"namespace\":\"default\"}}\n"
        },
        "creationTimestamp": "2019-12-13T07:13:08Z",
        "name": "configmap-demo",
        "namespace": "default",
        "resourceVersion": "696614",
        "selfLink": "/api/v1/namespaces/default/configmaps/configmap-demo",
        "uid": "04a34b9a-665c-48d6-8f2a-32a7f0c9a355"
    }
}
  • 注意
    • config1:这种方式后面没有“|”,文件生成后没有回车符,username左面虽然有两个空格,但是最终是顶格,username:yangche与sex:man之间没有回车符,有两个空格间距,最终是个字符串
    • user.config: |:这种方式后面都是字符串,最前面顶格,并且每一行都有回车符
Ⅰ、挂载configmap中所有key/value
apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap-demo1
spec:
  containers:
  - name: pod-configmap-demo
    image: tomcat
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      mountPath: /temp/config
  volumes:
  - name: config-volume
    configMap:
      name: configmap-demo

查看容器中的所有内容,进入到/temp/config目录中可以看到,config文件夹下,以每一个key为文件名,value为值创建了多个文件,如下所示:

root@pod-configmap-demo1:/temp/config# ls -lrt
total 0
lrwxrwxrwx 1 root root 18 Dec 13 07:30 user.config -> ..data/user.config
lrwxrwxrwx 1 root root 14 Dec 13 07:30 config2 -> ..data/config2
lrwxrwxrwx 1 root root 14 Dec 13 07:30 config1 -> ..data/config1
root@pod-configmap-demo1:/temp/config# cat user.config 
nimeide,
nidayede,
╭(╯^╰)╮
root@pod-configmap-demo1:/temp/config# cat config2
username:yangche sex:man iphone:18713161789root@pod-configmap-demo1:/temp/config# cat config1
yangcheroot
Ⅱ、挂载指定文件,自定义文件名

如果不想以key名作为配置文件名,可以引入items字段。在其中逐个指定要用相对路径path替换的key。

apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap-demo2
spec:
  containers:
  - name: pod-configmap-demo
    image: tomcat
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      mountPath: /temp/config
  volumes:
  - name: config-volume
    configMap:
      name: configmap-demo
      items:
      - key: config1
        path: config1abc
      - key: user.config
        path: userconfigabc

以下结果可以看到,进入/temp/config后会有两个文件,且文件名是以上yaml文件中定义的path作为文件名。上面只引用了两个文件,所以此处只生成了两个文件

root@pod-configmap-demo2:/temp/config# ls -lrt
total 0
lrwxrwxrwx 1 root root 20 Dec 13 07:46 userconfigabc -> ..data/userconfigabc
lrwxrwxrwx 1 root root 17 Dec 13 07:46 config1abc -> ..data/config1abc
root@pod-configmap-demo2:/temp/config# cat userconfigabc 
nimeide,
nidayede,
╭(╯^╰)╮
root@pod-configmap-demo2:/temp/config# cat config1abc 
yangche
Ⅲ、文件分别挂载到指定目录,自定义文件名

一个configmap中有三个文件,挂载的时候想分别挂载到不同的目录,且自定义文件的名称可以通过subPath完成

apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap-demo3
spec:
  containers:
  - name: pod-configmap-demo
    image: tomcat
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume #与volumes中的name相对应
      mountPath: /temp/config1/a.config #最后a.config是最终生成的文件名
      subPath: config1 #指向configmap的key
    - name: config-volume 
      mountPath: /temp/config1/b.config
      subPath: config2
    - name: config-volume
      mountPath: /temp/config2/c.config
      subPath: user.config
  volumes:
  - name: config-volume
    configMap:
      name: configmap-demo

在容器中生成的目录与文件如下结构,需要注意的是:Ⅲ这种方式,最终生成的是具体的文件,Ⅰ、Ⅱ形成的是个链接,链接到..data/

# 目录与文件结构如下
/temp
	/config1
		a.config #文件
		b.config #文件
	/config2
  		c.config #文件
  • 使用Ⅲ方式指定mountPath: /temp/config/abc_confg注意一下几种情况
    • 当/temp/config/abc_confg在文件中不存在时(/temp/config/abc_confg与/temp/config/abc_confg/无异),会自动创建abc_confg文件并把value写进去
    • 当/temp/config/abc_config在容器中存在且是个文件时,里面的内容会被configmap覆盖
    • 当/temp/config/abc_config在容器中存在且是个文件夹时,无论写/temp/config/abc_config还是写/temp/config/abc_config/都会报错,因为Ⅲ这种方式最后必须是个文件,已存在同名的文件夹会产生冲突。
小结
  • 使用Ⅰ、Ⅱ两种方式挂载configmap,如果mountPath指定的路径在容器中已经存在,则会覆盖该目录下的所有文件,从而可能会因为丢失一些文件导致容器启动失败
  • 使用Ⅲ方式挂载configmap,如果mountPath指定的文件在容器中已经存在,不会覆盖该目录下所有文件,只会覆盖该目录下的同名文件
  • 如果mountPath指定的路径在容器中不存在,三种方式都会自动在容器中创建该目录

ConfigMap 的热更新

apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: hub.atguigu.com/library/myapp:v1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
        volumes:
        - name: config-volume
          configMap:
            name: log-config
$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` cat
/etc/config/log_level
INFO

修改 ConfigMap

$ kubectl edit configmap log-config

修改 log_level 的值为 DEBUG 等待大概 10 秒钟时间,再次查看环境变量的值

$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` cat /tmp/log_level
DEBUG

ConfigMap 更新后滚动更新 Pod

更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新

$ kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations":
{"version/config": "20190411" }}}}}'

这个例子里我们在 .spec.template.metadata.annotations 中添加 version/config ,每次通过修改
version/config 来触发滚动更新

!!! 更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新
  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新{}

Secret

Secret 存在意义

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。Secret 可以以 Volume 或者环境变量的方式使用

Secret 有三种类型:

  • Service Account :用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的/run/secrets/kubernetes.io/serviceaccount 目录中
  • Opaque :base64编码格式的Secret,用来存储密码、密钥等
  • kubernetes.io/dockerconfigjson :用来存储私有 docker registry 的认证信息

Service Account

Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的/run/secrets/kubernetes.io/serviceaccount 目录中,了解即可。

$ kubectl run nginx --image nginx
deployment "nginx" created

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-3137573019-md1u2 1/1 Running 0 13s

$ kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token

Opaque Secret

Ⅰ、创建说明

Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式:

$ echo -n "admin" | base64
YWRtaW4=

$ echo -n "12345" | base64
MTIzNDUK

secrets.yml

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: MTIzNDUK
  username: YWRtaW4=

Ⅱ、使用方式

1、将 Secret 挂载到 Volume 中

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: seret-test
  name: seret-test
spec:
  volumes:
  - name: secrets
    secret:
      secretName: mysecret
  containers:
  - image: hub.atguigu.com/library/myapp:v1
    name: db
    volumeMounts:
    - name: secrets
      mountPath: "
      readOnly: true

2、将 Secret 导出到环境变量中

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pod-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: pod-deployment
    spec:
      containers:
      - name: pod-1
        image: hub.atguigu.com/library/myapp:v1
        ports:
        - containerPort: 80
        env:
        - name: TEST_USER
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: TEST_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password

kubernetes.io/dockerconfigjson

使用 Kuberctl 创建 docker registry 认证的 secret

$ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --
docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret "myregistrykey" created.

在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey

apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
  - name: foo
    image: roc/awangyang:v1
  imagePullSecrets:
  - name: myregistrykey

volume

容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题

背景

Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这
个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes
支持多种类型的卷,Pod 可以同时使用任意数量的卷

卷的类型

Kubernetes 支持以下类型的卷:

  • awsElasticBlockStore azureDisk azureFile cephfs csi downwardAPI emptyDir
  • fc flocker gcePersistentDisk gitRepo glusterfs hostPath iscsi local nfs
  • persistentVolumeClaim projected portworxVolume quobyte rbd scaleIO secret
  • storageos vsphereVolume

emptyDir

当一个pod中有多个容器时,这几个pod可以共享此卷

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除

emptyDir 的用法有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件
apiVersion: v1
kind: Pod
metadata:
  name: empty-dir-volume-pod
spec:
  containers:
  - image: tomcat
    name: container-1
    volumeMounts:
    - mountPath: /one
      name: cache-volume
  - image: nginx
    name: container-2
    volumeMounts:
    - mountPath: /two
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

**说明:**以上创建之后container-1的/one目录和container-2的/two目录是相互共享的,也就是在container-1中如果在/one目录放数据会在container-2的/two目录中看到。

如果一个pod中有多个容器,那么这多个容器的镜像不能相同,我试验时如果拉取的镜像相同第二个会报错CrashLoopBackOff

hostPath

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

hostPath 的用途如下:

  • 运行需要访问 Docker 内部的容器;使用 /var/lib/dockerhostPath
  • 在容器中运行 cAdvisor;使用 /dev/cgroupshostPath
  • 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

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

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

使用这种卷类型时请注意,因为:

  • 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同
  • 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  • 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath
  • 在主机上的文件或目录等要提前创建,如果类型为空在主机上的目录会自动创建,如果主机上的目录创建又删除,则容器挂载的卷将永久丢失,即使在主机再创建也于事无补。pod内的目录会自动创建

yaml示例:

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod-demo
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /demo
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

PV

概念

PersistentVolume (PV)

是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统

PersistentVolumeClaim (PVC)

是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)

静态 pv

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

动态pv

当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses :PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置

要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器]。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作

持久化卷声明的保护

PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失

当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的
删除将被推迟,直到 PVC 不再被任何 pod 使用

持久化卷类型

PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:

  • GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC (Fibre Channel)
  • FlexVolume Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
  • Cinder (OpenStack block storage) Glusterfs VsphereVolume Quobyte Volumes
  • HostPath VMware Photon Portworx Volumes ScaleIO Volumes StorageOS

持久卷演示yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
  - hard
  - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

PV 访问模式

PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的,用来描述特定功能的访问模式

  • ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  • ReadWriteMany——该卷可以被多个节点以读/写模式挂载

在命令行中,访问模式缩写为:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany

不同的volume类型支持的访问模式也不相同:

Volume 插件 ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStoreAWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
FC -
FlexVolume -
Flocker - -
GCEPersistentDisk -
Glusterfs
HostPath - -
iSCSI -
PhotonPersistentDisk - -
Quobyte
NFS
RBD -
VsphereVolume - -(当 pod 并列时有效)
PortworxVolume -
ScaleIO -
StorageOS - -

回收策略

  • Retain(保留)——手动回收
  • Recycle(回收)——基本擦除( rm -rf /thevolume/*
  • Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)
    将被删除

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

状态

卷可以处于以下的某种状态:

  • Available(可用)——一块空闲资源还没有被任何声明绑定
  • Bound(已绑定)——卷已经被声明绑定
  • Released(已释放)——声明被删除,但是资源还未被集群重新声明
  • Failed(失败)——该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

持久化演示说明 - NFS

Ⅰ、安装 NFS 服务器

yum install -y nfs-common nfs-utils rpcbind
mkdir /nfsdata
chmod 666 /nfsdata
chown nfsnobody /nfsdata
cat /etc/exports
/nfsdata *(rw,no_root_squash,no_all_squash,sync)
systemctl start rpcbind
systemctl start nfs

Ⅱ、部署 PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /data/nfs
    server: 10.66.66.10

Ⅲ、创建服务并使用 PVC

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
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
    labels:
      app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "nfs"
      resources:
        requests:
          storage: 1Gi

关于 StatefulSet

  • 匹配 Pod name ( 网络标识 ) 的模式为: ( s t a t e f u l s e t 名 称 ) − (statefulset名称)- (statefulset)(序号),比如上面的示例:web-0,web-1,web-2
  • StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless servername),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为: ( s e r v i c e n a m e ) . (servicename). (servicename).(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Podname=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
  • 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:

  • 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
  • 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
  • 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。

StatefulSet使用场景:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  • 稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
  • 有序部署,有序扩展,基于 init containers 来实现。
  • 有序收缩。

k8s命令

kubectl explain [资源名称] 查看资源的完整模板信息,如果想查看资源的子资源,可以用点的方式查看

kubectl get pod --show-labels 查看pod时展示pod的标签

DTR(镜像库)

DTR全称是Docker Trusted Registry。为Docker自带的

问题处理

  • The connection to the server 192.168.73.200:6443 was refused - did you specify the right host or port?

      1. 运行 systemctl status kubelet 查看kubelet状态,果然kubelet服务失败,也就是并不是API Server出错导致查不到Pod,而不是kubelet出错无法向API Server注册节点

      2. *运行journalctl -xefu kubelet 查看systemd日志发现真正的错误原因是 :kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"

      3. 此处有两种修改方式,可以修改docker也可以修改kubelet,我本处修改的docker,因为我知道以前我动过我的docker的配置文件。vim /etc/docker/daemon.json,最初的docker的内容为:

        { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "registry-mirrors": ["https://d3b5ib7d.mirror.aliyuncs.com"] }

        最后的registry-mirrors是我的阿里云的快速通道配置。每个人不同

      4. 设置完毕后我们重新加载配置文件,并重启docker。

        systemctl daemon-reload

        systemctl restart docker

会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化

  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为: ( s e r v i c e n a m e ) . (servicename). (servicename).(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Podname=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
  • 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:

  • 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
  • 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
  • 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。

StatefulSet使用场景:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  • 稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
  • 有序部署,有序扩展,基于 init containers 来实现。
  • 有序收缩。

k8s命令

kubectl explain [资源名称] 查看资源的完整模板信息,如果想查看资源的子资源,可以用点的方式查看

kubectl get pod --show-labels 查看pod时展示pod的标签

DTR(镜像库)

DTR全称是Docker Trusted Registry。为Docker自带的

问题处理

  • The connection to the server 192.168.73.200:6443 was refused - did you specify the right host or port?

      1. 运行 systemctl status kubelet 查看kubelet状态,果然kubelet服务失败,也就是并不是API Server出错导致查不到Pod,而不是kubelet出错无法向API Server注册节点

      2. *运行journalctl -xefu kubelet 查看systemd日志发现真正的错误原因是 :kubelet cgroup driver: "systemd" is different from docker cgroup driver: "cgroupfs"

      3. 此处有两种修改方式,可以修改docker也可以修改kubelet,我本处修改的docker,因为我知道以前我动过我的docker的配置文件。vim /etc/docker/daemon.json,最初的docker的内容为:

        { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "registry-mirrors": ["https://d3b5ib7d.mirror.aliyuncs.com"] }

        最后的registry-mirrors是我的阿里云的快速通道配置。每个人不同

      4. 设置完毕后我们重新加载配置文件,并重启docker。

        systemctl daemon-reload

        systemctl restart docker

你可能感兴趣的:(kubernetes)