定义

Kubernetes是Google于2014年基于其内部Brog系统开源的一个容器编排管理系统,可使用声明式的配置(以yaml文件的形式)自动地执行容器化应用程序的管理,包括部署、伸缩、负载均衡、回滚等。

Kubernetes的一大转变就是

从基础设施即代码转向为基础架构即YAML;

Kubernetes中所有的资源,包括Pod、配置、部署、数据卷等,都可以简单地用YAML文件来表示。

功能点

  • 自动发布

  • 动态扩容缩容(可自动)

  • 资源自动调度与手动配额

  • 滚动升级降级

  • 存储编排

  • 故障恢复

  • 配置与密钥管理

  • 多租户

  • 负载均衡

  • 云提供商集成

    云提供商可帮助自动创建一个LoadBalancer服务,该服务将自动配置一个Amazon Elastic Load Balancer,以将流量转发到应用程序Pod中

工作方式

K8s中,Service是核心,一个Service拥有一个名字,一个虚拟IP,可以提供某种服务,且映射到一组可以提供服务的容器上;

Service通过Socket方式对外提供服务(比如Mysql、Redis、Kafka等),K8s可以让我们通过Service的虚拟IP + 端口 访问到某个Service,K8s通过负载均衡与故障恢复机制维持Service的正常运行;

pod是K8s的基本操作单元,一个pod中可以有一个或多个容器,他们共享网络与存储;提供服务的一个或多个pod会映射到Service上,具体的映射方式为给提供服务的pod贴上label,比如提供mysql服务的pod贴上 name = mysql 标签,然后给对应的Service定义Label Selector ,比如 Mysql Service的标签选择条件写为 name=mysql,从而建立Service和pod的关联;

集群分为Master节点和Node节点;

Master节点运行着集群管理相关的进程,比如kube-apiserver、kube-controller-manager和kube-scheduler,可以自动化的进行集群的资源管理、pod调度、谈性伸缩、安全控制、系统监控与错误恢复等功能;

Node 节点运行kubelet、 kube-proxy等服务进程,用来实现pod的启动、创建、监控、销毁、负载均衡等;

扩容Service时,只需要给Service关联一个RC(Replication Controller),RC中定义了 目标pod的定义、目标pod需要的副本数和要监控的目标pod的Label,创建RC后,RC会根据Label监控pod,当pod实例小于需求数,则会根据RC中pod的定义生成新的pod,并调度到合适的 Node 节点。

常用 Add-ons

  • kube-dns:为整个集群提供DNS服务;
  • Ingress Controller:为服务提供外网入口;
  • Heapster:提供资源监控;
  • Dashboard:提供GUI;
  • Federation:提供跨可用区的集群;
  • Fluentd-elasticsearch:提供集群日志采集、存储与查询;

分层架构

  • 核心层:对外提供API构建服务,对内提供插件方式的执行环境;
  • 应用层:部署应用(无状态、有状态、批处理、集群)和路由(服务发现、DNS解析)
  • 管理层:系统度量、自动化、策略;
  • 接口层:kubectl命令行工具;
  • 生态系统:k8s外部(日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等),k8s内部(镜像仓库、集群配置等)

服务类型


k8s集群中的业务类型主要可以分为 长期伺服型(long-running)、批处理型(batch)、节点后台支撑型(node-daemon)和有状态应用型(stateful application);

长期伺服型(long-running)

长期伺服型业务是通过 Deployment 来管理;

批处理型(batch)

批处理型任务是通过 Job 来管理,Job 用来管理控制批处理型的业务,与长期伺服型业务对比,批处理任务 有开始和结束, Job 管理的 Pod完成了用户设定的任务后就会自动退出,完成任务的标志可以通过 spec.completions 来设定策略,比如 :

  • 单 Pod 任务,一个Pod完成就算完成;
  • 定数成功型任务,达到要求的完成数量就算完成;
  • 工作队列型任务,需要获得全局成功的标识;
节点后台支撑型(node-daemon)

后台支撑型服务 是通过 DaemonSet 来管理;有些服务是应用型的,而有些服务为 k8s 集群提供服务的服务,比如存储、日志、监控等;

有状态应用型(stateful application)

有状态应用型业务 是通过 StatefulSet 管理的;StatefulSet 中的 Pod的名称 需要事先确定且不可更改;如果 Pod 故障,则从其他节点启动的 Pod 需要保持一样的名称,且挂载到原来 Pod 的存储;

适合做集群服务,比如 Mysql、Zookeeper、etcd等;

资源对象

K8s 集群中大部分概念都可以当作 资源,比如 Node、Pod、Replication Controller、Service等,几乎所有资源对象都可以 通过 kubectl 或者 API 进行 增删改查、并保存到etcd,因此,K8s可以理解为一个自动化的资源控制系统,通过 对比 etcd中保存的资源期望的状态和资源的实际状态,来实现 自动化的控制与 纠错

每个资源对象都有三大属性:元数据metadata、规范spec和状态status;

metadata

元数据用来标识资源对象,每个对象都至少有3个元数据,分别是 namespace 、name 和 uid; Label 也是元数据,用来做标识,比如标识是开发版本还是测试版本还是生产版本;

spec

规范描述了期望的资源对象状态,比如副本数的期望值;

对资源对象的配置都是通过 spec 来设置的,即所有的操作都是声明式的,而非命令式,这么做的好处是 稳定、方便查看;

这也是 k8s集群的重要理念,即声明式分布式系统;

status

状态表示该资源对象目前的状态;

基础设施即 YAML

Kubernetes的一大转变就是从基础设施即代码转向为基础架构即YAML。Kubernetes中所有的资源,包括Pod、配置、部署、数据卷等,都可以简单地用YAML文件来表示。

使用 YAML 表示资源有以下优点:

1、通过 git 管理 K8S 的 YAML 文件,可以精确的跟踪更改情况,使组织架构更加透明;更改的时候只需要拉取即可,更加方便(可以配合远程仓库的webhook功能自动同步更改到 Master 节点);

2、简单的实现可伸缩,只需要修改 YAML 的副本数;

3、很容易的与云服务商集成(修改 Service,使用云服务商提供的 LoadBalancer 服务)

4、可扩展性很强,甚至可以自定义 资源,

工具

1、kubectl

  • Kubernetes官方提供的命令行工具(CLI),用户可以通过 kubectl 以命令行交互的方式对Kubernetes API Server进行操作,通信协议使用HTTP/JSON。

  • 工作方式:kubectl 发送 HTTP 请求,kube-apiserver 接收并返回结果,比如创建Pod;

2、client-go

  • 通过编程的方式与Kubernetes API Server进行通信的包,GO语言实现;

  • 在大部分基于Kubernetes做二次开发的程序中,建议通过 client-go 来实现与Kubernetes API Server的交互过程。这是因为client-go在Kubernetes系统上做了大量的优化;

  • Kubernetes核心组件(如kube-scheduler、kube-controller-manager等)都可以通过client-go与Kubernetes API Server进行交互。

Master

K8s集群的控制节点,控制命令基本都发到 Master 节点,Master节点再执行具体的控制;Master 节点一般需要一个或三个(高可用);

Master节点的主要进程

1、kube-apiserver

  • 将 Kubernetes 系统中的所有资源对象都封装成RESTful风格的API接口进行管理,比如资源对象的增删改查;
  • 对 K8s 集群进行状态管理和数据管理,是唯一与Etcd集群交互的组件;
  • 提供了集群各组件的通信和交互功能;

2、kube-controller-manager

  • 所有资源对象的自动化控制中心,确保集群始终处于预期的工作状态;
  • 提供了一些相关控制器,例如 DeploymentControllers 、StatefulSet 、Namespace控制器 及 PersistentVolume 控制器等;控制器通过 kube-apiserver 实时监控 集群中的每个资源对象的状态,发生故障时会自动修复到期望状态;

3、kube-scheduler

监控整个集群的Pod资源对象和Node资源对象,发现新的 Pod资源对象时,kube-scheduler 会根据调度算法去选择合适的Node节点,每次只调度一个 Pod 资源对象,为其寻找合适的节点;

4、etcd服务

key-value数据库,存储所有资源对象的相关数据,比如配置、状态等;

Node

Node节点是K8s的集群负载节点,每个 Node 上都会被分配一些工作负载(Docker),当某个 Node 宕机时,Master会把上面的工作负载转移到其他Node上。

Node 正确的安装配置并启动后,上面的kubelet进程会向Master注册自己,实现集群扩容;

Node上的kubelet 会定时向 Master 汇报自己的情况,超时不汇报则会被标记为 Not Ready,上面的服务会被转移;

Node节点的主要进程

1、kubelet

  • 运行在每个Node上,启动时会向 kube-apiserver 注册所在Node的信息;

  • 控制:用来接收、处理、上报 kube-apiserver 的任务,比如 根据 kube-apiserver 的指令 ,对所在Node上的Pod进行 创建、修改、删除,镜像和容器的清理等;

  • 监控:定期监控所在节点的资源使用状态并上报给 kube-apiserver,从而帮助 kube-apiserver 调度 Pod;

  • 接口:kubelet实现了三种接口,分别是 CRI 、 CNI 、 CSI ( Container Runtime Interface 、Container Network Interface 、 Container Storage Interface );

  • CRI :定义了容器和镜像的服务接口;

  • CNI:CNI定义了Kubernetes网络插件的基础,容器创建时通过CNI插件配置网络;

  • CSI:CSI定义了容器存储卷标准规范,容器创建时通过CSI插件配置存储卷;

2、kube-proxy

  • 监控kube-apiserver的服务和端点资源变化,并通过iptables/ipvs等配置负载均衡器,为一组Pod提供统一的TCP/UDP流量转发和负载均衡功能。
  • Pod-to-Service和External-to-Service网络的最重要的节点组件之一,负责将 IP:Port的请求 转发到响应的服务或应用;

3、Container引擎

  • (Docker)接收kubelete指令,负责容器的创建与管理;
# 查看Node
kubectl get nodes
# 查看某个Node详情
kubectl describe node 127.0.0.1

Pod


概念

Pod是在K8s集群中运行部署应用或服务的最小单元;

Pod 由 一个根容器(Pause容器) 和 一个或多个 业务容器组成,一个Pod中的容器共享网络地址和文件系统

Pause容器用于代表 一个Pod 中所有容器的状态;且 Pod 中的其他容器共享Pause容器的IP和挂载的Volume;

每个Pod 有自己的Pod IP,Pod 内的容器共享这个IP;Kubernetes集群中的任意两个Pod可以通过TCP/IP 方式 直接通信;

一个 Pod 的 多个容器可以互相配合,组成一个微服务;

工作流程

Pod被创建后,会放到 etcd,然后Master调度这个Pod到特定的Node并绑定(Binding)起来,这个Node上的kubelet进程会实例化这个Node,生成一组相关的Docker容器并启动;

当Pod内的某个容器停止,Kubernetes集群会自动检测到并重启这个Pod,如果Pod所在的Node宕机,则这个Node上的所有Pod会被调度到其他Node上;

定义
apiVersion: v1
kind: Pod
metadata:
  name: string
  namespace: string
  labels:
    - name: string
  annotations:
    - name: string
spec:
  containers:
  - name: string
    image: string
    imagePullPolicy: [Always | Never | IfNotPresent]  # 镜像拉取策略,默认Always,每次重新下载镜像
    command: [string] # 启动命令列表
    args: [string] # 启动命令参数
    workingDir: string # 工作目录
    volumeMounts:
    - name: string
      mountPath: string
      readOnly: boolean # 默认读写模式
    ports:
    - name: string
      containerPort: int # 暴露出来的端口号
      hostPort: ing	# 要监听的端口号
      protocol: string # 端口协议,默认TCP
    env:
    - name: string
      value: string
    resources:
      limits:
        cpu: string
        memory: string
      requests:
        cpu: string
        memory: string
    livenessProbe: # 容器健康检查方式
      exec:
        command: [string]
      httpGet:
        path: string
        port: number
        host: string
        scheme: string
        httpHeaders:
        - name: string
          value: string
      tcpSocket:
        port: number
      initialDelaySeconds: 0
      timeoutSeconds: 0
      periodSeconds: 0
      successThreshold: 0
      failureThreshold: 0
      securityContext:
        privileged: false
    restartPolicy: [Always | Never | OnFailure] # Pod 重启策略,默认 Always
    nodeSelector: object # key:value 的方式指定要调度到的Node的Label
    imagePullSecrets:
    - name: string
    hostNetwork: false #是否使用主机网络模式,默认false
    volumes:
    - name: string
      emptyDir: {}
      hostPath:
        path: string
      secret:
        secretName: string
        items:
        - key: string
        path: string
      configMap:
        name: string
        items:
        - key: string
          path: string
Pod操作

1、创建 Pod

vim pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: demo
spec:
  containers:
  - image: httpd
    name: httpd
    imagePullPolicy: Always

kubectl create -f pod.yaml

2、查看 Pod

kubectl get pods

kubectl describe pods

3、删除 Pod

kubectl delete pod pod_name

4、查看 Pod 内 某个容器的输出日志

kubectl logs Pod_name -c container_name

Pod的计算资源配额:可以为一个Pod中的每个容器设置计算资源配额 CPU 和 Memory,比如 CPU设置为300m,表示0.3个CPU;设置时需要在资源描述文件中对应的容器下设置 resources,resources 中需要设置 request 和 limits,分别表示计算资源的 下限 和 上限;

Label


定义

Label 就是标签,是一个 key-value 键值对,多个 Label 可以赋予到同一个资源对象,一个Label也可以赋予到多个资源对象;

用来实现对资源对象进行多维度分组管理,以便灵活的配置、调度、部署等;

可以通过 Label Selector 根据标签 查询 和 筛选 资源对象;

关联

管理对象RC和Service 可以在 spec 的 selector 中定义要关联的 Pod ;

apiVersion: v1
kind: ReplicationController
metadata:
  name: myweb
spec:
  replicas: 1
  selector:
    app: myweb

#####################

apiVersion: v1
kind: Service
metadata:
  name: myweb
spec:
  selector:
    app: myweb
  ports:
  - port: 8080

Deploment、ReplicaSet、DaemonSet 和 Job 可以在 Selector 中使用基于集合的筛选条件定义,matchLabels 等同于 直接写在 Selector 中,matchExpression 定义筛选条件,两者同时存在则需要同时满足;

selector:
  matchLabels:
    app: myweb
  matchExpressions:
    - {key: tier, operator: In, values: [frontend]}
    - {key: environment, operator: NorIn, values: [dev]}
应用场景举例
  • kube-controller 进程 通过 RC上定义 Label Selector 筛选 要监控的 Pod,保证 Pod 数量符合定义的要求;
  • kube-proxy 进程通过 Service的 Label Selector 选择对应的 Pod,自动建立 每个Service 到对应 Pod 的请求转发路由表,从而实现Service的智能负载均衡机制。
  • 某些Node 定义 Label,Pod 定义 NodeSelector,使 kube-scheduler 进程 实现Pod “定向调度”;

常用标签的类型:

"release":"stable","release":"Debug"...
"environment":"dev","environment":"production"...

Replication Controller


概念

Replication Controller 是保证 Pod 高可用的资源对象,通过保持 Pod的副本数量在任意时刻都符合某个预期值;

通过在RC中写入 副本数量,Pod定义,以及 Label Selector 实现 自动控制维护、滚动升级、动态扩容缩容等功能;

  • 自动控制(Label Selector)
  • 高可用(replicas)
  • 动态扩容缩容(replicas)
  • 滚动升级(image)
组成
  • Pod 期望的副本数;
  • 筛选目标 Pod 的 Label Selector
  • Pod 的 模板(template)
定义
apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    tier: frontend
template:
  metadata:
    labels:
      app: app-demo
      tier: frontend
  spec:
    containers:
     - name: tomcat-demo
       image: tomcat
       ports:
       imagePullPolicy: IfNotPresent
       env:
       - name: GET_HOSTS_FROM
         value: dns
       ports:
       - containerPort: 80
工作原理
  • 自动控制:通过 Label Selector 实现对指定的 Pod 实现自动控制;

  • 高可用性:定义一个 RC 并提交到集群后,Master的Controller Manager就会得到通知,然后定期检查系统存活的Pod,确保每个Pod的副本数 符合 期望值,多停 少建,从而保障 高可用性

  • 动态扩容缩容:在运行时,也可以通过修改RC的副本数量实现 动态的 扩容与缩容;

  • 滚动升级(Rolling Update):当升级时,会用新的镜像代替旧的镜像,通过改变 RC 中的镜像即可实现;比如系统中有10个旧版本的 Pod,每次 同时 停止一个旧的,启动一个新的,从而完成滚动升级;

kubectl scale rc redis-slave --replicas=3
scaled

Replica Sets


概念

RS 支持基于集合的Label selector,而RC只支持基于等式的Label Selector,以下是一个RS的定义;

Replica Set与Deployment这两个重要资源对象逐步替换了之前的RC的作用;

Replica Set 被Deployment 这个更高层的资源对象所使用,从而形成一整套Pod创建、删除、更新的编排机制;

RS 相关操作

1、创建 RS

vim replicaset.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: demo-rc
  labels:
    app: demo-rc
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-rc
  template:
    metadata:
      labels:
        app: demo-rc
    spec:
      containers:
      - name: httpd
        image: httpd
        imagePullPolicy: Always

kubectl create -f replicaset.yaml

2、查看 RS

kubectl get replicaset

kubectl describe replicaset

3、删除 RS

kubectl delete replicaset rs_name

Deployment


概念

Deployment 是保证 Pod 高可用,且可以满足服务创建与查看、动态扩容缩容、滚动升级降级的资源对象;

Deployment 适合管理 长期伺服型的的业务;

更好解决容器编排的问题,内部使用了Replica Set来实现;

相对于RC的一个最大升级是我们随时知道当前Pod 部署 的进度;

Pod的创建 > 调度 > 绑定节点 > 在目标Node上启动对应的容器;

使用场景
  • 创建 Deployment 对象 ,完成 Pod 及副本的创建;
  • 检查 Deployment 的状态 ,查看部署动作是否完成;
  • 更新Deployment 以创建新的Pod(比如镜像升级);
  • 如果当前 Deployment 不稳定,回滚到之前的版本;
  • 扩展 Deployment 应对高负载;
  • 查看Deployment的状态,以此作为发布是否成功的指标;
  • 清理不再需要的旧版本ReplicaSets;
  • 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项;
相关操作

1、创建 Deployment

vim deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  labels:
    app: httpd-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: httpd-demo
  template:
    metadata:
      labels:
        app: httpd-demo
    spec:
      containers:
      - name: httpd
        image: httpd
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        env:
        - name: VERSION
          value: "v1"
          
kubectl create -f deployment.yaml

2、查看Deployment

kubectl get deployment

kubectl get pods -o wide

kubectl describe deployment

3、更新 Deployment

kubectl edit -f deployment.yaml # 编辑

kubectl apply -f deployment.yaml # 使更新生效

kubectl get deployment # 查看是否更新

4、扩容与缩容

kubectl scale deployment/httpd-deployment --replicas=1

5、删除 Deployment

kubectl delete deployment httpd-deployment

StatefulSet


Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务,很多服务都是有状态的,比如 MySQL集群、MongoDB集群、Kafka集群、Zookeeper集群等;

有状态集群的特点:

  • 每个节点有固定ID,保证集群中各个节点互相识别与通信;
  • 集群规模固定不变;
  • 集群每个节点有状态,数据会持久化到存储;

Deployment 无法满足有状态集群的需求,无法保证 IP 和 节点 不变;

StatefulSet 作为 Deployment 的变种,解决了有状态集群的问题;

  • StatefulSet 的每个 Pod 都有 稳定、唯一的网络标识,用来发现集群的其他成员;
  • StatefulSet 可以控制 Pod副本的启停顺序;
  • StatefulSet 里的 Pod 采用稳定的持久化存储卷,通过PV/PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。

StatefulSet 需要 配合 Headless Service 使用,每个 StatefulSet 要声明属于哪个 Headless Service。Headless Service 是没有 Cluster IP 的 Service;

Service


概念

Service 用来为 k8s 集群提供 服务发现和负载均衡的能力;

服务发现是 集群 对客户端对 Service 发起的访问,可以找到后端对应的实例;

每个 Node 上的 Kube-proxy 实现了 K8s 集群 微服务的负载均衡;

每个Service是一个微服务,Service 是用来定义一个服务的访问入口地址,前端的应用 通过 入口地址 访问后端的 Pod副本集群;

Service 与 后端的副本集群 通过 Label Selector 对应起来;

其中RC的作用是保证 Service的服务能力和服务质量;

多个微服务通过 TCP/IP 进行通信;

访问集群

Service中的每个 Pod 都有自己的 Endpoint (Pod IP + Container Port),用来被客户端访问;

访问集群的一般做法是 部署一个负载均衡的软件,用来为这组 Pod 集群 开启一个对外的端口 比如8080,并将集群的 Pod 的 Endpoint 加入 8080 端口的转发列表;这样,客户端就可以通过 负载均衡的 对外IP + 服务端口 来访问该 Pod 集群,再有负载均衡算法决定将请求转发到哪个 Pod;

对于 K8s,运行在每个 Node上的 kube-proxy 就是一个 将 Service的请求转发到 Pod上的 负载均衡软件;

K8s集群中的每个Service 都分配了一个 Cluster IP,每个服务就是具有唯一IP的通信节点,服务调用简化为 TCP网络通信;

Service的整个生命周期中 Cluster IP不会改变,因此 Pod 重建带来的 Endpoint改变也不会影响 Service;

操作

1、定义

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
  selector:
    tier: frontend

frontend 是刚才 Deployment 中定义 tomcat 这个 Pod 用的标签;

2、创建

service "tomcat-service" created

3、查看

kubectl get endpoints
# NAME            ENDPOINTS           AGE
# tomcat-service  172.17.1.3:8080         1m

kubectl get svc tomcat-service -o yaml

上述命令可以查看 Service的详细情况,targetPort 属性用来确定提供该服务的容器所暴露(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供TCP/IP接入;而port属性则定义了Service的虚拟端口。前面我们定义Tomcat服务时,没有指定targetPort,则默认targetPort与port相同。

4、多端口

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  ports:
  - port: 8080
   name: service-port
  - port: 8005
   name: shutdown-port
  selector:
    tier: frontend
服务发现机制

每个 的Service 都有一个唯一的Cluster IP 及唯一的名字,服务发现则需要通过 Service 的名字找到对应的 Cluster IP;

Kubernetes 通过Add-On 增值包的方式引入了DNS系统,把服务名作为dns域名,这样一来,程序就可以直接使用服务名来建立通信连接了;

三种 IP 与 外部访问集群方案
  • Node IP:Node 节点的物理网卡地址,集群之外 访问 K8s 的节点或服务都需要经过 Node IP;
  • Pod IP:Pod 的 IP地址,Docker Engine根据docker0网桥的IP地址段进行分配,K8s集群中一个Pod内的容器访问另一个Pod内的容器就是通过这个虚拟二层网络通信的,真实的 TCP/IP 流量则通过Node IP;
  • Cluster IP:Service 的 IP地址,是一个虚拟IP,无法被PING,仅作用于 K8s 创建的Service,由 管理分配;结合 Service Port才能作为一个具体的通信端口;

如果外部想访问集群的服务,常用的做法是给Service 配置 nodePort;

通过手动配置 nodePort: 31002,来指定 tomcat-service 的访问地址为 https://nodePort IP:31002;

NodePort 的实现方式是在 Kubernetes 集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用 任意一个 Node 的 IP地址+具体的NodePort 端口号 即可访问此服务,在任意Node上运行netstat命令,我们就可以看到 NodePort端口 31002 被监听(LISTEN);

负载均衡:最好有一个 硬件的负载均衡器 或 负载均衡软件(HAProxy 、 Nginx),外部请求访问负载均衡的IP,然后根据算法转发到合适的Pod;很多公有云也提供了这个服务,只需要定义Service时 ,type=LoadBalancer 即可;

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
spec:
  type: NodePort
  ports:
   - port: 8080
     nodePort: 31002
  selector:
    tier: frontend

Volume

1、概念

K8s 的 Volume 和 Docker 的 Volume 概念、用途、目的类似;

K8s 的 Volume 定义在 Pod上,被 Pod 中的多个容器挂载到具体文件目录;K8s 的 Volume 中的数据不会丢失,支持多种类型,比如分布式文件系统 Ceph 等;

2、使用

只需要在 定义 Pod 时,先声明一个 Volume,然后在 定义 Pod 中的 Container 时 引用该Volume并Mount到容器里的某个目录上;

template:
  metadata:
    labels:
      app: app-demo
      tier: frontend
  spec:
    volumes:
    - name: datavol
      emptyDir: {}
    containers:
    - name: tomcat-demo
      image: tomcat
      volumeMounts:
       - mountPath: /mydata-data
         name: datavol
      imagePullPolicy: IfNotPersent

3、类型

  • emptyDir:Pod 被分配到 Node时创建的,不用指定宿主机对应的目录,K8s 会自动分配,Pod 从 Node 移除时 emptyDir 的数据也会自动删除;适合无须持久保存的 ,比如程序运行时需要的临时目录,也可以作为多容器共享数据的媒介;

  • hostPath:用于需要永久保存的日志文件;

    volumes:
    - name: "persistent-storage"
      hostPath:
        path: "/data"
    
  • gcePersistentDisk:使用谷歌公有云提供的永久磁盘(Persistent Disk,PD)

  • awsElasticBlockStore:亚马逊公有云提供的EBS Volume

  • NFS:网络文件系统,需要部署一个 NFS Server;

    volumes:
     - name: nfs
       nfs:
         # 改为你的NFS服务器地址
         server: nfs-server.localhost
         path: "/"
    

PersistentVolume 和 PersistentVolumeClaim


概念

PersistentVolume(持久卷,简称PV)是集群内,由管理员提供的网络存储的一部分,也是集群中的一种资源(比如 Node),是一种 Volume 插件,生命周期 与 Pod 独立;

PersistentVolumeClaim(持久卷声明,简称PVC)是用户的一种存储请求,和 Pod 类似, Pod 请求与消耗 Node 资源(CPU、内存),而 PVC 请求与消耗 PV 资源(存储大小与访问方式);

管理员只关注 提供 PV,无需关注用户如何使用,用户只需要 挂载 PVC 到 容器,无需关注 存储技术的实现;

PV的状态有:Available、Bound、Released、Failed;

PV 定义
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce # 只能被单个节点以读写方式映射,还可 ReadOnlyMany 、ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  nfs:
    path: /tmp
    server: 172.17.0.2
PVC 定义
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}
工作方式

静态提供 PV:管理员 创建 PV,每个 PV 有自己的存储信息,像 资源对象一样,提供给 用户 用于存储;

动态提供PV:静态提供的 PV 不满足 PVC时,集群会专门提供 Volume 给 PVC;

PVC 与 PV 绑定:用户创建 PVC ,并指定需求存储的大小与访问方式后,master 会发现新的 PVC,并查找可以匹配的 PV,如果有则绑定到一起,如果没有则 PVC 会处于 unbound 模式,直到发现可以匹配的 PV并绑定;PVC 可以通过 Selector 过滤 PV,只有匹配了 Selector 指定标签的 PV 才能绑定到 PVC;

使用:Pod 使用 PVC 和 使用 Volume 类似,指定 PVC ,集群检查 PVC 并绑定 PV,然后映射 PV 给 Pod 使用,一旦指定的 PVC 绑定了 PV,这个PV就只能被这个Pod使用;

kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: dockerfile/nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

释放与回收:PV 使用完毕后,可以通过API删除PVC,此时 PV 处于 released 状态,PV的回收策略会通知集群该如何处理这个PV,可以被 保留、再利用 或 删除;

PV 类型
GCEPersistentDisk
AWSElasticBlockStore
AzureFile
AzureDisk
FC (Fibre Channel)
Flocker
NFS
iSCSI
RBD (Ceph Block Device)
CephFS
Cinder (OpenStack block storage)
Glusterfs
VsphereVolume
Quobyte Volumes
HostPath (仅测试过单节点的情况——不支持任何形式的本地存储,多节点集群中不能工作)
VMware Photon
Portworx Volumes
ScaleIO Volumes

Namespace

概念

Namespace 是命名空间,多用于实现 多租户的资源隔离;

Nameaspace 通过将集群内部的资源对象“分配”到不同的Namespce中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理(比如给不同租户设置资源配额);

在定义资源时,如果不指定 Namespace,默认为 default;

定义
apiVersion: v1
kind: Namespace
metadata:
  name: development
在资源对象里指定

在metadata里通过namespace声明;

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: development
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "3600"
    name: busybox
使用

1、根据namespace 筛选 要查看的 pods

kubectl get pods --namespace=development

2、查看集群中的namespace

kubectl get namespaces

Annotation

对资源对象的注解,用户可自定义资源对象附加信息,便于外部工具查找,集群也会通过Annotation的方式对资源对象进行标记;

常用来记录:

  • build信息、release信息、Docker镜像信息;
  • 团队联系信息等;
  • 程序调试工具信息;

Configmap

概念

Configmap 为集群内的应用 提供 配置管理;

Configmap 是一个或多个 键值对,保存在集群中,可以表示变量的值 或 文件内容;

可以通过 yaml 文件 或 kubelet create configmap 命令 创建;

创建与查看

如果用 kubelet create configmap 创建 则参数可以是 文件 或 目录:

如果是文件,则可以在命令中指定key,文件的内容就是value,可以同时指定多个;

kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source

如果是文件夹,则文件夹内的文件名是key,对应的内容是value

kubectl create configmap NAME --from-file=config-files-dir

如果参数 是 --from-literal,则可以直接跟 key和value

--from-literal=key=value

yaml创建 与其他操作如下;

sudo vim cm-appvars.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir:/var/data

kubectl create -f cm-appvars.yaml

kubectl get configmap

kubectl describe configmap cm-appvars

kubectl get configmap cm-appvars -o yaml
使用

1、在pod的yaml定义中 导入环境变量

 containers:
    - name: cm-test
      image: busybox
      command: ["/bin/sh","-c","env"]
      envFrom:
      - configMapRef:
         name: cm-appvars # cm-appvars 是 Configmap 的名称,里面的 key-value 会当作这个容器的环境变量

2、通过 volumeMount 将 value 转化成文件挂载到容器内

containers:
  volumeMounts:
    - name: serverxml                             # 引用volume名
      mountPath: /configfiles                     # 挂载到容器内部目录
  volumes:
    - name: serverxml
      configMap:
        name: cm-appconfigfiles                  # configmap的名称
        items:
        - key: key-serverxml                    # configmap的key
          path: server.xml                      # value转化成 server.xml 文件
        - key: key-loggingproperties            
          path: logging.properties            

暴露服务的方式


Kubernetes 暴露服务的方式目前 有三种:LoadBlancer Service、NodePort Service、Ingress;

Ingress

所有的请求,通过Ingress对应的 IP:PORT 进入,过滤/转发/负载均衡到相应的service/pod;

Ingress 由 Ingress Controller 和 Ingress 服务 组成;

Ingress Contronler 不停 与 Kubernetes API 交互,读取到 域名 和 Service的对应关系,并生成 Nginx 配置,完成域名配置的动态更新;

使用方式是 通过官方镜像创建 ingress-controller,并以 Pod + Service 方式运行,然后编写 ingress规则从而实现域名转发;

详情见:https://www.orchome.com/1452

Pod调度策略


一般情况下,k8s 集群会智能的将 Pod 调度到 资源充足的 Node下;

但是有些情况,我们希望 Pod 放在 更希望放的某个或某些 Node下;

nodeSelector 方式

这种方式 给 Node 打上 Label,然后在 Pod中的spec里加入 nodeSelector的设置;

1、给 Node打标签;

kubectl get nodes # 查看所有nodes

kubectl label nodes nodename key=value # 给某个node打标签

kubectl get nodes --show-labels # 查看node的标签

kubectl describe node nodename  # 或者

2、修改 Pod 的 yaml,通过 nodeSelector 设定要调度的 Pod;

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    key: value

kubectl apply -f pod.yaml # 使生效

kubectl get pods -o wide # 查看是否生效
Node 亲和

1、概念

Node亲和由两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

两者的区别是 第一个单词 required(要求)和 preferred(倾向);

顾名思义,required 即 必须要满足,和 nodeSelector 一样,而 preferred 则是优先尝试的意思;

2、实现

下面的例子表示,这个 Pod 只能放在 nodeSelectorTerms 中指定的 标签里(只满足一个即可),标签的key 可以设置多个,一个key的value也可以设置多个;

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value

3、实现 反亲和 、污点等;

上述例子中的 operator 设置为 In,代表亲和,反亲和可以设置为 NotIn;

Pod亲和

1、概念

Pod 亲和 是指,希望将 Pod 调度到 已经运行了某个 Pod 的 Node上;

Pod 亲和也有两种类型 ,和 Node 亲和完全一样;

2、实现

将该 Pod 调度到 运行着 security:S1 的 Node上,且不希望调度到 运行着 security:S2 的 Node上;

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0
适用pod亲和与反亲和的场景

Pod 亲和 与反亲和 配合 ReplicaSet,StatefulSets,Deployments 等一起使用时,更加有用!

因为可以轻松将 我们部署的服务 调度到希望的 Node;

举个例子,想让一个服务的每个 Pod 放在不同的 Node下,就可以 在 Deployments 设置,反亲和自己的 Label;

Pod 与 Node 1对1 定向调度

通过 指定 nodeName 实现;

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01
Taint和Toleration(污点和容忍)

污点和容忍可以互相配合,原理是,每个 Node 可以设置 一个或多个 Taint,每个 Taint 都有自己的 key、value 和 effect ,只有在 Pod 中的 tolerations 设置 相同的 key 和 effect,才能分配到这个 Node;

# 添加 Taint
kubectl taint nodes nodename key=value:effect

# 删除 Taint
kubectl taint nodes key:effect-

更多操作:https://www.orchome.com/1807

其他


为什么使用容器
  • 快速创建、部署
  • 任何操作系统迁移
  • 资源隔离
  • 资源更高效利用
K8s工作原理
架构

一个K8S集群由 Master Node 和 Worker Node 两种节点角色 组成;

1、Master Node

  • kube-apiserver:集群对外的接口,提供一套 RESTful 的 Kubernetes API,可供客户端和其他组件调用;
  • etcd:存储集群的信息,比如配置(configmap,secret)、集群状态等,是分布式key-value数据库;
  • kube-scheduler:对集群内部的资源进行调度,分配 pod 和 service;
  • kube-controller-manager:处理集群常规任务的后台线程,负责启动和关闭pod,并维持pod在指定的状态;

2、Worker Node

  • pod:Kubernetes最基本的操作单元。一个Pod代表着集群中运行的一个进程,它内部封装了一个或多个紧密相关的容器。
  • kubelet:和 master node 通信,从 master 接收 pod 的定义,然后启动里面的容器,并监控容器是否一直正常运行;
  • kube-proxy :管理节点的网络规则,并转发连接;

3、Kubectl:命令管理工具

kubectl get - 列出资源
kubectl describe - 显示资源的详细信息
kubectl logs - 打印pod中的容器日志
kubectl exec - pod中容器内部执行命令

以下部分的参考链接:

https://purewhite.io/archives/page/3/

Service

1、作用

pod的IP是动态的,因为当pod挂了后,集群为了维持应用状态,会启动一个新的pod,此时IP发生变化;

Service 对 pod 进行逻辑分组,通过 label 和 selector 加以区分;

每个 Service 都会有一个 cluster 内部可以访问到的 ip 地址,叫做ClusterIP;

ENDPOINTS就是Service 所关联的pod的ip地址和端口,一个 Service 由一组 backend Pod 组成,这些 Pod 通过 endpoints 暴露出来;

当请求service时,Service 可以将请求转发到目标pod;

每个worker node 的 kube-proxy 则检测 API Server 上对于 Service 和 endpoint 的新增或者移除;
新增Service 时,kube-proxy会在对应的pod上写入ip转发规则,删除Service 时kube-proxy也会删除对应pod的iptables;

通过 kubernetes 上 dns 相关的 addon,可以实现service的服务发现,并赋予服务域名;

2、类型

参考链接:https://www.cnblogs.com/devilwind/p/8891636.html

Service 有 ClusterIP , NodePort , LoadBalancer,ExternalIP,ExternalName等类型;

ClusterIP :默认类型,有自己的虚拟IP,只能在集群内部被访问;

NodePort :可以被外网访问,通过暴露端口来暴露服务,一个端口对应一个服务;

Ingress:解耦路由与Service的依附,外网访问Service时,直接访问到 ingress 的 endpoint,然后通过 Ingress 再转发到 Service

Pod

kubernetes的最小对象,代表应用的一个单一实例;

定义时需要赋予 spec的值,代表期望的状态,创建后会有status字段,集群通过status和spec来维护Pod的状态;

是1个或多个容器的集合,这些容器在同一个host上、共享网络与存储;

通常和controller(Deployments,ReplicaSets) 一起用,把Pod的定义(spec)attach到controller ,来控制 pod 的 replica,容错,自我修复等等。

ConfigMap 和 Secret

当kubernetes上的应用需要读取一些配置,且这些配置有更改的可能时:

  • 如果放在镜像内,则需要创建新的镜像,并创建新的容器,很麻烦;
  • 如果通过env传入容器,则需要重启所有容器;

ConfigMap:通过命令或者yaml文件创建一个 ConfigMap 后,可以通过Volume的形式在pods中读取;

Secret:可以看作是加密后的ConfigMap,为安全性考虑。

Volume

一个 Volume 会 attach 到一个 Pod 上,在 Pod 里面网络和存储是共享的,所以这个 Volume 可以被 Pod 中所有的 container 所共享。一个 Volume 和 Pod 的生命周期是一样的,不过却比 containers 要更长,这样可以使得数据可以在容器之间共享。

网络

1、每个Pod分配独立IP

Kubernetes 用 CNI(Container Network Interface) 来给 Pod 分配 IP;容器运行时向 CNI 申请 IP,然后 CNI 通过其下面指定的 plugin 来获取到 IP,并且返回给容器运行时。

2、容器之间的通信

每个Linux 都有独立的隔离的网络整体,叫做Network Namespace,一个Pod在一个Node上,Pod里的所有容器共享该Node的Network Namespace,因此同一Pod里的容器通过localhost就可以互相访问。

3、不同Node上的Pod之间的通信

可以通过一些软件定义的网络(Software Defined Networking)实现,比如 flannel,weave,calico 等。

4、外网和集群的通信

通过kube-proxy 暴露service,供外网访问。

应用的角度了解概念

参考链接:https://zhuanlan.zhihu.com/p/100644716

一个应用的运行需要 计算资源、网络通信、工作模型、更新部署、数据持久化。

1、计算资源(pod)

docker可以提供应用需要的计算资源,比如CPU、内存、网络、基础文件系统等,但docker不能直接在k8s集群运行,k8s可以部署的最小单元是pod,pod的特点:

  • k8s 的最小计算单元;
  • 包括一个或多个容器,共享IP、hostname与存储资源;
  • 调度的最小单位,一个pod只可以放在一个机器上;
  • 一个容器一个 pod 是 k8s 最常见的场景

k8s集群不直接控制 docker,而是控制 pod,因为加入一个应用由多个容器组成,每个容器之间只需要localhost通信,如果这些容器在一个pod中,就很方便的实现容器间的localhost通信方式,微服务也是这个原理。

2、网络通信

pod 的 ip 是不固定的,挂了后重启 ip 会变,service 从逻辑上把 pod 分组,并设置访问策略,一般通过 labal 和 selector 分组;

每个 service 都有一个集群可以访问到的内部 ip 地址,当建立一个新的 service ,每个node上的 kube-proxy 会 设置 iptables 规则,删除 service 时,每个node上的 kube-proxy 也会随之删除对应的iptables。

service 的 type 有 ClusterIP 、 NodePort 、LoadBalancer 、 ExternalName 、ExternalIP 等:

  • ClusterIP:默认的service type,一个 service 通过 ClusterIP 获取唯一的 Virtual IP,只能在集群内部被访问;
  • NodePort:除了有可以供集群内部访问的 ClusterIP外,还会把所有worker node 的 30000 - 32767 之间的某个端口(可以随机或指定) 映射到这个 service ,这样不管哪个node,只要通过端口就能定位访问到这个 service ;

3、工作模型

为了方便管理多个 Pod,k8s 抽象出了几种工作模型,通过保持模型的状态,实现对 Pod 的管理

k8s 从目标状态开始,不断追踪集群现在的状态,并使其满足目标状态。如此循环,可以得到一个目标驱动的、有自治愈效果的系统。

模型类型:ReplicaSet、DaemonSet、Job、CronJob、StatefulSets:

  • ReplicaSet:保持指定个数的pod运行,适合无状态的服务,比如静态web服务器;
  • DaemonSet:保证每个宿主机上运行一个 Pod,适合节点常驻的服务,比如监控、日志收集等;
  • Job:ReplicaSet 中的pod 退出会自动重启, Job 允许进程正常退出不重启,适合指定次数的任务或定时任务,比如数据备份等;
  • CronJob:基于时间来调度和创建 Job ;
  • StatefulSets:有稳定的网络与数据存储, pod 的创建和销毁有序,适合有状态服务,比如 mysql ;

4、更新部署

Deployment 的两种更新策略:

  • Recreate:销毁并重建所有pod;
  • RollingUpdate:滚动升级;

5、数据持久化:

Persistent Volumes 子系统来实现支持(PV)

认识

虚拟化,容器化,云端化

服务器虚拟化,有太多的优势,低成本、高利用率、充分灵活、动态调度等。

优势
  • 快速部署应用
  • 快速扩展应用
  • 无缝对接新的应用功能
  • 节省资源,优化硬件资源的使用
特点

可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)
可扩展: 模块化, 插件化, 可挂载, 可组合
自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展

功能
  • 多个容器协同工作(pod)
  • 存储系统挂载
  • 应用健康检测
  • 应用实例复制
  • pod自动伸缩
  • 负载均衡
  • 滚动更新
  • 资源监控
  • 日志访问
  • 调试应用

K8S 相关资源

https://www.orchome.com/1786

https://yeasy.gitbook.io/docker_practice/setup/kubeadm

https://k8s-install.opsnull.com/

https://github.com/opsnull/follow-me-install-kubernetes-cluster

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐