Diagram

1 为什么有这个设计

Tip

我们前面这个那个的pod 各种搞, 是时候让外面的用户来访问我们通过pod 提供的服务了

为什么?
  1. 像deploy 部署的一组pod 为我们提供服务, 如果其中一个pod 重建了,ip修改了, 如何访问到呢?
  2. 就像我们去就餐, 我们不需要知道具体为你提供服务的人的名字, 只需要叫服务员,就会有人来提供服务.

就是很多pod在前端的一个负载均衡调度器 service 先到的endpoint controller 然后通过找到对应的pod

2 clusterIP

Tip

Service的默认类型,服务被发布至仅集群内部可见的虚拟IP地址上

deploy-nginx2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: deploy-nginx
  name: deploy-nginx2
spec:
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: pod-deploy-nginx2
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
       labels:
        app: pod-deploy-nginx2
    spec:
       restartPolicy: Always
       containers:
       - image: nginx:1.14.2-alpine
         imagePullPolicy: IfNotPresent
         name: c-deploy-nginx
         ports:
         - containerPort: 80
           name: web
           protocol: TCP
k apply -f deploy-nginx2.yaml
svc-deploy-nginx.yaml
kind: Service
apiVersion: v1
metadata:
  name: svc-deploy-nginx
spec:
  selector: # 关联哪些 pod ,上面我们deploy 创建的pod 就有这个标签
    app: pod-deploy-nginx2
 # clusterIP: 10.96.1.11 # 可以指定一个你要的ip,默认是随机分配的一个
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 8800   # service 端口
    targetPort: web  # 想要关联的pod 它们暴露的端口  可以写80 , 或者像这样 使用上面pod定义的端口名称
# 创建 svc, 必须和pod 在同一个namespace
k apply -f svc-deploy-nginx.yaml
k get svc
    NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    svc-deploy-nginx   ClusterIP   10.99.229.242   <none>        80/TCP    6m38s
# 服务选择pod的控制器不断扫描与标签选择匹配的 Pod,然后将所有更新发布到也称为 “svc-deploy-nginx” 同名的 Endpoint 对象
# ep endpoint
k get ep
    NAME               ENDPOINTS                       AGE
    svc-deploy-nginx   10.244.1.77:80,10.244.2.78:80   2m59s
# 同名ep中关联的2个 ip地址 正是 2个pod的ip
k get po -l app=pod-deploy-nginx2 -o wide
    NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE
    deploy-nginx2-57cfcdb8c7-6nhzj   1/1     Running   0          11m   10.244.2.78   node2
    deploy-nginx2-57cfcdb8c7-sqpr4   1/1     Running   0          11m   10.244.1.77   node1

# 直接访问 svc的ip和端口 ,就能访问到后面实际pod里的nginx 服务了. 而且负载均衡
# 访问svc, 会通过ep 找到对应的pod ip地址
curl 10.99.229.242:8800

# 进入pod 容器
cat /etc/resolv.conf
    nameserver 10.96.0.10
    search default.svc.cluster.local svc.cluster.local cluster.local
    options ndots:5
ping svc-deploy-nginx
wget svc-deploy-nginx:8800 #ok的
wget svc-deploy-nginx.default.svc.cluster.local:8800
# 删除pod 后, deploy 重建了pod ,然后ep 会重现关联新的pod ip

# 删除svc 会将关联的ep也删除
svc-nginx-multi-port.yaml
kind: Service
apiVersion: v1
metadata:
  name: svc-nginx-multi-port
spec:
  selector:
    app: pod-deploy-nginx
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: web
  - protocol: TCP
    port: 8080
    targetPort: web2

3 nodePort

ClusterIP 只能在集群内部访问 (默认)

映射一个端口 给node, 外部可以直接访问node的端口

svc-nginx-node-port.yaml
kind: Service
apiVersion: v1
metadata:
  name: svc-nginx-node-port
spec:
  selector:
    app: pod-deploy-nginx2
 # clusterIP: 10.96.1.11 # 可以指定一个你要的ip,默认是随机分配的一个
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: web
    #nodePort: 30192 # 可以指定 也可以不写, 不写就会随机
# 看到随机端口
k get svc
    NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    svc-nginx-node-port   NodePort    10.105.50.239   <none>        80:30046/TCP   1s

k get no -o wide
# 现在可以用任意一个node 的ip :30046 来访问pod服务
控制随机使用的nodePort
  • /etc/kubernetes/manifests/kube-apiserver.yaml
  • 在spec.containers.command 最后一行添加,比如- --service-node-port-range=40000-40100
  • 等待 kube-apiserver 重启完成,稍微等等, systemctl status kubelet

4 代理 k8s 外部应用

Tip
  1. 比如我们一个golang web服务, 需要连接外部的mysql或redis ,这就需要维护它们的ip地址
  2. 我们希望在生产环境中使用某个固定的名称而非IP地址来访问这些外部的中间件服务
  3. 某个项目正在迁移至k8s集群,但是一部分服务仍然在集群外部,此时可以使用service代理至 k8s 集群外部的服务 - 假定一个场景. 我们要从原先传统的服务迁移到k8s, a服务和b服务, 里面a访问b 是直接用的ip地址比如192.168.1.100:8080 (比如在a的配置文件里有这个ip地址 ) - 我们先迁移了a, b服务暂时还在外部,未来某一个时间可能才会迁移到k8s, 我们大概就会修改a的配置里的b ip地址, 过段时间 b迁移到 k8s,我们又要修改 这个ip地址, 然后重启 a服务,,, 我们不想要这么麻烦,
svc-nginx-external.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
   app: svc-nginx-external
  name: svc-nginx-external
spec:
  ports:
  - name: abc # 注意如果用了name,那么你ep里的ports name必须和这个一样
    port: 80
    protocol: TCP
    targetPort: 80 # 后端pod提供服务的端口, 后面ep配置里的ip地址提供的服务的端口
  type: ClusterIP
kubectl apply -f svc-nginx-external.yaml
# 查看是否有关联的ep , 这里我们还没关联ep
k describe svc svc-nginx-external |grep -i endpoint
# kubectl get ep 是没有自动创建的. 因为我们创建的svc没有没有selector, 就没关联pod
# 之所以我们以前能通过svc  访问到 pod 的ip ,是根据 ep的设置
# 那么现在我们手动创建一个ep,让ep指向外部的ip ,不就行了.
ep-single.yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: svc-nginx-external
  namespace: default
subsets:
- addresses:
  - ip: 10.244.1.79 # 写上你外部服务的ip, 我这里直接用pod的ip来测试
  - ip: 10.244.2.81
  ports:
  - port: 80 # 外部服务的端口
    name: abc
k apply -f ep-single.yaml
# 有了 关联的ep了
k get svc svc-nginx-external |grep -i endpoint
之前弄完后一直连接不了

原因是 svc ports 里设置了name, ep里没有设置, 必须设置,且name名字一样才可以.

5 ExternalName

5.1 外部域名

Tip
  • 通过svc访问外部的域名(就是给外部服务域名起一个别名)
  • ports 字段设置是无效的,因为这里就是域名的映射
kind: Service
apiVersion: v1
metadata:
  name: svc-external-name-baidu
spec:
  type: ExternalName
  externalName: www.baidu.com
k get svc
    NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
    svc-external-name-baidu   ExternalName   <none>          www.baidu.com   <none>       6s

# 进入一个容器.
kubectl exec -it dep-nginx-cc78676bc-ncrxb -- ping svc-external-name-baidu

5.2 内部svc 起个别名


kind: Service
apiVersion: v1
metadata:
  name: svc-external-name-another-svc
  namespace: test
spec:
  type: ExternalName
  # 这里写我们已经创建的svc的name, 作为已经它的一个svc别名
  #externalName: svc-deploy-nginx.default.svc.cluster.local 这里这样 就相当于给不同ns的svc 起个别名一样.
  # 要全, svc.cluster.local 不能省略, 本身全的 ,你在哪个ns下的pod 都能访问 ,每个pod里的 /etc/resolv.conf
  externalName: svc-deploy-nginx.default.svc.cluster.local
# 首先 pod 里能通过 svc-deploy-nginx 访问
k exec -it deploy-nginx2-667f55f99f-frtxh -- wget svc-deploy-nginx:8800
# test 命名空间下的pod里访问default命名空间下的 svc name, 你需要 指定 命名空间, .default
k exec -n test -it deploy-nginx-test-65cb6bf5f6-8mrxw -- wget svc-deploy-nginx.default:8800
# 直接访问, 其它namespace的svc 的别名, 就不需要带上 namespace
k exec -n test deploy-nginx-test-65cb6bf5f6-8mrxw  -- wget svc-external-name-another-svc:8800

5.3 headless svc

svc-headless-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-headless-nginx
  labels:
    app: svc-headless-nginx
spec:
  ports:
  - port: 80
    name: web
#它的 clusterIP 字段的值是:None,即:这个 Service,没有一个 VIP 作为“头”。 
#这也就是 Headless 的含义。所以,这个 Service 被创建后并不会被分配一个 VIP,
#而是会 以 DNS 记录的方式暴露出它所代理的 Pod。
  clusterIP: None
  selector:
    app: pod-deploy-nginx2
k get svc
    NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)    AGE
    svc-headless-nginx        ClusterIP      None             <none>          80/TCP     1s

k get ep
    NAME                  ENDPOINTS                       AGE
    svc-headless-nginx    10.244.1.82:80,10.244.2.81:80   11s

# 每次都会直接 ping 为 后端某一个pod的ip ,就是ep上设置的某一个ip
k exec deploy-nginx2-667f55f99f-frtxh -- ping svc-headless-nginx

6 几个注意的问题

6.1 pod 健康检查失败

Tip

前面我们说过, pod 就绪探针失败, 会将pod 切断流量

svc-pod-readiness.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: pod-readiness-liveness
  name: pod-readiness-liveness
spec:
  containers:
  - name: c-readiness-liveness
    image: busybox
    imagePullPolicy: IfNotPresent
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    readinessProbe:
      exec:
        command:
        - sh
        - -c
        - cat /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3
---

kind: Service
apiVersion: v1
metadata:
  name: svc-pod-readiness
spec:
  selector:
    test: pod-readiness-liveness
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
#k apply  -f  svc-pod-readiness.yaml -l test=pod-readiness-liveness
#k delete -f svc-pod-readiness.yaml  -l test=pod-readiness-liveness
k apply -f svc-pod-readiness.yaml
k get ep # 一开始是有的, 过了一段时间, pod 变成 不 ready了, ep里就删除了该pod ip

6.2 svc指定的selector 没有pod

kind: Service
apiVersion: v1
metadata:
  name: svc-selector-no-pod
spec:
  selector:
    app: pod-deploy-nginx3
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 8800
    targetPort: 80
Tip

会创建同名的ep, 只是ep里的ENDPOINTS 没有ip,是空的.

k get ep
    NAME                  ENDPOINTS                       AGE
    svc-selector-no-pod   <none>                          82s
Back to top