将Node.js应用程序部署到EKS上的教程

274 阅读13分钟

自2013年向公众发布以来,Docker已成为开发团队将应用程序打包成小型可执行容器的行业标准工具。一年后,谷歌发布了Kubernetes,以管理大量的容器,并提供高可用性(HA)和自动扩展的功能。

虽然Kubernetes为你的容器管理增加了许多好处,但你可能会发现在你的内部基础设施中设置Kubernetes的过程相当具有挑战性,特别是在第一次尝试时。为了克服这一挑战,一些云服务提供商将管理型Kubernetes作为一项服务提供。

在这篇文章中,你将了解到亚马逊网络服务(AWS)为使用Kubernetes提供的服务。

目录

前提条件

本文将介绍如何创建一个Kubernetes集群来部署一个用Okta保护的Node.js容器化应用程序。

要跟上集群的创建步骤,你将需要以下条件:

  • 一个AWS账户。注册一个账户以获得免费的AWS点数。
  • 在本地计算机上安装和配置的AWS CLI
  • 在本地机器上安装的eksctl 命令行工具。
  • 在本地计算机上安装的Docker,以建立克隆的Express.js应用程序的Docker镜像。

介绍亚马逊上的Kubernetes产品

虽然Kubernetes是开源的,可以在你的内部基础设施中使用,但Kubernetes也是云端的。主要的云服务厂商将Kubernetes作为平台即服务(PaaS)提供,以便在虚拟机上部署和扩展容器化应用。亚马逊网络服务的Elastic Kubernetes服务(EKS)就是这样一个例子。

亚马逊Elastic Kubernetes服务(EKS)让你在由作为工作节点的EC2实例组成的Kubernetes集群中部署、运行和扩展你的容器化应用程序。对于使用混合云模式的客户,亚马逊EKS Anywhere部署选项允许你在支持的内部基础设施内使用Kubernetes集群。

AWS上的Fargate服务也为云客户提供了使用Kubernetes的能力,但以无服务器的方式。当使用Fargate时,你可以在Kubernetes集群中按需运行一个pod。Fargate有助于降低基础设施的运营成本(OpEx),因为你将只为pod而不是EC2实例节点计费。

在下面的章节中,我们将重点介绍通过Elastic Kubernetes服务使用Kubernetes。

了解AWS上的EKS架构

EKS架构包括三个主要部分--集群、节点和虚拟私有云(VPC),后者构成了网络方面。让我们继续考虑集群和VPC。

EKS集群

EKS集群由控制平面和工作节点组成,工作节点驻扎在为该集群创建的VPC中。集群的控制平面被放置在AWS管理的账户中,并运行Kubernetes软件。另一方面,工作节点在用户的AWS账户中运行,是EC2实例,通过Kubernetes软件的API服务器连接到控制平面。

EKS集群可以通过几种工具创建,如AWS控制台AWS CLICloud Formationeksctl,甚至是Terraform,以及其他基础设施即代码工具。当使用基于终端的工具,如AWS CLI或eksctl ,你通过使用配置文件或通过命令行参数来提供EKS要创建的集群的属性。在本文的演示部分,你将使用eksctl 命令行工具来创建一个集群。

关于集群的安全性,控制平面和工作节点之间的内部连接使用设置时生成的证书文件。集群存储在etcd内的数据也默认使用AWS的密钥管理服务进行加密。

EKS的虚拟私有云(VPC)

AWS虚拟私有云(VPC)是一个孤立的虚拟网络,由较小的网络组件组成,如子网和互联网网关等。

创建集群时需要一个VPC。VPC还需要包含至少两个可用区内的子网。这些子网既可以是公共的,也可以是私有的;但是,建议将公共的和私有的子网结合起来,这样可以对进入的流量进行负载平衡。

EKS的正常运行时间服务水平协议为99.95%。作为确保高可用性(HA)的策略之一,可以在每个可用性区域内使用一个自动扩展组来监控集群内的节点,对它们进行扩展以满足你的应用程序的需求,甚至在健康探测重复失败时重新启动它们。

创建一个Docker容器

有了关于EKS的知识,让我们创建一个集群,其中有一个部署,以运行用Okta保护的Node.js应用程序的Docker镜像。我们将重新使用之前的博文《在15分钟内建立Express中的简单认证》中的Node.js应用程序。你只需要专注于创建一个应用程序的Docker镜像。

下面的步骤将指导你建立一个用Okta担保的Node.js应用程序的Docker镜像。

1. 打开你的终端,执行 git-clone命令,将Okta安全的Node.js应用程序从OktaDev仓库克隆到你的本地计算机上。

git clone https://github.com/oktadev/okta-node-express-15-minute-auth-example.git

2. 使用你喜欢的代码编辑器,打开上面克隆的okta-node-express-15-minute-auth-example 文件夹,创建一个名为Dockerfile 的文件。这个文件将存储从克隆的应用程序中构建Docker镜像的步骤。

接下来,将下面的代码块内的代码粘贴到Dockerfile

FROM node:alpine

WORKDIR /okta-k8-app

COPY . .

RUN npm install

EXPOSE 3000

CMD exec npm start

docker build 命令被执行时,Docker将执行以下步骤来构建一个应用程序的镜像。

  • 使用node:alpine 作为即将构建的docker镜像的基础镜像。
  • 将当前工作目录设置为okta-k8-app
  • 将根目录下的所有文件(okta-node-express-15-minute-auth-example)复制到正在构建的docker镜像的根目录中。
  • 运行npm install 命令,为应用程序安装package.json 文件中指定的node.js依赖项。
  • 暴露端口3000,允许外部HTTP请求到达运行在端口3000的Node.js应用程序。
  • 最后,执行npm start 命令来启动应用程序。

3. 在项目目录下创建另一个文件,并将其命名为.dockerignore 。这个文件将指定其他不应该被复制到Docker镜像中的文件,如包含敏感证书的.env 文件。

接下来,将下面的代码块的内容添加到.dockerignore 文件中。

.git
.env

4. 执行下面的docker build命令,使用你在上面创建的Dockerfile 中的步骤,建立一个应用程序的docker镜像。

在下面的命令中指定的标签-t ,将为即将建立的docker镜像附加一个okta-k8-app的标签。这个标签将在你运行docker镜像时派上用场。

docker build . -t okta-k8-app

5. 可以选择在项目目录下创建一个.env 文件,以安全地存储Okta客户端凭证。

将检索到的客户凭证按照以下格式添加到.env 文件中。(确保数值前没有空白)。

HOST_URL=<OKTA_APP_HOST_URL>
OKTA_ORG_URL=<OKTA_ORG_URL>
OKTA_CLIENT_ID=<OKTA_APP CLIENT_ID>
OKTA_CLIENT_SECRET=<OKTA APP CLIENT_SECRET>
OKTA_TOKEN=<OKTA_APP_TOKEN>
APP_SECRET=<OKTA_APP_SECRET>

6. 可以选择从你的终端执行下面的docker run 命令来运行上面建立的docker镜像。这个命令将允许你从你的网络浏览器测试运行中的docker镜像。

docker run --env-file .env -p 3000:3000 okta-k8-app

从终端的输出中,你可以看到日志,表明应用程序正在3000端口上运行。

Running Simple Node.js application on port 3000

随着Node.js应用程序现在在终端运行,你可以在Web浏览器内查看Node.js应用程序,网址是http://localhost:3000。

Local sign-in page for Simple Node.js application

将docker镜像推送到Elastic Container Registry(ECR)

在上一节中,你为你的应用程序建立了docker镜像。现在你可以继续在Elastic Container Registry中创建一个注册表,并将新创建的docker镜像推送到其中。

执行下面的ecr-public 命令,在Elastic Container Registry中创建一个公共资源库。标签标志--tags ,也会给仓库附加一个键为环境、值为开发的标签。这条命令适用于us-east-1地区,但不是在所有AWS地区都适用。

aws ecr-public create-repository --repository-name okta-k8-ecr-repo --tags Key=environment,Value=development --region us-east-1

上面执行的命令默认会返回一个JSON响应来描述创建的版本库。注意JSON响应中包含的repositoryUri ,因为你将在docker镜像中附加一个包含repositoryUri 的标签。

Creating a repository within the Elastic Container Registry

接下来,执行下面的docker tag命令,给之前构建的docker镜像打上标签。

注意: 将下面代码块中模板部分的<REPOSITORY_URI> 占位符替换为你推送到ECR的docker镜像的REPOSITORY_URI,格式为<REPOSITORY_URI>:latest

docker tag okta-k8-app:latest <REPOSITORY_URI>:latest

执行下面的命令,通过认证令牌将你的电脑的docker客户端与你的ECR repo进行认证。

aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

接下来,执行下面的命令,将有标签的docker镜像推送到你创建的ECR仓库中。

docker push <REPOSITORY_URI>

Pushing the app image to the ECR repository

创建一个EKS集群

到目前为止,你已经推送了一个docker镜像到Elastic Container Registry。让我们在EKS中创建一个集群,它将使用你之前推送的docker镜像。

使用eksctl CLI工具在EKS中创建一个集群。eksctl 是一个用Go编写的第三方CLI工具,通过利用AWS CloudFormation来管理你的集群和其他依赖对象,如VPC和子网,简化了EKS集群的管理。

当创建一个集群时,eksctl 需要非常少的配置。它将尝试使用其默认值来创建一个集群。然而,你可以在eksctl 命令的命令行参数中指定集群配置,或者更方便地指定一个包含你的集群配置的YAML文件。

create cluster 命令在没有配置文件的情况下被执行时,eksctl 将创建集群所依赖的以下AWS资源。

  • 一个有两个子网的VPC。一个是公共的,另一个是跨越VPC内三个可用区的私有子网。
  • 一个包含两个虚拟机的节点组,作为集群的工作节点。

执行下面的create cluster 命令,在指定的AWS区域内创建一个名为okta-k8-cluster 的新集群。这可能需要相当长的时间,所以要等到命令执行成功。

注意:用连接到你的AWS账户的区域替换<AWS_REGION> 占位符。在本教程中,我们使用的是us-east-2区域。

eksctl create cluster --name okta-k8-cluster --region <AWS_REGION>

Eksctl create cluster command

在创建了okta-k8-cluster 之后,执行命令将你的kubectl 上下文切换为okta-k8-cluster 。执行该命令将使kubectl 连接到你的EKS集群。

aws eks --region us-east-2 update-kubeconfig --name okta-k8-cluster

创建Kubernetes资源

在这一点上,你有一个空的Kubernetes集群在EKS上运行。让我们继续在集群中创建三个资源。

使用下面的三个步骤,你将在应用程序目录中创建三个YAML文件,以存储将在集群中创建的下列资源的配置。秘密部署服务

1. 创建一个名为loadbalancer.yaml 的文件。这个YAML文件将包含为okta-k8-cluster ,创建一个负载平衡器的配置,使你能够访问将在集群内运行的应用程序。

将下面的代码块的内容添加到loadbalancer.yaml 文件中。

apiVersion: v1
kind: Service
metadata:
  name: okta-express-k8-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: okta-express-k8
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000

执行下面的命令,使用上面loadbalancer.yaml 文件中的配置创建一个负载平衡器。这个命令在AWS上的弹性负载平衡(ELB)服务中创建了一个负载平衡器,你可以通过一个端点访问它。

kubectl apply -f loadbalancer.yaml

接下来,执行下面的命令,获得我们刚刚创建的负载平衡器的详细信息。

kubectl get service/okta-express-k8-loadbalancer

Cluster load balancer resource details

注意: 注意上面的负载平衡器的细节中返回的外部IP。你将在下面的步骤中使用这个外部IP作为端点来访问应用程序。

2. 创建一个名为secret.yaml 的文件。它将包含Kubernetes Secret的字段,存储Okta凭证。Kubernetes中的Secret是一种资源,用于存储敏感数据,如密码和API密钥,出于安全原因,这些数据被排除在你的应用代码之外。

将下面代码块中的<LOAD_BALANCER_EXTERNAL_IP> 占位符替换为步骤1中从负载均衡器细节中得出的外部IP值。

apiVersion: v1
kind: Secret
metadata:
  name: okta-node-app-secrets
type: Opaque
stringData:
  HOST_URL: http://<LOAD_BALANCER_EXTERNAL_IP>:3000
  OKTA_ORG_URL: <OKTA_ORG_URL>
  OKTA_CLIENT_ID: <OKTA_APP CLIENT_ID>
  OKTA_CLIENT_SECRET: <OKTA APP CLIENT_SECRET>
  OKTA_TOKEN: <OKTA_APP_TOKEN>
  APP_SECRET: <OKTA_APP_SECRET>

从文件所在的目录中执行下面的kubectl create 命令,使用上面的secret.yaml 文件创建一个Kubernetes Secret。请确保将占位符替换为实际值。

kubectl create -f secret.yaml

3. 创建一个deployment.yaml 文件,并添加下面代码块的内容,以定义部署资源类型的属性。

配置文件包含三个部分,定义了一个部署。元数据定义了资源信息;规格定义了要创建的pod数量,在pod内运行的docker镜像,以及容器内要公开的网络端口。部署也将为Kubernetes Secrets挂载卷。

注意:将下面代码块中模板部分的<DOCKER-IMAGE-URI> 占位符替换为你推送到ECR的docker镜像的URI。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: okta-express-k8-deployment
  namespace: default
  labels:
    app: okta-express-k8
spec:
  replicas: 2
  selector:
    matchLabels:
      app: okta-express-k8
  template:
    metadata:
      labels:
        app: okta-express-k8
    spec:
      containers:
        - name: okta-express-k8
          image: <DOCKER-IMAGE-URI>:latest
          ports:
            - containerPort: 3000
          env:
            - name: HOST_URL
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: HOST_URL
            - name: OKTA_ORG_URL
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: OKTA_ORG_URL
            - name: OKTA_CLIENT_ID
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: OKTA_CLIENT_ID
            - name: OKTA_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: OKTA_CLIENT_SECRET
            - name: OKTA_TOKEN
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: OKTA_TOKEN
            - name: APP_SECRET
              valueFrom:
                secretKeyRef:
                  name: okta-node-app-secrets
                  key: APP_SECRET

执行下面的kubectl创建命令,使用上面的配置文件内容创建部署资源。

kubectl create -f deployment.yaml

修改Okta客户端凭证

在这一点上,okta-k8-cluster 几乎已经可以使用了。你已经创建了一个负载均衡器和一个部署资源。然而,在用户可以通过集群内运行的Node.js应用程序进行完全认证之前,你必须更新Okta凭证内的redirect_uri ,以指向负载均衡器的okta-k8-cluster

导航到你的Okta开发者控制台,点击我们在本教程中使用的应用程序。Okta console application general settings

在 "LOGIN"部分,点击 "Sign-in redirect URIs"小节中的 "Add URI"按钮,添加一个新的URI,在启动认证过程中使用。将负载均衡器的外部 IP 添加到下面格式的输入字段中,将LOAD_BALANCER_EXTERNAL_IP 替换为你的集群负载均衡器的外部 IP。

http://LOAD_BALANCER_EXTERNAL_IP:3000/callback

接下来在签出重定向URI小节中点击添加URI按钮,添加一个新的URI,将在签出应用程序时使用。在输入字段中按以下格式添加负载均衡器的外部 IP。

http://LOAD_BALANCER_EXTERNAL_IP:3000

Okta console application login sections

点击 "保存"按钮,保存这两个添加的URI。

在这一点上,应用程序已经完全设置好,可以使用了。用你的网络浏览器,你可以通过负载均衡器的外部IP访问Node.js应用程序,格式为:http://LOAD_BALANCER_EXTERNAL_IP:3000

Running Simple Node.js application on EKS