在K3d中部署SpringBoot

在前面的文章中,我们学习了如何使用k3d在本地快速创建一个Kubernetes集群环境。作为一个轻量级的Kubernetes发行版,k3s为我们提供了简单高效的方式来学习和体验Kubernetes。

这次,我们将在k3d集群中部署一个基于SpringBoot的微服务应用,并通过Ingress暴露服务进行访问。让我们开始吧!

准备工作

如果你还没有搭建k3d环境,可以参考我的这篇文章 - 在K3d中部署Rancher

搭建k3d环境
sudo k3d cluster create hello-k3d \
    --api-port 6550 \
    -p "9080:80@loadbalancer" -p "9443:443@loadbalancer" \
    --servers-memory 4g \
    --agents-memory 4g \
    --servers 1 \
    --agents 2 

上面的命令会创建一个包含1个server节点和2个agent节点的集群。同时,它将主机的9080端口映射到集群的80端口(HTTP),将9443端口映射到集群的443端口(HTTPS)。

接下来,我们需要将kubeconfig文件复制到当前用户的目录下,这样普通用户就可以执行kubectl命令了:

sudo cat /root/.kube/config > ~/.kube/config

首先,我们需要从Github上克隆微服务应用的代码仓库:

git clone https://github.com/imzhoukunqiang/springboot-k3s.git
cd springboot-k3s

然后切换到1.2.0版本的分支:

git checkout 1.2.0

创建Namespace

我们将在springboot这个namespace下部署所有组件:

kubectl apply -f yamls/namespace.yaml

部署微服务

接下来,使用kubectl命令一次性部署food、supermarket和digital三个微服务:

kubectl apply -f yamls/food.yaml
kubectl apply -f yamls/supermarket.yaml
kubectl apply -f yamls/digital.yaml

这些YAML文件中定义了Deployment、Service和Ingress等Kubernetes对象。每个微服务有两个副本,通过Service暴露80端口,并且通过Ingress将supermarket-svc映射到supermarket-127-0-0-1.nip.io这个域名上。

部署完成后,我们可以通过下面的命令查看pods的运行状态:

kubectl get pods -n springboot

访问应用

最后,打开浏览器访问 http://supermarket-127-0-0-1.nip.io:9080,即可看到supermarket微服务的Web页面了。

通过上面的步骤,我们成功地将一个完整的SpringBoot微服务应用部署到了k3d集群中,并使用Ingress暴露了服务。在本地环境中,我们就可以方便地开发、测试和调试基于Kubernetes的应用程序了。

应用程序逻辑

这个SpringBoot应用由三个微服务组成:

Supermarket

Supermarket是一个聚合服务,它从digital和food两个服务中获取产品数据,并在”/shopping”路径下渲染成一个产品列表页面:

@RequestMapping("/shopping") 
public ModelAndView shopping() {
    ModelAndView modelAndView = new ModelAndView("shopping");
    Map<String, List<Product>> productMap = loadProductMap();
    modelAndView.addObject("productMap", productMap);
    return modelAndView;
}

private Map<String, List<Product>> loadProductMap() {
    LinkedHashMap<String, List<Product>> map = new LinkedHashMap<>();
    map.put("手机数码", digitalRemote.getProductList());
    map.put("零食饮料", foodRemote.getProductList());
    return map;
}

Food

Food服务提供了一个”/products”接口,返回一个零食饮料类的产品列表:

@RequestMapping("/products")
public List<Product> products() {
    ArrayList<Product> list = new ArrayList<>();
    list.add(new Product("百事可乐", 3, RNG.nextInt(5, 500)));
    list.add(new Product("风干牛肉", 49.9, RNG.nextInt(3, 300)));
    list.add(new Product("去骨鸡爪", 38.9, RNG.nextInt(1, 300)));
    list.add(new Product("乐事薯片", 7.8, RNG.nextInt(1, 300)));
    list.add(new Product("恰恰瓜子", 12.5, RNG.nextInt(1, 300)));
    list.add(new Product("雀巢咖啡", 32.9, RNG.nextInt(1, 300)));
    return list;
}

Digital

Digital服务提供了一个”/products”接口,返回一个手机数码类的产品列表:

@RequestMapping("/products")
public List<Product> products() {
    ArrayList<Product> list = new ArrayList<>();
    list.add(new Product("iPhone 15", 5999, RNG.nextInt(5, 500)));
    list.add(new Product("Xiaomi 14", 3799, RNG.nextInt(3, 300)));
    list.add(new Product("Huawei Mate 60 Pro", 7999, RNG.nextInt(1, 300)));
    return list;
}

微服务是如何治理的?

  • fooddigitalsupermarket 三个微服务我们分别部署了2个副本。
  • 我们还定义了 Service 资源对象,用于为每个微服务创建一个分布式的、集群内部可访问的入口地址。比如 food-svcsupermarket-svcdigital-svc
  • Kubernetes 集群为我们提供了一个扁平的、全局可访问的服务地址空间。也就是说,集群内的任何一个 Pod,都可以通过其他服务的名称直接访问它们。

我们在 supermarket 服务中,定义了下面的环境变量。

env:  # 环境变量
  - name: server.port
    value: '80'
  - name: remote.foodUrl
    value: 'http://food-svc'
  - name: remote.digitalUrl
    value: 'http://digital-svc'

其中:

  • server.port定义了supermarket服务自身监听的端口号为80。
  • remote.foodUrlremote.digitalUrl则指定了food服务和digital服务的访问地址。

这里的food-svcdigital-svc是两个服务在Kubernetes集群中的服务名称,通过DNS服务发现机制,它们会被自动解析为相应服务的ClusterIP地址。

我们再看下food-svc是怎么定义和工作的

apiVersion: v1
kind: Service
metadata:
  name: food-svc
  namespace: springboot
spec:
  selector:
    app: food
  ports:
    - name: tcp-80
      port: 80
      protocol: TCP
      targetPort: 80
  sessionAffinity: None
  type: ClusterIP

当我们在集群中创建了这个Service后,它会自动通过标签选择器发现所有具有app=food标签的Pods,并为它们提供负载均衡和反向代理功能。

任何发送到food-svc这个cluster IP的请求,都会被自动分发到关联Pod的80端口上。比如超市服务supermarket就可以通过访问http://food-svc来向food服务发起远程调用。

由于type设置为ClusterIP,所以这个Service只有集群内部可访问,集群外是无法直接访问的。如果需要暴露给外部客户端,我们需要额外创建一个Ingress或NodePort等资源对象。

That’s all, bye!


在K3d中部署SpringBoot
https://coding.gs/2024/04/15/k3d/springboot-in-k3d/
作者
K
发布于
2024年4月15日
许可协议