在本教程中,您将使用Kubernetes将Spring Boot微服务架构部署到Google Cloud,特别是Google Kubernetes Engine(GKE)。 您还将使用Istio创建服务网格层并创建公共网关。 整个事情将使用Okta OAuth JWT身份验证进行保护。

那是一堆烂话。 我们在这里不会深入解释微服务。 简而言之,微服务是一种设计模式,可将较大的单片服务拆分为较小的独立“微”服务。 这些服务通过网络松散耦合。 这种体系结构的好处是,每个服务都变得可测试,可维护且可独立部署。 在互联网规模上,在像Amazon和Netflix这样的大型公司中,这种架构都很棒,因为它允许公司将小型团队的职责分配给可管理的,离散的功能单元。 与拥有成千上万的人监督的巨大的整体代码块相反。 缺点是复杂性和基础架构的初期成本很高,这对于无法规模化的小型项目可能没有意义。

ķubernetes is a platform for deploying containerized services. You can think of it as a container orchestrator for Docker containers (this is a simplification, but it’ll work). It will allow us to write YAML scripts that automate the deployment of our microservice architecture to our chosen platform, GKE. It’s a huge project with lots to dig into. Take a look at their docs for more info.

一世stio adds another layer of features on top of Kubernetes, adding some great monitoring, security, access control, and load balancing features. Check out their website for more info.

微服务架构的最后一部分是Google Cloud和GKE。 这是您用来部署微服务的平台。 本教程中未介绍的另一个选项是Minikube。 Minikube在您的计算机上本地运行,可能对某些人有用。 我发现Google Kubernetes Engine更易于使用且性能更高。

我们假设您熟悉Spring Boot和Java。 如果不是这样,请查看教程末尾的一些链接以开始使用。

Requirements for Spring Boot and Kubernetes

HTTPie: Install HTTPie from their website so that we can run HTTP requests easily from the terminal.

docker: Please download and install Docker Desktop from their website if you don’t have it already installed.

ķubectl: This is Kubernetes’ command line interface. Instructions for installing it are on their website.

Google Cloud: You’ll need a Google Cloud account with billing enabled. A free trial is available and should include more than enough credit and time to get you through this tutorial. Go to the Google Cloud website and sign up.

developer.okta.com: We offer free developer accounts on our developer site. Please sign up for one now. You’ll use it toward the end of the tutorial.

Gcloud: This is the Google Cloud CLI. Install it using the instructions from their website. Once that is done, you’ll need to install the Gcloud kubectl components by running the following command:



gcloud components install kubectl



我是否提到微服务的初始复杂性成本很高?

Create a Google Kubernetes Engine Project with Istio

您现在应该拥有一个启用了结算功能的Google Cloud帐户。 同样,您实际上不需要花任何钱,但是如果不付费,您将无法使用免费试用版。

创建一个新项目。 命名春靴(或您想要的任何内容,但您需要使用各个项目的项目ID)。 等待项目创建。

项目名称的结尾可能会带有一个ID号,例如弹簧靴gke-232934。 您将需要多次使用该项目名称,因此请继续将其存储在shell变量中并加以注意。



PROJECT_NAME=<your project name and ID>



项目准备就绪后,打开项目仪表板,打开导航菜单,然后单击Kubernetes引擎。 点击启用帐单按钮(如果您尚未启用结算功能),然后选择一个结算帐户。

请点击创建集群。

Kubernetes在微服务架构中 基于kubernetes的微服务_java

在左侧面板中,选择您的第一个集群。

将集群命名为“ spring-boot-cluster”。

选择区域“ us-west1-a”。

点击高级选项群集配置面板底部的链接以显示高级选项。 向下滚动到底部,然后选中复选框启用Istio(测试版)。 这将自动在群集上安装Istio。

Kubernetes在微服务架构中 基于kubernetes的微服务_TCP_02

在底部,单击创建创建集群。 喝咖啡或休息一下; 创建集群将需要几分钟。

同时,如果您还没有的话,请继续进行初始化gcloudCLI通过运行:



gcloud init



在初始化过程中,您可以将新项目设置为默认项目,并将项目区域设置为默认区域。

部署集群后,您需要连接本地gcloud和Kubectl使用以下命令对其进行CLI:



gcloud container clusters get-credentials {yourClusterName} --zone us-west1-a --project {yourProjectId}



如果您使用其他项目名称,则需要更改命令以反映该名称。

注意:如果您点击连接Google Cloud Platform仪表板右侧的按钮,您将看到正确的命令,可输入:

Kubernetes在微服务架构中 基于kubernetes的微服务_数据库_03

结果,您应该看到类似以下的内容:



Fetching cluster endpoint and auth data.
kubeconfig entry generated for spring-boot-cluster.



您还需要为自己赋予集群管理员权限:



kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value core/account)



现在,您需要检查并确保Istio服务已安装并正在运行。 有两种检查方法。 首先,在您的Google Cloud Platform Kubernetes Engine仪表板中,单击服务按钮。 您应该在自己的计算机上看到Istio服务列表春季引导集群。 它们的状态栏中都应有绿色的“确定”。

在那里,请注意名为历史门户类型负载均衡器。 这是群集的公共负载平衡器,该条目显示公共IP和开放端口。

Kubernetes在微服务架构中 基于kubernetes的微服务_Kubernetes在微服务架构中_04

另一种检查方法是使用KubectlCLI。

要检查服务,请使用以下命令:kubectl获取服务-所有命名空间。 的--all-命名空间需要显示Istio服务,该服务位于组织系统命名空间。



$ kubectl get services --all-namespaces
NAMESPACE      NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                                                                                                                   AGE
default        kubernetes               ClusterIP      10.31.240.1     <none>           443/TCP                                                                                                                   5m
istio-system   istio-citadel            ClusterIP      10.31.252.214   <none>           8060/TCP,9093/TCP                                                                                                         3m
istio-system   istio-egressgateway      ClusterIP      10.31.247.186   <none>           80/TCP,443/TCP                                                                                                            3m
istio-system   istio-galley             ClusterIP      10.31.249.131   <none>           443/TCP,9093/TCP                                                                                                          3m
istio-system   istio-ingressgateway     LoadBalancer   10.31.244.186   35.185.213.229   80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:30675/TCP,8060:31581/TCP,853:32460/TCP,15030:30998/TCP,15031:31606/TCP   3m
istio-system   istio-pilot              ClusterIP      10.31.251.44    <none>           15010/TCP,15011/TCP,8080/TCP,9093/TCP                                                                                     3m
istio-system   istio-policy             ClusterIP      10.31.246.176   <none>           9091/TCP,15004/TCP,9093/TCP                                                                                               3m
istio-system   istio-sidecar-injector   ClusterIP      10.31.240.214   <none>           443/TCP                                                                                                                   3m
istio-system   istio-telemetry          ClusterIP      10.31.247.23    <none>           9091/TCP,15004/TCP,9093/TCP,42422/TCP                                                                                     3m
istio-system   promsd                   ClusterIP      10.31.246.88    <none>           9090/TCP                                                                                                                  3m
kube-system    default-http-backend     NodePort       10.31.250.134   <none>           80:31955/TCP                                                                                                              4m
kube-system    heapster                 ClusterIP      10.31.250.242   <none>           80/TCP                                                                                                                    4m
kube-system    kube-dns                 ClusterIP      10.31.240.10    <none>           53/UDP,53/TCP                                                                                                             4m
kube-system    metrics-server           ClusterIP      10.31.245.127   <none>           443/TCP



要检查Kubernetes容器,请使用:kubectl获取pods --all-namespaces



$ kubectl get pods --all-namespaces
NAMESPACE      NAME                                                      READY     STATUS      RESTARTS   AGE
istio-system   istio-citadel-7c4864c9d5-7xq9x                            1/1       Running     0          10m
istio-system   istio-cleanup-secrets-ghqbl                               0/1       Completed   0          10m
istio-system   istio-egressgateway-c7f44ff8-tz7br                        1/1       Running     0          10m
istio-system   istio-galley-698f5c74d6-hmntq                             1/1       Running     0          10m
istio-system   istio-ingressgateway-774d77cb7c-qvhkb                     1/1       Running     0          10m
istio-system   istio-pilot-6bd6f7cdb-gb2gd                               2/2       Running     0          10m
istio-system   istio-policy-678bd4cf9-r8p6z                              2/2       Running     0          10m
istio-system   istio-sidecar-injector-6555557c7b-99c6k                   1/1       Running     0          10m
istio-system   istio-telemetry-5f4cfc5b6-vj8cf                           2/2       Running     0          10m
istio-system   promsd-ff878d44b-hlkpg                                    2/2       Running     1          10m
kube-system    heapster-v1.6.0-beta.1-8c76f98c7-2b4dm                    2/2       Running     0          9m
kube-system    kube-dns-7549f99fcc-z5trl                                 4/4       Running     0          10m
kube-system    kube-dns-autoscaler-67c97c87fb-m52vb                      1/1       Running     0          10m
kube-system    kube-proxy-gke-spring-boot-cluster-pool-1-b6988227-p09h   1/1       Running     0          10m
kube-system    l7-default-backend-7ff48cffd7-ppvnn                       1/1       Running     0          10m
kube-system    metrics-server-v0.2.1-fd596d746-njws2                     2/2       Running     0          10m



吊舱的所有状态都必须为已完成要么跑步。 我遇到了几次问题,即自动配置无法正常工作,并且某些吊舱从未到达跑步身份,被困在容器创建。 我必须删除群集并重新安装它才能正常工作。

如果发生这种情况,您可以使用描述豆荚命令以查看发生了什么事情:kubectl 描述豆荚 -n 组织系统。 这将为您提供有关所有Pod中所有豆荚的信息组织系统命名空间,使用-n选项。

如果到此为止没有问题,现在您已经在安装了Istio的GKE上部署了Kubernetes集群! 很甜

Both Google and Istio have some pretty helpful docs if you have a problem. Check out the Google GKE docs and the Istio GKE docs for further support.

Create a Spring Boot Project for Your Microservices

Now go to the Spring Initializer and create your starter project.

  • 将构建工具从Maven更改为摇篮使用爪哇和Spring Boot版本2.1.3更新组至:com.okta.spring使用神器:springbootkbe加三依存关系:响应式网页,反应性MongoDB,and 龙目岛

Kubernetes在微服务架构中 基于kubernetes的微服务_TCP_05

请点击产生专案并下载项目。 将项目解压缩到本地计算机上的某个位置,然后在您喜欢的IDE或编辑器中将其打开。

Spring Initializer创建了一个带有MongoDB支持的准系统响应式Webflux项目,供您扩展。

与我的其他一些教程一样,并且由于我喜欢皮划艇,因此您将构建一个简单的反应式REST服务,该服务维护皮艇条目的数据库。 它实际上只是为了演示CRUD的基本功能(创建,读取,更新和删除),并且可以推广到任何类型的资源。

在里面com.okta.spring.springbootkbe包下src /主/ java,创建一个名为Kayak.java并将以下内容粘贴到其中。 这是您的反应性数据模型文档。



package com.okta.spring.springbootkbe;

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import org.springframework.data.mongodb.core.mapping.Document;  

@Document  
@Data  
@AllArgsConstructor  
@NoArgsConstructor  
public class Kayak {  

    private String name;  
    private String owner;  
    private Number value;  
    private String makeModel;  
}


package com.okta.spring.springbootkbe;

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import org.springframework.data.mongodb.core.mapping.Document;  

@Document  
@Data  
@AllArgsConstructor  
@NoArgsConstructor  
public class Kayak {  

    private String name;  
    private String owner;  
    private Number value;  
    private String makeModel;  
}



现在在同一个包中创建另一个文件,名为KayakRepository。java。



package com.okta.spring.springbootkbe;  

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;  

public interface KayakRepository extends ReactiveMongoRepository<Kayak, Long> {  
}


package com.okta.spring.springbootkbe;  

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;  

public interface KayakRepository extends ReactiveMongoRepository<Kayak, Long> {  
}



在本教程中,我不会对这里发生的事情进行过多的详细介绍。 Spring Boot在这两个文件之间进行了大量自动匹配,以创建功能齐全的反应式Mongo文档。

接下来,您需要添加一个控制器以允许访问Kayak文档数据模型。 创建一个名为皮划艇控制器在里面com.okta.spring.springbootkbe包。



package com.okta.spring.springbootkbe;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Controller
@RequestMapping(path = "/kayaks")
public class KayakController {

    private final KayakRepository kayakRepository;

    public KayakController(KayakRepository kayakRepository) {
        this.kayakRepository = kayakRepository;
    }

    @PostMapping()
    public @ResponseBody
    Mono<Kayak> addKayak(@RequestBody Kayak kayak) {
        return kayakRepository.save(kayak);
    }

    @GetMapping()
    public @ResponseBody
    Flux<Kayak> getAllKayaks() {
        Flux<Kayak> result = kayakRepository.findAll();
        return result;
    }}


package com.okta.spring.springbootkbe;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Controller
@RequestMapping(path = "/kayaks")
public class KayakController {

    private final KayakRepository kayakRepository;

    public KayakController(KayakRepository kayakRepository) {
        this.kayakRepository = kayakRepository;
    }

    @PostMapping()
    public @ResponseBody
    Mono<Kayak> addKayak(@RequestBody Kayak kayak) {
        return kayakRepository.save(kayak);
    }

    @GetMapping()
    public @ResponseBody
    Flux<Kayak> getAllKayaks() {
        Flux<Kayak> result = kayakRepository.findAll();
        return result;
    }}



该控制器将两种方法添加到/皮划艇端点,即POST和GET端点,它们分别添加新的皮划艇并列出所有皮划艇。

最后,添加一个简单的根控制器,名为根控制器。



package com.okta.spring.springbootkbe;  

import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.*;  
import reactor.core.publisher.Flux;  

@Controller  
public class RootController {  

    @GetMapping("/")  
    @ResponseBody  
    public Flux<String> getRoot() {  
      return Flux.just("Alive");  
    }
}


package com.okta.spring.springbootkbe;  

import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.*;  
import reactor.core.publisher.Flux;  

@Controller  
public class RootController {  

    @GetMapping("/")  
    @ResponseBody  
    public Flux<String> getRoot() {  
      return Flux.just("Alive");  
    }
}



该控制器是必需的,因为Kuberenetes在我们服务的根端点上执行运行状况检查,并且需要返回响应,否则群集将认为您的服务已关闭。 实际的端点是可配置的,但是您现在可以将其留在根目录下。

要将一些示例数据引导到我们的数据库中,请更新Springbootkbe应用程序类定义要匹配以下内容。



package com.okta.spring.springbootkbe;  

import org.springframework.boot.ApplicationRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.annotation.Bean;  
import reactor.core.publisher.Flux;  

@SpringBootApplication  
public class SpringbootkbeApplication {

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

    @Bean
    ApplicationRunner init(KayakRepository repository) {

        Object[][] data = {
            {"sea", "Andrew", 300.12, "NDK"},
            {"creek", "Andrew", 100.75, "Piranha"},
            {"loaner", "Andrew", 75, "Necky"}
        };

        return args -> {
            repository
                .deleteAll()
                .thenMany(
                    Flux
                        .just(data)
                        .map(array -> {
                            return new Kayak((String) array[0], (String) array[1], (Number) array[2], (String) array[3]);
                        })
                        .flatMap(repository::save)
                )
                .thenMany(repository.findAll())
                .subscribe(kayak -> System.out.println("saving " + kayak.toString()));

        };
    }
}


package com.okta.spring.springbootkbe;  

import org.springframework.boot.ApplicationRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.annotation.Bean;  
import reactor.core.publisher.Flux;  

@SpringBootApplication  
public class SpringbootkbeApplication {

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

    @Bean
    ApplicationRunner init(KayakRepository repository) {

        Object[][] data = {
            {"sea", "Andrew", 300.12, "NDK"},
            {"creek", "Andrew", 100.75, "Piranha"},
            {"loaner", "Andrew", 75, "Necky"}
        };

        return args -> {
            repository
                .deleteAll()
                .thenMany(
                    Flux
                        .just(data)
                        .map(array -> {
                            return new Kayak((String) array[0], (String) array[1], (Number) array[2], (String) array[3]);
                        })
                        .flatMap(repository::save)
                )
                .thenMany(repository.findAll())
                .subscribe(kayak -> System.out.println("saving " + kayak.toString()));

        };
    }
}



至此,您已经有了一个功能齐全的Spring Boot应用程序(减去了MongoDB服务器)。 要测试您的应用,请将以下依赖项添加到您的build.gradle文件。



compile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'



这会将嵌入式MongoDB数据库添加到您的项目。 在部署到集群之前,您需要删除此依赖关系,但这将使您可以在本地运行Spring Boot应用程序。

使用以下命令运行Spring Boot应用程序:gradle bootRun。

您应该看到一堆以结尾的输出:



2019-02-14 19:29:34.941 INFO 35982 --- [ntLoopGroup-2-4] org.mongodb.driver.connection : Opened connection [connectionId{localValue:5, serverValue:5}] to localhost:61858
2019-02-14 19:29:34.946 INFO 35982 --- [ntLoopGroup-2-3] org.mongodb.driver.connection : Opened connection [connectionId{localValue:4, serverValue:4}] to localhost:61858
saving Kayak(name=sea, owner=Andrew, value=300.12, makeModel=NDK)
saving Kayak(name=loaner, owner=Andrew, value=75, makeModel=Necky)
saving Kayak(name=creek, owner=Andrew, value=100.75, makeModel=Piranha)



使用HTTPie测试应用程序:http:8080(这会在默认的Spring Boot端口上运行一个get请求)。



HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
transfer-encoding: chunked

Alive



并获取您的/皮划艇端点使用:http :8080/皮划艇。



HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
transfer-encoding: chunked
[
  {
    "makeModel": "NDK",
    "name": "sea",
    "owner": "Andrew",
    "value": 300.12
  },
  {
    "makeModel": "Necky",
    "name": "loaner",
    "owner": "Andrew",
    "value": 75
  },
  {
    "makeModel": "Piranha",
    "name": "creek",
    "owner": "Andrew",
    "value": 100.75
  }
]



假设一切正常,删除嵌入式Mongo依赖项。 您将使用Mongo Kubernetes窗格,这种依赖性将导致群集部署出现问题。



compile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'



Deploy the MongoDB Kubernetes Pod for Your Spring Boot App

Kubernetes通过使用YAML部署脚本部署Docker容器来工作(大致概括和简化)。

创建一个名为部署mongo.yml在项目的根目录中。



apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: mongodb  
  labels:  
    appdb: mongodb  
spec:  
  replicas: 1  
  selector:  
    matchLabels:  
      appdb: mongodb  
  template:  
    metadata:  
      labels:  
        appdb: mongodb  
    spec:  
      containers:  
        - name: mongodb  
          image: mongo:3.6.6  
          ports:  
            - containerPort: 27017  
---  
apiVersion: v1  
kind: Service  
metadata:  
  name: mongodb  
  labels:  
    app: mongodb  
spec:  
  ports:  
    - port: 27017  
      protocol: TCP  
  selector:  
    appdb: mongodb



This defines the MongoDB Kubernetes Deployment and Service required to create the Mongo database on the cluster. I’m not going to try and fully explain what these objects are here, but you can read the Kubernetes deployment docs and the service docs. Roughly speaking, deployments define the micro-applications that run in the deployed pods while services provide the overarching abstraction that defines the access point to the apps in the pods. This abstraction provides a necessary continuity because pods may be killed and restarted and there may be multiple pods running a single service.

现在有些激动! 您将把Mongo数据库部署和服务部署到GKE集群。

使用以下命令:



kubectl apply -f deployment-mongo.yml



您应该看到:



deployment.apps "mongodb" created
service "mongodb" created



通过运行以下命令来检查Pod:



$ kubectl get pods



您应该看到:



NAME READY STATUS RESTARTS AGE
mongodb-c5b8bf947-rkw5f 1/1 Running 0 21s



如果状态列为容器创建,请稍等片刻,然后再次运行命令。 如果卡住了容器创建超过几分钟,可能出了点问题。 您可以使用kubectl描述豆荚和kubectl获取事件命令以了解发生的情况。

这使用标准docker映像配置了在端口27017上运行的Mongo数据库mongo:3。6。6。

太好了吧? 下一站,火箭科学!

Deploy the Spring Boot App to the Cluster

添加一个名为Docker文件在根目录中:



FROM openjdk:8-jdk-alpine  
ENV APP_FILE springbootkbe-0.1.0-SNAPSHOT.jar  
ENV APP_HOME /usr/app  
EXPOSE 8000  
COPY build/libs/*.jar $APP_HOME/  
WORKDIR $APP_HOME  
ENTRYPOINT ["sh", "-c"]  
CMD ["exec java -jar $APP_FILE"]



更新src / main / resources / application.properties:



server.port=8000
spring.data.mongodb.host=mongodb
spring.data.mongodb.port=27017



这会将您的Spring Boot端口配置为您在Docker文件以及配置MongoDB主机和端口。 默认情况下,主机将是集群中服务的名称。

再次构建您的应用(您确实删除了梦呓依赖,对吧?):



gradle clean build



创建另一个名为的Kubernetes部署脚本Deployment.yml在根项目目录中:



apiVersion: v1  
kind: Service  
metadata:  
  name: kayak-service  
  labels:  
    app: kayak-service  
spec:  
  ports:  
    - name: http  
      port: 8000  
  selector:  
    app: kayak-service  
---  
apiVersion: extensions/v1beta1  
kind: Deployment  
metadata:  
  name: kayak-service  
spec:  
  replicas: 1  
  template:  
    metadata:  
      labels:  
        app: kayak-service  
        version: v1  
    spec:  
      containers:  
        - name: kayak-app  
          image: gcr.io/spring-boot-gke-<id>/kayak-app:1.0  
          imagePullPolicy: IfNotPresent  
          env:  
            - name: MONGODB_HOST  
              value: mongodb  
          ports:  
            - containerPort: 8000  
          livenessProbe:  
            httpGet:  
              path: /  
              port: 8000  
            initialDelaySeconds: 5  
            periodSeconds: 5



注意:仔细看看gcr.io/spring-boot-gke/kayak-app:1.0。 中间部分是Google Cloud项目名称。 这需要与您使用的项目名称以及分配的ID号(例如弹簧靴gke-43234)。

Gcr.io specifies a Google Cloud host for the docker image in the United States. It’s possible to specify other locations. See the GooGle Container Registry docs for more info.

即将发生的事情的简要摘要,因为有很多活动的部分。 Spring Boot应用程序将进行docker化:内置于docker映像中。 在群集上运行部署脚本时,它将尝试从Google Container注册表中提取此映像。 因此,您需要将映像推送到容器注册表并对其进行标记,以便Kubernetes可以找到正确的映像。

如果您使用的是本地Docker桌面,请继续启动并等待其启动。

在执行任何操作之前,您将需要配置Google Cloud和docker才能很好地协同工作:



gcloud auth configure-docker



构建docker镜像:



docker build -t kayak-app:1.0 .



标记图像并将其推送到Google容器注册表(再次注意Google Cloud项目名称):



docker tag kayak-app:1.0 gcr.io/$PROJECT_NAME/kayak-app:1.0;  
docker push gcr.io/$PROJECT_NAME/kayak-app:1.0



现在应用Deployment.yml文件到GKE集群:



kubectl apply -f deployment.yml



检查以确保Pod正确部署:



kubectl get pods



NAME                             READY     STATUS    RESTARTS   AGE
kayak-service-7df4fb9c88-srqkr   1/1       Running   0          56s
mongodb-c5b8bf947-dmghb          1/1       Running   0          16m



但是,此时集群还没有准备好。 它不能公开访问。

创建一个名为istio-gateway。yml。



apiVersion: networking.istio.io/v1alpha3  
kind: Gateway  
metadata:  
  name: kayak-gateway  
spec:  
  selector:  
    istio: ingressgateway # use Istio default gateway implementation  
  servers:  
  - port:  
      name: http  
      number: 80  
      protocol: HTTP  
    hosts:  
    - '*'  
---  
apiVersion: networking.istio.io/v1alpha3  
kind: VirtualService  
metadata:  
  name: kayak-service  
spec:  
  hosts:  
  - "*"  
  gateways:  
  - kayak-gateway  
  http:  
  - match:  
    - uri:  
        prefix: /  
    route:  
    - destination:  
        port:  
          number: 8000  
        host: kayak-service



并应用:



kubectl apply -f istio-gateway.yml



您应该得到:



gateway.networking.istio.io "kayak-gateway" created
virtualservice.networking.istio.io "kayak-service" created



Test the Deployed Google Kubernetes Engine + Spring Boot App

既然您已成功将Spring Boot应用程序部署到Google Kubernetes集群,并创建了将服务链接到外部世界的网关,那么您将需要测试端点。

There are some good docs on the Istio website about ingress traffic that have a lot of good information. Below, copied from that page, are some commands that will determine the public-facing host/ip address and ports and save them into shell variables.



export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}');
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}');



在您的配置中,INGRESS_PORT可能只是HTTP的默认值80(无SSL)。

运行以下命令以查看主机和端口:



echo "$INGRESS_HOST, HTTP PORT=$INGRESS_PORT";



您还可以通过查看负载均衡器的IP地址来找到公共IP地址。Cloud Platform Dashboard -> Kubernetes Engine -> Services。 寻找历史门户服务类型负载均衡器。

测试应用程序!



http $INGRESS_HOST:$INGRESS_PORT/



您应该看到:



HTTP/1.1 200 OK
content-type: text/plain;charset=UTF-8
...

Alive



然后打/皮划艇端点:



http $INGRESS_HOST:$INGRESS_PORT/kayaks



您应该看到:



HTTP/1.1 200 OK
content-type: application/json;charset=UTF-8
...
[
  {
    "makeModel": "NDK",
    "name": "sea",
    "owner": "Andrew",
    "value": 300.12
  },
  {
    "makeModel": "Piranha",
    "name": "creek",
    "owner": "Andrew",
    "value": 100.75
  },
  {
    "makeModel": "Necky",
    "name": "loaner",
    "owner": "Andrew",
    "value": 75
  }
]



欢迎来到微服务的世界!

GKE和Istio显然可以做很多工作。 在实践中,微服务通常管理大型服务网格和已部署的Pod,可以根据需要进行扩展和缩小,并且可以在不同部分之间以及与外部世界一起管理复杂的安全体系结构。 本教程将不涉及更多内容,但还剩一个步骤:使用Okta添加JSON Web令牌身份验证。

Create an OpenID Connect App on Okta

大号og into your developer.okta.com account (You did sign up for one of their free developer accounts, right? If not head over to developer.okta.com).

点击应用顶层菜单,然后Add 应用按钮。

选择应用程序类型网页。

Kubernetes在微服务架构中 基于kubernetes的微服务_Kubernetes在微服务架构中_06

请点击下一个。

为应用命名。 我将其命名为“ Spring Boot GKE”。

下登录重定向URI加https://oidcdebugger。com/debug。

往底部下方允许的赠款类型, 检查隐式(混合)框。

Kubernetes在微服务架构中 基于kubernetes的微服务_java_07

请点击完成。

让页面保持打开状态并注意客户编号和客户机密。 使用OIDC调试器生成JSON网络令牌时,一分钟之内将需要它们。

Update Your Spring Boot Microservices for OAuth 2.0

将以下依赖项添加到您的build.gradle:



compile 'org.springframework.security:spring-security-oauth2-client'
compile 'org.springframework.security:spring-security-oauth2-resource-server'



您还需要将以下内容添加到您的src / main / resources / application.properties文件(填写您自己的Oktadeveloper URL,类似于dev-123456.okta.com):



spring.security.oauth2.resourceserver.jwt.issuer-uri=https://{yourOktaDomain}/oauth2/default



这告诉Spring需要在什么地方对您稍后要生成的JSON Web令牌(JWT)进行身份验证。

最后,您需要添加一个新的Java类,名为SecurityConfiguration.java:



package com.okta.spring.springbootkbe;  

import org.springframework.context.annotation.Bean;  
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;  
import org.springframework.security.config.web.server.ServerHttpSecurity;  
import org.springframework.security.web.server.SecurityWebFilterChain;  

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange()
            .pathMatchers("/").permitAll()
            .anyExchange().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt();
        return http.build();
    }
}


package com.okta.spring.springbootkbe;  

import org.springframework.context.annotation.Bean;  
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;  
import org.springframework.security.config.web.server.ServerHttpSecurity;  
import org.springframework.security.web.server.SecurityWebFilterChain;  

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange()
            .pathMatchers("/").permitAll()
            .anyExchange().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt();
        return http.build();
    }
}



此文件将项目配置为允许根端点上的所有事务,但授权所有其他事务。

Build A New Docker Image and Push to the GKE Cluster

现在您有了一个新的,启用了身份验证的Spring Boot应用程序,您需要对其进行构建,将其打包在Docker容器中,将其推送到Google Cloud Docker注册表中,然后将新的部署应用于Kubernetes集群。

从您的外壳转到项目根目录。

使用身份验证更新构建Spring Boot应用程序:



gradle clean build



构建新的Docker映像。 注意新的图像名称(其中包括-身份验证)。 另外:确保您的Docker桌面正在运行。



docker build -t kayak-app-auth:1.0 .



标记您的Docker映像并将其推送到Google Cloud容器注册表。 如有必要,请在回购路径中更改项目名称。



docker tag kayak-app-auth:1.0 gcr.io/$PROJECT_NAME/kayak-app-auth:1.0;  
docker push gcr.io/$PROJECT_NAME/kayak-app-auth:1.0;



删除集群上已部署的Pod:



kubectl delete -f deployment.yml



更新Deployment.yml文件以反映新的图像名称(文件中的第28行):



spec:  
  containers:  
    - name: kayak-app  
      image: gcr.io/spring-boot-gke/kayak-app-auth:1.0



重新部署更新的Kubernetes部署:



kubectl apply -f deployment.yml



使用kubectl得到豆荚检查吊舱的状态。 完全更新将需要几秒钟。 准备就绪后,请测试/端点。



http $INGRESS_HOST:$INGRESS_PORT/
HTTP/1.1 200 OK
...

Alive



和/皮划艇端点,应加以保护:



$ http $INGRESS_HOST:$INGRESS_PORT/kayaks
HTTP/1.1 401 Unauthorized
...



很近! 您需要做的最后一件事是使用OIDC调试器工具生成令牌并测试JWT身份验证。

Generate A JWT and Test OAuth 2.0

Go to the OIDC Debugger. You’ll need your Client ID from your Okta OIDC application.

  • 填写授权URI:https:// {yourOktaDomain} / oauth2 / default / v1 / authorize。填写客户编号。放abcdef为了州。在底部,单击发送请求。

复制生成的令牌,并将其存储在shell变量中以方便使用:



TOKEN=eyJraWQiOiI4UlE5REJGVUJOTnJER0VGaEExekd6bWJqREp...



在上运行GET/皮划艇再次成为端点,这次使用令牌:



http $INGRESS_HOST:$INGRESS_PORT/kayaks Authorization:"Bearer $TOKEN"



注意双引号!单引号无效,因为该变量不会在字符串中扩展。

您应该得到:



HTTP/1.1 200 OK
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-type: application/json;charset=UTF-8
...
[
  {
    "makeModel": "NDK",
    "name": "sea",
    "owner": "Andrew",
    "value": 300.12
  },
  {
    "makeModel": "Piranha",
    "name": "creek",
    "owner": "Andrew",
    "value": 100.75
  },
  {
    "makeModel": "Necky",
    "name": "loaner",
    "owner": "Andrew",
    "value": 75
  }
]



Move Forward with Spring Boot Microservices and Kubernetes

而已! 您已经在这里铺了很多土地。 您使用Istio在Google Cloud上使用Google Kubernetes创建了Kubernetes集群。 您将本地系统配置为使用以下命令与集群交互gcloud和Kubectl. You created a Spring Boot app that used a MongoDB backend, dockerized it, pushed it to the Google Cloud registry,和deployed it to your cluster. You also added OIDC authentication to the app.

You can find the source code for this example on GitHub at oktadeveloper/okta-spring-boot-microservice-kubernetes.

如果您喜欢微服务和Spring Boot,那么您可能也喜欢这些帖子:

If you have any questions about this post, please add a comment below. For more awesome content, follow @oktadev on Twitter, like us on Facebook, or subscribe to our YouTube channel.