基于Spring Boot框架的应用如何在kubernetes环境部署,是沿用Docker容器的做法还是另有其他?接下来咱们从理论到设计再到实战,一起来感受传统后台应用在容器环境的微妙变化;
原文地址:https://blog.csdn.net/boling_cavalry/article/details/83784113
基于Spring Boot框架的应用,通常会构建成XXX.jar文件,执行java -jar XXX.jar来运行该应用;
Docker环境下,通常用Maven的docker-maven-plugin插件将应用打包成镜像,例如以java:8u111-jdk作为基础镜像再加入jar文件,这样容器启动的时候执行java -jar XXX.jar就能将应用运行起来了;
Spring Boot应用镜像在kubernetes声明为Pod,即可正常运行;
但是,这是合适的做法么?去K8S官网需要一些理论上的指导吧;
官方文档地址:https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
上图红框中提到一个容器基于共享资源对外提供服务,另一个"sidecar"容器负责更新这些共享资源;
在Kubernetes中文社区的文档中也对此作了阐述,地址是:http://docs.kubernetes.org.cn/312.html
在提到Pod中的sidecar模式时,官方文档用到"relatively advanced"来形容,进一步证实了当下该模式的正确性,如下图:
具体的实现模型如下图:
根据kubernetes官方文档的指导,再结合SpringBoot应用的特点,我设计出的sidecar部署方式如下:
[root@localhost work]# docker history openjdk:8u181-jre-alpine3.8
IMAGE CREATED CREATED BY SIZE COMMENT
2e01f547f003 12 days ago /bin/sh -c set -x && apk add --no-cache ... 78.6 MB
<missing> 12 days ago /bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION... 0 B
<missing> 12 days ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u181 0 B
<missing> 7 weeks ago /bin/sh -c #(nop) ENV PATH=/usr/local/sbi... 0 B
<missing> 7 weeks ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/... 0 B
<missing> 7 weeks ago /bin/sh -c { echo '#!/bin/sh'; echo 's... 87 B
<missing> 7 weeks ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0 B
<missing> 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0 B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:25c10b1d1b41d46... 4.41 MB
之所以要用jre-alpine版本,是因为8u181-jdk版本相比之下大了很多,如下所示:
[root@localhost work]# docker history openjdk:8u181-jdk
IMAGE CREATED CREATED BY SIZE COMMENT
954739b8bdfb 7 days ago /bin/sh -c /var/lib/dpkg/info/ca-certifica... 355 kB
<missing> 7 days ago /bin/sh -c set -ex; if [ ! -d /usr/share... 348 MB
<missing> 7 days ago /bin/sh -c #(nop) ENV CA_CERTIFICATES_JAV... 0 B
<missing> 7 days ago /bin/sh -c #(nop) ENV JAVA_DEBIAN_VERSION... 0 B
<missing> 3 weeks ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u181 0 B
<missing> 3 weeks ago /bin/sh -c #(nop) ENV JAVA_HOME=/docker-j... 0 B
<missing> 3 weeks ago /bin/sh -c ln -svT "/usr/lib/jvm/java-8-op... 33 B
3 weeks ago /bin/sh -c { echo '#!/bin/sh'; echo 's... 87 B
3 weeks ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0 B
3 weeks ago /bin/sh -c apt-get update && apt-get insta... 2.21 MB
3 weeks ago /bin/sh -c apt-get update && apt-get insta... 142 MB
3 weeks ago /bin/sh -c set -ex; if ! command -v gpg >... 7.8 MB
3 weeks ago /bin/sh -c apt-get update && apt-get insta... 23.2 MB
3 weeks ago /bin/sh -c #(nop) CMD [" bash"] 0 B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:b3598c18dc39584... 101 MB
以上就是整体设计思路,接下来咱们就来实战吧,分三步完成:
本次实战分为以下几部分组成:
本次实战一共有四台CentOS7机器,基本信息如下:
hostname | IP地址 | 身份 | 配置 |
---|---|---|---|
localhost | 192.168.119.157 | master,主控节点 | 双核,2G内存 |
node1 | 192.168.119.156 | node,一号业务节点 | 双核,4G内存 |
node2 | 192.168.119.159 | node,二号业务节点 | 双核,2G内存 |
maven | 192.168.119.155 | 负责编译构建Spring Boot应用 | 双核,2G内存 |
kubernetes环境由localhost、node1、node2三台机器组成,maven负责编译构建、生成镜像、上传到镜像仓库等操作;
这是个简单的Spring Boot应用,对外提供一个http接口,返回一个字符串;
您可以选择直接从GitHub下载这个工程的源码,地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | [email protected]:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
这个git项目中有多个文件夹,本章源码在springbootsidecardemo这个文件夹下,如下图红框所示:
您也可以随本文一起来开发这个应用:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.bolingcavalrygroupId>
<artifactId>springbootsidecardemoartifactId>
<version>0.0.1version>
<packaging>jarpackaging>
<name>springbootsidecardemoname>
<description>Demo project for Spring Boot sidecard demo in K8Sdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.0.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<finalName>appfinalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>com.spotifygroupId>
<artifactId>docker-maven-pluginartifactId>
<version>0.4.12version>
<configuration>
<imageName>bolingcavalry/${project.artifactId}imageName>
<imageTags>
<imageTag>${project.version}imageTag>
imageTags>
<baseImage>busyboxbaseImage>
<resources>
<resource>
<targetPath>/targetPath>
<directory>${project.build.directory}directory>
<include>app.jarinclude>
resource>
resources>
configuration>
plugin>
plugins>
build>
project>
上面的配置有一处需要注意,就是基础镜像的选择(就是baseImage节点中的内容),我用了busybox,用它是因为够小,来看docker镜像仓库中的描述,地址是https://hub.docker.com/_/busybox/:
看到这里,可能会有朋友问"为什么不用scratch?它比busybox更小",scratch虽小,但不带基本的shell命令,例如cp命令,而后容器启动时要用cp命令对文件做复制操作,因此只能选择busybox了;
package com.bolingcavalry.springbootsidecardemo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* @Description: 一个最普通的Controller,hello接口返回一个字符串并带上当前时间
* @author: willzhao E-mail: [email protected]
* @date: 2018/11/6 14:15
*/
@RestController
public class HelloController {
@RequestMapping(value = "/hello")
public String hello(){
return "Hello version 1.0 " + new Date();
}
}
mvn clean package -U -DskipTests docker:build
构建成功的控制台输出如下:
[INFO] Building image bolingcavalry/springbootsidecardemo
[INFO] Building image bolingcavalry/springbootsidecardemo
Step 1/2 : FROM busybox
---> 59788edf1f3e
Step 2/2 : ADD /app.jar //
---> 8105c9ac033b
Removing intermediate container fdc62513abf6
Successfully built 8105c9ac033b
[INFO] Built bolingcavalry/springbootsidecardemo
[INFO] Tagging bolingcavalry/springbootsidecardemo with 0.0.1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.354 s
[INFO] Finished at: 2018-11-06T05:07:08-08:00
[INFO] Final Memory: 42M/225M
[INFO] ------------------------------------------------------------------------
root@maven:~# docker history bolingcavalry/springbootsidecardemo:0.0.1
IMAGE CREATED CREATED BY SIZE COMMENT
8105c9ac033b 46 minutes ago /bin/sh -c #(nop) ADD file:909ca8e9c8898cd... 16.6 MB
59788edf1f3e 4 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:63eebd629a5f755... 1.15 MB
root@maven:/usr/local/work/github/blog_demos/springbootsidecardemo/target# ls -al
total 16276
drwxr-xr-x 9 root root 4096 Nov 6 05:08 .
drwxr-xr-x 5 root root 4096 Nov 6 05:29 ..
-rw-r--r-- 1 root root 16621351 Nov 6 05:08 app.jar
-rw-r--r-- 1 root root 4432 Nov 6 05:08 app.jar.original
drwxr-xr-x 3 root root 4096 Nov 6 05:08 classes
drwxr-xr-x 2 root root 4096 Nov 6 05:08 docker
drwxr-xr-x 3 root root 4096 Nov 6 05:08 generated-sources
drwxr-xr-x 3 root root 4096 Nov 6 05:08 generated-test-sources
drwxr-xr-x 2 root root 4096 Nov 6 05:08 maven-archiver
drwxr-xr-x 3 root root 4096 Nov 6 05:08 maven-status
drwxr-xr-x 3 root root 4096 Nov 6 05:08 test-classes
执行docker push命令,将镜像推送到镜像仓库中,我这里是推送到了hub.docker.com,您可以根据实际情况来执行,例如私有仓库、阿里云等都可以,当然了,如果当前机器就是K8S的机器就不用推送了,毕竟此镜像就是在K8S环境用的;
如果觉得推送到仓库太慢,或者从仓库下载太慢,也可以使用文件导入导出的方式,具体操作如下:
#将镜像导出为tar文件
docker save 2e01f547f003 > 1.tar
###将tar文件还原为镜像
docker load < 1.tar
###还原后的镜像的名称和tag都不对,要用tag命令来设置
docker tag 8105c9ac033b bolingcavalry/springbootsidecardemo:0.0.1
root@maven:~# docker images | grep sidecar
bolingcavalry/springbootsidecardemo 0.0.2 f6ba01c33388 11 hours ago 17.8 MB
bolingcavalry/springbootsidecardemo 0.0.1 8105c9ac033b 11 hours ago 17.8 MB
现在镜像已经OK,该准备部署到kubernetes环境了;
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: javaweb
spec:
replicas: 1
template:
metadata:
labels:
name: javaweb
spec:
initContainers:
- image: bolingcavalry/springbootsidecardemo:0.0.1
name: appjar
command: ["cp", "/app.jar", "/app"]
volumeMounts:
- mountPath: /app
name: app-volume
containers:
- image: openjdk:8u181-jre-alpine3.8
name: openjdk8u181
command: ["java","-jar","/webapp/app.jar"]
volumeMounts:
- mountPath: /webapp
name: app-volume
ports:
- containerPort: 8080
volumes:
- name: app-volume
emptyDir: {}
从上述配置中,有两处需要注意:
第一,应用镜像被设置为initConteiners类型的容器,被设置执行cp命令将自己的app.jar文件复制到共享的 Volume;
第二,一直运行用于提供服务的容器,来自openjdk镜像的java进程,该进程加载的jar文件就是共享的 Volume中的app.jar;
apiVersion: v1
kind: Service
metadata:
name: javaweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30008
selector:
name: javaweb
kubectl create -f javaweb-deploy.yaml \
&& kubectl create -f javaweb-svc.yaml
符合预期,证明主容器加sidecar容器的组合方式是可以正常工作的,接下来试试升级;
接下来模拟生产环境的应用升级,前面准好了两个版本的应用镜像:0.0.1和0.0.2,现在K8S环境运行的是0.0.1,咱们将其升级为0.0.2:
kubectl apply -f javaweb-deploy.yaml
至此,Spring Boot应用在kubernetes的sidecar设计与实战就全部完成了,希望此文能助您将应用以sidecar模式部署在kubernetes,如果您发现我对官网的指导内容理解有误或有偏差,欢迎您的热心指正,谢谢!
下面是文中涉及到的一些知识点的细节,欢迎点击参考:
Docker下构建java应用镜像:
《maven构建docker镜像三部曲之一:准备环境》;
《maven构建docker镜像三部曲之二:编码和构建镜像》;
《maven构建docker镜像三部曲之三:推送到远程仓库(内网和阿里云)》;
kubernetes环境:
《CentOS7环境部署kubenetes1.12版本五部曲之一:标准化机器》;
《CentOS7环境部署kubenetes1.12版本五部曲之二:创建master节点》;
《CentOS7环境部署kubenetes1.12版本五部曲之三:node节点加入》;
《CentOS7环境部署kubenetes1.12版本五部曲之四:安装dashboard》;
《CentOS7环境部署kubenetes1.12版本五部曲之五:安装kubectl》;
kubernetes上部署应用和升级:
《kubernetes下的Nginx加Tomcat三部曲之一:极速体验》;
《kubernetes下的Nginx加Tomcat三部曲之二:细说开发》;
《kubernetes下的Nginx加Tomcat三部曲之三:实战扩容和升级》;