作者:qingmu
qingmu.io/2020/04/08/Spring-Boot-Operator-User-Guide/
前言 在Kubernetes中部署spring boot应用整体上来说是一件比较繁琐的事情,而Spring Boot Operator则能带给你更清爽简单的体验。
Spring Boot Operator基于Kubernetes的custom resource definitions (CRDs)扩展API进行的开发。
打包Docker镜像 在讲部署之前我们需要先将我们的SpringBoot应用打包成标准的DockerImage。
java项目打包镜像用maven/gradle插件比较多,我的另一篇文章构建SpringBoot的Docker镜像,这里在介绍一个新的google开源的插件Jib,该插件使用起来比较方便。
注意:jib打包的镜像会导致java应用的pid=1,在使用SpringBootOperator进行发布时候,Operator会设置kubernetes的ShareProcessNamespace参数为true(v1.10+版本都可使用)来解决该问题。
下面就来演示一下我们通过https://start.spring.io生成一个标准的SpringBoot项目operator-demo,然后使用jib插件进行镜像打包
1 2 3 4 5 6 mvn com.google .cloud .tools :jib-maven-plugin:build -Djib.to .auth .username=${{ secrets.MY_USERNAME }} -Djib.to .auth .password=${{ secrets.MY_PASSWORD }} -Djib.container .jvmFlags=--add-opens ,java.base/sun.nio .ch=ALL-UNNAMED -Djib.from .image=freemanliu/oprenjre:11.0 .5 -Dimage=registry.cn-shanghai .aliyuncs .com/qingmuio/operator-demo/operator-demo:v1.0.0
执行上面的命令之后我们将得到一个标准的docker镜像,该镜像会被推送到远程仓库。
Operator快速体验 完成了镜像的构建之后,我们紧接着来安装我们的Operator到kubernetes集群,当然了首先你需要一套集群,可以参考我之前一篇文章部署高可用kubernetes,虽然版本比较老,但是新版本其实也差不多的一个思路。
https://qingmu.io/2019/05/17/Deploy-a-highly-available-cluster-with-kubeadm/
快速安装 此处快速安装只是为了快速体验demo
1 kubectl apply -f https:// raw.githubusercontent.com/goudai/ spring-boot-operator/master/m anifests/deployment.yaml
apply成功之后控制台输出
1 2 3 4 5 6 7 8 9 10 11 namespace/spring-boot-operator-system created customresourcedefinition.apiextensions .k8s .io/springbootapplications.springboot .qingmu .io created role.rbac .authorization .k8s .io/spring-boot-operator-leader-election-role created clusterrole.rbac .authorization .k8s .io/spring-boot-operator-manager-role created clusterrole.rbac .authorization .k8s .io/spring-boot-operator-proxy-role created clusterrole.rbac .authorization .k8s .io/spring-boot-operator-metrics-reader created rolebinding.rbac .authorization .k8s .io/spring-boot-operator-leader-election-rolebinding created clusterrolebinding.rbac .authorization .k8s .io/spring-boot-operator-manager-rolebinding created clusterrolebinding.rbac .authorization .k8s .io/spring-boot-operator-proxy-rolebinding created service/spring-boot-operator-controller-manager-metrics-service created deployment.apps/spring-boot-operator-controller-manager created
稍等片刻查看是否已经安装成功
1 kubectl get po -n spring-boot-operator -system
成功如下输出
1 NAME READY STATUS RESTARTS AGEspring-boot-operator-controller-manager-7 f498596bb-wcwtn 2 /2 Running 0 2 m15s
部署OperatorDemo应用 完成了Operator的部署之后,我们来部署我们第一个应用,这里我们就发布上面我们编写的springboot应用opreator-demo。
首先我们需要先编写一个Spring Boot Application 的CRD部署yaml,如下
1 2 3 4 5 6 7 8 9 # Demo.yaml apiVersion: springboot.qingmu.io/v1alpha1kind: SpringBootApplicationmetadata: name: operator-demo spec: springBoot: version: v1.0 .0 # image: registry.cn-shanghai.aliyuncs.com/qingmuio/operator-demo/operator-demo:v1.0.0
细心的同学可能发现了,为啥连Image都没有?这怎么发布,就name,version,就能完成发布?是的没错!就能完成发布,后面我讲详细讲到他是如何完成的。
接着我们apply一下
1 kubectl apply -f Demo.yaml
看到console输出
1 springbootapplication.springboot .qingmu .io/operator-demo created
验证 表示创建成功了,接着我们来看下我们部署的第一个应用,这里我们直接用上面的yaml中的name过滤即可。
查看pod
1 2 3 4 ~# kubectl get po | grep operator -demooperator -demo-7574 f4789c-mg58m 1 /1 Running 0 76 soperator -demo-7574 f4789c-ssr8v 1 /1 Running 0 76 soperator -demo-7574 f4789c-sznww 1 /1 Running 0 76 s
查看下我们的pid不等于1的设置是否生效,根据下面的结果可以看到通过设置ShareProcessNamespace该参数我们可以在Kubernetes层面来解决这个pid=1的问题。
1 2 3 4 5 6 7 kubectl exec -it operator-demo-7574 f4789c-mg58m bashbash -5 .0 # ps -efUID PID PPID C STIME TTY TIME CMDroot 1 0 0 02 :06 ? 00 :00 :00 /pauseroot 6 0 26 02 :06 ? 00 :00 :09 java --add-opens java.base/sun.nio.ch=ALL -UNNAMED -cp /app/resources:/app/classes:/app/libs/* io.qingmu.operator.operatordemo.Oper...root 38 0 0 02 :07 pts/0 00 :00 :00 bashroot 44 38 0 02 :07 pts/0 00 :00 :00 ps -ef
查看svc
1 2 ~# kubectl get svc | grep operator -demooperator -demo ClusterIP 10.101 .128 .6 <none > 8080 /TCP 2 m52s
我们来访问一下试试。
1 2 3 4 5 6 7 root @server1:~# curl -i http://10.101.128.6:8080 HTTP /1 .1 200 Content -Type: text/plain;charset=UTF-8 Content -Length: 9 Date : Wed, 08 Apr 2020 08 :45 :46 GMThello !!!
我们来试着缩减他的副本数到1个
编辑我们的Demo.yaml,加入一个新的属性replicas
1 2 3 4 5 6 7 8 9 # Demo.yaml apiVersion: springboot.qingmu.io/v1alpha1kind: SpringBootApplicationmetadata: name: operator-demo spec: springBoot: version: v1.0 .0 replicas: 1
应用一下
1 2 root@server1 :~ springbootapplication.springboot.qingmu.io/operator-demo configured
再次查看pod,你会发现我们的pod已经缩放为一个副本了
1 2 ~# kubectl get po | grep operator -demooperator -demo-7574 f4789c-sznww 1 /1 Running 0 8 m29s
清理operator-demo 要删除该pod 我们只需要执行delete即可
1 2 ~ springbootapplication.springboot.qingmu.io "operator-demo" deleted
再次查看pod,已经没了
1 kubectl get po | grep operator-demo
部署自己的应用 部署自己私有仓库的应用需要需要先创建secret(如果已经创建跳过即可)
创建docker-registry的secret
1 2 3 4 5 6 kubectl create secret docker-registry aliyun-registry-secret --docker-server =registry-vpc.cn-hangzhou.aliyuncs.com --docker-username =*** --docker-password =*** --docker-email =***
自己应用的crd Yaml
1 2 3 4 5 6 7 8 9 10 11 apiVersion : springboot.qingmu.io/v1alpha1 kind : SpringBootApplication metadata : name : 你的应用的名称 spec : springBoot : version : v1.0.0 replicas : 1 image : 你的image地址 imagePullSecrets : - 上面创建的secret
一个完整的Spring Boot Application Yaml 下面是一个完整的yaml属性结构,大部分属性我们都可以用默认配置的即可。推荐:一百期Java面试题汇总
不设置属性,默认使用Operator中设置的通用值详见后面的自定义安装Operator。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 apiVersion: springboot.qingmu.io/v1alpha1kind: SpringBootApplicationmetadata: name: operator-demo namespace: defaultspec: springBoot: # image 可以不设置,如果不设置默认使用 IMAGE_REPOSITORY+/+mate.name+:+spec.springBoot.version # registry.cn-shanghai.aliyuncs.com/qingmuio + / + operator-demo + : + v1.0.0 image: registry.cn-shanghai.aliyuncs.com/qingmuio/ operator-demo:v1.0 .0 clusterIp: "" version: v1.0 .0 replicas: 1 resource: cpu: request: 50 m limit: "" memory: request: 1 Gi limit: 1 Gi path: liveness: /actuator/ health readiness: /actuator/ health hostLog: /var/ applog shutdown: /spring/ shutdown imagePullSecrets: - aliyun-docker-registry-secret env: - name: EUREKA_SERVERS value: http: nodeAffinity: key: "failure-domain.beta.kubernetes.io/zone" operator: "In" values: - "cn-i" - "cn-h" - "cn-g"
优雅停机的路径 由于优雅停机默认是关闭的并且并不支持Get请求所以我们需要开启和搭个桥
首先在application.yml中启用
1 2 3 4 5 6 7 8 management: endpoints: web: exposure: include: "*" endpoint: shutdown: enabled: true
然后桥接一个Get方法
1 2 3 4 5 6 7 8 9 10 @RestController public class ShutdownController { @Autowired private ShutdownEndpoint shutdownEndpoint; @GetMapping ("/spring/shutdown" ) public Map <String , String > shutdown (HttpServletRequest request ) { return shutdownEndpoint.shutdown (); } }
node亲和的使用 举一个列子 我们有一个springboot应用 user-service 希望他能分布到3个可用区的6个节点上:
首先我们把机器划分多个可用区
1 2 3 cn-i区(node -i1 ,node -i02 ) cn-h区(node -g1 ,node -g02 ) cn-g区(node -h1 ,node -h02 )
现在我们有三个可以区 每个区有2台workload,一共6台。然后我们需要给这些机器分别打上label。
将全部的i区机器标注为cn-i
1 2 kubectl label node node-i1 failure-domain .beta .kubernetes.io/zone=cn-i kubectl label node node-i2 failure-domain .beta .kubernetes.io/zone=cn-i
同理将h区的标注为h,g区同理
1 2 kubectl label node node-h1 failure-domain.beta .kubernetes .io/zone=cn-i kubectl label node node-ih2 failure-domain.beta .kubernetes .io/zone=cn-i
现在准备工作我们就绪了,现在我们来设置让它达到我们的调度效果,像如下编写即可。
1 2 3 4 5 6 7 8 9 spec: springBoot: nodeAffinity: #可以不设置 节点亲和 这里演示的是尽量将pod分散到 i h g 三个可用区,默认设置了pod反亲和 key: "failure-domain.beta.kubernetes.io/zone" operator: "In" values: - "cn-i" - "cn-h" - "cn-g"
Operator 自定义安装 上面我们快速的安装了好了,接着我们来讲解下如何自定义安装,以及有哪些自定义的参数,可以个性化的参数我们用环境变量的方式注入。
下面来修改Deployment完成自己个性化的配置部署,从我提供的部署yaml中拉倒最后,找到name是spring-boot-operator-controller-manager的Deployment,我们将修改它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 apiVersion : apps/v1 kind : Deployment metadata : labels : control-plane : controller-manager name : spring-boot-operator-controller-manager namespace : spring-boot-operator-system ..... - name: IMAGE_REPOSITORY value : registry.cn-shanghai.aliyuncs.com/qingmuio - name: REQUEST_CPU value : 50m - name: LIMIT_CPU value : "" - name: REQUEST_MEMORY value : 500Mi - name: LIMIT_MEMORY value : 500Mi - name: READINESS_PATH value : /actuator/health - name: LIVENESS_PATH value : /actuator/health - name: SHUTDOWN_PATH value : /spring/shutdown - name: REPLICAS value : "3" - name: HOST_LOG_PATH value : /var/applog - name: IMAGE_PULL_SECRETS value : "" - name: SPRING_BOOT_DEFAULT_PORT value : "8080" - name: NODE_AFFINITY_KEY value : "" - name: NODE_AFFINITY_OPERATOR value : "" - name: NODE_AFFINITY_VALUES value : "" - name: SPRING_BOOT_ENV value : "" image : registry.cn-shanghai.aliyuncs.com/qingmuio/spring-boot-operator-controller:latest
自定义安装之后部署 yaml可以简化为如下。
1 2 3 4 5 6 7 apiVersion: springboot.qingmu.io/v1alpha1kind: SpringBootApplicationmetadata: name: 你的应用的名称spec: springBoot: version: v1.0 .0
附录 环境变量表格
Github仓库
SpringBootOperator: https://github.com/goudai/spring-boot-operator