Kubernetes 服务是一种为一组功能相同的pod 提供单一不变的接入点的资源。当服务存在时, 它的IP地址和端口不会改变。客户端通过IP 地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod 上。通过这种方式, 客户端不需要知道每个单独的提供服务的pod 的地址,这样这些pod 就可以在集群中随时被创建或移除。
在继续往下学习Service之前,需要先弄明白Kubernetes系统中的三种IP这个问题。
- Node IP:Node节点的IP地址
- Pod IP: Pod的IP地址
- Cluster IP: Service的IP地址
首先,Node IP是Kubernetes集群中节点的物理网卡IP地址(一般为内网),所有属于这个网络的服务器之间都可以直接通信,所以Kubernetes集群外要想访问Kubernetes集群内部的某个节点或者服务,肯定得通过Node IP进行通信(这个时候一般是通过外网IP了)
然后Pod IP是每个Pod的IP地址。
最后Cluster IP是一个虚拟的IP,仅仅作用于Kubernetes Service这个对象,由Kubernetes自己来进行管理和分配地址,当然我们也无法ping这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port来组成一个可以通信的服务。
准备
[root@master01 ~]# kubectl explain Service
KIND: Service
VERSION: v1
DESCRIPTION:
Service is a named abstraction of software service (for example, mysql)
consisting of local port (for example 3306) that the proxy listens on, and
the selector that determines which pods will answer requests sent through
the proxy.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
Spec defines the behavior of a service.
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
status <Object>
Most recently observed status of the service. Populated by the system.
Read-only. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
[root@master01 ~]# kubectl explain service.spec.type
KIND: Service
VERSION: v1
FIELD: type <string>
DESCRIPTION:
type determines how the Service is exposed. Defaults to ClusterIP. Valid
options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
"ExternalName" maps to the specified externalName. "ClusterIP" allocates a
cluster-internal IP address for load-balancing to endpoints. Endpoints are
determined by the selector or if that is not specified, by manual
construction of an Endpoints object. If clusterIP is "None", no virtual IP
is allocated and the endpoints are published as a set of endpoints rather
than a stable IP. "NodePort" builds on ClusterIP and allocates a port on
every node which routes to the clusterIP. "LoadBalancer" builds on NodePort
and creates an external load-balancer (if supported in the current cloud)
which routes to the clusterIP. More info:
https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
service 类型有
- ExternalName
- ClusterIP
- NodePort
- LoadBalancer
先创建deployment
cat > myapp-deploy.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
labels:
app: myapp
type: deploy
spec:
replicas: 3
selector:
matchLabels:
app: myapp
type: deploy
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: deploy
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
EOF
kubectl apply -f myapp-deploy.yaml
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-deploy-696fdb5868-sgfv8 1/1 Running 0 9s 10.244.196.167 node01 <none> <none>
myapp-deploy-696fdb5868-xddzb 1/1 Running 0 9s 10.244.186.233 node03 <none> <none>
myapp-deploy-696fdb5868-z5tzx 1/1 Running 0 9s 10.244.140.96 node02 <none> <none>
直接访问pod ip
[root@master01 ~]# curl 10.244.196.167/hostname.html
myapp-deploy-696fdb5868-sgfv8
[root@master01 ~]# curl 10.244.186.233/hostname.html
myapp-deploy-696fdb5868-xddzb
[root@master01 ~]# curl 10.244.140.96/hostname.html
myapp-deploy-696fdb5868-z5tzx
ClusterIP
ClusterIP仅仅使用一个集群内部的IP地址,这是默认值。选择这个值意味着你只想这个服务在集群内部才可以被访问到
创建service
cat > myapp-svc.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
EOF
kubectl apply -f myapp-svc.yaml
查看service
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d20h
myapp-svc ClusterIP 10.96.104.163 <none> 80/TCP 59s
[root@master01 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.33.101:6443,192.168.33.102:6443,192.168.33.103:6443 7d20h
myapp-svc 10.244.140.96:80,10.244.186.233:80,10.244.196.167:80 5m
[root@master01 ~]# kubectl describe svc myapp-svc
Name: myapp-svc
Namespace: default
Labels: app=myapp
type=svc
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"myapp","type":"svc"},"name":"myapp-svc","namespace":"def...
Selector: app=myapp,type=deploy
Type: ClusterIP
IP: 10.107.17.98
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.140.97:80,10.244.186.238:80,10.244.196.170:80
Session Affinity: None
Events: <none>
测试
[root@master01 ~]# while true; do curl http://10.96.104.163/hostname.html;sleep 1;done
myapp-deploy-696fdb5868-xddzb
myapp-deploy-696fdb5868-z5tzx
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-xddzb
myapp-deploy-696fdb5868-z5tzx
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-xddzb
myapp-deploy-696fdb5868-z5tzx
通过DNS 发现服务
[root@master01 ~]# kubectl exec -it myapp-deploy-696fdb5868-z5tzx -- /bin/sh
/ # env
MYAPP_SVC_PORT_80_TCP_ADDR=10.98.57.156
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
MYAPP_SVC_PORT_80_TCP_PORT=80
HOSTNAME=myapp-deploy-696fdb5868-z5tzx
SHLVL=1
MYAPP_SVC_PORT_80_TCP_PROTO=tcp
HOME=/root
MYAPP_SVC_PORT_80_TCP=tcp://10.98.57.156:80
TERM=xterm
NGINX_VERSION=1.12.2
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
MYAPP_SVC_SERVICE_HOST=10.98.57.156
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
PWD=/
KUBERNETES_SERVICE_HOST=10.96.0.1
MYAPP_SVC_SERVICE_PORT=80
MYAPP_SVC_PORT=tcp://10.98.57.156:80
cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28.4
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
[root@master01 ~]# kubectl exec -it busybox -- /bin/sh
/ # nslookup myapp-svc
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: myapp-svc
Address 1: 10.96.104.163 myapp-svc.default.svc.cluster.local
删除service
kubectl delete svc myapp-svc
NodePort
将一组pod公开给外部客户端的第一种方法是创建一个服务并将其类型设置为NodePort 。通过创建NodePort服务,可以让Kubernetes 在其所有节点上保留一个端口(所有节点上都使用相同的端口号),并将传入的连接转发给作为服务部分的pod 。在集群内部IP的基础上,在集群的每一个节点的端口上开放这个服务。你可以在任意
创建service
cat > myapp-svc.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
name: http
EOF
kubectl apply -f myapp-svc.yaml
查看service
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d21h
myapp-svc NodePort 10.103.48.58 <none> 80:30080/TCP 4s
noteport会由kube-proxy创建
[root@master01 ~]# netstat -ntlp | grep 30080
tcp 0 0 0.0.0.0:30080 0.0.0.0:* LISTEN 3495/kube-proxy
测试
[root@master01 ~]# while true;do curl master01:30080/hostname.html;sleep 1;done
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-xddzb
myapp-deploy-696fdb5868-z5tzx
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-xddzb
myapp-deploy-696fdb5868-z5tzx
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-xddzb
默认是轮询,可以修改为pod会话绑定
会话绑定
sessionAffinity: ClientIP
cat > myapp-svc.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
namespace: default
labels:
type: svc
app: myapp
spec:
selector:
app: myapp
type: deploy
type: NodePort
sessionAffinity: ClientIP
ports:
- port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
name: http
EOF
kubectl apply -f myapp-svc.yaml
[root@master01 ~]# while true;do curl master01:30080/hostname.html;sleep 1;done
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
myapp-deploy-696fdb5868-sgfv8
ExternalName
ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local 时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。