文章目录
- Kubernetes介绍
- 术语
- Label
- StatefulSet
- 存储类
- Volume
- Persistent Volume
- IP地址
- Node IP
- Pod IP
- Service Cluster IP
- External IP
- Pod
- Pod定义
- Pod生命周期与重启策略
- NodeSelector(定向调度)
- NodeAffinity(亲和性调度)
- PodAffinity(Pod亲和性)
- ConfigMap
容器管理工具可以完成容器的基础管理,但是容器的应用并不是只能进行简单应用部署的,可以使用容器完成企业中更加复杂的应用部署,当需要对多应用的系统进行部署时,就需要更加复杂的工具来完成对容器运行应用的编排,这就是容器编排部署工具。
Kubernetes介绍
k8s是一个轻便的和可扩展的开源平台,用于管理多个主机上的容器化的应用,让部署容器化的应用简单并且高效,提供了应用部署、规划、更新、维护的一种机制,能进行应用的自动化部署及扩缩容。
k8s集群是Master和Worker的模式:
- Master节点上有kube-apiserver、kube-controller-mansger、kube-scheduler以及etcd进程,分别用于接收客户端请求并控制集群、资源对象的控制中心并监控容器健康、资源调度、资源对象数据存储等功能
- Worker节点上有kubelet、kube-proxy、docker,分别用于管理Pod及Pod容器并定时向Master汇报节点资源信息、实现Service的透明代理及负载均衡、运行容器。
术语
k8s中概念与术语大多围绕资源对象(Resource Object),其一般包括几个通用属性:版本、类别(Kind)、名称、标签和注解。
- 对象的名称要唯一;
- 对象的标签很重要(如表明对象的特征、类别);
- 注解通常用于对象属性的自定义扩展;
Label
Label(标签)是一个key=value
的键值对,可以附加到任何资源上。例如Node,Pod,Service,RC等,一个资源对象可以定义多个Label,一个Label可以被添加到多个资源上,通常在资源定义时确定,也可在在对象创建后动态添加或删除。
给资源对象定义个Label,就相当于给他打了一个标签;随后,可通过Label Selector(标签选择器)查询或筛选拥有某些Label的资源对象。标签匹配:
{key}={value}
:匹配所有拥有{key}={value}
标签的资源;{key}!={value}
:匹配所有不具有{key}={value}
标签的资源;{key} in ({v1}, {v2}
:匹配任意指定的标签;{key} not in ({v1}, {v2}
:匹配不具有指定的标签;- AND操作:通过逗号组合多个选择条件,如
name=redis, env!=test
label定义在metadata中:
metadata:
name: redis
labels:
name: redis
app: red-example
StatefulSet
StatefulSet用于有状态的中间件集群(每个节点有固定身份ID,规模固定,每个节点有状态且持久化数据到永久存储中):
- StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来 发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod 叫kafka-0,第2个叫kafka-1,以此类推。
- StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod 时,前n-1个Pod已经是运行且准备好的状态。
- StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来 实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数 据的安全)。
STS模式下的POD中,可通过{pod-name}.{service}
来访问节点。
存储类
存储类对象包括Volume、PersistentVolume、PVC和StorageClass
Volume
Volume是Pod中能够被多个容器访问的共享目录,k8s的Volume定义在Pod上,然后被Pod的多个容器挂载到具体的文件目录下;与Pod的生命周期相同,与容器的生命周期无关,当容器终止或重启时,Volume中的数据也不会丢失。
先在Pod上声明一个Volume,然后在容器中引用该Volume并Mount到容器中的某个目录上
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: test-volume
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
k8s提供了丰富的volume类型:
- emptyDir:在Pod分配到Node时创建的,初始内容为空,无需指定宿主机上对应的目录文件,K8S自动分配,当Pod从Node上移除时,emptyDir中的数据也会被永久删除。可用于:
- 临时空间
- 长时间任务的中间过程CheckPoint的临时保存目录
- 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
- 使用的是节点的存储介质,可通过设定emptyDir.medium为Memory以使用内存(会被记入容器的内存消耗,受到资源限制和配额机制的管理)
- hostPath:在Pod上挂载宿主机上的文件或目录。可用于:
volumes: - name: hostpath hostpath: path: "/path"
- 容器生成的日志需要永久保存时
- 需要访问宿主机上的Docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机
/var/lib/docker
目录,使容器内部应用可以直接访问Docker的文件系统。
- NFS:使用NFS网络文件系统提供的共享目录存储数据时,需在系统中部署一个NFS Server。
volumes: - name: nfs nfs: server: {nfs-server address} path: "/"
Persistent Volume
Persistent Volume(PV)和与之关联的Persistent Volume Claim(PVC)是一块网络存储,挂接到虚机上的‘网盘’。PV是K8s集群中某个网络存储中对应的的一块存储,与Volume相似,但有以下区别:
- PV只能是网络存储,不属于任何Node,但可以在任何Node上访问。
- PV并不是定义在Pod上的,而是独立于Pod之外定义的。
- PV目前只有几种类型:GCE Persistent Disks、NFS、RBD、iSCSCI、AWS ElasticBlockStore、GlusterFS
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvtest
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
server: {nfs-server address}
path: /path
PV的accessModes属性,目前有以下几种类型:
- ReadWriteOnce:读写权限,并且只能被单个Node挂载
- ReadOnlyMany:只读权限,允许被多个Node挂载
- ReadWriteMany:读写权限,允许被多个Node挂载
某个Pod想申请某种条件的PV,则首先需要定义一个Persistent Volume Claim(PVC)对象
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
然后,在Pod的Volume定义中引用上述PVC即可
volumes:
- name: pvtest
persistentVolumeClaim:
claimName: myclaim
PV有以下几种状态:
- Available:空闲状态
- Bound:已经绑定到某个PVC上
- Released:对应的PVC已经删除,但资源还没有被集群回收
- Failed:PV自动回收失败。
IP地址
k8s中有三种网络(每种代表不同的寻址空间):
- Node IP:Node节点IP地址,包含内部(集群节点间访问)和外部IP地址;
- Pod IP:访问Pod(仅限集群内部访问),Pod挂掉重启后会改变;
- Cluster IP:是访问Service的IP地址(仅能从内部访问);该服务提供通过标签管理Pod的功能(Pod重启后,IP地址改变,但标签不变);
- External IP:提供从外部访问Service访问的功能;
Node IP
Node IP是k8s集群中每个节点物理网卡的IP地址(真实存在的),查询Node IP:
kubectl get nodes # 获取节点名称
kubectl describe node {node-name} # 显示node详情,其中Address下的InternalIP字段即为Node IP
# 或直接查询
kubectl get nodes -o wide
Pod IP
是Docker Engine根据网桥的IP地址段分配的(通常为虚拟的二层网络)
kubectl get pods [-n admin] # 获取所有(admin空间下)的pod
kubectl describe pod [-n admin] {pod-name} # 获取pod的详情,IP字段即为分配的IP地址
# 或直接查询
kubectl get pod [-n admin] -o wide
Service Cluster IP
Service是Kubernetes最核心的概念,通过创建Service,可以为一组具有相同功能的容器用用提供一个统一的入口地址,并且将请求进行负载分发到后端的各个容器应用上。
kubectl get svc [-n admin] # 查询cluster IP
Service的Cluster IP(集群IP,是一个虚拟的IP):
- 仅仅作用于kubernetes Service这个对象,并由Kubernetes管理和分配IP地址;
- 无法被ping;
- 只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信的基础;
External IP
xternal IP是为了解决如何从外部访问service服务的,外部访问Service的方式有两种:
- 通过设置nodePort映射到物理机,同时设置Service的类型为NodePort;
- 通过设置LoadBalancer映射到云服务上提供的LoadBalancer地址(仅适用公用云)。
Pod
Kubernetes使用Pod来管理容器。每个Pod都有个一个特殊的Pause容器(称为‘根容器’),其对应的镜像属于Kubernetes平台一部分;除此之外,Pod还包含一个或多个用户业务容器。
- Pod是调度(以及复制管理)的基本单位,让多个应用进程一起有效地调度和伸缩。
- Pod中容器,共享PID、IPC、Network 和UTS namespace。
K8S对长时间运行容器的要求是:其主程序要一直在前台运行。
Pod定义
定义包含一个容器的Pod:
apiVersion: v1
kind: Pod
metadata: # 定义Pod元数据
nam: nginx-test
labels:
app: nginx-test
spec: # 容器详细定义
containers:
- name: nginx-test
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
containers是Pod中的容器列表:
spec:
containers: #容器列表
- name: string #容器名称
image: string #所用镜像
imagePullPolicy: [Always|Never|IfNotPresent] #镜像拉取策略
command: [string] #容器的启动命令列表
args: [string] #启动命令参数列表
workingDir: string #工作目录
volumeMounts: #挂载在容器内部的存储卷配置
- name: string #共享存储卷名称
mountPath: string #存储卷绝对路径
readOnly: boolean #是否只读
ports: #容器需要暴露的端口号列表
- name: string #端口名称
containerPort: int #容器监听端口
hostPort: int #映射宿主机端口
protocol: string #端口协议
env: #环境变量
- name: string
value: string
- name: INJECT_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: INJECT_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
resources: #资源限制
limits:
cpu: string #单位是core
memory: string #单位是MiB、GiB
livenessProbe: #探针,对Pod各容器健康检查的设置,如几次无回应,则会自动重启
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 #是否允许创建特权模式的Pod
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations
环境变量注入:
- 通过valueFrom,可把Pod或container信息注入到容器运行环境,方便获取;
- 通过downwardAPI,可把Pod或container信息挂载为文件(如上,会挂载后会在目录下生成labels和annotations两个文件);
Pod生命周期与重启策略
系统为Pod定义了各种生命周期状态:
状态值 | 说明 |
---|---|
Pending | Pod已创建,但Pod内的容器(一个或多个)镜像还没创建(包括在下载过程中) |
Running | Pod内的所有容器以创建,且至少一个容器处于运行状态、启动状态或正在重启 |
Succeeded | Pod内所有容器均成功执行后退出(且不会再重启) |
Failed | Pod内所有容器均已退出(且至少一个退出状态为失败) |
Unknown | 由于某些原因无法获取该Pod的状态(如网络通讯问题等) |
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,包括:
- 重启间隔为
sync-frequency*2n
(n为1、2、4、8…,最长为5分钟);成功重启10分钟后重置改间隔。
策略 | 说明 |
---|---|
Always | 容器失效时,kubelet自动重启改容器 |
OnFailure | 容器终止且退出码不为0时,重启 |
Never | 不会重启 |
NodeSelector(定向调度)
K8S的Scheduler服务负责Pod的调度(通常无法知道Pod最终会被调度到哪个节点上,但实际可通过标签来指定调度到的Node):
- 给Node打标签:
kubectl label nodes {node-name} {lbl-key}={lal-value}
; - 在Pod定义中添加nodeSelector设置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deploy
labels:
app: redis-app
spec:
replicas: 2
selector:
matchLabels:
app: redis-app
template:
metadata:
labels:
app: redis-app
spec:
containers:
- name: redis-pod
image: kubeguide/redis-master
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
# 选择有SSD硬盘的Node
nodeSelector:
disk-type: ssd
指定选择标签后,若未找到指定标签的Node,即使有其他空余Node,也无法正常调度。K8S还给Node预定义了一些标签:
- kubernetes.io/hostname
- kubernetes.io/os
- kubernetes.io/arch
NodeAffinity(亲和性调度)
NodeAffinity为亲和性调度策略(用于替换NodeSelector):
- RequiredDuringSchedulingIgnoredDuringExecution:必须满足指定规则(与NodeSelector类似,为硬限制);
- PreferredDuringSchedulingIgnoreDuringExecution:优先满足,尝试调度指定Node(为软限制);
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
containers:
- name: with-node-affinity
image: google/pause
NodeAffinity语法支持操作符有:In, NotIn, Exists, DoesNotExit, Gt, Lt
;有多个matchExpressions时,需满足所有才匹配。
PodAffinity(Pod亲和性)
Affinity标识多个Pod在同一拓扑域中共存;而Anti Affinity标识多个Pod在同一拓扑域中互斥。K8S内置了一下常用拓扑域:
- topology.kubernetes.io/region
- topology.kubernetes.io/zone
要求新Pod与security=s1
的Pod在同一Zone;但不与app=nginx
的Pod在同一个Node;
apiVersion: v1
kind: Pod
metadata:
name: anti-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- s1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
containers:
- name: anti-affinity
image: k8s.gcr.io/pause:3.1
ConfigMap
ConfigMap是k8s的一个配置管理组件,可以将配置以key-value的形式传递(通常用来保存不需要加密的配置信息,加密信息则需用到Secret)主要用来应对以下场景:
- 将配置信息和docker镜像解耦;
- 多个服务共用配置的情况;
有三种常见创建方式:
- 通过yaml/json文件创建(推荐):
kubectl create -f configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: test-conf namespace: test data: # 两个键key1、conf key1:value1 conf: |+ SESSION_LIFETIME: 3600 URL: "http://test-server:8080"
- 通过–from-file创建:
kubectl create configmap {cm-name} --from-file=app.properties
(文件名作为key,整个内容作为键)key.1 = value-1 key.2 = value-2
- 通过key-value字符串创建:
kubectl create configmap {cm-name} --from-literal=key1=value1 --from-literal=key2=value2
通过create方式创建若已存在,则报错;可通过以下方式更新(不存在时自动创建):
kubectl create configmap nginx-config --from-file first.yaml --from-file second.yaml -o yaml --dry-run=client | kubectl apply -f -
用户配置文件:
- 将配置文件内容保存到ConfigMap中:key为文件名,value为文件内容;
- 在Pod配置文件中,将ConfigMap定义为特殊的Volume进行挂载。当Pod创建时,ConfigMap中配置文件会自动还原到Node本地目录,并映射到Pod里;
- 当ConfigMap内容修改后,K8s会自动重新获取,并更新节点上对应的文件。