一 Service的引出背景
早期的'解决'方式 -->'Consul、Zookeeper'组件
'拓扑结构图'
二 Service
为'解决这个问题' Kubernetes 就为我们提供了'这样的一个对象' - Service
Service 是Kubernetes一种'抽象的对象',它定义了'一组 Pod 的逻辑集合'和一个用于'访问它们的策略',其实这个概念和'微服务'非常类似,可以理解为'一个Service'就是一个'微服务'
Service作用: 主要是提供'负载均衡'和'服务自动发现-->依赖于CoreDns'
具体解释: Kubernetes 为 Pods 提供自己的 IP 地址,并为'一组 Pod' 提供'相同的 DNS 名',并且可以在'Pods之间'进行'负载均衡'
Service 定义的抽象能够'解耦'这种关联-->'前端和后端'
+++++++++++++++++'Service解耦理解'+++++++++++++++++
假如:我们'后端'运行了3个副本,这些'副本都是可以替代的',因为前端'并不关心'它们使用的是哪一个后端服务,尽管由于各种原因'后端的 Pod 集合会发送变化',但是前端却'不需要'知道这些'变化',也不需要自己用一个'列表来记录'这些后端的服务,Service 的'这种抽象'就可以帮我们'达到这种解耦'的目的
(1)了解各种IP的概念
Node IP: Node '节点的 IP 地址' -->节点网络地址是配置在'节点网络'之上
Pod IP: 'Pod 的 IP 地址'
Cluster IP: 'Service 的 IP 地址'-->'Service VIP'-->没有配置在某个网络接口上'ICMP协议:不能ping',它只是'存在Service的规则'当中
(2)Service的类型
常见的'四种'形式
补充:Headless Service 算是'ClusterIP'的'特例' -->'None'
说明1: 创建'Nodeport类型'的Service,也会自动创建'Cluster IP'
说明2: 创建'LoadBlance类型'的Service,也会'自动创建'-->'NodePort IP'和'Cluster IP'
说明3: Service 能够支持 TCP 和 UDP 协议,'默认是 TCP 协议'
说明4: 使用'LoadBlance',如果不是在云上,会一直'处于pending状态'
(3)k8s各种port
1)'port'
不管Service是哪种类型'Type',这里的'port表示':Service暴露在Cluster IP上的端口
备注: <cluster ip>:port 是提供给'集群内部客户'访问Service的入口
2)'nodePort'
nodePort('首字母小写')是Kubernetes提供给'集群外部客户'访问Service入口的一种方式(另一种方式是LoadBalancer)
备注: <nodeIP>:nodePort 是提供'给集群外部客户'访问Service的入口
3)'targetPort'
targetPort是'pod上的启动监听端口',一般与'Dockerfile中EXPOSE声明暴露的端口一致'
备注:从port和nodePort上到来的数'据最终经过kube-proxy'流入到'后端pod的targetPort上'进入容器
4)'port、nodePort总结'
总的来说port和nodePort都是'Service的端口'
'差异点': 前者暴露给'集群内'客户访问服务,后者暴露给'集群外'客户访问服务
'相同点': 从这两个端口到来的数据都需要'经过反向代理kube-proxy'流入'后端pod的targetPod',从而到达pod上的容器内
+++++++++++++'理解误区'+++++++++++++
下面的'name'没有特殊的含义-->不是'endpoints'
注意
1) 如果'不指定targetPort',默认情况下'targetPort 将被设置'为与 port 字段'相同的值'
'出错(error)'的场景: targetPort省略,但是实际容器的端口'不等于'Cluster IP的服务端口
2)LoadBlance对应'暴露'的端口和集群内的端口'一致'
3) 关于Servic的Type='NodePort'的形式,如果不指定'nodePort',会随机分配一个'30000~32767'端口,也可以'手动指定'
备注1: Kubernetes 服务的 'NodePort 默认端口'范围是 30000-32767
备注2: '不建议'修改,让kubernetes自己来管理,手动可能'造成冲突'
++++'kubeadm修改方式'++++
vim /etc/kubernetes/manifests/'kube-apiserver.yaml'
修改端口的场景: '防火墙'内网'只开放8000-9000'端口,其他都墙了
备注: 关于是否'重启'kube-apiserver'有待'考证
systemctl daemon-reload
systemctl restart 'kubelet'
systemctl status kubelet -l -->'查看状态'
1)修改'/etc/kubernetes/apiserver'文件
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.96.0.0/12 --service-node-port-range=8000-9000"
2)Centos7 '重启服务'
systemctl daemon-reload
systemctl restart kube-apiserver
因为'标识端口的数据段'只有'两个字节',只有16位,2的16次方是65536,所以只有从0到65535这65536个端口了
备注: 0到1023是'被保留的',不能'自定义'的
说明: 先'粗浅'的这样认知
三 Service的创建
Service是'四层',不关心'其上payload-->载荷'的信息
Service也是属于'ns'下的资源
(1)Service有selector字段
1)k8s在创建Service时,会根据'标签选择器selector'(lable selector)来查找'具有该标签的Pod'
2)一旦'找到对应的Pod',据此创建与Service'同名的Endpoint对象'
3)通过'Service IP: port'访问的时候,Service接收'前端client请求'的时候,就会通过endpoint根据'转发策略'找到转发到'某一个Pod'上的容器中
备注: 具体转发到'哪个节点的Pod',由负载均衡'kube-proxy初期'就决定好的
4)理解上:Service 到 'pod backend' 的选择相当于'一个VIP' -->'类同'理解'Keepalived+Haproxy'
1.Endpoint是k8s集群中的一个'资源对象','存储在etcd中',用来记录一个Service对应的所有pod的'访问地址'
endpoint组成: 就是Service关联的'Pod的ip地址'+'端口'
2.只有当Service'配置selector(选择器)',endpoint controller才会'自动创建'对应的endpoint对象,否则'不会生成'endpoint对象
3.在k8s集群中创建'nginx的service','如果'ports不指定name,'默认'就会生成一个'同名'的endpoint对象
① 案例1:ClusterIP
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 2 # 期望的 Pod 副本数量,默认值为1
selector: '有这个字段'
matchLabels:
app: nginx
template: # Pod 模板
metadata:
labels:
app: nginx '具有该标签的Pod被deployments纳管、进而来管理pod的副本'
spec:
containers:
- name: nginx
image: nginx
ports: '列表的形式,container可以暴露多个端口,一般我们只暴露一个端口'
- name: wzj
containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx '选择具有哪些标签的pod' --> '一个 Serivce 下面包含的 Pod 集合是由 Label Selector 来决定的'
ports:
- name: http
protocol: TCP '可以省略'
port: 8080 'Service的端口'
targetPort: wzj '两种方式均可'
#targetPort: 80 '容器中的端口'
细节: 如果'不指定targetPort',默认targetport和port一致,可能会'访问不到'
部署
查看信息
已知: 我们知道Deployment通过控制'ReplicaSet',而'Replicaset'是通过'app=nginx'和'pod-template-hash'来管理pod的
思考: 我们再创建一个具有'app=nginx'的pod,是否会导致最终'Deployment控制两个副本',但是Endpoints'列表却有三个'? -->'后续验证'
继续查看
查看对应'service'的endpoints
访问方式
curl 'Cluster IP:port'
强调: 默认是'趋近于'-->'rr'
无头'没有Cluster IP'服务的特点:'spec:clusterIP: None',但是还是'需要slector'选择对应的pod
② 案例2:NodePort
备注: NodePort类型,两个'核心'的字段
1)spec.ports[].nodePort --> '不指定的话,系统在区间范围自行设置'
2)spec.clusterIp:spec.ports[].port
++++'观察实验现象'++++
观察1: 看是否'也创建Cluster IP'
观察2: 是否会起一个'随机端口'
备注: 'kubeadm方式'搭建的集群,'以上描述的并不准确',会在'集群中所有的节点(包含master)',起一个端口
为了说明: 会在'所有的node'启动一个端口,而不是'只在有pod的node'上启动端口,我们把副本数目改为1,观察'没有pod的node上'是否'有这个端口'
kubectl 'edit' deployments.apps nginx
说明: '在线编辑'的方式修改副本数目
说明: 事实上'master'也会i启动一个端口
访问方式
集群内: 'Cluster IP':'port'
集群外: '集群内的任何一个节点':'nodePort'
备注: 关于'kube-proxy'是什么? 我们'稍后'讲解
(2) Service没有slector字段
原理: 当service'没有配置'selector选择器,endpoint controller'不会'自动创建对应的'endpoint'对象
③ 案例3:自定义Endpoint
'自己定义'Endpoints来'创建Service'
++++++++++'要求如下'++++++++++
1)自定义Service没有IP,'ClusterIP=None'
2)自定义Service的没有'slector参数'-->'不需要选择pod'
3)'Endpoint的名称'要和 'Service 保持'一致
++++++++++'应用场景'++++++++++
场景:etcd集群部署在'k8s集群之外'的,换句话说'并没有以pod的形式'部署在集群内
前提:api-server与etcd做过'认证',是可以'相互通信'的
模板文件
apiVersion: v1
kind: Service
metadata:
name: etcd-k8s
namespace: kube-system '上面和下面要一致'
labels:
k8s-app: etcd
spec:
type: ClusterIP
clusterIP: None
ports:
- name: port
port: 2379 'etcd暴露的端口'
---
apiVersion: v1
kind: Endpoints '上面和下面要一致'
metadata:
name: etcd-k8s 注意: 名称必须'和 Service 一致'
namespace: kube-system
labels:
k8s-app: etcd
subsets:
- addresses:
- ip: 10.151.30.57 说明:Service 将连接'重定向'到 endpoint
- ip: 10.151.30.58
- ip: 10.151.30.59
ports:
- name: port
port: 2379 endpoint 的'目标端口'
备注:上面这个服务就是将外部的 etcd 服务引入到 Kubernetes 集群中来
④ 案例4:ExternalName
应用场景
'应用场景': 后续决定要将'集群外的数据库'迁移到 'Kubernetes 集群中',可以启动对应的 Pod,'只需要'增加'合适的' Selector 或 Endpoint,修改 Service 的 type,完全'不需要修改调用的代码',这样就完全'解耦了'
特点
说明: ExternalName 是 Service 的'特例','特点如下'
1)它没有 'selector' -->'不是典型的选择器'
2)也没有定义任何的'端口'和'Endpoint'
作用: 对于运行在'集群外部'的服务,它通过'返回该外部服务的别名'这种方式来提供服务
模板文件
kind: Service
apiVersion: v1
metadata:
name: mysql
spec:
type: ExternalName
externalName: database.example.com --> '指定外部服务的域名'
说明: 进入'busybox'这个pod,'nsllokup无法'解析该Cname原因,集群'找不到对应的ip'
达到的效果
当访问地址 mysql.default.svc.cluster.local'涉及服务发现'时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 'CNAME 记录'
注意:访问这个服务的工作方式与其它的相同,唯一'不同'的是'重定向发生在 DNS 层',而且'不会'进行代理或转发
备注: 由于上述'externalName'无法解析,这里换成'www.baidu.com'
两个不同的namespace之间的不同pod可以通过name的形式访问
实现方式:
A:在其他pod内ping [svcname].[namespace] ping出来到结果就是svc的ip地址
B:通过externalname,把'对方'到[svcname].[namespace].svc.cluster.local,'绑定到'externalname定义到字符串上 --> '核心关注这个'