k8s 如何对外提供服务

本文将介绍在 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,输入以下内容即可:

192.168.56.22 kubia.example.com

修改完了之后,我们就可以进行访问了,执行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连接。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容