Kubernetes应用访问管理(下)
作者 | 老郑
来源 |运维开发故事(ID:mygsdcsf)
转载请联系授权(微信ID:wanger5354)
安装Nginx Ingress Controller
安装方式非常简单,可以使用Helm和普通的YAML进行安装,由于我们还没有学习Helm,所以采用YAML清单的方式。
如果网络条件好(可以翻墙),可直接使用以下命令安装:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml
如果网络条件不好,可以使用:
$ kubectl apply -f https://raw.githubusercontent.com/joker-bai/kubernetes-software-yaml/main/ingress/nginx/ingress-nginx.yaml
安装完成过后,资源信息如下:
$ kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-kj7ch 0/1 Completed 0 8m16s
pod/ingress-nginx-admission-patch-gtncg 0/1 Completed 0 8m16s
pod/ingress-nginx-controller-d5c4c7ffb-6q5gn 1/1 Running 0 8m16s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.109.208.249 192.168.205.51 80:31306/TCP,443:31079/TCP 8m16s
service/ingress-nginx-controller-admission ClusterIP 10.103.169.99 <none> 443/TCP 8m16s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 8m16s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-d5c4c7ffb 1 1 1 8m16s
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 5s 8m16s
job.batch/ingress-nginx-admission-patch 1/1 6s 8m16s
在这里,ingress-nginx对外暴露采用的是LoadBalancer,也就是说把域名解析到192.168.205.51地址上,可以直接通过80端口访问。
暴露第一个服务
上面已经把ingress-nginx controller部署好了,接下来就可以创建一个应用进行暴露。
我们这里通过暴露一个nginx服务,如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
注意:对于不同版本的Ingress配置可能不一样,在实际中可以通过kubectl explain ingrss来查看具体的配置。
部署完成过后,查看ingress信息如下:
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx <none> nginx.dev.jokerbai.com 192.168.205.51 80 88s
将域名进行解析,则可以访问,如下:
$ curl -x 192.168.205.51:80 http://nginx.dev.jokerbai.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
进阶使用
上面使用ingress暴露了第一个域名,但是在实际的生成中,可能还有更多复杂的应用,比如:
域名使用SSL证书访问
- 域名重定向
- 地址重写
- 认证访问
- 黑白名单
- 域名限速
- 灰度发布
- ......
使用SSL证书
在实际工作中,为了安全考虑,更多的是采用HTTPS进行访问,Ingress也支持配置HTTPS。
首先需要域名证书,这里手动创建私有证书进行配置。
$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.crt -subj "/C=CN/ST=Chongqing/L=Chongqing/OU=DevOps/CN=nginx.dev.jokerbai.com"
然后将证书保存到secret对象中(在Kubernetes应用配置管理会介绍)。
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
证书创建好了之后,就可以配置ingress了,如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- nginx.dev.jokerbai.com
secretName: tls-secret
rules:
- host: nginx.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
其中Ingress.spec.tls字段用来配置域名证书。
接下来就可以通过HTTPS进行访问。
PS:由于是自定义的证书,是无效的,所以访问的时候会报错,在实际工作中是会购买专用证书。
域名重定向
有时候需要把域名请求重定向到另外的域名,在nginx中,我们可以配置redirect,在ingress中,也可以使用redirect,不过是配置在annotation中,如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-redirect
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/permanent-redirect: "https://www.baidu.com"
spec:
rules:
- host: nginx-redirect.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
访问的时候可以看到301重定向,如下:
$ curl -I -x 192.168.205.51:80 http://nginx-redirect.dev.jokerbai.com
HTTP/1.1 301 Moved Permanently
Date: Fri, 22 Jul 2022 05:50:59 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://www.baidu.com
其实现方式是在ingress配置的annotation中添加声明配置:nginx.ingress.kubernetes.io/permanent-redirect: "?https://www.baidu.com" 即可。
地址重写
地址重写在ingress中通过在annotation中添加nginx.ingress.kubernetes.io/rewrite-target: "/$1"这种类型的配置即可。
通过地址重写,我们可以实现诸如访问a.com/foo 重写到a.com,访问a.com/foo重写到a.com/foo/bar,需要注意的是重写后的地址需要是能真实访问到资源的地址,不然重写也没什么意义。
实例如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-rewrite
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: "/$1"
spec:
rules:
- host: nginx-rewrite.dev.jokerbai.com
http:
paths:
- path: /foo/?(.*)
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
然后通过在访问URL中带/foo即可访问到Nginx服务,如下:
$ curl -x 192.168.205.51:80 http://nginx-rewrite.dev.jokerbai.com/foo
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
认证访问
有时候,我们暴露出来的域名不想被其他人使用(可能有人会说:那你不暴露出来不就完事了),比如说prometheus的后台,默认是没有登录管理的,而运维是需要到后台去查询监控信息的,为了安全,我们需要给这类域名加上认证,以便在一定程度上降低风险。
ingress提供base auth的认证方式,我们可以通过这种方式为我们的域名提供认证。
(1)创建密码
$ htpasswd -c auth joker
New password:
Re-type new password:
Adding password for user joker
(2)将密码保存到secret中
$ kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
(3)在Ingress中配置认证 只需要在ingress配置中添加两个annotation即可完成。
- nginx.ingress.kubernetes.io/auth-type: basic 指明认证方式
- nginx.ingress.kubernetes.io/auth-secret: basic-auth 指定认证的账号密码
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-auth annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: basic-auth spec: rules: - host: nginx-auth.dev.jokerbai.com http: paths: - path: / backend: service: name: nginx-svc port: number: 80 pathType: Prefix
然后不带认证信息访问,结果如下:
$ curl -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/foo
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
直接返回401,需要认证才能访问。
下面带上认证信息访问,结果如下:
$ curl -u "joker:123" -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
如果是从浏览器访问,会给我们弹出一个输入框,输入账号密码即可进入应用。
黑白名单
有时候,光有认证访问也并不安全,这时候我们可以通过配置黑白名单的方式,把访问的范围降低,在nginx中,我们可以通过配置allow和deny来配置,在ingress中,也支持类似的配置。
配置白名单
在ingress里配置白名单可以通过两种方式实现:
- 添加annotation,这种是只针对单个域名
- 在ingress-nginx的configmap中配置,全局有效
(1)通过annotation配置,如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-whitelist
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/whitelist-source-range: 10.1.10.2
spec:
rules:
- host: nginx-whitelist.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
配置了一个不存在的地址10.1.10.2,查看访问效果。
$ curl -I -x 192.168.205.51:80 http://nginx-whitelist.dev.jokerbai.com
HTTP/1.1 403 Forbidden
Date: Fri, 22 Jul 2022 06:57:45 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
给我们返回403拒绝访问。
如果我们把地址配置成可以访问的IP,则可以访问。
(2)配置ConfigMap 部署好ingress-nginx后,会在ingress-nginx的namespace下生成叫“ingress-nginx-controller”的ConfigMap配置文件,我们只需要在这个配置文件进行配置即可。
$ kubectl edit cm -n ingress-nginx ingress-nginx-controller
apiVersion: v1
data:
allow-snippet-annotations: "true"
compute-full-forwarded-for: "true"
use-forwarded-headers: "true"
whitelist-source-range: 172.16.0.0/24
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.3.0
name: ingress-nginx-controller
namespace: ingress-nginx
配置完保存即可,ingress-nginx的pod会自动reload。不过这种配置是全局生效,在使用的时候慎重。
配置黑名单
有白名单就有黑名单,ingress的黑名单配置只能通过ConfigMap来,而且是全局生效的。
目前支持以下三种黑名单:
- block-cidrs:限制IP
- block-user-agents:限制User-Agent
- block-referers:限制referer
配置很简单,如下:
$ kubectl edit cm -n ingress-nginx ingress-nginx-controller
apiVersion: v1
data:
allow-snippet-annotations: "true"
compute-full-forwarded-for: "true"
use-forwarded-headers: "true"
whitelist-source-range: 172.16.0.0/24
block-cidrs: 10.1.10.100
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.3.0
name: ingress-nginx-controller
namespace: ingress-nginx
访问限速
有时候访问量太大,可以通过在ingress进行限速,配置如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-limit
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/limit-rate: "100K"
nginx.ingress.kubernetes.io/limit-whitelist: "10.1.10.100"
nginx.ingress.kubernetes.io/limit-rps: "1"
nginx.ingress.kubernetes.io/limit-rpm: "30"
spec:
rules:
- host: nginx-limit.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: nginx-svc
port:
number: 80
pathType: Prefix
其中:
- nginx.ingress.kubernetes.io/limit-rate:限制客户端每秒传输的字节数
- nginx.ingress.kubernetes.io/limit-whitelist:白名单中的IP不限速
- nginx.ingress.kubernetes.io/limit-rps:单个IP每秒的连接数
- nginx.ingress.kubernetes.io/limit-rpm:单个IP每分钟的连接数
灰度发布
?Nginx Annotations 支持以下 4 种 Canary 规则: - nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。
- nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (即:canary-by-header)一起使用。
- nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 100 意味着所有请求都将被发送到 Canary 入口。
- nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的cookie。当 cookie 值设置为 always时,它将被路由到 Canary 入口;当 cookie 值设置为 never时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。
定义两个版本的代码。V1版本代码如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main(){
g:=gin.Default()
g.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{
"version": "v1",
"data": "hello world",
})
})
_ = g.Run("8080")
}
V2版本代码如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main(){
g:=gin.Default()
g.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{
"version": "v2",
"data": "hello world,SB",
})
})
_ = g.Run("8080")
}
然后制作镜像,Dockerfile如下:
FROM golang AS build-env
ADD . /go/src/app
WORKDIR /go/src/app
RUN go get -u -v github.com/gin-gonic/gin
RUN govendor sync
RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server-v1
FROM alpine
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=build-env /go/src/app/app-server-v1 /usr/local/bin/app-server-v1
EXPOSE 8080
CMD [ "/usr/local/bin/app-server-v1" ]
制作镜像并上传:
$ docker build -t registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1 .
$ docker push registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
PS:V2版本操作类似
V1和V2版本的Deployment和Service如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-server-v1
spec:
selector:
matchLabels:
app: app-server-v1
replicas: 2
template:
metadata:
labels:
app: app-server-v1
spec:
containers:
- name: app-server-v1
image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: app-server-v1-svc
spec:
selector:
app: app-server-v1
ports:
- name: http
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-server-v1
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: canary.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: app-server-v1-svc
port:
number: 8080
pathType: Prefix
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-server-v2
spec:
selector:
matchLabels:
app: app-server-v2
replicas: 2
template:
metadata:
labels:
app: app-server-v2
spec:
containers:
- name: app-server-v2
image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: app-server-v2-svc
spec:
selector:
app: app-server-v2
ports:
- name: http
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-server-v2
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: canary.dev.jokerbai.com
http:
paths:
- path: /
backend:
service:
name: app-server-v2-svc
port:
number: 8080
pathType: Prefix
说明:
- nginx.ingress.kubernetes.io/canary: true 表示开启canary
- nginx.ingress.kubernetes.io/canary-weight: 10 表示权重为10,也就是v1:v2大致为9:1
在实际使用中,也是可以通过部署不同版本的Deployment来实现灰度发布。
总结
目前集群内应用的访问主要是通过Service和Ingress两种方式,其中Service是四层,Ingress是七层,在企业应用中,除了一些必须使用四层的应用,比如MySQL、Redis,其他的基本都采用Ingress进行访问。
Ingress的选型非常多,可以根据企业的实际情况(比如技术栈、熟悉程度、规模大小)进行选择,而Service主要还是在集群内部使用。