一、为什么要有Service?
我们通过Deployment/StatefulSet/DaemonSet 创建的Pod的生命周期是短暂的,如果节点出现故障、容器内应用程序错误等原因,Pod随时会被销毁和重新创建,因为我们不能直接通过IP:PORT的方式来访问Pod。Pod类似于微服务,在SpringCloud微服务架构中,消费者并不是直接请求微服务,则是先请求注册中心来获取到可用的微服务列表,在k8s中的Service就类似于SpringCloud的注册中心,提供服务发现的功能。
k8s通过Service对一组Pod进行抽象,消费者只需要请求对应的Service即可访问到Pod,而不需要直接去访问Pod,此外Service起到了负载均衡的作用。
二、Service的简单使用
假设我们已经在k8s环境中创建了一个Deployment,该Deployment创建了2个Pod,具体配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
1. 定义一个Service
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
Service的kind配置为Service,selector配置的label需要匹配上Pod,见上面Deployment的label配置app=nginx,此外.spec.ports.port为Service对外暴露的端口,.spec.ports.targetPort为Pod的端口。
2. 创建Service
kubectl apply -f 文件名.yml
[root@k8s-master1 services]# kubectl apply -f 01-create-service.yml
service/nginx-service created
注:修改和删除与Deployment、StatefulSet、DaemonSet类型,修改:kubectl replace -f 文件名.yml,删除:kubectl delete -f 文件名.yml
3. 查看Service
kubectl get service
或者 kubectl get svc
[root@k8s-master1 ~]# kubectl get service -owide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d <none>
nginx-service ClusterIP 10.100.208.173 <none> 80/TCP 21m app=nginx
[root@k8s-master1 ~]# kubectl get svc -owide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d <none>
nginx-service ClusterIP 10.100.208.173 <none> 80/TCP 22m app=nginx
查看配置文件:kubectl get service nginx-service -o yaml
或者 kubectl edit service nginx-service
[root@k8s-master1 ~]# kubectl get service nginx-service -o yaml
apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-service","namespace":"default"},"spec":{"ports":[{"port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"nginx"}}}
creationTimestamp: "2022-12-22T03:09:09Z"
name: nginx-service
namespace: default
resourceVersion: "361278"
uid: 147678f2-f1df-47fb-b834-cd70a7b73ee5
spec:
clusterIP: 10.100.208.173
clusterIPs:
- 10.100.208.173
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
[root@k8s-master1 ~]# kubectl edit service nginx-service
Edit cancelled, no changes made.
查看Endpoints:kubectl get endpoints
[root@k8s-master1 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.253.110:6443 8d
nginx-service 172.16.36.65:80,172.16.36.70:80 26m
Endpoints定义了网络端点的列表,通常由Service引用,以定义可以将流量发送到哪些Pod。Endpoints的name与Service的name一致。
4. 简单测试
(1)可以通过Service的IP和端口进行访问Pod:curl http://10.100.208.173
(2)在Pod内部,可以直接通过Service的名称进行访问:curl http://nginx-service
(3)在Pod内部访问跨namespace的Service,需要指定namespace,格式为http://ServiceName.namespace,例如:
curl http://nginx-service.default
三、Service的类型
Service提供多种类型,可以通过.spec.type进行配置,默认情况下为ClusterIP,具体的类型有:
(1)ClusterIP
默认类型,通过集群内部的一个IP地址暴露Service,只能在集群内部进行访问。
(2)NodePort
通过每一个节点闪给的静态端口(NodePort)暴露Service,同时自动创建ClusterIP类型的访问方式。
- 在集群内部通过{Port}访问
- 在集群外部通过{NodePort}访问
比如我们按照了kubernetes dashboard,其Service就是采用NodePort的类型发布,我们可以在集群外部通过浏览器访问dashboard页面。
另外NodePort的端口范围是30000-32767,可以通过cat /usr/lib/systemd/system/kube-apiserver.service 进行查看。
[root@k8s-master1 ~]# cat /usr/lib/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
--v=2 \
--logtostderr=true \
--allow-privileged=true \
--bind-address=0.0.0.0 \
--secure-port=6443 \
--insecure-port=0 \
--advertise-address=192.168.253.110 \
--service-cluster-ip-range=10.96.0.0/12 \
--service-node-port-range=30000-32767 \
--etcd-servers=https://192.168.253.110:2379 \
--etcd-cafile=/etc/etcd/ssl/etcd-ca.pem \
--etcd-certfile=/etc/etcd/ssl/etcd.pem \
--etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \
--client-ca-file=/etc/kubernetes/pki/ca.pem \
--tls-cert-file=/etc/kubernetes/pki/apiserver.pem \
--tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem \
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem \
--kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem \
--service-account-key-file=/etc/kubernetes/pki/sa.pub \
--service-account-signing-key-file=/etc/kubernetes/pki/sa.key \
--service-account-issuer=https://kubernetes.default.svc.cluster.local \
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota \
--authorization-mode=Node,RBAC \
--enable-bootstrap-token-auth=true \
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem \
--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem \
--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem \
--requestheader-allowed-names=aggregator \
--requestheader-group-headers=X-Remote-Group \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-username-headers=X-Remote-User
# --token-auth-file=/etc/kubernetes/token.csv
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
[root@k8s-master1 ~]#
(3)LoadBalancer
通过云服务供应商的负载均衡器在集群外部暴露Service,同时自动创建NodePort和ClusterIP类型的访问方式。
- 在集群内部通过{Port}访问
- 在集群外部通过{NodePort}访问
- 在集群外部通过{Port}访问
(4)ExternalName:
将Service映射到externalName指定的地址,例如:www.baidu.com,返回值是一个CNAME记录。
四、使用Service代理k8s外部服务
当我们有一些应用系统没有在kubernetes环境部署,或者我们的平台正在分批次迁移到kubernetes,那么在kubernetes环境中的Pod如何访问到外部的服务呢?
(1)定义一个Service
apiVersion: v1
kind: Service
metadata:
name: nginx-external-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
(2)定义一个Endpoints
apiVersion: v1
kind: Endpoints
metadata:
name: nginx-external-service
subsets:
- addresses:
- ip: 172.16.36.65
- ip: 172.16.36.70
ports:
- port: 80
protocol: TCP
注意:Endpoints的name必须与Service的name一致,这样kubernetes会自动把Endpoints绑定到Service中,另外subsets.ip列表配置的是外部服务的IP。
(3)测试
[root@k8s-master1 services]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.253.110:6443 8d
nginx-external-service 172.16.36.65:80,172.16.36.70:80 3s
nginx-service 172.16.36.65:80,172.16.36.70:80 3h56m
nginx-service-external 172.16.36.65:80,172.16.36.70:80 108s
[root@k8s-master1 services]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d
nginx-external-service ClusterIP 10.99.156.71 <none> 80/TCP 12m
nginx-service ClusterIP 10.100.208.173 <none> 80/TCP 3h56m
[root@k8s-master1 services]#
[root@k8s-master1 services]# curl http://10.99.156.71
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@k8s-master1 services]#
通过kubectl get service查看Service的ClusterIP为10.99.156.71 ,然后通过curl http://10.99.156.71可以访问到nginx。