• k8s——知识点回顾(1)



    怎么理解云原生

    用任意的编程语言(例如Java、python、go、c++等)开发的应用称为原生应用。这些应用开发完之后需要将其部署,可以选择将部署到云上。

    因此,可以将云原生简洁的概括为原生应用部署到云上的整个过程,以及云上的一系列解决方案。

    云平台

    ● 环境统一
    ● 按需付费
    ● 即开即用
    ● 稳定性强
    ● …
    国内常见云平台:
    ● 阿里云、百度云、腾讯云、华为云、青云…
    国外常见云平台:
    ● 亚马逊 AWS、微软 Azure …

    公有云

    购买云服务提供商的公共资源。
    公有云是最常见的云计算部署类型。公有云资源(例如服务器和存储空间)由第三方云服务提供商拥有和运营,这些资源通过 Internet 提供。
    公有云的优势:
    ● 成本更低:无需购买硬件或软件,仅对使用的服务付费。
    ● 无需维护:维护由服务提供商提供。
    ● 近乎无限制的缩放性:提供按需资源,可满足业务需求。
    ● 高可靠性:具备众多服务器,确保免受故障影响。
    高可靠新衡量指标:
    可用性:N个9,(1个9就是90%,2个9就是99%。。。)
    全年的故障时间:365243600*(100%-99.999%)故障时间越短,服务越稳定可靠

    私有云

    私有云相比于公有云,安全性更可控
    私有云由专供一个企业或组织使用的云计算资源构成。私有云可在物理上位于组织的现场数据中心,也可由第三方服务提供商托管。但是,在私有云中,服务和基础结构始终在私有网络上进行维护,硬件和软件专供组织使用。
    私有云优势:
    ● 灵活性更强:组织可自定义云环境以满足特定业务需求。
    ● 控制力更强:资源不与其他组织共享,因此能获得更高的控制力以及更高的隐私级别。
    ● 可伸缩性更强:与本地基础结构相比,私有云通常具有更强的可伸缩性。

    没有一种云计算类型适用于所有人。多种不同的云计算模型、类型和服务已得到发展,可以满足组织快速变化的技术需求。

    部署云计算资源有三种不同的方法:公共云、私有云和混合云(一部分业务托管到第三方公有云平台,核心业务运行在私有云上更易于控制)。采用的部署方法取决于业务需求。

    安全组:防火墙相关的端口设置
    VPC:专有网络、私有网络(划分网段)

    在这里插入图片描述
    不同的VPC之间是相互隔离。即使不同的VPC的网段一样,但是彼此是不通的。

    kubernetes是什么

    应用开发完成之后有以下几种部署方式:

    • 传统方式
      将若干应用直接部署到某一台机器上,由于应用之间没有隔离性,可能由于某些应用内存泄漏,导致内存可用数量减少,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者其他应用程序无法正常运行,安全性弱。
      在这里插入图片描述

    • 虚拟化方式
      在物理机上通过虚拟化技术开通几个虚拟机,把应用部署到虚拟机上。虚拟机之间是相互隔离的,安全性强,但是虚拟机笨重。
      在这里插入图片描述

    • docker容器
      在物理机上安装docker容器化运行环境,应用就能以容器的方式运行起来。容器是非常轻量级的。容器之间也是相互隔离的。
      在这里插入图片描述

    docker容器化技术是目前主流技术,但是考虑这样一个问题,为了部署一个完整的大项目,例如,商城等等。项目中服务模块很多。通过微服务开发了很多小项目,每个小项目都以容器化方式部署。而且需要将这些小项目分配到不同的服务器上运行,每个服务器上可能运行的容器数量也不一样。进而,不同主机上的这些许多的容器管理起来就比较麻烦。因此,十分需要一个统一管理大量容器的系统,即容器编排系统。这正是kubernetes的功能。

    Kubernetes 提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移、部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary 部署。

    kubernetes架构

    工作方式

    k8s集群由M个master节点+N个worker节点组成。M大于等于1,N大于等于1。

    组件架构

    在这里插入图片描述
    为了更好理解上述架构图,将k8s集群理解为某个集团。该集团需要不断开发项目。真正实践研发项目的可能是该集团的分厂,这些分厂称为节点node。(集群中的节点node是一些服务器,是worker)control plane是集团的总部,是控制中心。由集团总部发布命令,决定哪些分厂做哪些事。(集群中的control plane是master节点)。集团总部里的controller manager是集团的决策者,决定集团要做什么事。集团运行产生的核心数据保存在etcd,是集团的资料库。集团决策者不亲自将决策文件存储到etcd,是通过秘书api帮助其存储,api-server可以理解为集团的秘书部。秘书负责干杂活。scheduler调度者按照决策者制定的决策,即集团要干什么事,并根据各个分厂(node节点)的配置数据,选择出最合适的分厂。同时,调度者scheduler制定的调度策略也要通过秘书部(api-server)记录到资料库etcd中。最终,由秘书部(api-server)根据调度者的调度策略来通知某个分厂完成工作。具体的说,秘书部是通过通知分厂的厂长kubelet完成的。厂长kubelet是用于控制分厂做什么并监控该分厂(节点node)的运行情况的,厂长能管理项目的起停以及销毁。集团的各个分厂(node)是不清楚集团的决策者controller manager、scheduler调度者的,只能和秘书部api-server交互。至于kube-proxy,可以考虑这样一种情况:突然有领导要来视察该集团的A项目,但是领导怎么知道A项目是由哪个分厂完成的呢?由kube-proxy告知领导该项目由哪个分成完成。kube-proxy相当于分厂的门卫,它清楚集团完成了哪些项目,哪个分厂有哪些设施等(node节点的kube-proxy之间经常会同步信息)。

    上图中还有一个cloud controller manager,云决策者,用于决策要不要和外部厂商进行合作。相当于外联部。

    值得注意的是,分厂node联系集团总部controller plane,和调度者scheduler联系总部controller plane都需要通过秘书部api-server(访问集团总部的入口是秘书部api-server,)。访问各个分厂的项目,就需要访问门卫(kube-proxy)。
    kube-proxy在集群中控制网络访问,kube-proxy能控制所有应用的访问。
    api-server在集群中用于控制master与worker之间、或者master节点组件之间的交互访问。这就是外观模式或者蒙面模式。即api-server隐藏了集团总部的核心。要访问集团总部,只能通过api-server进行,而且集团总部的决策需要靠api-server来传达。

    1.集群内所有组件的交互都是通过api-server进行的。
    2.集群内的网络访问都是通过kube-proxy进行的。
    3.集群内运行的所有应用程序都需要有一个容器运行时环境(docker或者其他)。
    4.集群内所有节点都需要有一个“监工”kubelet,负责监控节点上部署的所有应用,并及时给api-server汇报状况信息。然后,api-server反馈给controller manager.进一步决策层再进行调度、决策。

    控制平面组件

    控制平面的组件对集群做出全局决策(比如调度),以及检测和响应集群事件(例如,当不满足部署的 replicas 字段时,启动新的 pod)。
    控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器。

    kube-apiserver

    API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。
    Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

    etcd

    etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。
    您的 Kubernetes 集群的 etcd 数据库通常需要有个备份计划。
    详细信息参考:https://etcd.io/docs/

    kube-scheduler

    调度器
    控制平面组件,负责监视新创建的、未指定运行节点(node)的 Pods,选择节点让Pods在上面运行。
    调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

    kube-controller-manager

    在主节点上运行 控制器 的组件。
    从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。
    这些控制器包括:
    ● 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应
    ● 任务控制器(Job controller): 监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成
    ● 端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)
    ● 服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌

    cloud-controller-manager

    云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器允许您链接集群到云提供商的应用编程接口中, 并把和该云平台交互的组件与只和您的集群交互的组件分离开。
    cloud-controller-manager 仅运行特定于云平台的控制回路。 如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器。
    与 kube-controller-manager 类似,cloud-controller-manager 将若干逻辑上独立的 控制回路组合到同一个可执行文件中,供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。
    下面的控制器都包含对云平台驱动的依赖:
    ● 节点控制器(Node Controller): 用于在节点终止响应后检查云提供商以确定节点是否已被删除
    ● 路由控制器(Route Controller): 用于在底层云基础架构中设置路由
    ● 服务控制器(Service Controller): 用于创建、更新和删除云提供商负载均衡器

    Node 组件

    节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。

    kubelet

    一个在集群中每个节点(node)上都运行的代理。 它保证容器(containers)都 运行在 Pod 中。
    kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。

    kube-proxy

    kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。
    kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。
    如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。

    kubernetes集群安装

    有一种说法k8s要放弃使用docker了,那为什么k8s现在还在基于docker?
    docker是提供容器化运行环境的,而k8s需要这个环境。k8s之前为了适配docker,写了很多代码,如果更换别的容器化运行环境比较麻烦。因此k8s希望docker能适配自己。所以,docker未来会提升自己的兼容性,使其更完美。

    docker是容器引擎,k8s是容器编排系统。k8s是docker的高级化产品。

    1. 首先,每个节点都必须先安装docker
    2. 每个节点安装kubelet(各个分厂的厂长)、kubectl(命令行,发布命令,实际上只需要master节点安装即可)、kubeadm(帮助管理集群,集群搭建完之后可以卸载)
      只有docker和kubelet是每个节点都必须的,
    3. 选中一个节点用作master,然后使用kubeadm init初始化master节点,之后kubelet会安装master节点所需的各种组件
    4. 其他节点为worker,使用kubeadm join加入集群。厂长kubelet会安装kube-proxy组件

    在这里插入图片描述
    具体安装过程参照之前的博客,k8s集群搭建。

    k8s集群的自我修复能力

    假设集群上运行了很多应用,但是突然节点宕机了。在之后节点能正常工作了,之前运行的服务也能自动恢复,不用人工干涉。

    安装k8s的可视化界面dashboard

    kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.3.1/aio/deploy/recommended.yaml
    
    或者先把该资源下载,再应用
    wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.3.1/aio/deploy/recommended.yaml
    然后
    kubectl apply -f ...
    
    #设置访问端口
    kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
    把其中的type: ClusterIP 改为 type: NodePort
    
    
    kubectl get svc -A |grep kubernetes-dashboard
    ## 找到端口,在安全组放行
    
    访问dashboard方式:
     https://集群任意节点IP:端口      
     例如https://139.198.165.238:32759
    
    # 创建访问账号
    #编写一个yaml文件; vi dash.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: admin-user
      namespace: kubernetes-dashboard
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: admin-user
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: cluster-admin
    subjects:
    - kind: ServiceAccount
      name: admin-user
      namespace: kubernetes-dashboard
      
    #并应用该yaml文件
    kubectl apply -f dash.yaml
    
    
    #使用令牌访问,首先获取访问令牌
    kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
    
    建议将token先保存下来,之后需要经常复制该token,登录dashboard
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    kubernetes资源

    创建资源的方式

    • 使用yaml文件创建
    • 使用命令行创建

    namespace命名空间

    用于对集群资源进行隔离划分,也可理解为对资源分组
    默认只隔离资源,不隔离网络

    #查看默认default命名空间里的应用pods
    kubectl get pods 
    #查看命名空间kube-system里的应用pods
    kubectl get pods -n kube-system
    #查看所有命名空间下的所有资源pods
    kubectl get pods -A
    #查看所有的命名空间
    kubectl get ns
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    使用命令行的方式创建namespace# 创建命名空间test
    kubectl create ns test
    #删除命名空间test
    kubectl delete ns test
    # 删除命名空间会把该命名空间下的所有资源都删除
    #默认的命名空间default不能删除 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    使用资源配置yaml文件的方式创建namespace:
    vim test.yaml
    apiVersion: v1   (相当于版本号)
    kind: Namespace   (资源类型)
    metadata:      (元数据)
      name: test
      
    生成命名空间
    kubectl apply -f test.yaml
    删除命名空间
    kubectl delete ns test
    或者(一般用配置文件创建的资源也用配置文件删除)
    kubectl delete -f test.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    pod

    docker中运行的应用称为容器,而在k8s中称为pod。
    pod是运行中的一组容器,pod是k8s中应用的最小单位。

    容器与pod的区别:
    容器是单个的应用,而一个pod内是可以运行多个容器。只有pod内的所有容器应用都正常工作了,该pod才能正常工作。

    pod与pod之间是相互隔离的。
    pod中运行了一组容器,每个pod内的容器能组合形成一个完整的应用。

    例如下图中,一个pod内运行了两个容器,其中一个容器负责从远程下载文件,另一个容器是web服务器,如果外部访问该web服务器,web服务器就能将另一个容器下载的文件给展示出来。
    在这里插入图片描述

    使用命令行创建一个pod 示例:
    kubectl run mynginx --image=nginx
    # 查看default名称空间的Pod
    kubectl get pod 
    # 描述
    kubectl describe pod mynginx(Pod名称)
    # 删除
    kubectl delete pod mynginx(Pod名字)
    # 查看Pod的运行日志
    kubectl logs mynginx(Pod名字)
    #持续追踪日志
    kubectl logs -f mynginx
    
    # k8s给每个Pod分配一个ip,这样就能通过IP访问集群中部署的应用
    kubectl get pod -o wide
    # 集群内使用Pod的ip+pod里面运行容器的端口访问应用
    curl 192.168.169.136
    
    # 集群内的任意一个机器以及任意的应用都能通过给Pod分配的ip来访问这个Pod,
    #集群外的机器不能通过给pod分配的IP直接访问,需要给pod设置端口
    #集群给pod分配IP的地址范围是在初始化集群的master节点时设置的,通过使用 --pod-network-cidr设置的
    
    #docker中通过使用
    docker exec -it mynginx /bin/bash 
    #能进入容器进行交互,在k8s中使用
    kubectl exec -it mynginx -- /bin/bash   
    #进入容器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    使用yaml文件创建pod:

    vim pod.yaml:
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        run: mynginx
      name: mynginx   (pod的名称)
      namespace: default
    spec:    
      containers:
      - image: nginx
        name: mynginx (容器的名称,可以随意)
    
    #删除创建的pod:
    kubectl delete -f pod.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    pod内多个容器

    上述创建的pod内只有一个容器,下述yaml文件构建一个含有两个容器的pod

    vim multiContainer.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        run: myapp
      name: myapp
    spec:
      containers:
      - image: nginx
        name: nginx
      - image: tomcat:8.5.68
        name: tomcat
    
    kubectl apply -f multiContainer.yaml
    kubectl get pods
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述集群内任意节点或者任意应用可以通过IP:80访问pod内的nginx容器
    通过IP:8080访问pod内的tomcat容器

    处于同一个pod内的容器,共享网络空间且共享存储。
    上述创建的pod myapp内nginx容器通过curl 127.0.0.1:8080即可访问tomcat。同样地,在tomcat内通过使用curl 127.0.0.1:80就能访问nginx。

    思考

    同一个pod内能不能有两个相同的容器?
    例如同一个pod内能运行两个nginx镜像的容器实例吗?

    vim multiContainer.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        run: myapp02
      name: myapp02
    spec:
      containers:
      - image: nginx
        name: nginx01
      - image: nginx
        name: nginx02
    
    kubectl apply -f multiContainer.yaml
    #查看pod启动失败的描述
    kubectl describe pod myapp02
    #删除pod
    kubectl delete -f multiContainer.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    同一个镜像运行出来的两个相同容器实例会出现端口占用冲突的问题。第一个容器可以正常运行,第二个容器因为端口被占用,启动失败。

    Deployment

    deployment用于控制Pod,使Pod拥有多副本,自愈,扩缩容等能力

    自愈能力

    #使用nginx镜像创建一个pod,使用这种方式创建的pod删除了就是彻底删除了
    kubectl run mynginx --image=nginx
    
    #创建deployment,使用的镜像是tomcat,
    #使用这种方式创建的pod在delete之后,deployment会自动再创建一个pod
    kubectl create deployment mytomcat --image=tomcat:8.5.68
    #删除deployment
    kubectl delete deployment mytomcat
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    k8s的自愈能力: 使用deployment创建的pod应用,不用担心应用崩溃或者机器宕机的问题。因为k8s的deployment还会在别的机器上再拉起一个pod,保证应用服务不间断。

    多副本

    #创建一个部署my-dep,该部署把pod应用启动3份部署到3台机器上
    #如果一台机器有100并发,那么3台机器就要300并发
    kubectl create deployment my-dep --image=nginx --replicas=3
    #删除时,只有删除部署deployment才能把创建的应用都删除
    kubectl delete deployment my-dep
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用yaml文件创建deployment

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: my-dep
      name: my-dep
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: my-dep
      template:
        metadata:
          labels:
            app: my-dep
        spec:
          containers:
          - image: nginx
            name: nginx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    扩缩容

    比如,在k8s上使用deployment部署一个应用,deployment在两台机器上部署了两个pod。但是在运行过程中,发现这个应用的请求量过大,者两台机器已经处理不了这些请求了,想要把这个应用再多部署几台。因此,需要将deployment扩容。当流量高峰过后,也可以让一些pod下线。这就需要缩容。k8s也能工作到自动扩缩容,就是k8s自己判断如果负载过高了,就自动将pod应用副本多扩几份,负载降低之后,自动将副本数降低。

    上述创建了含有3个pod副本数的deployment,对此扩容为5个副本数:
    kubectl scale --replicas=5 deployment/my-dep
    缩容为3个副本数,随机选2个pod停止
    kubectl scale --replicas=3 deployment/my-dep
    
    • 1
    • 2
    • 3
    • 4

    也可以使用下述方式实现扩缩容:

    kubectl edit deployment my-dep
    
    #修改 replicas的数量即可
    
    • 1
    • 2
    • 3

    自愈and故障转移

    在k8s中使用deployment部署一个应用。集群中总有4台worker。假设deployment在三台机器上创建了pod应用的三个副本。但是应用在运行过程中可能会遭遇一些特殊情况,比如机器宕机或者pod的应用发生了故障。在这种情况下,就希望k8s能感知到哪些应用出现了故障,并且能自我修复。

    自愈能力: 假设某个节点上的pod在运行过程中突然崩溃了,k8s感知到故障之后,首先会尝试重启该pod,如果重启后故障修复,那么一切就正常运行。这时就称为k8s的自愈能力。
    故障转移: 如果上述故障没修复,例如某一节点宕机了,k8s集群感知到之后先回等待一会(例如5分钟,这是因为集群中的主机可能会由于网络故障失联一小会,这是正常现象。因此需要将这个等待时间调成合理阈值)就会将该机器上所有应用(pod)在别的机器上再同样运行一份。最终能达到保证deployment副本数量的目的。比如deployment需要3个副本数,那么一定能运行3个副本数。

    滚动更新

    假设k8s使用deployment部署一个应用。假设deployment保证了该应用有3个副本数,也即3个pod。第一次部署是使用该应用的v1版本,目前想要将其升级成v2版本。该怎么升级呢?值得注意的是,再此过程中不断有流量访问该应用。该应用需要一直处理这些请求。要兼顾版本更新,且要不间断流量的处理过程,该怎么做?
    首先先启动一个节点上pod的v2版本,v2版pod启动成功,deployment控制v2版pod之后,将该节点上的v1 版本下线。两个版本切换过程中应用的访问流量会由另两个v1版本的pod处理。只要v2版pod能正常运行,新进入的流量就会由v2版的pod处理。【起一个新版本的容器,起成功之后,总共就有4个副本了,然后就会删除一个旧版本的容器】 值得注意的是,deployment控制的pod是有两个版本的。而对于下一个节点,pod的升级过程也是一样的。全程无需停机维护,且是滚动更新,即一个节点更新完了才能更新下一个。只要前一个更新出现错误,之后的节点也不会更新。一个节点出错了,还有节点上的应用仍然能提供服务。这个过程中也保证了服务的不间断运行。

    #查看控制器my-dep,结果以yaml文件形式输出
    kubectl get deployment my-dep -o yaml
    #查看到其中容器的镜像nginx版本
    #滚动更新其中的镜像版本,--record表示记录下此次更新
    kubectl set image deployment/my-dep nginx=nginx:1.16.1 --record
    #查看状态
    kubectl rollout status deployment/my-dep
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    也可以直接修改生成deployment的yaml文件,将其中的镜像版本改变,然后直接应用该yaml文件。

    版本回退

    过程和版本更新的过程类似。【起一个新的pod,这样pod的副本数就增加了一个,然后,删除一个旧的pod】

    #历史版本更新记录
    kubectl rollout history deployment/my-dep
    
    
    #查看某个历史详情
    kubectl rollout history deployment/my-dep --revision=2
    
    #回滚(回到上次)
    kubectl rollout undo deployment/my-dep
    
    #回滚(回到指定版本)
    kubectl rollout undo deployment/my-dep --to-revision=2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在k8s中部署应用,应优先考虑使用deployment来部署,而不是使用kubectl run来启动pod。
    除了Deployment,k8s还有 StatefulSet 、DaemonSet 、Job 等 类型资源。我们都称为 工作负载。
    有状态应用使用 StatefulSet 部署,无状态应用使用 Deployment 部署。

    • Deployment无状态副本集:无状态应用部署,比如微服务,提供多副本等功能。
    • StatefulSet有状态副本集:有状态应用部署,比如redis,提供稳定的存储、网络 等功能。(有状态:一个pod发生故障无法启动,那么在别的节点再拉起的pod应知道该应用原来的数据)
    • DaemonSet守护进程集:可能集群中有十几个节点,每个节点都产生大量日志,因此需要在每个节点上都设置一个日志收集器,收集器将日志都发送到日志收集中心。DaemonSet将在集群每个节点都部署有且仅有一个pod
    • Job/CronJob:定时任务部署,比如垃圾清理组件,可以在指定时间运行。
      这部分内容查看k8s官网介绍

    在k8s中应用的载体就是pod,但是很多时候不是直接部署pod,而是通过使用上述工作负载,从而使得pod具有更强大的功能。
    但是,目前使用控制器部署的应用还不能用外网访问。

    service

    k8s中的service是用于pod的服务发现与负载均衡。
    使用Deployment创建了pod的一些副本。所有的这些pod副本对外是一组服务,就称为一个service。正式的说,service是将一组pods公开为网络服务的抽象方法。 通过访问服务service的IP,能实现对这组pods的负载均衡访问;并且如果其中一个pod出现无法修复的故障,service能及时发现,之后的访问流量就转向给其他正常的pods。而当该deployment再多开启一个pod之后,service也能及时发现,并能将访问流量转入新加入的pod(这就是服务发现功能:service能及时发现pod 的上线和下线)。

    在这里插入图片描述

    clusterIP模式

    该模式,只能在集群内部访问。

    上述已经创建deployment my-dep,副本数为3#将deployment部署的一组pods暴露为一个service,访问该service的端口为8000,实则访问pods的80端口
    kubectl expose deployment my-dep --port=8000 --target-port=80
    参数--type=ClusterIP可加可不加;因为默认使用的是clusterIP模式
    #此时就创建了service my-dep,且能看到my-dep的IP
    kuebctl get svc
    #访问service的IP+端口,能发现自动对pods访问负载均衡
    curl serviceIP:8000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个service的IP:端口号在集群内任意节点或者任意容器内部都是有效可负载均衡访问的。
    也可以通过service域名加端口号访问,能实现同样的效果,但是只能在pod应用内使用域名访问。
    域名的组成规则:服务名.所在命名空间.svc
    例如此时可直接访问 curl my-dep.default.svc:8000

    在这里插入图片描述

    也可使用yaml文件创建service:

    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: my-dep
      name: my-dep
    spec:
      selector:
        app: my-dep
      ports:
      - port: 8000
        protocol: TCP
        targetPort: 80
    
    注意,上述有一个字段为
    selector:
      app:my-dep
    这里和kubectl get pods --show-labels命令查看到my-dep创建的pod的标签app=my-dep一致。
    service是按照标签来选择一组pod的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    NodePort模式

    在集群外可以使用节点IP:端口号访问该service。

    上述已经创建deployment my-dep,副本数为3#将deployment部署的一组pods暴露为一个service,访问该service的端口为8000,实则访问pods的80端口
    kubectl expose deployment my-dep --port=8000 --target-port=80 --type=NodePort
    #service为NodePort模式
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    在集群内部可以使用clusterIP,即10.96.91.238 访问该service。能看到service my-dep暴露了30948端口。(k8s规定,NodePort类型的端口范围在30000至32767之间。)
    在集群内的容器中也能通过service的域名访问该service,和clusterIP方式一样(例如此时可直接访问 curl my-dep.default.svc:8000)。
    在集群外部可以使用集群内任意节点的公网IP+30948端口访问该service,进而访问到pod的80端口。
    在这里插入图片描述

    ingress

    入口,正式定义为service的统一网关入口。
    k8s希望ingress能称为集群流量的唯一入口。
    在k8s集群中可能会部署很多的服务,有很多的pod。这些服务对外提供功能。那么集群的所有流量都应该先通过ingress,由ingress决定哪些流量转向给哪些服务。ingress相当于k8s集群的统一网关入口
    可以这么理解,ingress是service的入口。所有的请求流量转交给ingress(底层是由nginx实现的负载均衡、反向代理),再由ingress决定转交给哪些service。而service相当于是访问pods的入口,由service实现对pods的负载均衡访问。

    在这里插入图片描述

    安装ingress

    wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
    
    #修改镜像
    vi deploy.yaml
    #将image的值改为如下值:
    registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0
    #应用yaml文件
    kubectl apply -f deploy.yaml
    # 检查安装的结果
    kubectl get pod,svc -n ingress-nginx
    
    #ingress安装完成后对应的service是NodePort类型的,
    #因此,需要在云主机上把svc暴露的端口放行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    访问ingress

    可以查看官网文档

    http://集群任意节点公网IP:service的端口1 (处理http请求,对应于http80端口)
    https://集群任意节点公网IP:service的端口2(处理https请求,对应于https443端口)
    在本实例中:
    https://139.198.163.211:32401/
    http://139.198.163.211:31405/

    测试实验:
    首先创建deployment和service:

    vim test.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-server
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-server
      template:
        metadata:
          labels:
            app: hello-server
        spec:
          containers:
          - name: hello-server
            image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server
            ports:
            - containerPort: 9000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx-demo
      name: nginx-demo
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx-demo
      template:
        metadata:
          labels:
            app: nginx-demo
        spec:
          containers:
          - image: nginx
            name: nginx
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: nginx-demo
      name: nginx-demo
    spec:
      selector:
        app: nginx-demo
      ports:
      - port: 8000
        protocol: TCP
        targetPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: hello-server
      name: hello-server
    spec:
      selector:
        app: hello-server
      ports:
      - port: 8000
        protocol: TCP
        targetPort: 9000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    配置ingress,实现:
    如果访问hello.atguigu.com:31405就把请求转给service hello-server进行处理
    如果访问demo.atguigu.com:31405就把请求转给service nginx-demo进行处理

    vim ingress-rule.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress  
    metadata:
      name: ingress-host-bar
    spec:
      ingressClassName: nginx
      rules:
      - host: "hello.atguigu.com"
        http:
          paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: hello-server
                port:
                  number: 8000
      - host: "demo.atguigu.com"
        http:
          paths:
          - pathType: Prefix
            path: "/nginx"  # 把请求会转给下面的服务,下面的服务一定要能处理这个路径,不能处理就是404
            backend:
              service:
                name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
                port:
                  number: 8000
    
    kubectl apply -f ingress-rule.yaml
    kubectl get ingress
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ingress创建成功之后,自动分配了两个端口,31405和32401。

    在这里插入图片描述
    使用域名工具,hosts,或者域名购买。
    给Windows宿主机添加两条域名解析信息,实现当访问hello.atguigu.com和demo.atguigu.com这两个域名时,直接访问集群master节点。

    在这里插入图片描述

    在这里插入图片描述

    域名访问

    kubectl get ingress
    kubectl edit ingress ingress-host-bar
    将- host demo.atguigu.com下的path:/
    写成path:/nginx
    
      - host: "demo.atguigu.com"
        http:
          paths:
          - pathType: Prefix
            path: "/nginx"  # 把请求会转给下面的服务,下面的服务一定要能处理这个路径,不能处理就是404
            backend:
              service:
                name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
                port:
                  number: 8000
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    此时,浏览器输入demo.atguigu.com:31405发现404错误,这是因为刚才的更改将此路径的映射取消了。此时的404错误是由ingress层返回的。当发来该路径的请求,ingress上没有配置该路径的映射,所以ingress返回404.
    在这里插入图片描述浏览器输入demo.atguigu.com:31405/nginx也发现404错误,且nginx带版本号。此时ingress上配置了该路径的映射规则,因此ingress能将该请求转发给service nginx-demo,,之后由service对应的一组pod处理该请求,pod内部需要处理/nginx,而pod内不存在该资源,所以pod返回了404请求。
    在这里插入图片描述
    如果此时进入pod内部,进入/usr/share/nginx/html目录,生成文件 echo 1111 > nginx。
    这是浏览器再次输入demo.atguigu.com:31405/nginx,浏览器会下载nginx文件,打开文件正好显示的就是1111内容。

    路径重写

    查看官方文档说明
    意思是如果要访问上述实例中的
    demo.atguigu.com:31405/nginx/xxxxx,那么重写该路径为demo.atguigu.com:31405/xxxxx
    demo.atguigu.com:31405/nginx,那么重写该路径为demo.atguigu.com:31405
    demo.atguigu.com:31405/nginx/,那么重写该路径为demo.atguigu.com:31405/

    kubectl edit ingress xxx或者
    直接修改生成ingress的yaml文件:
    vim ingress-rule.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress  
    metadata:
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$2
      name: ingress-host-bar
    spec:
      ingressClassName: nginx
      rules:
      - host: "hello.atguigu.com"
        http:
          paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: hello-server
                port:
                  number: 8000
      - host: "demo.atguigu.com"
        http:
          paths:
          - pathType: Prefix
            path: "/nginx(/|$)(.*)"  # 把请求会转给下面的服务,下面的服务一定要能处理这个路径,不能处理就是404
            backend:
              service:
                name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
                port:
                  number: 8000
    
    kubectl apply -f ingress-rule.yaml
    kubectl get ingress
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这时浏览器访问demo.atguigu.com:31405/nginx,但是在转发给service nginx-demo对应pod的时候,将该路径重定向为demo.atguigu.com:31405,最终输出为nginx欢迎页面。

    流量限制

    官方文档说明
    在Windows宿主机添加一条新的域名解析记录:实现当访问域名haha.atguigu.com的时候,直接访问到k8s集群宿主机上。

    vim ingress-rule-2.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingress-limit-rate
      annotations:
        nginx.ingress.kubernetes.io/limit-rps: "1"
    spec:
      ingressClassName: nginx
      rules:
      - host: "haha.atguigu.com"
        http:
          paths:
          - pathType: Exact   #精确匹配,域名需要完全匹配
            path: "/"
            backend:
              service:
                name: nginx-demo
                port:
                  number: 8000
    kubectl apply -f ingress-rule-2.yaml
    kubectl get ingress
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    浏览器输入haha.atguigu.com:31405
    能看到nginx的欢迎页面。
    但是如果不断刷新页面,就会出现503 service temporarily unavailable,这是因为对请求流量进行了限制。当超过这个限制就会报503的错误。

    k8s网络模型总结

    在这里插入图片描述
    假设集群有3台机器,3台机器上会部署很多的pod。每组功能相同的pod抽象成一个service。外部访问流量首先访问的是集群节点IP的31405(80)和32401(443)NodePort端口,并把这一层称为ingress层。有时候还可以给ingress层设置负载均衡。负载均衡器把请求的流量负载均衡的发送给三台机器中的任意一台,相当于发送给任意一台的ingress。ingress收到请求之后,可以按照域名、路径等规则把他交给相应的service,再有service交给对应的一组pods,由pods处理请求。因此,k8s产生了3层网络,所有的pods组成了一层网络,称为pod层。再初始化master节点的时候,指定了pods的网络范围,因此pods层的网络是互通的。也就是说任意一个pod要访问别的pod,可以通过直接访问pod的IP地址。还有一层是service层网络,service层之间是互通的,pod跟service层之间也是互通的。同样地,再初始化master节点的时候也指定了service的网络范围。也就是说在k8s上部署的应用,可以通过service负载均衡访问到对应的pod,也可以直接通过pod的IP直接访问。k8s打通了整个的访问过程,即在集群里面,不管是给应用的pod还是service开辟了IP地址,在集群内就可以随便访问都能访问的通。而对于外部的请求访问,先到达ingress层(总网关),再到达service层,再到达pods层。

    存储抽象

    在docker中,通过使用-v参数,就能将容器内的某个路径挂载到docker主机上的路径。这样在docker主机上修改路径中的内容就能将容器内挂载路径的内容修改。但是,k8s中,如果还是使用这种挂载的方式,就会有一个问题。假设pod1将/data挂载到主机的/a,pod2将/tmp路径挂载到主机的/tmp,pod3也将/logs路径挂载到主机的/logs,。。。如下图

    在这里插入图片描述
    假设上图中3号机器上将pod内/hello挂载到主机/tmp的pod出现故障,宕机了。按照k8s的故障转移策略,5分钟之后会在别的节点再起一份pod.假设在2号主机上再次启动一个pod。而2号主机上新起的pod上没有原来3号主机上原pod之前的数据。因此,面临着一个问题,当一个pod发生故障,而故障转移后,在新节点启动的pod是没有原来数据的。 而如果该pod是MySQL应用,那后果就无法想象。怎么样才能把pod内的数据挂载到宿主机上,且迁移之后还有原来的数据呢?**k8s单独设置了一个存储层。众多pods将挂载到存储层,就算之后某一个pod故障了,重新启动的pod还能找到之前pod挂载的空间,还能找到之前的数据。**存储层的实现技术有nfs、Glusterfs、CephFS、、、
    在这里插入图片描述
    以nfs网络文件系统为例
    比如在1号主机设置了/nfs/data目录,为50G,之后所有的pods都挂载/nfs/data目录。为了安全起见,在其他节点都设置/bak/data备份目录,均为50G。/bak/data备份目录与/nfs/data目录数据是一样的。这就保证了,就算某一个pod故障之后,在另一个节点重新启动,仍然能从该节点上获取到原先的数据。
    在这里插入图片描述
    具体实现:
    首先需要搭建nfs网络文件系统,设置集群master为nfs-server,集群的两个worker节点为nfs-client。三个主机都新建了/nfs/data目录,nfs-client会主动同步nfs-server上的数据。

    安装nfs

    #所有机器安装
    yum install -y nfs-utils
    
    • 1
    • 2
    #nfs主节点----k8s集群master节点
    echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
    
    mkdir -p /nfs/data
    systemctl enable rpcbind --now   
    systemctl enable nfs-server --now
    #配置生效
    exportfs -r
    #检查
    exportfs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    #nfs-client ----- k8s worker节点
    #查看远程nfs-server 172.31.0.4可供挂载的目录是什么
    showmount -e 172.31.0.4  
    
    #执行以下命令挂载 nfs 服务器上的共享目录到本机路径 /root/nfsmount
    mkdir -p /nfs/data
    
    mount -t nfs 172.31.0.4:/nfs/data /nfs/data
    # 写入一个测试文件
    echo "hello nfs server" > /nfs/data/test.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    原生方式数据挂载

    vim deploy.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx-pv-demo
      name: nginx-pv-demo
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx-pv-demo
      template:
        metadata:
          labels:
            app: nginx-pv-demo
        spec:
          containers:
          - image: nginx
            name: nginx
            volumeMounts:
            - name: html  (此次挂载对外名称)
              mountPath: /usr/share/nginx/html   (挂载容器中的该目录)
          volumes:
            - name: html   (与上面html对应)
              nfs:   (挂载方式为nfs网络文件系统)
                server: 172.31.0.4  (挂载到集群master节点)
                path: /nfs/data/nginx-pv (挂载到的路径,集群master节点需要先创建该目录)
                
    kubectl apply -f deploy.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    最后达到的效果就是/nfs/data/nginx-pv目录中的修改能立即同步到两个pods的 /usr/share/nginx/html目录。

    PV & PVC

    上述使用nfs对pods内数据目录挂载。这种方式也有一些问题,
    首先pods内数据目录的目标挂载位置需要自己创建;
    其次,如果某个pod确定不需要了,但是删除pod之后,对应的挂载目录内数据不会自动删除。如果pods的数量足够大,而删除一些pods之后,希望对应的挂载目录中数据也能自动删除。
    再次,目前pods挂载目录之后没有限制pods能使用的数据存储空间大小,希望对pods挂载数据的大小做限制。
    综上,就需要另一种数据存储方式,PV和PVC。

    PV:持久卷(Persistent Volume),将应用需要持久化的数据保存到指定位置(数据真正存储的位置)
    PVC:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格(pods对自己所需持久卷规格的申请)

    因此,之后当pod被删除,就可以根据PVC将起挂载的PV删除。

    假设集群有3台节点,提前在这3台节点上准备一些存储空间。例如下图中的PV01、PV02、、、、将这些开辟的存储空间成为静态供应(提前创建好)的PV池。假设一台节点上的pod希望将容器内的/data目录挂载,该pod就需要准备PVC“申请书”,如果这个PVC声明需要1MB的存储空间,那么就从PV池中寻找一个大小最合适的PV,即20MB的。最终这个PVC就可以和20MB的PV进行绑定。而如果某时刻该pod故障,需要在另一个节点再起一份,因为PVC上指明了申请的哪一块PV,所以根据PVC能找到绑定的PV,进而能找到原先的数据。当pod被删除,那么连带着将PVC也一起删除,对应的PV空间也能一起被回收。
    在这里插入图片描述

    创建PV池

    #在nfs-server,k8s master节点
    mkdir -p /nfs/data/01
    mkdir -p /nfs/data/02
    mkdir -p /nfs/data/03
    
    • 1
    • 2
    • 3
    • 4
    vim pv.yaml
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv01-10m
    spec:
      capacity:
        storage: 10M
      accessModes:
        - ReadWriteMany
      storageClassName: nfs
      nfs:
        path: /nfs/data/01
        server: 172.31.0.4 (k8s masterIP)
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv02-1gi
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteMany
      storageClassName: nfs
      nfs:
        path: /nfs/data/02
        server: 172.31.0.4
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv03-3gi
    spec:
      capacity:
        storage: 3Gi
      accessModes:
        - ReadWriteMany
      storageClassName: nfs
      nfs:
        path: /nfs/data/03
        server: 172.31.0.4
    
    kubectl apply -f pv.yaml
    kubectl get persistentvolume
    #此时能看到三个pv的状态都是available
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    pvc的创建与绑定

    vim pvc.yaml
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: nginx-pvc
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 200Mi
      storageClassName: nfs (与上述pv的存储类型对应)
    
    kubectl apply -f pvc.yaml
    kubectl get pv
    #发现大小为1Gi的pv的状态变成了bound,与名称为nginx-pvc的pvc绑定
    #其他2个pv的状态都是available
    #这是因为10MB的pv大小不够,而3Gi的pv太大,为了避免浪费,该pvc就和大小为1Gi的pv绑定
    
    kubectl delete -f pvc.yaml
    kubectl get pv
    #能看到1Gi的pv的状态转换:bound---released----available
    #再次应用pvc.yaml,能查看到pvc与1Gi的pv绑定,即pv02-1gi
    kubectl apply -f pvc.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    pvc申请书不是直接单独创建的,而是pod需要使用pv的时候再使用pvc。

    创建pod绑定pvc

    vim dep02.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx-deploy-pvc
      name: nginx-deploy-pvc
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx-deploy-pvc
      template:
        metadata:
          labels:
            app: nginx-deploy-pvc
        spec:
          containers:
          - image: nginx
            name: nginx
            volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html
          volumes:
            - name: html
              persistentVolumeClaim:
                claimName: nginx-pvc
      kubectl apply -f dep02.yaml
      kubectl get pods
      kubectl get pvc,pv
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    最终实现的效果是,pvc nginx-pvc与名称为pv02-1gi,大小为1Gi的pv绑定,而pod在创建时声明使用nginx-pvc作为自己的挂载卷,最终将/usr/share/nginx/html挂载到pv02-1gi。因此,当在pv02-1gi所在目录/nfs/data/02内做数据修改,该修改能立即同步到pods的容器内。

    注意,由于pv02-1gi大小只有1Gi,当pod用超了,就会报错。

    上面的实验,pv池是静态供应的,而在很多情况下,需要用到动态供应方式。在动态供应的方式中,不需人为的事先建立若干pv。假设新建的pod需要将容器内的/data目录挂载出去,因此在pvc中声明所需pv的大小,假设只需10M的大小。而pv动态供应池就会创建一个大小为10M的pv空间,和这个pvc绑定,供pod使用。

    configMap(配置集)

    抽取应用配置,并且可以自动更新
    用于管理一些配置信息

    如前所述,为了实现将容器内的某一个目录挂载出来,可以使用PV和PVC或者使用原生的方式。这些方式的特点是最好在pods需要挂载目录的时候使用。而在一些场景中,比如我们部署了redis应用,我们希望能将redis的配置文件挂载出来。这样以后直接修改redis的配置文件,然后重启redis,这样就能使用最新的配置启动reids服务。而对于其他的中间件,比如MySQL、rabbitmq、。。。可能都会有它的配置文件,如果能将它们的配置文件挂载出来,那么修改配置将会非常方便。

    在k8s中,可以使用configMap来做配置文件的挂载。
    整体来说,该过程分为两步:首先将配置文件创建为配置集。然后创建pod时,引用该配置集即可。

    把配置文件创建为配置集

    vim redis.conf
    #redis的数据以后需要持久化存储
    appendonly yes
    
    kubectl create cm redis-conf --from-file=redis.conf
    kubectl get cm
    #可以看到创建了configmap,名称为redis-conf
    #实际上创建的配置集是在k8s的数据库etcd中存储着的
    rm -fr redif.conf
    #这时可以将redis.conf文件删除
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    kubectl get cm redis-conf -o yaml
    #能看到上面创建的cm,输出为yaml文件是什么样
    #去除时间戳等信息,得到:
    apiVersion: v1
    data:    #data是所有真正的数据,key:默认是文件名   value:配置文件的内容
      abc: |
        appendonly yes
    kind: ConfigMap
    metadata:
      name: redis-conf
      namespace: default
    
    因此接下来就可以创建pod,在启动pod的时候挂载该cm即可
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建pod

    vim redis-cm.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: redis
    spec:
      containers:
      - name: redis
        image: redis
        command:
          - redis-server    
          - "/redis-master/redis.conf"  #指的是redis容器内部的位置,redis-server使用该处配置文件加载redis
        ports:
        - containerPort: 6379
        volumeMounts:
        - mountPath: /data  #将容器内/data目录以名称为data的方式挂载
          name: data
        - mountPath: /redis-master  #该目录是redis服务启动要加载的配置目录,而该目录使用了名为config的挂载方式
          name: config 
      volumes:
        - name: data  #名称为data的挂载方式
          emptyDir: {}  #表示随意分配一个目录,使得该挂载能运行即可
        - name: config #名为config的挂载方式
          configMap: #具体挂载方式为configmap形式,说明具体的配置文件需要从配置集中取
            name: redis-conf #具体该configmap的名称为redis-conf,正是上面创建的配置集
            items:
            - key: abc #指向configmap yaml文件中的data里的abc这一项
              path: redis.conf  #把abc这一项的数据放在redis.conf这个文件中,而这个文件是挂载在/redis-master这个目录下的
    
    kubectl apply -f redis-cm.yaml
    kubectl get pods
    #应用完成,能看到pod正常启动
    #修改cm
    kubectl get cm
    kubectl edit cm redis-conf
    在appendonly yes这一行下面添加:
    requirepass 123456
    表示要输入密码123456
    #能看到pod容器内/redis-master/redis.conf文件也发生了相应更改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    能看到修改了CM。Pod里面的配置文件会跟着变

    检查该配置是否生效:

    kubectl exec -it redis -- redis-cli
    
    127.0.0.1:6379> CONFIG GET appendonly
    127.0.0.1:6379> CONFIG GET requirepass
    appendonly生效
    equirepass未生效,因为redis服务还未重启
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    配置文件更改了,但是未生效,因为需要重新启动 Pod 才能从关联的 ConfigMap 中获取更新的值。
    原因:我们的Pod部署的中间件自己本身没有热更新能力

    secret

    Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。

    secret与configmap的工作原理一样,只是configmap用来保存配置文件等明文信息。而secret用于保存需要保密的信息,以密文保存。

    secret怎么应用可以查看该文章.

    下一步

    不管是使用k8s的dashboard还是命令行,实际应用中可能开发的微服务众多,需要划分成成千上百个模块,为了部署这些应用pod,使用yaml文件或者命令行做配置是很麻烦的。所以目前很需要一个一站式的完备平台。这个基于kubernetes的一站式平台希望能整合自动化部署的所有技术,日志监控的所有技术,告警系统的所有i系统等等。而kubesphere正式这样一个平台。这个平台全是可视化界面,通过点点点就能实现应用部署到k8s上。且该平台具有期望的一站式能力,比如自动化的运维部署、应用的监控、日志的收集、系统的告警等等。

    往小的说,kubesphere就是kubernetes的可视化界面,往大的说,kubernetes是基于kubernetes构建的分布式、多租户、多集群、企业级开源容器平台,具有强大且完善的网络与存储能力,并通过极简的人机交互提供完善的多集群管理、CI/CD、微服务治理、应用管理等功能,帮助企业在云、虚拟化集物理机等异构设施上快速构建、部署及运维容器架构,实现应用的敏捷开发与全生命周期管理。

    多租户:允许用户自定义注册,并允许给用户分配操作权限,进而操作k8s集群。
    多集群:可能未来会拥有用于开发的k8s集群、用于测试的k8s集群、以及用于生产环境的k8s集群,而kubesphere能对这些集群一站式管理。而且由于具有多租户的能力,所以能对用户的权限有效管理。

    可以查看kubesphere官方网站

  • 相关阅读:
    docker数据卷
    计算机毕业设计Java妇女健康保健系统(源码+系统+mysql数据库+lw文档)
    java计算机毕业设计竞赛信息发布及组队系统源码+数据库+lw文档+系统
    【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem
    Optional 详解
    C++ Tutorials: C++ Language: Other language features: Exceptions
    ES6高级-Promise的用法
    【Selenium & Other】一键杀死进程 & 进程清理大师
    (附源码)springboot高校社团管理系统 毕业设计 231128
    22/11/24
  • 原文地址:https://blog.csdn.net/qq_43604376/article/details/126221903