用Kubernetes在DigitalOcean上运行微服务

417 阅读17分钟

云计算的采用在全球范围内继续快速增长,而不仅仅是在软件行业。每年都有越来越多的公司将其应用程序转移到云端。在2021年12月的最后一次JHipster社区调查中,参与者重视JHipster能够让他们更快地投入生产,并要求提供更多关于部署到云平台的教程。根据一些调查,DigitalOcean是最受欢迎的 "其他 "云供应商之一。这篇文章是一个快速演练,介绍了将JHipster微服务架构部署到DigitalOcean的云中的Kubernetes集群。

本教程是用以下框架和工具创建的:

目录

关于DigitalOcean

DigitalOcean是一家云服务公司,由Ben和Moisey Uretsky兄弟在2011年创立。他们的总部位于美国纽约市,在马萨诸塞州和班加罗尔也有办事处。去年3月2022年,DigitalOcean达到IPO(首次公开募股),一些媒体文章将其描述为小企业的云服务提供商。"小人物的云服务"。

DigitalOcean Kubernetes(DOKS)是一个可管理的Kubernetes服务,让你部署Kubernetes集群,而不需要处理控制窗格和容器化基础设施的复杂问题。集群与标准的Kubernetes工具链兼容,并与DigitalOcean负载均衡器和块存储卷原生集成。DOKS提供快速配置和部署,并提供一个免费的高可用性控制窗格,用于可靠性管理。它还可以提供一个集群自动调节器,通过根据集群的容量增加或删除节点来自动调整集群的规模,以安排吊舱。Kubernetes工作负载的定价是基于集群、液滴、块存储和负载平衡器所需的资源。

该公司在其网站上公布了其数据中心的认证报告,所有的数据中心都批准了以下两个或更多的认证。SOC(系统和组织控制)1型II,SOC 2型II,SOC 3型II,以及ISO/IEC 27001:2013(安全技术-信息安全管理系统)。所有数据中心都通过了PCI-DSS(支付卡行业-数据安全标准)的认证。

为Kubernetes设置一个微服务架构

在进行应用工作之前,你需要安装JHipster。使用JHipster的经典方式是用NPM进行本地安装。

npm install -g generator-jhipster@7

如果你愿意使用Yarn或Docker,请按照jhipster.tech上的说明进行。

对于这个测试,从GitHub上的java-microservices-examples 仓库中的reactive-jhipster 示例开始。这个例子是一个JHipster反应式微服务架构,采用Spring Cloud Gateway和Spring WebFlux,Vue作为客户端框架,Gradle作为构建工具。你可以在Reactive Java Microservices with Spring Boot and JHipster中阅读它是如何构建的。

git clone https://github.com/oktadev/java-microservices-examples.git
cd java-microservices-examples/reactive-jhipster

如果你检查项目文件夹,你会发现gateway 服务的子文件夹,它将作为前端应用程序,以及通往storeblog 微服务的网关,它们也有各自的子文件夹。一个docker-compose 子文件夹包含运行应用程序容器的服务定义。

下一步是生成Kubernetes部署描述符。在项目根文件夹中,创建一个kubernetes 目录并运行k8s JHipster子生成器。

mkdir kubernetes
cd kubernetes
jhipster k8s

在提示时选择以下选项:

  • 应用程序的类型。微服务应用
  • 根目录:.../
  • 哪些应用程序?(选择所有)
  • 是否设置了监控?没有
  • 哪些应用有集群数据库? 选择存储
  • JHipster注册表的管理密码:(生成一个)。
  • Kubernetes命名空间:Demo
  • Docker仓库名称:(你的docker hub用户名)
  • 推送Docker镜像的命令。docker push
  • 启用Istio?没有
  • Kubernetes服务类型?负载平衡器
  • 使用动态存储配置?
  • 使用特定的存储类别? (留空)。

注意:在本地运行Kubernetes时,你可以不填Docker仓库名称,但在云端部署时需要一个仓库,所以继续创建一个DockerHub个人账户,镜像拉动配置将为本地和云端部署做准备。

用Jib构建gateway,store, 和blog 服务容器镜像。由于Jib将推送镜像到DockerHub,你首先需要做一个docker login

docker login

如果你启用了双因素认证,你必须生成一个令牌,并将其作为登录的密码。在Docker网页中,进入用户菜单,选择账户设置。然后在左边的菜单中选择安全新访问令牌

例如,用于构建gateway 服务镜像。

cd ../gateway
./gradlew bootJar -Pprod jib -Djib.to.image=<docker-repo-name>/gateway

重要提示:不幸的是,在写这篇文章的时候,这个应用实例并没有用Java 17构建。正如介绍中所述,这个例子是用Java 11构建的。

检查镜像是否上传到了DockerHub,并在终端中导航到项目根目录进行下一步操作。

用OpenID Connect配置认证

在本地运行架构之前还有一个配置步骤,让我们配置Okta进行认证。

在你开始之前,你需要一个免费的Okta开发者账户。安装Okta CLI并运行okta register ,以注册一个新账户。如果你已经有一个账户,运行okta login 。然后,运行okta apps create jhipster 。选择默认的应用程序名称,或根据您的需要进行更改。 接受为您提供的默认重定向URI值。

Okta CLI是做什么的?

Okta CLI简化了对JHipster应用程序的配置,并为您做了几件事。

  1. 创建一个具有正确重定向URI的OIDC应用程序。
    • 登录:http://localhost:8080/login/oauth2/code/oidchttp://localhost:8761/login/oauth2/code/oidc
    • 注销:http://localhost:8080http://localhost:8761
  2. 创建JHipster期望的ROLE_ADMINROLE_USER
  3. 将你的当前用户添加到ROLE_ADMINROLE_USER 组中。
  4. 在你的默认授权服务器中创建一个groups ,并将用户的组添加到其中。

注意http://localhost:8761* 重定向URI是为JHipster注册处准备的,在用JHipster创建微服务时经常使用。Okta CLI默认会添加这些。

完成后,你会看到如下的输出。

Okta application configuration has been written to: /path/to/app/.okta.env

运行cat .okta.env (或Windows上的type .okta.env ),查看你的应用程序的发行者和凭证。它看起来会像这样(除了占位符的值会被填充)。

export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="/oauth2/default"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="{clientId}"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="{clientSecret}"

注意:您也可以使用Okta管理控制台来创建您的应用程序。参见在Okta上创建一个JHipster应用程序以获得更多信息。

将生成的.okta.env 中的设置添加到kubernetes/registry-k8s/application-configmap.yml

data:
  application.yml: |-
    ...
    spring:
      security:
        oauth2:
          client:
            provider:
              oidc:
                issuer-uri: https://<your-okta-domain>/oauth2/default
            registration:
              oidc:
                client-id: <client-id>
                client-secret: <client-secret>

通过在kubernetes/registry-k8s/jhipster-registry.yml 文件中添加oauth2 配置文件,在jhipster-registry 服务中启用OIDC认证。

- name: SPRING_PROFILES_ACTIVE
  value: prod,k8s,oauth2

用minikube在本地运行

安装minikubekubectl.

对于minikube,你将需要至少2个CPU。用你的CPU数量启动minikube。

cd kubernetes
minikube --cpus <ncpu> start

minikube会在启动时记录Kubernetes和Docker版本。

Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...

为了使store-mongodb 部署工作,需要属性Service.spec.publishNotReadyAddresses ,而不是注释service.alpha.kubernetes.io/tolerate-unready-endpoints ,因为后者在Kubernetes 1.11版本中被废弃。

编辑kubernetes/store-k8s/store-mongodb.yml ,将publishNotReadyAddresses: true 属性添加到文件底部附近的spec 关键中。

...
# Headless service for DNS record
apiVersion: v1
kind: Service
metadata:
  name: store-mongodb
  namespace: demo
spec:
  type: ClusterIP
  clusterIP: None
  publishNotReadyAddresses: true
  ports:
    - name: peer
      port: 27017
  selector:
    app: store-mongodb

然后将该应用程序部署到minikube。 在kubernetes 目录中,运行。

./kubectl-apply.sh -f

现在是安装k9s的好时机,这是一个基于终端的UI,用于与Kubernetes集群互动。然后运行k9s与。

k9s -n demo

k9s UI

查看命令列表,一些有用的命令是:

  • :namespace:显示所有可用的命名空间
  • :pods:显示所有可用的pods

你可以用ENTER 来浏览pod,用ESC 键返回。

为JHipster注册表设置端口转发。

kubectl port-forward svc/jhipster-registry -n demo 8761

导航到http://localhost:8761 ,用你的Okta凭证登录。当注册表显示所有服务为绿色时,也为网关设置端口转发。

kubectl port-forward svc/gateway -n demo 8080

导航到http://localhost:8080 ,登录,并创建一些实体,以验证一切工作是否正常。

在四处查看后,在云部署前停止minikube。

minikube stop

同时删除minikube集群,为将来的测试提供一个干净的环境。

minikube delete

部署到DigitalOcean Kubernetes上

现在,该架构在本地工作,让我们继续进行云部署。首先,创建一个DigitalOcean账户。注册时需要支付5美元的PayPal,或者提供一张信用卡。

大多数集群任务,如果不是全部,都可以使用doctl,即DigitalOcean API的命令行接口(CLI)来完成。安装该工具,并执行与DigitalOcean的认证。

doctl auth init

你会被提示输入DigitalOcean的访问令牌,你可以在DigitalOcean控制面板中生成。登录,然后在左边的菜单中进入API,点击生成新令牌。输入一个令牌名称,并点击生成令牌。从Tokens/Keys表中复制新的令牌。

DigitalOcean Control Panel for Token Create

你可以在DigitalOcean找到集群资源定价的详细列表,通过doctl ,你可以快速检索到你的账户可用的节点大小选项列表。

doctl k options sizes

用以下命令行创建集群。

doctl k cluster create do1 -v --size s-4vcpu-8gb-intel

创建集群后,doctl ,将配置上下文添加到kubectl ,并使其处于活动状态,因此你可以立即开始用k9s监控你的集群。首先,将资源配置应用到DigitalOcean集群上。

./kubectl-apply.sh -f

用k9s监控部署。

k9s -n demo

k9s user interface monitoring DigitalOcean Kubernetes

一旦你看到jhipster-registry pods已经建立,再次设置端口转发,这样你也可以在注册表UI中监控服务的状态。

关于DigitalOcean的节点大小和体积的说明

如果你不从一开始就指定足够的容量,在DigitalOcean的云上部署到Kubernetes集群是很棘手的。

起初,我测试了我的账户可用的最高尺寸英特尔节点的集群,s-2vcpu-4gb-intel 。我试图在默认的集群配置中运行该应用程序,即在nyc1区域的一个单节点池的三节点集群,使用最新的Kubernetes版本。当我开始看到pods由于insufficient CPU ,而无法运行时,我增加了节点的数量,并使一切正常。在写这篇文章的时候,DigitalOcean的最新和默认的Kubernetes版本是1.22.8。

如果你需要找出一个pod不运行的原因,你可以用kubectl describe ,比如说jhipster-registry-0 pod,来获取pod的事件。

kubectl describe pod jhipster-registry-0 -n demo

该命令的输出,在事件部分,对于一个由于cpu不足而失败的pod,看起来如下。

Events:
  Type     Reason                  Age                   From                     Message
  ----     ------                  ----                  ----                     -------
  Normal   NotTriggerScaleUp       4m30s                 cluster-autoscaler       pod didn't trigger scale-up:
  Warning  FailedScheduling        69s (x3 over 95s)     default-scheduler        0/3 nodes are available: 3 Insufficient cpu.

如果你在k9s 中看到一个 pod 失败了,用kubectl describe pod 检查该 pod 的事件,如上面的例子。

要增加CPU,用doctl 增加节点的数量。

doctl k cluster node-pool update do1 do1-default-pool --count 4

增加节点时,你不需要重新启动pod。

在我增加CPU后(在集群中增加一个节点),一些pod,在有3个副本的store-mongodb 有状态的集合中,由于未绑定即时的PersistentVolumeClaims而不能运行。这在pod事件中,在kubectl describe pod 的输出中,对失败的pod进行了报告。然后我用下面的命令检查了持久性卷要求。

kubectl get pvc -n demo
kubectl describe pvc datadir-store-mongodb-2 -n demo

kubectl describe pvc 命令输出显示配置错误,并指示联系支持。

Events:
  Type     Reason                Age                  From                                                                       Message
  ----     ------                ----                 ----                                                                       -------
  Normal   Provisioning          3m6s (x10 over 11m)  dobs.csi.digitalocean.com_master-do3_cebb3451-5eba-4805-956c-753ec148e2ea  External provisioner is provisioning volume for claim "demo/datadir-store-mongodb-2"
  Warning  ProvisioningFailed    3m5s (x10 over 11m)  dobs.csi.digitalocean.com_master-do3_cebb3451-5eba-4805-956c-753ec148e2ea  failed to provision volume with StorageClass "do-block-storage": rpc error: code = ResourceExhausted desc = volume limit (10) has been reached. Current number of volumes: 10. Please contact support.
  Normal   ExternalProvisioning  103s (x43 over 11m)  persistentvolume-controller                                                waiting for a volume to be created, either by external provisioner "dobs.csi.digitalocean.com" or manually created by system administrator

按照事件消息的指示,我联系了DigitalOcean的支持,他们修复了它。

最后,由于我首先注册了免费试用账户,我不得不开了第二张支持票,要求提高节点尺寸,这样我就可以使用尺寸s-4vcpu-8gb-intel 。在注册试用后,并不是所有的尺寸选项都可用,但对于标准账户来说,情况并非如此。对于一个简短的Kubernetes服务测试,账单应该在10美元以下。

找到你的网关的外部IP并更新重定向URI

一旦所有的pod都在运行,用kubectl describe 命令找到网关的外部IP。

kubectl describe service gateway -n demo

输出会是这样的:

Name:                     gateway
Namespace:                demo
Labels:                   app=gateway
Annotations:              kubernetes.digitalocean.com/load-balancer-id: e81d2b8c-6c28-430d-8ba8-6dab29a1ba76
                          service.beta.kubernetes.io/do-loadbalancer-certificate-id: bd0b1d03-0f90-449d-abbe-ac6a4026c133
Selector:                 app=gateway
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.245.47.51
IPs:                      10.245.47.51
LoadBalancer Ingress:     157.230.200.181
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31048/TCP
Endpoints:                10.244.1.42:8080
Session Affinity:         None
External Traffic Policy:  Cluster

更新Okta中的重定向URI,允许网关地址作为有效重定向。运行okta login ,在浏览器中打开返回的URL,并登录到Okta管理控制台。进入应用程序部分,找到你的应用程序,编辑,然后添加

  • 签入重定向URI。http://<load-balancer-ingress-ip>:8080/login/oauth2/code/oidc
  • 签出重定向URI。http://<load-balancer-ingress-ip>:8080

导航到http://<load-balancer-ingress-ip>:8080 ,当一切正常时,你会感到欣喜!

用HTTPS保护网络流量

由于网关服务作为应用程序的前端,在k8s描述符中它被定义为LoadBalancer 服务类型。这使得该服务使用云提供商的负载平衡器对外公开。DigitalOcean负载平衡器是一个完全管理的、高度可用的网络负载平衡服务。负载平衡器将流量分配到液滴组,这将后端服务的整体健康状况与单个服务器的健康状况解耦,以确保你的服务保持在线。

标准的做法是用HTTPS来保护你的应用程序的网络流量。对于流量加密,你需要一个TLS(SSL)证书。如果你用DigitalOcean的DNS管理你的域名,DigitalOcean还提供自动证书创建和更新,这是免费的。但不提供域名注册。 要使用DigitalOcean的DNS,你需要在注册商那里注册一个域名,并更新你的域名的NS记录,以指向DigitalOcean的名称服务器。

然后,为了使用DigitalOcean的管理域名和证书,你必须委托域名,更新注册商的NS记录。由于这一要求,你不能使用免费的DNS服务,你不能设置NS记录,如nip.io

重要提示:在改变注册商的NS记录(nameservers)之前,将你的域名添加到DigitalOcean,以尽量减少服务中断。

你可以在DigitalOcean控制面板中同时创建证书和域名,当你设置负载平衡器的HTTPS转发时。

DigitalOcean负载平衡器支持两个主要配置,用于加密的网络流量。

  • SSL终止: 在负载平衡器上解密SSL请求,并将其未加密地发送到Droplets私人IP地址的后端。在负载均衡器上进行较慢的和CPU密集型的解密工作,并简化证书管理。负载均衡器和后端之间的流量通过VPC网络的路由得到保障,但数据在私有网络内是可读的。
  • SSL直通: 将加密的SSL请求直接发送到Droplets的私有IP地址的后端,负载平衡器和后端之间的流量是安全的。每个后端服务器都必须有证书,X-forwarded-* headers中包含的客户信息可能会丢失。

利用简化的证书管理,SSL终止将在以下步骤中通过DigitalOcean控制面板进行配置。

登录到你的DigitalOcean账户,在左边的菜单中选择Kubernetes。然后选择你的集群,在集群页面上,选择资源。在LOAD BALANCERS列表中,选择必须创建的单个负载平衡器。在负载平衡器页面,选择设置标签。在转发规则中点击编辑。在 443 端口添加一个 HTTPS 的转发规则,在证书下拉菜单中,选择新证书

DigitalOcean new certificate form

新证书表格中,选择使用Let's Encrypt标签,然后在域名框中,选择添加新域名。然后输入你的域名,并列出要包括的其他子域。为证书添加一个名称,然后点击生成证书

DigitalOcean new domain form

回到转发规则页面,检查生成的证书是否被选中,并将流量转发到网关运行的dropplet HTTP端口。勾选为所有新Let's Encrypt证书创建DNS记录的复选框,然后保存转发设置。

DigitalOcean load balancer forwarding settings

最后,在设置的SSL部分,勾选重定向HTTP到HTTPS的复选框。

DigitalOcean load balancer ssl redirect settings

再次,更新Okta中的重定向URI,以允许新配置的域名。添加以下重定向URI。

  • 签入重定向URI:https://<your-domain>/login/oauth2/code/oidc
  • 签出重定向URI。https://<your-domain>

通过导航到http://<your-domain> 来测试配置。首先,负载平衡器应该重定向到HTTPs,然后网关应该重定向到Okta的登录页面。

检查和管理集群资源

你可以用下面的doctl 命令来检索集群的相关资源。

doctl k cluster list-associated-resources do1

输出将列出为集群创建的卷和负载均衡器,除了节点的成本外,它们还产生计费。

Volumes                                                                                                                                                                                       Volume Snapshots    Load Balancers
[a33dbaba-cb22-11ec-9383-0a58ac145375 a6e18809-cb22-11ec-8723-0a58ac14468c 65fb3778-cb23-11ec-9383-0a58ac145375 ac106c49-cb22-11ec-9383-0a58ac145375 c7122288-cb22-11ec-bd08-0a58ac14467d]    []                  [8ebbcbf2-1e67-46f5-b38a-eddae17f00f3]

不幸的是,没有办法暂停集群的运行。根据DigitalOcean的说法,你可以关闭一个液滴的电源,但这并不能停止计费。通过对液滴进行快照,然后将其销毁,可以降低计费成本。快照的费用比较低。

在测试平台时,你可以在两次会议之间删除集群,以避免在不积极工作时花费试用信用。

doctl k cluster delete do1

提供给集群的卷不会与集群一起删除,并且每小时都会产生费用。你可以使用控制面板或以下命令来删除卷。

doctl compute volume list
doctl compute volume delete <volume-id>

负载均衡器也不会随集群一起删除。

doctl compute load-balancer list
doctl compute load-balancer delete <load-balancer-id>

在注册期间,必须已经创建了一个项目,允许你组织资源。你可以用下面的命令列出与一个项目相关的所有资源。

doctl projects list
doctl projects resources list <project-id>