“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
在本教程中,您将使用Kubernetes将Spring Boot微服务架构部署到Google Cloud,特别是Google Kubernetes Engine(GKE)。 您还将使用Istio创建服务网格层并创建公共网关。 整个事情将使用Okta OAuth JWT身份验证进行保护。
那是一堆烂话。 我们将不在这里深入解释微服务。 简而言之,微服务是一种设计模式,可将较大的单片服务拆分为较小的独立“微”服务。 这些服务通过网络松散耦合。 这种体系结构的好处是,每个服务都变得可测试,可维护且可独立部署。 在互联网规模上,在像Amazon和Netflix这样的大型公司中,这种架构都很棒,因为它允许公司将小型团队的职责分配给可管理的,离散的功能单元。 而不是拥有成千上万的人监督巨大的整体代码块。 不利之处是复杂性和基础架构的高昂初始成本,这对于无法规模化的较小项目可能没有意义。
Kubernetes是用于部署容器化服务的平台。 您可以将其视为Docker容器的容器协调器(这是一种简化,但是可以使用)。 这将使我们能够编写YAML脚本,以自动将微服务架构部署到我们选择的平台GKE。 这是一个庞大的项目,需要深入研究。 看看他们的文档以了解更多信息。
Istio在Kubernetes的基础上又增加了一层功能,增加了一些出色的监视,安全性,访问控制和负载平衡功能。 查看他们的网站以获取更多信息。
微服务架构的最后一部分是Google Cloud和GKE 。 这是您将用于部署微服务的平台。 本教程中未介绍的另一个选项是Minikube。 Minikube在您的计算机上本地运行,可能对某些人有用。 我发现Google Kubernetes Engine更易于使用且性能更高。
我们假设您熟悉Spring Boot和Java。 如果不是这样,请查看教程末尾的一些链接以帮助您入门。
Spring Boot和Kubernetes的要求
HTTPie :从他们的网站安装HTTPie,以便我们可以轻松地从终端运行HTTP请求。
Docker :如果尚未安装Docker Desktop,请从其网站下载并安装。
kubectl :这是Kubernetes的命令行界面。 有关安装说明,请参见其网站 。
Google Cloud :您需要一个启用了计费功能的Google Cloud帐户。 有一个免费试用版,其中应包括足够的学分和时间,以帮助您完成本教程。 转到Google Cloud网站并注册。
developer.okta.com :我们在开发人员网站上提供免费的开发人员帐户。 请立即注册一个。 您将在本教程结束时使用它。
gcloud :这是Google Cloud CLI。 按照其网站上的说明进行安装。 。 完成此操作后,您需要通过运行以下命令来安装gcloud kubectl
组件:
gcloud components install kubectl
我是否提到微服务的初始复杂性成本很高?
使用Istio创建一个Google Kubernetes Engine项目
您现在应该拥有一个启用了结算功能的Google Cloud帐户。 同样,您实际上不需要花任何钱,但是如果不付款,您将无法访问免费试用版。
创建一个新项目。 将其命名为spring-boot-gke
(或任何您想使用的名称,但是您需要各种命令的项目ID)。 等待项目创建。
项目名称的结尾可能会带有一个ID号,例如spring-boot-gke-232934
。 您将需要几次该项目名称,因此请继续将其存储在shell变量中并记下它。
PROJECT_NAME=
项目准备就绪后,打开项目仪表板,打开导航菜单,然后单击Kubernetes Engine 。 单击启用帐单按钮(如果尚未启用帐单),然后选择一个帐单帐户。
单击创建集群 。
在左侧面板中,选择您的第一个集群 。
将集群命名为“ spring-boot-cluster”。
选择区域“ us-west1-a”。
单击集群配置面板底部的“ 高级选项”链接以显示高级选项。 向下滚动到底部,然后选中Enable Istio(beta)复选框。 这将自动在群集上安装Istio。
在底部,单击创建以创建集群。 喝咖啡或休息一下; 创建集群将需要几分钟。
同时,如果尚未安装,请继续运行以下命令来初始化gcloud
CLI:
gcloud init
在初始化过程中,可以将新项目设置为默认项目,并将项目区域设置为默认区域。
部署集群后,您需要使用以下命令将本地gcloud
和kubectl
CLI连接到该集群:
gcloud container clusters get-credentials {yourClusterName} --zone us-west1-a --project {yourProjectId}
如果您使用了其他项目名称,则需要更改命令以反映该名称。
注意:如果单击Google Cloud Platform仪表板右侧的“ 连接”按钮,您将看到正确的命令,可输入:
结果,您应该看到类似以下的内容:
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仪表板中,单击“ 服务”按钮。 您应该在spring-boot-cluster
看到Istio服务的列表。 它们的状态列下都应有绿色的“确定”。
在此期间,请注意类型为LoadBalancer
名为istio-ingressgateway
的服务。 这是群集的公共负载平衡器,该条目显示公共IP和开放端口。
另一种检查方法是使用kubectl
CLI。
要检查服务,请使用以下命令: kubectl get services --all-namespaces
。 必须使用--all-namespaces
才能显示istio-system
命名空间中的Istio服务。
$ kubectl get services --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.31.240.1 443/TCP 5m
istio-system istio-citadel ClusterIP 10.31.252.214 8060/TCP,9093/TCP 3m
istio-system istio-egressgateway ClusterIP 10.31.247.186 80/TCP,443/TCP 3m
istio-system istio-galley ClusterIP 10.31.249.131 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 15010/TCP,15011/TCP,8080/TCP,9093/TCP 3m
istio-system istio-policy ClusterIP 10.31.246.176 9091/TCP,15004/TCP,9093/TCP 3m
istio-system istio-sidecar-injector ClusterIP 10.31.240.214 443/TCP 3m
istio-system istio-telemetry ClusterIP 10.31.247.23 9091/TCP,15004/TCP,9093/TCP,42422/TCP 3m
istio-system promsd ClusterIP 10.31.246.88 9090/TCP 3m
kube-system default-http-backend NodePort 10.31.250.134 80:31955/TCP 4m
kube-system heapster ClusterIP 10.31.250.242 80/TCP 4m
kube-system kube-dns ClusterIP 10.31.240.10 53/UDP,53/TCP 4m
kube-system metrics-server ClusterIP 10.31.245.127 443/TCP
要检查Kubernetes Pod,请使用: kubectl get 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
各个Pod的状态都必须为Completed
或Running
。 我遇到了几次问题,其中自动配置不起作用,并且某些吊舱从未达到Running
状态,并停留在ContainerCreating
。 我必须删除群集并重新安装它才能正常工作。
如果发生这种情况,您可以使用describe pods
命令查看发生了什么: kubectl describe pods -n istio-system
。 这将为您提供有关istio-system
名称空间中所有Pod的istio-system
,这些信息是使用-n
选项指定的。
如果到此为止没有问题,那么现在您已经在安装了Istio的GKE上部署了Kubernetes集群! 很甜
如果您遇到问题,Google和Istio都会提供一些非常有用的文档。 查看Google GKE文档和Istio GKE文档以获得进一步的支持。
为您的微服务创建Spring Boot项目
现在转到Spring Initializer并创建您的启动项目。
- 将构建工具从Maven更改为Gradle
- 使用Java和Spring Boot版本2.1.3
- 将组更新为:
com.okta.spring
- 使用工件 :
springbootkbe
- 添加三个依赖项 :
Reactive Web
,Reactive MongoDB
和Lombok
单击生成项目并下载项目。 将项目解压缩到本地计算机上的某个位置,然后在您喜欢的IDE或编辑器中将其打开。
Spring Initializer已创建了带有MongoDB支持的准系统响应式Webflux项目,供您扩展。
与我的其他一些教程一样,并且由于我喜欢皮划艇,因此您将构建一个简单的反应式REST服务,该服务维护皮划艇条目的数据库。 它实际上只是为了演示基本的CRUD功能(创建,读取,更新和删除),并且可以推广到任何类型的资源。
在src/main/java
下的com.okta.spring.springbootkbe
包中,创建一个名为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;
}
现在,在相同的包KayakRepository.java
创建另一个文件。
package com.okta.spring.springbootkbe;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
public interface KayakRepository extends ReactiveMongoRepository {
}
在本教程中,我不会对这里发生的事情进行过多的详细介绍。 Spring Boot在这两个文件之间进行了大量自动匹配,以创建功能齐全的反应式Mongo文档。
接下来,您需要添加一个控制器以允许访问Kayak文档数据模型。 在com.okta.spring.springbootkbe
包中创建一个名为KayakController
的文件。
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 addKayak(@RequestBody Kayak kayak) {
return kayakRepository.save(kayak);
}
@GetMapping()
public @ResponseBody
Flux getAllKayaks() {
Flux result = kayakRepository.findAll();
return result;
}}
该控制器向/kayaks
端点添加了两种方法,即POST和GET端点,它们分别添加了新的皮划艇并列出所有皮划艇。
最后,添加一个简单的根控制器RootController
。
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 getRoot() {
return Flux.just("Alive");
}
}
此控制器是必需的,因为Kuberenetes在我们服务的根端点上执行运行状况检查,并且需要返回响应,否则群集将认为您的服务已关闭。 实际的端点是可配置的,但您现在可以将其留在根目录下。
要将一些示例数据引导到我们的数据库中,请更新SpringbootkbeApplication
类定义以匹配以下内容。
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应用程序。
使用以下gradle bootRun
运行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
并使用以下命令获取/kayaks
端点: http :8080/kayaks
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 Pod,这种依赖性将导致集群部署出现问题。
compile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
为您的Spring Boot App部署MongoDB Kubernetes Pod
Kubernetes通过使用YAML部署脚本部署Docker容器来工作(大致概括和简化)。
在项目的根目录中创建一个名为deployment-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
这定义了在集群上创建Mongo数据库所需的MongoDB Kubernetes Deployment
和Service
。 我不会尝试完全解释这些对象在这里,但是您可以阅读Kubernetes 部署文档和服务文档 。 粗略地说,部署定义了在已部署的Pod中运行的微应用程序,而服务则提供了总体抽象,该抽象定义了Pod中对应用程序的访问点。 这种抽象提供了必要的连续性,因为Pod可能会被杀死并重新启动,并且可能有多个Pod运行一项服务。
现在有些激动! 您将把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
如果状态列为ContainerCreating
,请稍等片刻,然后再次运行命令。 如果它在ContainerCreating
停留超过几分钟,则可能出现了问题。 您可以使用kubectl describe pods
和kubectl get events
命令来了解正在发生的事情。
这使用标准docker映像mongo:3.6.6
配置了在端口27017上运行的Mongo数据库。
太好了吧? 下一站,火箭科学!
将Spring Boot App部署到集群
在根目录中添加一个名为Dockerfile
的文件:
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
这Dockerfile
您的Spring Boot端口配置为要在Dockerfile
公开的端口,并配置MongoDB主机和端口。 默认情况下,主机将是群集中服务的名称。
再次构建您的应用程序(您确实删除了flapdoodle
依赖项,对吗?):
gradle clean build
在根项目目录中创建另一个名为deployment.yml
Kubernetes部署脚本:
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-/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号(例如spring-boot-gke-43234
) spring-boot-gke-43234
。
gcr.io
为美国的gcr.io
指定了Google Cloud主机。 可以指定其他位置。 有关更多信息,请参见Google Container Registry文档 。
即将发生的事情的简要摘要,因为其中有很多活动的部分。 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
测试已部署的Google Kubernetes Engine + Spring Boot App
既然您已成功将Spring Boot应用程序部署到Google Kubernetes集群并创建了将服务链接到外部世界的网关,您将需要测试端点。
Istio网站上有一些关于入口流量的优秀文档,这些文档具有很多很好的信息。 下面是从该页面复制的一些命令,这些命令将确定面向公众的主机/ ip地址和端口并将其保存到shell变量中
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";
也可以通过在Cloud Platform仪表板-> Kubernetes Engine->服务中查看负载均衡器IP地址来找到公共IP地址。 寻找类型LoadBalancer的 istio-ingressgateway服务。
测试应用程序!
http $INGRESS_HOST:$INGRESS_PORT/
您应该看到:
HTTP/1.1 200 OK
content-type: text/plain;charset=UTF-8
...
Alive
并点击/kayaks
端点:
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令牌身份验证。
在Okta上创建OpenID Connect应用程序
登录到您的developer.okta.com帐户(您确实注册了他们的免费开发者帐户之一,对吗?如果不是,请前往developer.okta.com )。
单击应用程序顶部菜单,然后单击添加应用程序按钮。
选择应用程序类型Web 。
单击下一步 。
为应用命名。 我将其命名为“ Spring Boot GKE”。
在登录重定向URI下,添加https://oidcdebugger.com/debug
。
在底部,在允许的授予类型下 ,选中隐式(混合)框。
单击完成 。
让页面保持打开状态,并记下Client ID和Client Secret 。 使用OIDC调试器生成JSON Web令牌时,一分钟之内将需要它们。
更新OAuth 2.0的Spring Boot微服务
将以下依赖项添加到您的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)进行身份验证。
最后,您需要添加一个名为SecurityConfiguration.java
的新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();
}
}
此文件将项目配置为允许根端点上的所有事务,但授权所有其他事务。
构建一个新的Docker映像并推送到GKE集群
现在您有了一个新的,启用了身份验证的Spring Boot应用程序,您需要对其进行构建,将其打包在Docker容器中,将其推送到Google Cloud Docker注册表中,然后将新的部署应用于Kubernetes集群。
从您的外壳转到项目根目录。
使用身份验证更新构建Spring Boot应用程序:
gradle clean build
构建新的Docker映像。 注意新的映像名称(包括-auth
)。 另外:确保您的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 get pods
检查pod的状态。 完全更新将需要几秒钟。 准备就绪后,测试/
端点。
http $INGRESS_HOST:$INGRESS_PORT/
HTTP/1.1 200 OK
...
Alive
和/kayaks
端点,应该对其进行保护:
$ http $INGRESS_HOST:$INGRESS_PORT/kayaks
HTTP/1.1 401 Unauthorized
...
很近! 您需要做的最后一件事是使用OIDC调试器工具生成令牌并测试JWT身份验证。
生成一个JWT并测试OAuth 2.0
转到OIDC调试器 。 您需要从Okta OIDC应用程序中获取客户ID 。
- 填写授权URI:
https://{yourOktaDomain} /oauth2/default/v1/authorize
- 填写客户ID 。
- 将
abcdef
为state 。 - 点击底部的发送请求 。
复制生成的令牌,并将其存储在shell变量中以方便使用:
TOKEN=eyJraWQiOiI4UlE5REJGVUJOTnJER0VGaEExekd6bWJqREp...
再次在/kayaks
端点上运行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
}
]
使用Spring Boot微服务和Kubernetes向前发展
而已! 您已经在这里盖了一吨土地。 您使用Istio在Google Cloud上使用Google Kubernetes创建了Kubernetes集群。 您已将本地系统配置为使用gcloud
和kubectl
与集群进行kubectl
。 您创建了一个使用MongoDB后端的Spring Boot应用,对其进行了docker化处理,并将其推送到Google Cloud注册表中,然后将其部署到您的集群中。 您还向应用程序添加了OIDC身份验证。
您可以在oktadeveloper / okta-spring-boot-microservice-kubernetes的 GitHub上找到此示例的源代码。
如果您喜欢微服务和Spring Boot,那么您可能也喜欢这些帖子:
- 构建Spring微服务并对其进行Dockerize生产
- 使用HTTPS和OAuth 2.0保护服务到服务的Spring微服务
- 使用Spring Boot 2.0和OAuth 2.0构建并保护微服务
如果您对此帖子有任何疑问,请在下面添加评论。 有关更多精彩内容, 请在Twitter上关注@oktadev , 在Facebook上关注我们,或订阅我们的YouTube频道 。
“ 使用Spring Boot和Kubernetes构建微服务架构 ”最初于2019年4月1日发布在Okta开发者博客上。
“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。
翻译自: https://www.javacodegeeks.com/2019/04/build-microservice-architecture-spring-boot-kubernetes.html