Kubernetes 服务是一种为一组功能相同的pod 提供单一不变的接入点的资源。当服务存在时, 它的IP地址和端口不会改变。客户端通过IP 地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod 上。通过这种方式, 客户端不需要知道每个单独的提供服务的pod 的地址,这样这些pod 就可以在集群中随时被创建或移除。

10.Service - 图1

在继续往下学习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来组成一个可以通信的服务。

准备

  1. [root@master01 ~]# kubectl explain Service
  2. KIND: Service
  3. VERSION: v1
  4. DESCRIPTION:
  5. Service is a named abstraction of software service (for example, mysql)
  6. consisting of local port (for example 3306) that the proxy listens on, and
  7. the selector that determines which pods will answer requests sent through
  8. the proxy.
  9. FIELDS:
  10. apiVersion <string>
  11. APIVersion defines the versioned schema of this representation of an
  12. object. Servers should convert recognized schemas to the latest internal
  13. value, and may reject unrecognized values. More info:
  14. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
  15. kind <string>
  16. Kind is a string value representing the REST resource this object
  17. represents. Servers may infer this from the endpoint the client submits
  18. requests to. Cannot be updated. In CamelCase. More info:
  19. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
  20. metadata <Object>
  21. Standard object's metadata. More info:
  22. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
  23. spec <Object>
  24. Spec defines the behavior of a service.
  25. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
  26. status <Object>
  27. Most recently observed status of the service. Populated by the system.
  28. Read-only. More info:
  29. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
  1. [root@master01 ~]# kubectl explain service.spec.type
  2. KIND: Service
  3. VERSION: v1
  4. FIELD: type <string>
  5. DESCRIPTION:
  6. type determines how the Service is exposed. Defaults to ClusterIP. Valid
  7. options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
  8. "ExternalName" maps to the specified externalName. "ClusterIP" allocates a
  9. cluster-internal IP address for load-balancing to endpoints. Endpoints are
  10. determined by the selector or if that is not specified, by manual
  11. construction of an Endpoints object. If clusterIP is "None", no virtual IP
  12. is allocated and the endpoints are published as a set of endpoints rather
  13. than a stable IP. "NodePort" builds on ClusterIP and allocates a port on
  14. every node which routes to the clusterIP. "LoadBalancer" builds on NodePort
  15. and creates an external load-balancer (if supported in the current cloud)
  16. which routes to the clusterIP. More info:
  17. https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types

service 类型有

  • ExternalName
  • ClusterIP
  • NodePort
  • LoadBalancer

先创建deployment

  1. cat > myapp-deploy.yaml << EOF
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: myapp-deploy
  6. namespace: default
  7. labels:
  8. app: myapp
  9. type: deploy
  10. spec:
  11. replicas: 3
  12. selector:
  13. matchLabels:
  14. app: myapp
  15. type: deploy
  16. template:
  17. metadata:
  18. name: myapp-pod
  19. labels:
  20. app: myapp
  21. type: deploy
  22. spec:
  23. containers:
  24. - name: myapp
  25. image: ikubernetes/myapp:v1
  26. ports:
  27. - name: http
  28. containerPort: 80
  29. 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的基础上,在集群的每一个节点的端口上开放这个服务。你可以在任意:NodePort地址上访问到这个服务。

10.Service - 图2

创建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 层,而且不会进行代理或转发。