kubernetes 部署 springboot 和 vue 项目
前提条件
搭建好了一套 kubernetes 环境。如学习环境:minikube, 并启用了 Ingress。
部署 relx-ui.yaml (Vue 项目)
apiVersion: apps/v1
kind: Deployment
metadata:
name: relx-ui
labels:
app: relx-ui
spec:
replicas: 1
selector:
matchLabels:
app: relx-ui
template:
metadata:
labels:
app: relx-ui
spec:
containers:
- name: relx-ui
image: registry.cn-hangzhou.aliyuncs.com/mengweijin/relx-ui:0.0.1-SNAPSHOT
# IfNotPresent, Always, Never
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: relx-ui
spec:
#type: NodePort
selector:
app: relx-ui
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: mengweijin.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relx-ui
port:
number: 80
# 部署
kubectl apply -f nginx.yaml --record
# 配置 hosts 文件
kubectl get ingress
minikube ip
# (root用户)
vi /etc/hosts
# 在 hosts 文件的末尾添加以下内容
192.168.49.2 mengweijin.com
# 使hosts立刻生效
/etc/init.d/network restart
# 在CentOS虚拟机中访问(或用CentOS中的浏览器访问 mengweijin.com)能看到 relx 首页正常显示
curl mengweijin.com
部署 relx.yaml (SpringBoot 项目)
apiVersion: apps/v1
kind: Deployment
metadata:
name: relx-club-invoicing-management-system
labels:
app: relx-club-invoicing-management-system
spec:
replicas: 1
selector:
matchLabels:
app: relx-club-invoicing-management-system
template:
metadata:
labels:
app: relx-club-invoicing-management-system
spec:
containers:
- name: relx-club-invoicing-management-system
image: registry.cn-hangzhou.aliyuncs.com/mengweijin/relx-club-invoicing-management-system:0.0.1-SNAPSHOT
# IfNotPresent, Always, Never
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
name: relx-club-invoicing-management-system
spec:
#type: NodePort
selector:
app: relx-club-invoicing-management-system
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 8081
targetPort: 8081
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: relx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relx-club-invoicing-management-system
port:
number: 8081
# 部署
kubectl apply -f relx.yaml --record
# 配置 hosts 文件
kubectl get ingress
minikube ip
# (root用户)
vi /etc/hosts
# 在 hosts 文件的末尾添加以下内容
192.168.49.2 relx.com
# 使hosts立刻生效
/etc/init.d/network restart
# 项目原始接口:http://localhost:8081/goods
# 在CentOS虚拟机中访问(或用CentOS中的浏览器访问 relx.com/goods)能看到返回了JSON数据
curl relx.com/goods
部署 callrelx.yaml (SpringBoot 项目< 里面调用了 relx.yaml 项目)
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-call-relx
labels:
app: kubernetes-call-relx
spec:
replicas: 1
selector:
matchLabels:
app: kubernetes-call-relx
template:
metadata:
labels:
app: kubernetes-call-relx
spec:
containers:
- name: kubernetes-call-relx
image: registry.cn-hangzhou.aliyuncs.com/mengweijin/kubernetes-call-relx:1.0-SNAPSHOT
# IfNotPresent, Always, Never
imagePullPolicy: IfNotPresent
# 覆盖 Dockerfile 中的 CMD 启动命令
#command: ['java', '-jar', '/app/application.jar', '--server.port=8082', '--relxurl=http://relx-club-invoicing-management-system:8081/goods']
# 给应用程序传递参数,个人推荐的方式
args:
[
"--server.port=8082",
"--relxurl=http://relx-club-invoicing-management-system:8081/goods",
]
ports:
- containerPort: 8082
---
apiVersion: v1
kind: Service
metadata:
name: kubernetes-call-relx
spec:
#type: NodePort
selector:
app: kubernetes-call-relx
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 8082
targetPort: 8082
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: callrelx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: callrelx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-call-relx
port:
number: 8082
# 部署
kubectl apply -f callrelx.yaml --record
# 配置 hosts 文件
kubectl get ingress
minikube ip
# (root用户)
vi /etc/hosts
# 在 hosts 文件的末尾添加以下内容
192.168.49.2 relx.com callrelx.com
# 使hosts立刻生效
/etc/init.d/network restart
# 项目原始接口(返回字符串:SUCCESS):http://localhost:8082/relx/test
# 在CentOS虚拟机中访问(或用CentOS中的浏览器访问: callrelx.com/relx/test)
curl callrelx.com/relx/test
# 项目原始接口(调用了http://localhost:8081/goods):http://localhost:8082/relx/goods
# 在CentOS虚拟机中访问(或用CentOS中的浏览器访问: callrelx.com/relx/goods)
# 发现可以成功获取到 relx.yaml 部署的 rex.com/goods 接口返回的数据
curl callrelx.com/relx/goods
# 进入Pod容器里面
kubectl exec -it kubernetes-call-relx-6965478f48-gh4lv -- /bin/bash
同一 namespace 下两个不同的 service 的 pod 之间如何调用?
假如 A 服务(serviceName=aaaa) 调用 B 服务(serviceName=bbbb)中的接口:http://localhost:8080/test
那么在 A 工程中可以通过 B 服务的 serviceName 来调用 B 服务。例如在 A 服务中配置 B 服务中的接口格式为:http://bbbb:8080/test
开启 Ingress TLS
# 创建 secret
kubectl create secret tls tls-secret --key test.key --cert test.crt
# 解码查看
kubectl get secret tls-secret -o jsonpath='{.data}'
添加 tls 配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
tls:
- hosts:
- relx.com
# 此处名称为创建 secret 时命名的名称,需要保持一致
secretName: tls-secret
rules:
- host: relx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relx-club-invoicing-management-system
port:
number: 8081
部署后访问即可获取到数据:
curl https://relx.com/goods -k
给 SpringBoot 应用 开启单向认证 TLS
# 创建 secret。secret的名称为 jks, 文件的名称为:test.jks
kubectl create secret generic jks --from-file=test.jks=./test.jks
# 解码查看
kubectl get secret jks -o jsonpath='{.data}'
添加 tls 配置,Deployment 需要添加 spec.containers.volumeMounts 和 spec.volumes 配置。
apiVersion: apps/v1
kind: Deployment
metadata:
name: relx-club-invoicing-management-system
labels:
app: relx-club-invoicing-management-system
spec:
replicas: 1
selector:
matchLabels:
app: relx-club-invoicing-management-system
template:
metadata:
labels:
app: relx-club-invoicing-management-system
spec:
containers:
- name: relx-club-invoicing-management-system
image: registry.cn-hangzhou.aliyuncs.com/mengweijin/relx-club-invoicing-management-system:0.0.1-SNAPSHOT
# IfNotPresent, Always, Never
imagePullPolicy: IfNotPresent
# SpringBoot 程序中应该这样配置。注意 server.ssl.key-store 的位置对应卷目录和创建的 secret 中的文件名。(当前为了方便测试,所以配置在这里)
args:
[
"--server.port=8081",
"--server.ssl.enabled=true",
"--server.ssl.key-store=file:/etc/tls/test.jks",
"--server.ssl.key-store-password=123456",
"--server.ssl.key-store-type=JKS",
"--server.ssl.key-alias=test",
]
ports:
- containerPort: 8081
volumeMounts:
# 名称和下面的卷名称一致
- name: jks-volume
# Secret 将被挂载到 /etc/tls 目录
mountPath: "/etc/tls"
readOnly: true
volumes:
# 这个卷的名称可以随意命名
- name: jks-volume
secret:
# 这个名字必须和创建的 secret 名称一致
secretName: jks
# 部署后进入 Pod 容器内部访问:
kubectl exec -it relx-club-invoicing-management-system-7b9cf4b4bf-hh9lw -- /bin/bash
# 通过 https 成功获取到数据
curl https://localhost:8081/goods -k
走到这一步,在 SpringBoot 中开启了 https 访问,只能在 Pod 容器内部通过 curl https://localhost:8081/goods -k 访问到数据。
但是在 VMware 中的 CentOS 虚拟机中使用 Ingress 访问 curl https://relx.com/goods -k 无法访问到数据。如下:
[mengweijin@mengweijin ~]$ curl https://relx.com/goods -k
Bad Request
This combination of host and port requires TLS.
SpringBoot 开启 https 后,上面无法通过 Ingress 正常访问后台应用的问题
通过上面的报错信息可以发现,应该是 ingress 和 后台 SpringBoot 应用通信之间有证书的问题。
Ingress 添加一行执行后端服务协议为 https:nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"。如下所示:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
# 注意这里:必须指定后端服务为HTTPS服务,默认为 HTTP。
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- relx.com
# 此处名称为创建 secret 时命名的名称,需要保持一致
secretName: tls-secret
rules:
- host: relx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relx-club-invoicing-management-system
port:
number: 8081
重新部署 Ingress 后,在 VMware 中的 CentOS 虚拟机中使用 Ingress 访问 curl https://relx.com/goods -k 就可以正常访问到数据了。
SpringBoot 开启双向认证 TLS
SpringBoot 开启双向认证 TLS 参考博客:keytool 生成服务端和客户端证书实现 SpringBoot 双向认证
这里只阐述在 Ingress 中如何转发到后台加了双向认证 TLS 的 SpringBoot 应用。
部署好后台 SpringBoot 应用后,进入 pod 里验证后台双向认证是否成功的。
# 从本地复制客户端公钥和私钥到 pod 容器中
kubectl cp client.crt sample-quickboot-mybatis-plus-695ff6bfd9-b7sc7:/
kubectl cp client.key sample-quickboot-mybatis-plus-695ff6bfd9-b7sc7:/
# 不携带证书访问
curl -k https://localhost:8080/user/get
# 返回如下错误信息:
curl: (35) error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate
# 携带证书访问,正确返回了接口内容的 JSON 数据。说明双向认证是成功的。
curl -k --cert client.crt --key client.key https://localhost:8080/user/get
SpringBoot 开启双向认证 TLS 后通过 ingress ssl-passthrough 传递请求证书到后端服务
上面 SpringBoot 开启双向认证 TLS 后,调用方请求接口时,必须携带客户端证书才能访问接口。
然而,ingress 默认不会把请求过来携带的客户端传递给后端应用,这时候就需要开启 ingress 的 ssl-passthrough。
首先要开启 ingress controller 组件的 --enable-ssl-passthrough 功能。
其次,修改配置,添加 nginx.ingress.kubernetes.io/ssl-passthrough: "true"
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: relx
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
# 注意这里:必须指定后端服务为HTTPS服务,默认为 HTTP。
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
# 增加这一行
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
tls:
- hosts:
- relx.com
# 此处名称为创建 secret 时命名的名称,需要保持一致
secretName: tls-secret
rules:
- host: relx.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: relx-club-invoicing-management-system
port:
number: 8081
然后在 postman 中的设置中,添加针对访问 https://relx.com/ 主机的客户端证书(.crt、.key), 此时,就可以放问后台接口了,说明,证书传递到后端应用了。
Ingress 常用 annotations 参考
- https://kubernetes.github.io/ingress-nginx/examples/auth/client-certs/
- https://kubernetes.github.io/ingress-nginx/examples/PREREQUISITES/#client-certificate-authentication
- https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# 指定了我们使用后端ingress controller的类别,如果后端有多个ingress controller的时候很重要
kubernetes.io/ingress.class: "nginx"
# nginx.ingress.kubernetes.io/rewrite-target: /$1
# 指定后端服务为HTTPS服务。
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
# ingress 证书双向认证
# 开启客户端认证
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
# 指定root ca 的 secret。<namespace>为当前命名空间,ca-secret 为在 k8s 中创建的客户端证书的 secret 类型的对象。配置示例:"default/client-tls-secret"
nginx.ingress.kubernetes.io/auth-tls-secret: "<namespace>/ca-secret"
# 指定验证证书链的深度
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
# 指定url,将验证失败的请求重定向到该url
nginx.ingress.kubernetes.io/auth-tls-error-page: "http://www.mysite.com/error-cert.html"
# 是否将证书传递给后端的服务
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
# ingress 跨域问题 需要在ingress中添加配置下面annotations
nginx.ingress.kubernetes.io/cors-allow-headers: >-
DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
nginx.ingress.kubernetes.io/enable-cors: "true"
# ingress 强制443
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# ingress 白名单访问
nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.0.1"