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
如果没有任何第三方工具,可以使用:
进入 pod 的 exec 模式
kubectl exec pod_name -- /bin/bash
转到
cd /sys/fs/cgroup/cpucpu
使用情况运行cat cpuacct.usage
转到
cd /sys/fs/cgroup/memory
内存使用运行cat memory.usage_in_bytes
cpu文件夹下,可以使用cpu.cfs_quota_us
和cpu.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部分
评论区