Skip to content

kubernetes 部署 springboot 和 vue 项目

前提条件

搭建好了一套 kubernetes 环境。如学习环境:minikube, 并启用了 Ingress。

部署 relx-ui.yaml (Vue 项目)

yaml
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
bash
# 部署
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 项目)

yaml
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
bash
# 部署
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 项目)

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
bash
# 部署
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

bash
# 创建 secret
kubectl create secret tls tls-secret --key test.key --cert test.crt

# 解码查看
kubectl get secret tls-secret -o jsonpath='{.data}'

添加 tls 配置

yaml
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

部署后访问即可获取到数据:

bash
curl https://relx.com/goods -k

给 SpringBoot 应用 开启单向认证 TLS

bash
# 创建 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 配置。

yaml
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
bash
# 部署后进入 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 无法访问到数据。如下:

bash
[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"。如下所示:

yaml
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 里验证后台双向认证是否成功的。

bash
# 从本地复制客户端公钥和私钥到 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"

yaml
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 参考

yaml
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"