k8s 如何对外提供服务

作者: HoPGoldy | 来源:发表于2019-08-05 11:04 被阅读0次

    本文将介绍在 k8s 中向外界提供服务的几种方法port-forwardNodePort,以及 更加常用的提供服务的资源ingress。本文要求对service资源有一定的了解,如果你还不清楚的话,可以点击 k8s 中的服务如何沟通 简单学习一下。

    接下来的内容需要一些pod作为实际的服务处理者,你可以通过下面命令新建一个rs,这个rs会新建三个名为kubia-xxxx的 pod,其8080端口会开放一个服务,请求时会返回其所在节点名称:

    kubectl create -f https://raw.githubusercontent.com/luksa/kubernetes-in-action/master/Chapter04/kubia-replicaset.yaml
    

    port-forward 映射服务到端口

    首先来看一下最简单粗暴的方法,我们可以通过一条 k8s 命令来将指定的 pod 端口映射到本地的端口上,基本用法如下:

    kubectl port-forward <资源类型>/<资源名> <本机端口>:<资源端口>
    

    例如,我有个 pod 叫做kubia,它通过80端口对外提供服务,那么我就可以使用kubectl port-forward pod/kubia 8080:80将其映射到本机的8080端口上。你可以使用kubectl port-forward -h查看更多用法。

    启用后会占用当前终端的标准输出,可以在后面添加&来指定后台运行:

    root@master1:~# k port-forward kubia-5k2zr 8080:8080 &
    [1] 13949
    Forwarding from 127.0.0.1:8080 -> 8080
    Forwarding from [::1]:8080 -> 8080
    
    root@master1:~# curl http://localhost:8080
    Handling connection for 8080
    You've hit kubia-5k2zr
    

    port-forward可以将 pod 临时映射出来,一般用于测试资源是否可用,在生产环境并不会大规模应用。

    NodePort 映射服务到节点端口

    相对于上一种port-forward来说,这一种要正式的多,NodePort可以将其 转发到所有 k8s 节点的指定端口上,并且不会像port-forward一样在前台运行。我们先来新建一个NodePort,新建nodeport.yaml文件并填写如下内容,然后使用kubectl create -f nodeport.yaml新建。:

    apiVersion: v1
    kind: Service
    metadata:
      name: kubia-nodeport
    spec:
      type: NodePort
      ports:
      - port: 80
        targetPort: 8080
        nodePort: 30123
      selector:
        app: kubia
    

    通过kind属性可以看到NodePort本质上是一个svc资源,他通过指定spec.ports.nodePort来讲服务映射到节点的端口上,接下来我们就可以访问下试试,直接curl访问节点ip + 端口号即可,你可以通过kubectl get nodes -o wide找到所有 k8s 节点的 ip:

    root@master1:~# curl http://192.168.56.11:30123
    You've hit kubia-m68bq
    
    root@master1:~# curl http://192.168.56.11:30123
    You've hit kubia-flg8w
    
    root@master1:~# curl http://192.168.56.11:30123
    You've hit kubia-flg8w
    
    root@master1:~# curl http://192.168.56.11:30123
    You've hit kubia-8r2cg
    
    root@master1:~# curl http://192.168.56.11:30123
    You've hit kubia-m68bq
    

    可以看到,使用了nodePort之后,服务被正常的请求,并且也正常的被均衡到每一个 pod 上。

    但是这里有个问题,假如我的 k8s 里运行了多个 web 应用服务器,我总不能让用户通过端口号http://domain:8081http://domain:8082来访问不同的 web 服务吧。能不能处理成http://domain/web1http://domain/web2 ...这种形式呢。

    当然可以,这个就是接下来要说的Ingress

    通过 Ingress 暴露服务

    ingress 是啥?其实,ingress 就是一个nginx服务器。从本质上说,我们可以直接通过配置nginx服务器来实现刚才说的访问方式,但是这样每次svc发生变更了我们就要重新手动配置一遍,好麻烦的,于是就有聪明的人想出来了,为什么我们不把复杂的配置操作抽象成一个文件,这样有新的变更的话我们直接修改文件,不就可以避免直接操作nginx服务器了么?于是,ingress诞生了。记住,ingress本质上就是一个nginx和许多个配置文件

    ingress 有两部分构成,负责转发服务的nginx-ingress-controller和每个服务的配置文件ingress资源,如下:

    通过 ingress 配置 service 访问

    每个想要对外暴露的svc都需要有一个对应的ingress资源(配置文件)才能对外提供服务,ingress资源并不负责实际的流量转发,它只是告诉nginx-ingress-controller应该把流量转发到哪个svc

    接下来,我们实践一下,从头部署一个可用的ingress

    安装 ingress

    k8s 并不自带 ingress,所以我们需要重新安装,幸好安装比较简单,直接执行如下命令即可,k8s 会根据其内容安装所有需要的资源。

    kubectl apply -f https://raw.githubusercontent.com/StudyXX/google-containers/v1.10.2/install/ingress-nginx/ingress-nginx-controller.yaml
    

    如果出现了镜像拉取失败的情况可以先手动将命令中的配置文件下载下来,然后将其的镜像地址从quay.io/替换成quay-mirror.qiniu.com/,重新kubectl apply即可。文件地址为 github - StudyXX/ingress-nginx-controller.yaml

    安装成功之后可以执行如下操作查看 ingress 是否安装完成,输入kubectl get namespaces,查看ingress-nginx(ingress相关的pod组) 的状态是否为Active。输入kubectl get pod -n ingress-nginx,查看nginx-ingress-controller(负责实际转发流量的nginx) 的状态是否为Running

    root@master1:~# kubectl get namespaces 
    NAME              STATUS   AGE
    default           Active   34d
    ingress-nginx     Active   27d
    kube-node-lease   Active   34d
    kube-public       Active   34d
    kube-system       Active   34d
    
    root@master1:~# kubectl get pod -n ingress-nginx 
    NAME                                        READY   STATUS    RESTARTS   AGE
    default-http-backend-7vg6q                  1/1     Running   1          26d
    nginx-ingress-controller-5c4964c449-9ccbm   1/1     Running   2          26d
    

    这两者正常基本可以断定ingress安装成功了,接下来我们来创建一个对外服务。

    使用 ingress 创建对外服务

    在"通过 ingress 配置 service 访问"图里我们可以看出,使用 ingress 暴露服务需要先新建一个svc,所以我们先使用如下内容创建一个,它会把 pod kubia端口8080上的服务转发到自己的8080上,他控制的 pod 就是我们文章开头时新建的那些

    kubia-svc.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: kubia
    spec:
      ports:
      - port: 8080
        targetPort: 8080
      selector:
        app: kubia
    

    然后使用kubectl create -f kubia-svc.yaml创建该svc,接下来我们创建一个ingress。ingress 支持创建HTTPHTTPS的服务,接下来我们先来创建一个HTTP的:

    创建 HTTP 协议的访问

    http协议的访问比较简单,直接创建如下配置文件即可,通过这个ingress配置文件,nginx-ingress-controller就知道如何对外开发服务了。

    kubia-http-ingress.yaml

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: kubia
    spec:
      rules:
        # 将服务映射到该域名
      - host: kubia.example.com
        http:
          paths:
            # 通过 / 路径就可以访问该服务
          - path: /
            # 该服务后端 svc 的名称及端口号
            backend:
              serviceName: kubia
              servicePort: 8080
    

    然后使用kubectl create -f kubia-http-ingress.yaml就可以创建ingress了。我们可以使用kubectl describe ingress kubia来查看他的介绍:

    root@master1:~# k describe ingress kubia
    Name:             kubia
    Namespace:        default
    Address:          
    Default backend:  default-http-backend:80 (<none>)
    Rules:
      Host               Path  Backends
      ----               ----  --------
      kubia.example.com  /     kubia:8080 (10.244.1.59:8080,10.244.2.31:8080,10.244.3.30:8080)
    Annotations:
    Events:  <none>
    

    ok,现在我们就可以通过http://kubia.example.com/来访问目标svc啦,这时你可能会疑惑,我没这个域名啊?没关系,在系统的hosts文件里配置一下,将这个域名映射到nginx-ingress-controller的 ip 地址就可以了,详情见下方:

    配置域名到 ip 地址

    首先获取nginx-ingress-controller的 ip 地址,执行如下命令后按Tab补全名称,然后在IP列就可以找到对应的 ip 了。

    kubectl describe pod -n ingress-nginx nginx-ingress-controller-
    

    我的 ip 地址为192.168.56.22,执行vi /etc/hosts,输入以下内容即可:

    kubia.example.com 192.168.56.22
    

    修改完了之后,我们就可以进行访问了,执行curl http://kubia.example.com/,就可以看到来自 svc kubia的响应了。

    root@master1:~# curl http://kubia.example.com/
    You've hit kubia-m68bq
    
    root@master1:~# curl http://kubia.example.com/
    You've hit kubia-8r2cg
    
    root@master1:~# curl http://kubia.example.com/
    You've hit kubia-flg8w
    

    创建 HTTPS 协议的访问

    这里的 https 访问是指从客户端到 ingress 控制器之间的连接是加密的,而控制器与后端svcpod之间的连接则还是 http,如下所示:

    https 请求链路

    想要让ingress可以提供https服务,我们首先需要有证书和私钥,这里我们先来创建他们俩:

    openssl genrsa -out tls.key 2048
    
    openssl req -new -x509 \
      -key tls.key \
      -out tls.cert \
      -days 360 \
      -subj /CN=kubia.example.com
    

    执行完之后你就可以在当前文件夹下发现两个文件,分别为tls.certtls.key。因为这种秘钥比较敏感,不适合直接挂载到 pod 上,所以可以使用 k8s 提供的专门用于提供敏感数据的资源secret来存放它,我们先来新建一个名为tls-secretsecret资源:

    kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
    

    什么是secret资源?

    secret用于存放一些敏感的配置信息,如密码、密钥等,你可以把他理解成一个更安全的ConfigMap资源,k8s 提供了很多类型的secret,例如上文的kubectl create secret tls就是一个常用的secret

    然后我们就可以修改上文中的kubia-http-ingress.yaml,将这个secret挂载上去:

    kubia-https-ingress.yaml

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: kubia
    spec:
      # 添加 tls 字段来启用 https
      tls:
      - hosts: 
          # 启用 https 的域名
        - kubia.example.com
        # 给其分配的证书及密钥,要求 tls 类型的 k8s secret 资源
        secretName: tls-secret
      rules:
      - host: kubia.example.com
        http:
          paths:
          - path: /
            backend:
              serviceName: kubia
              servicePort: 8080
    

    然后我们就可以通过以下命令来讲刚才创建的http服务提升至httpskubectl apply依靠kindmetadata字段中的数据来寻找要修改的资源,所以改名字并不会造成什么影响

    kubectl apply -f kubia-https-ingress.yaml
    

    然后输入curl -k -v https://kubia.example.com,就可以发现我们的服务已经启用的https连接:

    root@master1:~# curl -k -v https://kubia.example.com
    ...
    * successfully set certificate verify locations:
    *   CAfile: /etc/ssl/certs/ca-certificates.crt
    ...
    * Server certificate:
    *  subject: CN=kubia.example.com
    ...
    You've hit kubia-flg8w
    

    使用 ingress 暴露多个服务

    kubia-ingress.yaml文件中可以看到,rulespaths都是数组,所以我们可以通过其暴露多个服务,如下:

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: kubia
    spec:
      rules:
      - host: foo.example.com
        http:
          paths:
            # 通过指定不同的路径来访问不同的服务
          - path: /foo
            backend:
              serviceName: foo-svc
              servicePort: 8080
          - path: /bar
            backend:
              serviceName: bar-svc
              servicePort: 8080
      # 也可以通过指定多个 host 来配置不同的主机
      - host: foo.example.com
        http:
          paths:
          - path: /kubia
            backend:
              serviceName: kubia
              servicePort: 8080
    

    但是哪怕在一个文件中可以配置多个规则,但是依旧推荐为每一个svc都创建一个自己专属的ingress,这样条理会比较清晰,也方便日后的管理。

    总结

    本文介绍了如何将服务暴露出去,最简单的是port-forward命令,他可以将指定资源的端口转发到本机,但是一般都用于测试。

    其次是NodePort模式,NodePort本质上也是个svc,只不过他在完成svc本质工作的同时还将服务开放到了集群中所有节点的指定端口上。

    但是用户更倾向于通过子路径访问服务,而不是端口。为了实现这个目标,我们可以使用ingress资源,通过发布ingress配置文件,nginx-ingress-controller就可以将指定的服务发布到指定域名的指定路径上,ingress支持发布HTTPHTTPS连接。

    参考

    相关文章

      网友评论

        本文标题:k8s 如何对外提供服务

        本文链接:https://www.haomeiwen.com/subject/smunkctx.html