自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 CLI、Cloud Formation。 eksctl
,甚至是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端口上运行。
随着Node.js应用程序现在在终端运行,你可以在Web浏览器内查看Node.js应用程序,网址是http://localhost:3000。
将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
的标签。
接下来,执行下面的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>
创建一个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>
在创建了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
注意: 注意上面的负载平衡器的细节中返回的外部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开发者控制台,点击我们在本教程中使用的应用程序。
在 "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
点击 "保存"按钮,保存这两个添加的URI。
在这一点上,应用程序已经完全设置好,可以使用了。用你的网络浏览器,你可以通过负载均衡器的外部IP访问Node.js应用程序,格式为:http://LOAD_BALANCER_EXTERNAL_IP:3000
。