目 录CONTENT

文章目录

kubernetes - k8s一切皆容器

FatFish1
2024-12-18 / 0 评论 / 0 点赞 / 76 阅读 / 0 字 / 正在检测是否收录...

k8s概述

k8s是一个管理跨主机容器化应用的系统,功能包括应用部署、高可用管理、弹性伸缩等,具备强壮的集群自恢复机制,包括容器启动、自动重调度、自动备份。

pod是k8s对容器分组管理的核心概念

pod高于容器,可以想象成是一个篮子,容器是篮子里面的鸡蛋

以一个PHP网站为例,3个前端副本,1个master+2个slave组成的redis集群,前端向redis中不定期读写数据,在使用k8s管理的情况下,将使用replication controller(副本控制器)进行管理。3个前端副本将启动3个pod,他们的pod标签都是name=frontend,且具有该标签的pod不会调度在同一个node节点上。

这些容器可以直接使用ip:port的方式进行通信,且为了避免pod漂移后IP发生变化,pod里面还会启动service组件用于分配固定ip。

可以看出来有以下几点:

  • pod里面的容器共享1个pod的总资源

  • label是贴在pod上面,而非容器上

  • IP分配给pod,容器共享这个IP

  • 哪怕只有一个容器,k8s也会给他分配一个pod

pod设计解读

pod可以共享IP、资源、volume、namespace(例如IPC、UTS)。

pod内容器具备以下特性:

  • 通过kubernetes的volume机制,在容器之间可以共享存储

  • 可以通过localhost直接访问另一个容器

pod的标签属性

labels的组织形式是”key1”:”value1”的形式,一个pod可以有多个labels

查询指定标签的pod

kubectl get pods -l name=nginx

labels的一个key可以用于一个资源管理维度,不同K8s对象携带一个或一组相同的label是k8s进行多维度资源管理的精华所在。

label selector是Kubernetes核心的分组机制,通过selector,客户端或用户可以识别一组具有共同特征的Kubernetes对象。selector有两种查询条件:

  • 基于值相等的查询条件:支持=、==、!=,即选中的key等于对应value的查询条件

  • 基于子集的查询条件:支持in、notin、exists条件,即选中的key包含/不包含xx值

pod使用和建立

根据k8s设置构建和查看pod

// 构建pod
kubectl create -f obj.json
// 查看pod
kubectl get pods
// 查看pod日志信息,容器可选
kubectl logs podtest <master1>

其中obj.json是k8s配置文件k8sManifest

编写k8sManifest

上例中的obj.json是一个简单的k8sManifest,内容如下:

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "podtest,",
    "labels": {
      "name": "redis-master"
    }
  },
  "spec": {
    "containers": [
      {
        "name": "master1",
        "image": "k8stest/redis:test",
        "port": [
          {
            "containerPort": 6379,
            "hostPort": 6388
          }
        ]
      },
      {
        "name": "master2",
        "image": "k8stest/sshd:test",
        "port": [
          {
            "containerPort": 22,
            "hostPort": 8888
          }
        ]
      }
    ]
  }
}

k8sManifest字段释义

  • kind:必填,Endpoints、ConfigMap、ReplicationController、Deployment、Job、Pod、ReplicaSet、Secret、Service等

    Endpoints:可以把外部连接引入到k8s系统

    Service:部署一个内部虚拟IP,做k8s的端口映射,其他deployment可以链接,

    Secretes:存储和管理敏感数据,例如密码、token、秘钥等

    Depolyment:部署一个Pod,内部只能链接servie,无法互联。支持滚动升级和回滚应用,扩缩容、暂停和继续deployment

    Pod:表面该对象是一个Pod,无特殊含义

  • metadata:必填

    • name:必填,Pod名,同一个namespace下唯一

    • label:非必填,标签,用户自主标识

  • namespace:非必填,命名空间,为空则为default

  • sepc:必填

    • replicas:非必填,副本数

    • strategy:非必填,升级策略

    • container:必填,容器,核心标签,下面的子标签见下面spec.container属性列表

    • volumes:非必填,如果在spec.volumes下面填写,则代表的是容器的公用volumes,注意与container里面的volume区分

spec.container标签释义

  • name:必填,容器名

  • image:必填,镜像地址

  • command:非必填,启动docker的命令,即容器启动后自动执行的某个命令,可以用这个命令唤醒脚本

  • imagePullPolicy:非必填,镜像拉取策略,IfNotPresent为不存在时拉取

  • resources:非必填

    • request:非必填,对cpu、memory需求进行指定

    • limits:非必填,对cpu、memory限制值进行限定

  • env:非必填,环境变量,如果填的话,格式是name: value:

  • ports:非必填,端口暴露

  • volumeMounts:非必填,挂载卷相关

    • name:非必填,挂载volume名字

    • mountPath:非必填,挂载volume路径

容器网络

使用docker ps可以查看k8s启动的容器,可以看到一个gcr.io/google_containers镜像启动,负责完成端口映射

进到master1容器里面,直接使用ssh可以登录到master2的sshd容器

ssh root@127.0.0.1 -p 8888

在master2也可以对master1进行telnet端口6379发现也通

各种kind设计理念

pod

以第2节为例,一个基础的pod具备的能力就是管理容器。

pod是k8s中最最基础的设计理念。

不同的kind资源对象,在manifest文件定义中实际是spec字段的差异,前面的apiVersion、metadata是基本一致的

ReplicationController

作为pod的守护进程,管理pod的状态值:

  • pending:pod创建请求已被系统接受,但pod内还有一个或多个容器未启动,可能涉及下载docker镜像的网络传输时间和调度时间

  • running:容器处于运行状态或重启过程

  • succeeded:pod中所有容器正常退出,不需重启

  • failed:pod中所有容器已退出,且至少有一个容器因为发生错误而退出,即非exit 0

  • unknown:因为某些未知原因,k8s无法获取pod状态

RC的作用就是保证集群中有指定数目的 pod 运行。

当前运行的 pod 数目少于指定的数目,RC 就会启动新的 pod 副本,保证运行 pod 数量等于指定数目。当前运行的 pod 数目大于指定的数目,RC 就会杀死多余的 pod 副本。

在RC这类kind中,k8smanifest文件使用template标签,预定义pod模板来创建pod

"spec": {
  "replicas": 2,
  "selector": {
    "name": "redis"
  },
  "template": {
    "metadata": {
      "labels": ……
      "spec": {
        "containers": ……
      }
      
    }
  }
}

可以使用kubectl get 命令查看相关类型资源:

kubectl get replicationController -o wide
kubectl get pods -o wide

可以看到RC自动创建了一个pod。

RC常常用于重调度场景,例如删pod,自动创建。

service

重调度场景中IP可能会变,这里就使用service作为IP代理

service主要由一个IP和一个label selector组成,当声明Service的时候,会自动生成一个cluster IP,这个IP是虚拟IP。我们就可以通过这个IP来访问后端的Pod,当然,如果集群配置了DNS服务,比如现在 的CoreDNS,那么也可以通过Service的名字来访问,它会通过DNS自动解析Service的IP地址。

"spec": {
  "selector": {
    "app": "MyApp"
  },
  "ports": [{
    "protocol": "TCP",
    "port": 80,
    "targetPort": 9376
  }]
}

创建一个service后,可以使用get命令查看

kubectl get service -o wide
kubectl get svc -o wide

该service会将监听80端口,并将外部访全部转发到9376端口,label匹配app:MyApp的pod,这个service可以称为一个微服务,这个service由replicas参数决定,可能有多个pod,共同负责提供服务,通过微服务管理的架构提供轮询能力,例如SpringCloud架构中的Ribbon,一个通过Ribbon组织的微服务架构,k8s将请求转发到Ribbon监听的端口,再由Ribbon转发到内部各个pod中

有时会考虑到每一组pod作为一个service,都提供对外服务,暴露端口是不安全的,一个常见的做法就是,构建一个专门对外的edge转发服务作为service,其他微服务不作为service,不对外暴露端口,所有请求全部来到edge服务,并由edge服务经过一定策略转发给对应的内部微服务

ReplicaSet

新一代副本控制器

相比于RC,新增了对label selector中基于子集的查询条件。deployment基于RS进行pod创建、更新、删除,从而使deployment成了目前最主流的部署模式。

manifest与RC类似,使用template构造模板。也可以使用get指令查找

kubectl get relicaset -o wide

deployment

deployment基于RS提供副本管理能力,同时支持pod的rolling-update。创建deplyment和RC也类似,使用template进行创建,也可以使用get方法进行查看:

kubectl get deployment -o wide

而deployment最核心的在于其更新能力,有两种方式:

// 使用apply命令应用新yaml或json
kubectl apply -f new-deployment.yaml
// 使用kubectl edit命令在线修改
kubectl edit pod xxxx <-n namespace>

这时pod会进行滚动升级,首先创造一个新的RS,设定副本数为1,然后旧的RS减1,新的加1,依次完成替换

此外deployment支持回滚,当pod启动失败时执行:

// 首先查看版本
kubectl rollout history deployment/my-deployment
// 执行回滚操作
kubectl rollout undo history deployment/my-deployment --to-revision=xx
// 暂停或恢复回滚过程
kubectl rollout pause   kubectl rollout resume

DaemonSet

用于在每个工作节点上运行相同的pod副本,例如在每个工作节点上运行一个logstash日志收集。

ConfigMap、Secret

使用一系列键值对存储被pod或系统组件访问的信息。ConfigMap存储简单文本,Secret存储敏感信息

假设有配置文件prop.properties,可以使用create方法基于properties构造congfigMap,也可以基于get操作导出

kubectl create configmap prop --from-file=configmap
kubectl get configmaps prop -o yaml

生成类似的yaml:

metadata:
  name: prop
data:
  example.property.1: hello
  example.property.2: world
  example.property.file: |-
    property.1=value-1
    property.2=value-2
    property.3=value-3

data标签用于管理键值对

此外还可以在pod的manifest中使用env进行引用

spec:
  containers:
    - name: test-container
      image: xxxxx
      command: ["/bin/sh", "-c", "env"]
      env:
        - name: SPECIAL_KEY
          valueFrom:
            configMapKeyRef:
              name: prop
              key: example.property.1

Job

job用于管理一些一次性的任务,例如服务升级前刷库操作,刷完就可以停了。job可以作为其他pod的前置条件

kubernetes核心组件

包括APIServer、scheduler、controller manager

k8s的架构也是master节点+node节点

APIServer

提供restful接口做k8s对象增删改查、配置资源对象、提供可定制插件、系统日志收集

scheduler

资源调度器,根据特定的调度算法把pod调度到指定的工作节点上

调度策略包括Predicates和Priorities,前者计算能不能,后者在能的基础上计算哪个pod优先级更高。

Predicates

  • PodFitsHostPorts:端口不冲突

  • PodFitsResource:资源够用

  • NoDiskConflict:挂载卷volume不冲突

  • NoVolumeZoneConflict:挂载卷的zone限制与node的zone-label匹配

  • MatchNodeSelector:符合NodeSelector选择条件

  • HostName:是否指定了pod.Spec.Host要求在特定节点运行

此外还会检查挂载的AWS EBS Volume是否超过限制(默认39),检查挂载GCE Persistent Disk是否超过限制(默认16)

Priorities

  • LeastRequestedPriority:尽量把pod调度到资源占用小的节点

  • BalancedResourceAllocation:调度时偏好CPU和内存利用率相近的节点

  • SelectorSpreadPriority:基于rc和service负载均衡的高可用及流量分布均衡

  • ServiceSpreadingPriority:SelectorSpreadPriority的古早版本

  • NodeAffinityPriority:通过用户在manifest中指定的pod工作节点亲和性

  • EqualPriority:平等对待每个节点

  • ImageLocalityPriority:根据主机上已存在的且将会被待调度pod使用到的镜像大小进行打分

controller manager

负责管理各种控制器,例如replication controller、node controller

包括:

  • endpoint controller:维护endpoint及其对应service的关系

  • replication controller:负责保证rc管理的pod的期望副本数与实际运行的pod数量匹配,在pod期待状态发生变化时向APIServer发送请求,调整系统中endpoint对象的状态

  • gc controller:在用户启动pod的垃圾回收功能时,将系统处于终止状态的pod删除

  • node controller:检查kubernetes工作节点是否可用,定期检查所有运行在工作节点上的kubelet进程

  • resource quota controller:资源配额控制器,以一个namespace为单位进行配置,期望值由集群管理员静态设置,实际使用值会在集群运行过程中随着资源的动态增删不断变化

kubernetes存储

kubernetes的volume类似于虚拟机磁盘,通过volumeMounts的方式给pod挂一个逻辑磁盘,pod就可以访问了。

pod容器内的进程可以看到的文件系统包括:docker镜像文件系统、零个或多个volume。

在k8s中,volume和pod的生命周期是一致的,但是volume不会随着pod内容器的销毁而销毁,即volume生命周期>=容器

挂载容器的方法

spec:
  containers:
    - name: test-container
      image: xxxxx
      volumeMounts:
        - mountPath: /redis-master-data
          name: redis-data
  volumes:
    - name: redis-data
     emptyDir: {}

首先在pod里面声明了一个volume叫redis-data。声明类型是emptyDir,声明后可以在宿主机/var/lib/kubelet/pods/<podid>/volumeskubernetes.io~empty-dir看到它。

然后在containers里面通过volumeMounts挂载它。这时name要与声明的name相同,而moutPath标识挂载点,挂载后在pod里面也可以看到/redis-master-data目录。

volume的类型

  • EmptyDir:pod被创建的时候一起创建的空目录,随pod删除而删除

  • HostDir:江苏主机上的文件或目录挂载到pod,不随pod迁移而迁移

  • Secret:用于将敏感信息例如密码以文件形式传递给pod

其他类型用到再看

kubernetes网络

k8s使用的网络模型是单pod单ip模型,即为每个pod分配一个k8s集群私有网络地址段的IP地址,通过该IP,pod可以跨网络与其他物理机、虚拟机或容器通信,pod内的容器共享pod网络,使用localhost通信

kubernetes多租户管理与资源控制

namespace是k8s进行多租户资源隔离的主要手段

查看namespace相关命令如下:

kubectl namespace myspace
kubectl get namespace

k8s支持namespace隔离资源对象类型包括pod、service、replication controller、event、endpoint等,可以在etcd上的/registry目录看到:

/registry/<resourceType>/<resource.Namespace>/<resource.Name>

当将一个pod调度到一个特定主机上时,该pod在etcd上的存储路径形如:

/host/<host>/pod/<pod.Namespace>/<pod.Name>

k8s资源组和docker联动与检查

检查k8s pod的内存和cpu情况

最简单直观的方法 - 使用metrics:

kubectl top pod podname --namespace=default

如果没有任何第三方工具,可以使用:

  1. 进入 pod 的 exec 模式 kubectl exec pod_name -- /bin/bash

  2. 转到cd /sys/fs/cgroup/cpucpu 使用情况运行cat cpuacct.usage

  3. 转到cd /sys/fs/cgroup/memory内存使用运行cat memory.usage_in_bytes

cpu文件夹下,可以使用cpu.cfs_quota_uscpu.cfs_period_us计算核数

#!/bin/sh
quoto = `cat cpu.cfs_quota_us`
period = `cat cpu.cfs_period_us`
cpu_core = `$quoto / $period`
echo "cpu_core:$cpu_core"

k8s节点

通过以下命令可以快速找到对应节点

kubectl get pods -o wide -n xxx | grep xxx

查看pod的message日志:

cd /var/log/containers

cgroup机制

k8s继承了linux的cgroup机制,对于k8s来说,它的进程组就是一个Manifest文件中组织的所有container

假如Manifest中定义了三个container,replicas=3,即启动3个pod,每个pod都由3个container组成,这样每个pod都是一个cgroup,各自管理各自的虚拟内存

在k8s场景下,linux中的OOM_Killer同样工作在每个cgroup中,当一个pod组内存过高,OOM_killer就会杀掉这个pod,再根据Manifest中定义的恢复策略进行重启

cgroup机制可参考linux部分

0

评论区