花仲抒站在荒凉的山丘上,看着两位截移派大神WebLogic和WebSphere老态龙钟渐行渐远的背影,心中竟有些许伤感,不禁吟道:
滚滚长江东逝水,浪花淘尽英雄。
是非成败转头空。
青山依旧在,几度夕阳红。
白发渔樵江渚上,惯看秋月春风。
一壶浊酒喜相逢。
古今多少IT技,都付笑谈中。
吟罢双腿微曲,体内运行着库泊大法,将真虚二气容纳其中,纵身一跃,跳上旁边一颗古树,起落之间已经站在树梢,一套SpringBoot春初拳施展开来,真是行云流水,简约轻盈至极。
本文介绍如何将一个SpringBoot应用打docker镜像包,推送到docker镜像库,然后部署到kubernetes上运行。
一、环境准备
首先检查你的kubernetes集群环境已经就绪,如果你没有的话,最简单的方式是使用Minikube,如何安装详见kubernetes官网 https://kubernetes.io/docs/tasks/tools/install-minikube/
执行以下命令检查k8s环境
jared@ubuntu-box:~$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-07T21:20:10Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.2", GitCommit:"59603c6e503c87169aea6106f57b9f242f64df89", GitTreeState:"clean", BuildDate:"2020-01-18T23:22:30Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/amd64"}
jared@ubuntu-box:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ubuntu-box Ready master 40d v1.17.2
这是一个单节点的minikube环境,一切正常,Let us go!
二、编写一个简单的SpringBoot应用
本文采用在 https://start.spring.io/ 创建SpringBoot应用的方式,创建一个只需要Spring Web依赖的简单web应用,如下图所示
点击GENERATE
按钮,将下载到本地的zip包解压,然后用IDE打开,添加一个Controller类
package com.tiehuapen.hellok8s;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Hellok8sController {
@GetMapping("/")
public String home() {
return "Hello K8S!";
}
}
执行 mvn clean package
并检查在本地能正常运行。
三、打包docker镜像
你可以采用Dockerfile方式创建Docker镜像,这种方式需要你本地安装了Docker。你也可以采用谷歌提供的容器化工具JIB来打包镜像,这种方式更方便,无需本地安装Docker,maven/gradle命令调用插件即可。
方式一:使用Dockerfile
在Spring boot项目根目录下创建一个名为Dockerfile的文件,文件内容如下:
FROM openjdk:8-jdk-alpine
ARG JARFILE=target/*.jar
COPY ${JARFILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
然后在项目根目录下执行构建镜像包的命令:
$ docker build -t tiehuapen/hellok8s:v1 .
构建完成后,执行镜像列表查询的命令:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tiehuapen/hellok8s v1 9228d650f5e3 22 seconds ago 122MB
可以看到该镜像已经构建好了,接下来在本地docker环境测试一下该镜像是否可以正常运行,执行运行容器的命令:
$ docker run -p 8080:8080 --rm tiehuapen/hellok8s:v1
打开浏览器访问 http://localhost:8080/ ,运行正常。
上面那个Dockerfile非常简洁,但是它默认采用root用户执行程序,另外,为了将Spring Boot fat jar中依赖项和应用程序资源之间干净分离,我们将Dockerfile做一些改进,改进后如下所示:
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.tiehuapen.hellok8s.Hellok8sApplication"]
然后在项目根目录下执行构建镜像包的命令,并将tag设置为v2,注意:在构建之前要先将Spring Boot far jar解压到Dockerfile中指定的路径中。
$ mkdir -p target/dependency
$ cd .\target\dependency\
$ jar -xf ../*.jar
$ docker build -t tiehuapen/hellok8s:v2 .
构建完成后,执行镜像列表查询的命令:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tiehuapen/hellok8s v2 81e53377d1ea 9 minutes ago 122MB
tiehuapen/hellok8s v1 f79127e183a1 About an hour ago 122MB
可以看到镜像v2也已经构建好了,类似v1版本,在本地docker环境测试一下是否可以正常运行。
方式二:使用Google GIB插件
采用Google GIB插件无需调用docker命令,可直接使用maven/gradle命令构建docker镜像。
首先,在pom.xml中添加GIB插件:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.1.0</version>
<configuration>
<from>
<image>openjdk:alpine</image>
</from>
<to>
<image>tiehuapen/hellok8s:v3</image>
</to>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
需要配置的主要信息就是from.image和to.image,from.image设置基础镜像,to.image设置将要构建的镜像名和标签。上面的from.image没有设置仓库地址,这表示将从默认的仓库地址docker.io下载基础镜像,速度一般会比较慢。
如果你本地docker环境已经有该基础镜像了,那你也可以直接从本地docker环境获取基础镜像,如下所示:
<from>
<image>docker://openjdk:alpine</image>
</from>
构建镜像命令如下所示,你可以选择将镜像生成在本地docker环境,也可以选择生成并上传到远程的镜像仓库。
$ mvn compile jib:dockerBuild
$ mvn compile jib:build
上面的例子中,将镜像生成在本地docker环境会执行成功,构建完成后,执行镜像列表查询的命令,可以看到本地docker已经有hellok8s:v3了。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tiehuapen/hellok8s v2 81e53377d1ea 19 hours ago 122MB
tiehuapen/hellok8s v1 f79127e183a1 20 hours ago 122MB
openjdk alpine 5801f7d008e5 21 months ago 103MB
tiehuapen/hellok8s v3 a26b5c4f4e7b 50 years ago 120MB
但是生成并上传到远程的镜像仓库的命令会执行失败,报 401 Unauthorized 错误,这是因为to.image没有设置仓库地址(默认为docker hub),也没有设置用户鉴权信息。
现在我们来设置将生成镜像上传到阿里云容器镜像仓库,同时还可以指定多个标签,如下所示:
<to>
<image>registry.cn-beijing.aliyuncs.com/tiehuapen/hellok8s:v3</image>
<auth>
<username>xxx</username>
<password>yyy</password>
</auth>
<tags>
<tag>jib</tag>
<tag>latest</tag>
</tags>
</to>
其中的username和password是阿里云容器镜像服务的账号密码,而tiehuapen则是阿里云容器镜像服务的命名空间。
再次执行mvn compile jib:build
,结果显示 BUILD SUCCESS。在阿里云控制台访问容器镜像服务,可以发现该镜像已经上传成功。
关于GIB最后要说的是,可以将jib的构建目标绑定到maven的生命周期,比如package
,如下所示:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
...
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
</executions>
</plugin>
现在执行mvn package
,将会自动调用JIB插件命令,构建镜像。
四、部署到kubernetes
有了docker镜像之后,我们来将该镜像部署到kubernetes。首先创建一个namespace给我们这个演示使用,你当然也可以用已经存在的namespace,不过单独的命名空间更利于资源管理和清理。
$ kubectl create ns demo
namespace/demo created
接下来,我们以两种不同的方式来将hellok8s应用部署到Kubernetes,一种是命令行方式,一种是yaml文件方式。
方式一:命令行方式部署
首先执行kubectl create deployment
来部署hellok8s应用,如下所示,在命令中指定image和命名空间。
$ kubectl create deployment hellok8s --image=registry.cn-beijing.aliyuncs.com/tiehuapen/hellok8s:v3 -n demo
deployment.apps/hellok8s created
查询命名空间demo中的资源,发现deployment资源已经成功创建,并且随之创建了replicaset和pod。
$ kubectl get all -n demo
NAME READY STATUS RESTARTS AGE
pod/hellok8s-dd4ddd4dc-gt6v2 1/1 Running 0 8s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hellok8s 1/1 1 1 8s
NAME DESIRED CURRENT READY AGE
replicaset.apps/hellok8s-dd4ddd4dc 1 1 1 8s
现在,hellok8s应用可以通过K8S内部pod进行访问了,但是我们想通过浏览器来外部访问,为此,我们可以通过kubectl expose deployment
命令,创建service资源来暴露hellok8s给外部访问。
$ kubectl expose deployment hellok8s --type=LoadBalancer --name hellok8s-svc \
> --port 80 --target-port 8080 -n demo
service/hellok8s-svc exposed
执行成功后,执行kubectl get svc
命令来查询结果。
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hellok8s-svc LoadBalancer 10.108.10.165 <pending> 80:32483/TCP 5m49s
$ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
ubuntu-box Ready master 44d v1.17.2 192.168.1.109 <none> Ubuntu 16.04.3 LTS 4.4.0-87-generic docker://19.3.6
其中,CLUSTER-IP为k8s集群内部IP,只可内部访问;可以发现该service在k8s节点的端口32483上对外暴露访问,执行kubectl get node
查询node的IP地址后,打开浏览器即可成功访问了。
方式二:yaml文件方式部署
更好的方式是编写yaml文件,在yaml文件中规定要创建的deployment和service,然后只需要apply这个文件就可以了,hellok8s.yaml文件如下所示。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hellok8s
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: hellok8s
template:
metadata:
name: hellok8s
labels:
app: hellok8s
spec:
containers:
- image: registry.cn-beijing.aliyuncs.com/tiehuapen/hellok8s:v3
name: hellok8s-web
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: hellok8s-svc
namespace: demo
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30123
selector:
app: hellok8s
通过kubectl apply
命令部署该文件所描述的资源。
$ kubectl apply -f hellok8s.yaml
deployment.apps/hellok8s created
service/hellok8s-svc created
查询执行结果,结果显示2个Pod实例已经成功运行,服务已经在指定的端口30123对外暴露。
$ kubectl get all -n demo
NAME READY STATUS RESTARTS AGE
pod/hellok8s-68dffd9c4-dp8vj 1/1 Running 0 10s
pod/hellok8s-68dffd9c4-jmqlv 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hellok8s-svc NodePort 10.108.245.158 <none> 80:30123/TCP 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hellok8s 2/2 2 2 10s
NAME DESIRED CURRENT READY AGE
replicaset.apps/hellok8s-68dffd9c4 2 2 2 10s
打开浏览器输入 192.168.1.109:30123 即可成功访问。
五、从Kubernetes清除应用
文章的最后,我们来清理干净上面演示中所创建的资源,因为上面都是在命名空间demo中操作的,所以就非常容易清理了。
$ kubectl delete all --all -n demo
pod "hellok8s-68dffd9c4-dp8vj" deleted
pod "hellok8s-68dffd9c4-jmqlv" deleted
service "hellok8s-svc" deleted
deployment.apps "hellok8s" deleted
replicaset.apps "hellok8s-68dffd9c4" deleted
当然,你也可以直接删除命名空间demo,那么它里面的资源也会自动删除。
$ kubectl delete ns demo
namespace "demo" deleted
看完这篇文章之后,你是不是跃跃欲试,想把自己的SpringBoot应用跑到Kubernetes上面去呢。
更多精彩文章,请扫码关注我的微信公众号。
评论