2、深入掌握Service
Service是Kubernetes中访问应用容器的入口,可以为一组相同功能容器提供一个统一的入口地址,提供负载均衡服务。
关键:服务访问入口、负载均衡、一组容器
1、基本用法
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: init-nginx-index
image: busybox
command:
- /bin/sh
- -c
- 'ls /; echo "this is $(hostname)" > /work-dir/index.html; ls /;'
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
nodePort: 30001
1、根据RC快速创建Service
kubectl expose rc [rc-name]
2、多端口Service
一个容器应用可以暴露多个端口服务,一个Service也能映射多个端口
1、一个Service映射容器多个端口也可以映射不同协议
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
- protocol: TCP
port: 80
targetPort: 9376
2、一个Service映射同一端口的
3、负载策略
1、默认是轮训(每个IP都是独立的轮训)
2、设置基于IP的会话保持
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
nodePort: 30001
sessionAffinity: ClientIP
4、指向固定IP的Service
上面的所有Service都是通过Label Selector负载到指向Pod的IP,Pod的IP发生改变时,Service中的IP集合也会发生改变。
这个IP集合就是EndPoint对象,EndPoint的内容是动态的。
Service创建时会创建一个EndPoint,EndPoint和Service的Name相同,我们可以通过Service的Name找到对应的EndPoint
我们可以创建没有Label Selector的Service,然后添加一个Name和Service一样的EndPoint,就可以得倒一个固定IP的Service了。
apiVersion: v1
kind: Service
metadata:
name: endpoint-test
spec:
type: NodePort
ports:
- port: 80
nodePort: 30002
sessionAffinity: ClientIP
---
apiVersion: v1
kind: Endpoints
metadata:
name: endpoint-test
subsets:
- addresses:
- ip: 8.8.8.8
ports:
- port: 80
protocol: TCP
kubectl get ep
kubectl edit ep [ep-name]
5、Headless Service
一个Service包括:1、service IP 2、Endpoint
没有Service IP的Service就是Headless Service,没有Service IP,就不能通过该Service IP对EndPoint中的Pod IP集合进行负载均衡访问。而只是存放着Pod的IP集合,Pod变动IP时,该集合也会变。
类似于注册中心,可以获取某个RC或者Deployment下的所有Pod的地址,可以在此基础上实现自定义的负载均衡。
1、创建一个Headless Service
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: init-nginx-index
image: busybox
command:
- /bin/sh
- -c
- 'ls /; echo "this is $(hostname)" > /work-dir/index.html; ls /;'
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: headless-service
spec:
selector:
app: nginx
ports:
- port: 80
clusterIP: None
2、程序中获取Service中的Endpoint
API文档:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#endpoints-v1-core
不同语言调用:https://kubernetes.io/zh/docs/reference/using-api/client-libraries/
https://{ip}:{port}/api/v1/namespaces/{namespace}/endpoints
ip和port在kube-api-server启动参数中配置,–advertise-address和–secure-port
如果是kubeadm的方式配置的,可以通过kubectl edit修改或者在/etc/kubernetes/manifests路径下进行修改
6、集群外部访问Pod或Service
Pod和Service是Kubernetes集群内部的一个虚拟网络环境,主要由docker网络和网络插件CNI 配置Linux网络提供集群机器间内部网络的通信,但是集群外部无法通过Pod和Service的IP进行通信。
集群外部要想访问内部的地址,主要是通过NodePort的方式,即类似docker容器做端口映射的方式将容器的端口映射到Kubernetes集群的Node节点上的某个节点,实现可以通过Node节点的IP+端口的方式实现通信。
docker容器只能做本机的映射,但是Kubernetes通过网络插件CNI的方式可以将一个Service映射到所有Node节点的端口上。
1、Pod通过NodePort向外暴露服务(只能通过Pod部署的那台Node节点的IP+Port访问)
apiVersion: v1
kind: Pod
metadata:
name: external-pod
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
hostPort: 80
或者
apiVersion: v1
kind: Pod
metadata:
name: external-pod
spec:
hostNetwork: true
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
2、Service通过NodePort向外暴露服务(所有Node节点包括Master的IP+Port都可以访问)
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: init-nginx-index
image: busybox
command:
- /bin/sh
- -c
- 'ls /; echo "this is $(hostname)" > /work-dir/index.html; ls /;'
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
selector:
app: nginx
type: NodePort
ports:
- port: 80
nodePort: 30000
我们创建以NodePort方式暴露的Service时,就算只有一个Pod运行在某台Node节点上,我们也是可以通过其他Node节点的IP+Service的nodePort端口进行访问的。
如果有需求是只能通过Pod所在Node的IP+Service的nodePort端口进行访问的话,可以在Service的配置中加上externalTrafficPolicy: Local
7、DNS服务
前面我们使用Service访问服务都是通过ServiceIP的方式,但是service ip和pod ip都是kubernetes的内部网络,属于动态可变的,我们需要一种机制能够使用固定的服务名对这些ip进行访问。
Pod的IP集合被Service动态管控,Pod的变化也会导致Service映射的Pod IP发生变化。而Service的IP变化,在Kubernetes中是通过DNS的方式进行管控的。
Kubernetes中,通过coredns服务管控service,域名是service的name,ip是service的ip,我们可以通过固定的service name去访问这个service。
CoreDNS是Kubernetes的DNS服务器和服务发现的参考解决方案。
1、配置core-dns服务
我前面是用的kubeadm安装的集群,默认已经安装了core-dns,这里贴一下主要的配置
1、configMap,这里知道coredns的配置文件
apiVersion: v1
data:
Corefile: |
cluster.local:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2021-07-03T20:01:41Z"
name: coredns
namespace: kube-system
resourceVersion: "215"
uid: 179254c8-aa87-4867-8174-a27a0686571f
2、Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2021-07-03T20:01:41Z"
generation: 1
labels:
k8s-app: kube-dns
name: coredns
namespace: kube-system
resourceVersion: "3534"
uid: a9ce8038-1431-4c0a-bdf6-f6c4e43d361f
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kube-dns
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: kube-dns
spec:
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: registry.aliyuncs.com/google_containers/coredns:1.7.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
3、Service
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
creationTimestamp: "2021-07-03T20:01:41Z"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: KubeDNS
name: kube-dns
namespace: kube-system
resourceVersion: "221"
uid: 9edd5e0e-6fdb-48c6-8d72-11ff73449b68
spec:
clusterIP: 10.30.0.10
clusterIPs:
- 10.30.0.10
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
- name: metrics
port: 9153
protocol: TCP
targetPort: 9153
selector:
k8s-app: kube-dns
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
2、Node节点修改启动参数
通过修改kubelet服务的启动参数,在其中指定DNS即可实现集群全局范围内的DNS服务。
--cluster-dns=10.30.0.10
--cluster-domain=cluster.local
3、测试效果
1、用之前nginx的例子,新建一个rc和service
2、开启一个测试容器
apiVersion: v1
kind: Pod
metadata:
name: test-contaienr
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
3、进入容器,执行nslookup service名称就可以看到能正常解析service name。
kubectl exec test-contaienr -- nslookup external-service
这个主要是通过修改容器内的DNS服务器实现的
kubectl exec test-contaienr -- cat /etc/resolv.conf
我们通过查看容器内的dns服务器,可以看到就是我们在kubelet中设置的coredns的service ip
4、Pod级别的DNS配置
前面我们通过修改kubelet的启动参数实现集群范围内的DNS,我们也可以指定单个Pod的dns策略
apiVersion: v1
kind: Pod
metadata:
name: test-contaienr
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
dnsPolicy: Default
这里Pod的dns策略有:
- Default:继承Pod所在宿主机的DNS设置。
- ClusterFirst:优先使用Kubernetes环境的DNS服务,无法解析的转发到宿主机继承的DNS服务器。
- ClusterFirstWithHostNet:同上,以hostNetwork模式运行的Pod需要明确指定。
- None:忽略Kubernetes的DNS配置,有spec.dnsConfig自定义DNS配置。
以前面的test-contaienr容器为例,指定不同dnsPolicy的效果:
1、不指定dnsPolicy,则默认使用kubernetes的DNS服务,可以解析coredns配置的service name,无法解析外网域名。
2、dnsPolicy=Default,则默认使用宿主机的DNS服务,可以解析外网域名,无法解析coredns配置的service name。
3、dnsPolicy=ClusterFirst/ClusterFirstWithHostNet,可以解析coredns配置的service name,可以解析外网域名。
4、None,自己指定Pod容器中的dns服务器配置,全由配置的dns服务器解析。
None配置同下面resolve.conf文件一样,分为nameserver、searches、options
nameserver 10.30.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
8、Ingress
Kubernetes官方文档地址:https://kubernetes.io/zh/docs/concepts/services-networking/ingress/
Ingress是Kubernetes对外暴露服务的一种方式,主要是通过Controller和Ingress规则实现。
Controller是一个Pod,他作为Kubernetes集群外部流量的入口,通过配置的不同Ingress规则让不同的流量转发到Service,然后Service通过负载均衡到Pod上。
Ingress Controller有多种选择,我们选择安装常用的Nginx Controller
官方文档地址:https://kubernetes.github.io/ingress-nginx/deploy/
1、通过容器的方式部署Controller
yaml文件地址:https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.48.1/deploy/static/provider/cloud/deploy.yaml
这里需要修改一些配置
1、容器镜像的地址
我们需要从国内镜像源中下载,可以直接从dockerhub上搜索对应的ingress-controller镜像。
2、向外通过NodePort的方式暴露Controller服务
官网下载的默认配置文件,contoller对应的Service是通过LoadBalancer的方式对外暴露服务的,LoadBalancer依赖于云提供商的外部负载均衡方案,我们测试时使用NodePort的方式暴露服务。
官方提供的配置是使用Deployment的方式去创建的Controller,且Controller对应的Service是只能在Controller的Pod所在的那台Node机器的IP+Service的nodePort端口才能访问,我们如果想每个node节点的IP+Service的nodePort端口都能访问的话,可以
1、通过DaemonSet的方式在每一个Node节点上进行部署。
2、去掉Controller的Service中的externalTrafficPolicy: Local配置。
2、创建测试容器
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
template:
metadata:
labels:
app: nginx-deployment
spec:
initContainers:
- name: init-nginx-index
image: busybox
command:
- /bin/sh
- -c
- 'ls /; echo "this is $(hostname)" > /work-dir/index.html; ls /;'
volumeMounts:
- name: workdir
mountPath: /work-dir
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "250m"
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
volumes:
- name: workdir
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx-deployment
type: ClusterIP
ports:
- port: 80
3、添加Ingress规则
主要是配置对某个路径的访问,转发到那个Service上
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /nginx
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
4、访问
此时我们就向添加了ingress规则,我们访问kubernetes中的服务,都去访问nginx-controller的service服务,然后controller会根据ingress规则访问context对应的service,然后service转发到对应的Pod容器。
使用虚拟机部署Kubernetes集群的话,我们就可以在本机通过Kubernetes集群的某个节点IP+Controller Service端口和Ingress配置的path访问到对应Kubernetes容器中的服务了。
如果想要使用域名的方式去访问的话,可以在Ingress规则中配置host,例如
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: 10.211.55.30.sslip.io
http:
paths:
- path: /nginx
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
10.211.55.30是我内网的虚拟机master节点IP地址;sslip.io是一个公网的DNS服务,我们可以使用其做域名测试。详情见
http://sslip.io
|