Spring on Kubernetes 最佳实践(一)基本篇

通过此篇文章你讲会学会什么

  • 创建一个基本的Spring Boot应用
  • 为Spring Boot应用构建一个Docker镜像
  • 自动提交镜像到镜像仓库(下一篇)
  • 在Kubernetes上创建一个Deployment和Service
  • 在Kubernetes上部署Spring Boot应用
  • 额外的Kubernetes配置和服务发现(下一篇)

环境准备

  • 了解Spring和Kubernetes基本的知识
  • JDK 8 或者更高的版本
  • 安装Docker Desktop
  • 安装一个IDE, 推荐 IntelliJ

现阶段Docker Desktop 对Kubernetes有良好的支持,可以开箱即用,搭建本地Kubernetes集群. 参考官方文档安装Docker Desktop


创建一个Spring Boot 应用

  • 下载一个基本的Spring Boot应用, 或者在 start.spring.io 创建
  • 解压下载的zip文件,并使用IDE打开项目

添加一个RestController

修改 K8sDemoApplication.java 添加 @RestController , 添加一个@GetMapping

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class K8sDemoAppApplication {

    public static void main(String[] args) {
        SpringApplication.run(K8sDemoAppApplication.class, args);
    }

    @GetMapping("/")
    public String hello() {
        return "Hello World";
    }
}

运行Spring Boot应用

在命令行输入

./mvnw clean package && java -jar ./target/*.jar

测试Spring Boot应用

访问一个HTTP请求 http://localhost:8080

$curl http://localhost:8080
Hello World

测试Spring Boot Actuator Endpoint

在Spring Boot应用添加了Actuator包之后, Spring会提供一些Production ready Features

$ curl localhost:8080/actuator; echo
{
  "_links": {
    "self": {
      "href": "http://localhost:8080/actuator",
      "templated": false
    },
    "health": {
      "href": "http://localhost:8080/actuator/health",
      "templated": false
    },
    "info": {
      "href": "http://localhost:8080/actuator/info",
      "templated": false
    }
}

继续下面的步骤前先确保停止之前启动的Spring Boot应用, 否则可能会导致端口冲突因为端口8080被占用


容器化应用

在Kubernetes上运行该应用程序的第一步是为该应用生成一个镜像,然后将其部署到Kubernetes


构建一个Docker容器

  • Spring Boot 2.3.x 支持直接构建一个Docker容器无需任何额外的配置文件或者插件

  • 使用命令构建Docker容器

    $ ./mvnw spring-boot:build-image

运行 docker images 查看构建的Docker容器

docker images | grep k8s-demo-app
k8s-demo-app                         0.0.1-SNAPSHOT            c7f56cf022f7        40 years ago        224MB

使用Docker命令运行Docker容器

$ docker run --name k8s-demo-app -p 8080:8080 k8s-demo-app:0.0.1-SNAPSHOT

测试在Docker运行中的Spring Boot应用

$ curl http://localhost:8080
Hello World

继续下面的步骤前先确保停止在Docker运行的应用


部署到Kubernetes

我们现在可以在本地Kubernetes集群上运行打包好的应用. 如果需要在非本地Kubernetes集群运行的话,需要推送镜像到镜像仓库,此处先略过推送到镜像仓库的步骤,下一篇会讲。


Deployment 描述文件

Deployment 官方概念的概述

  • Kubernetes 使用yaml文件提供了一种方法来描述如何将应用部署到集群
  • 你可以使用Kubernetes文档作为参考如何编写这些文件
  • 或者你可以使用kubectel来生成一个Deployment文件
  • 使用—dry-run 标记运行我们生成文件而且不会真正的运行kubectl命令
mkdir k8s
kubectl create deployment k8s-demo-app --image k8s-demo-app:0.0.1-SNAPSHOT -o yaml --dry-run > k8s/deployment.yaml

生成的deployment.yaml文件如下

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: k8s-demo-app
  name: k8s-demo-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: k8s-demo-app
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: k8s-demo-app
    spec:
      containers:
      - image: k8s-demo-app:0.0.1-SNAPSHOT
        name: k8s-demo-app
        imagePullPolicy: IfNotPresent
        resources: {}
status: {}

注意需要在deployment.yaml 文件需要添加一个imagePullPolicy: IfNotPresent,因为我们还没有将镜像推送到镜像仓库中,因此需要使用本地镜像


Service 描述文件

  • 将运行在一组Pod上的应用程序公开为网络服务的抽象方法。
    $ kubectl create service clusterip k8s-demo-app --tcp 80:8080 -o yaml --dry-run > k8s/service.yaml

生成的service.yaml文件如下

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: k8s-demo-app
  name: k8s-demo-app
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: k8s-demo-app
  type: ClusterIP
status:
  loadBalancer: {}


部署 Deployment 和 Service YAML

  • Deployment 和Service 通过命令创建在k8s文件夹里面
  • 运行它们
$kubectl apply -f ./k8s
deployment.apps/k8s-demo-app created
service/k8s-demo-app created
$kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/k8s-demo-app-56bb6b8ddf-2w9ct   1/1     Running   0          3s

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/k8s-demo-app   ClusterIP   10.109.188.161   <none>        80/TCP    3s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/k8s-demo-app   1/1     1            1           3s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/k8s-demo-app-56bb6b8ddf   1         1         1       3s

测试运行在Kubernetes上的应用

  • Service 的类型是 ClusterIP, 这意味着只有集群内部才可以访问
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/k8s-demo-app   ClusterIP   10.109.188.161   <none>        80/TCP    3s
  • 可以使用 kubectl pord-forward 命令来访问 Service
    kubectl port-forward service/k8s-demo-app 8080:80
    $ curl http://localhost:8080
    Hello World

Congrats you have deployed your first app to Kubernetes 🎉

继续下面的步骤前先确保停止在Docker运行的应用, 使用命令 docker rm -f k8s-demo-app


公开应用服务

NOTE: LoadBalancer 类型的 Service 是基于特定平台的, 更改为此类型后,应用的可见性很大程度上取决于其部署位置(Google Cloud Platform 等),因此在本地测试我们可以使用NodePort 类型来暴露服务

修改service.yaml 里面type为NotePort,且添加nodePort参数,注意nodePort范围为(30000-32767)

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: k8s-demo-app
  name: k8s-demo-app
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
    nodePort: 30081
  selector:
    app: k8s-demo-app
  type: NodePort
status:
  loadBalancer: {}

  • 更新文件后,使用命令使其生效
    kubectl apply -f ./k8s

测试公开的服务

现在我们可以通过 http://{任何集群节点的IP}:{NodePort} 的方式来访问

$ kubectl get service k8s-demo-app -w
NAME           TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
k8s-demo-app   NodePort   10.109.188.161   <none>        80:30080/TCP   7m41s

curl http://localhost:30080
hello World

恭喜, 你已经将你的Spring Boot 应用成功部署到Kubernetes!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容