为何使用K8S
由于大型单体应用转变成微服务架构,因为微服务能独立开发、部署、升级、伸缩,更加灵活。但由于微服务组件越来越多,使得配置、管理并保持系统的正常运行越来越困难,所以K8S提供了对它们的自动调度、配置、监管和故障处理的功能。开发者可以自主部署应用,选择回退版本、多版本部署、控制部署的频率。
Pod
Pod是容器组。一个Pod下的所有容器共享相同的主机名和网络,能够在内部通过回环地址与其他容器互相通信,所以注意同个pod下的容器不能绑定相同的端口号,否则会导致端口冲突。不同Pod下的容器永远不会遇到端口冲突。同一Pod中的各容器是共享一些资源的,其实就是namespace是打通了共享的资源。Pod有如下状态:
- pending(挂起) 例如没有适合的节点运行pod
- running (运行)
- failed (失败)
- succeeded(成功)
- unknown
同一pod下的共享资源有如下:
PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID;
网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围;
IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信;
UTS命名空间:Pod中的多个容器共享一个主机名;
Volumes(共享存储卷):Pod中的各个容器可以访问在Pod级别定义的Volumes
Node
Node是Pod真正运行的主机,可以是物理机,也可以是虚拟机。为了管理Pod,每个Node节点上至少要运行container runtime(例如docker)、kubelet和kube-proxy服务,Node有如下状态:
- Ready:节点正常
- NotReady: 节点断开
Namespace
相同Namespace的服务可以相互通讯,反之相互隔离。此Namespace与pod中的命名空间是不一样的,k8s 中的Namespace指的是提供了一个作用域,而Pod中命名空间是linux内核提供的命名空间(主机名、网络、文件系统、进程等各个维度)。所谓的作用域,例如通过建立Service访问Pod时,如果Service的Namespace不正确,那么就无法正常关联到Pod。k8s中的集群默认会有一个叫default的Namespace。主要是2个:
- default:App默认被创建于此。
- kube-system:kubernetes系统组件使用。
这个默认(default)的Namespace并没什么特别,但你不能删除它。这很适合刚刚开始使用k8s和一些小的服务的系统。但不建议应用于大型生产系统。因为在复杂系统中,团队会非常容易意外地或者无意识地重写或者中断其他服务Service。相反,请创建多个命名空间来把你的服务Service分割成更容易管理的块。
kubectl get ns
查看命名空间列表
--通过文件创建--
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: new-namespace
$ kubectl create -f ./my-namespace.yaml
--或者使用apply--更新
--删除,注意会删除该空间下所有资源--
$ kubectl delete namespaces new-namespace
注意删除一个Namespace后,会把此Namespace下的所有资源删除,还有可能会出现Terminating状态的namespace,这里不细讲
kubectl delete ns 名称 --force --grace-period=0
Deployment
ReplicationController现在已经过时了,建议使用Deployment 配合ReplicaSet。ReplicationController的主要功能是保证Pod的数量、健康,弹性收缩等。但是Deployment除了有这些功能之外,还增加了回滚功能(当升级 pod 镜像或者相关参数的时候,如果有错误,可以回滚到上一个稳定版本),版本记录(每一次对 Deployment 的操作都能保存下来)。暂停和启动(升级的时候,能随时暂停和启动)。虽然ReplicaSets可以独立使用,但是它主要被作为协调作用的Pod创建,删除和更新的机制。在k8s中,有一个叫做kube-controller-manager的组件。这个组件,是一系列控制器的集合,我们创建的Controller按定义的配置进行容器编排
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment-v1
spec:
replicas: 3
selector:
matchLabels:
app: busybox-v1
template:
metadata:
labels:
app: busybox-v1
spec:
containers:
- name: busybox-host
image: busybox:1.31.1
command: ["sleep"]
args: ["1000"]
Labels
spec.selector.matchLables
与spec.template.metadata.lables
的区别。正确的Deployment书写方式,是要让这2个值完全匹配,这样才不会报错。matchLables是必须的。例如app:nginx表示用app=nginx的标签来创建pod实例
StatefulSet
Deployment不足以覆盖所有的应用编排问题,因为在它看来,一个应用的所有Pod是完全一样的,所以它们之间就没有顺序,也无所谓运行在哪台宿主机上。需要时,Deployment就通过Pod模板创建新的Pod,不需要时,就"杀掉"任意一个Pod。但是在实际场景中,并不是所有应用都满足这样的要求。比如:主从关系、主备关系、还有就是数据存储类的多个实例通常会在本地磁盘上保存一份数据,而这些实例一旦被杀掉,即使重建出来,实例与数据之间的对应关系也已经丢失,从而导致应用失败。这种实例之间有不对等关系,或者有依赖关系的应用,被称为“有状态应用(Stateful Application)”,为了支持这种,Kubernetes在Deployment基础上扩展出了StatefulSet。这块功能可以用其他方式解决,对于公用的基础依赖,应该单独抽离出来,配合Deployment也可以落地实现
Service
通过Service来访问Pod,外部的Service可以通过其他的Service的ClusterIP来访问Pod。每个PodIP由网络插件动态随机分配(Pod重启后IP地址会改变)。ClusterIP只在集群内部可访问
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector: #service通过selector和pod建立关联
app: nginx
不设置type,这种Service查看详情会发现
spec:clusterIP=None,这种被称为headless serivces,通过这种方式selector选择的pod可以直接从service的endpoints暴露的ip端口进行连接,而不是通过service的CLUSTER-IP间接访问pod
kubectl describe svc myservice
查看详情
ClusterIP类型
当使用ClusterIP类型,可以使用以下方式访问,这种方式的负载均衡已经确定,可以使用Istio组件来替换默认的负载均衡
$ kubectl proxy --port=8080
curl http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/
NodePort类型
当使用NodePort类型,在所有Node上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。NodePort 服务主要有两点区别于普通的“ClusterIP”服务。第一,它的类型是“NodePort”。有一个额外的端口,称为 nodePort,它指定节点上开放的端口值 。如果你不指定这个端口,系统将选择一个随机端口。大多数时候我们应该让 Kubernetes 来选择端口,让开发者自己来选择可用端口比较麻烦。这种方式每个端口只能是一种服务,而且端口范围只能是 30000-32767。不建议在生产环境上用这种方式暴露服务。如果你运行的服务不要求一直可用,或者对成本比较敏感,你可以使用这种方法。这样的应用的最佳例子是 demo 应用,或者某些临时应用
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
selector:
app: my-app
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30036
protocol: TCP
LoadBalancer类型
使用LoadBalancer类型。这个方式的最大缺点是每一个用 LoadBalancer 暴露的服务都会有它自己的实际的IP地址,用在公有云上,不允考虑
Endpoints
Endpoints把k8s外部应用或服务的address和port发布到k8s,作为内部Service的依赖。如果endpoints和service是同一个名字,那么就自动关联
apiVersion: v1
kind: Endpoints
metadata:
name: mysql-process
namespace: mysql
subsets:
# k8s外部服务的地址和端口
- addresses:
- ip: 10.0.0.82
ports:
- port: 3306
内部虚拟IP,只有k8s内部的控制器可连接
apiVersion: v1
kind: Service
metadata:
name: mysql-process
namespace: mysql
spec:
ports:
- port: 3306
Ingress
Ingress是对service的更高层次的抽象,service是工作在tcp/ip层,基于ip和port的,那么ingress是针对http 7层路由机制,将客户端的请求直接转发到service对应的后端pod服务上
使用Ingress类型。最强大的,最灵活。Nginx ingress是k8s所推荐的默认的ingress。缺点:当路由配置非常大的时候,Nginx reload 会耗时非常久,可以达到几秒甚至十几秒,Nginx ingress的插件能力和可扩展性比较差
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: other
servicePort: 8080
rules:
- host: foo.mydomain.com
http:
paths:
- backend:
serviceName: foo
servicePort: 8080
- host: mydomain.com
http:
paths:
- path: /bar/*
backend:
serviceName: bar
servicePort: 8080
发布文件配置说明
appVersion
1.extensions/v1beta1是用于kubernetes版本在1.6之前
2.apps/v1beta1是用于1.6-1.9版本之间
3.apps/v1是1.9版本以后使用
目前使用的是1.16.3版本可以正常使用apps/v1,使用下面命令查看是否支持某项声明
kubectl api-versions