【初学者专用】尝试使用EKS发布ReactApp
首先
「有一個模糊的詞浮現出來了,叫做EKS。」
因为我有点想试一试,所以我试用了EKS。EKS是AWS上可用的Kubernetes服务。除了EKS之外,AWS还提供了一种名为ECS的容器编排工具。考虑到学习成本和维护操作等因素,我现在觉得如果要尝试的话,可能会选择ECS。毕竟我只是隐约想到了这个选项。
Kubernetes是什么?
我认为聪明的大家已经知道,但为了确保,我还是解释一下Kubernetes。(读作”Ku-ba-Ne-tisu”或”Ku-bu-Ne-tisu”。我以傲慢的表情读作”kuu↑ba-neitsu↓”)
Kubernetes(K8s)是一种开源系统,用于自动化部署和扩展,以及管理容器化应用程序。
引用:Kubernetes官方文档
你也许能够察觉出来,但即便读了三遍也依然无法理解。
大家有没有将应用程序进行容器化的经验呢?
那么,有没有经历过部署多个容器作为应用程序时,容器间的协作、资源管理和部署等过程呢?
这些步骤需要相当长的时间,并且对于经验丰富的人来说,其中有些部分会很难。
(实际上因为没有这样的经验,所以不太明白为什么会那么麻烦)
Kubernetes是一个能够很好地解决这些麻烦问题的功能,尤其是在考虑多个容器运行时。这样的功能被称为“编排工具”。
EKS是什么
EKS 是 AWS 上可以运行 Kubernetes 的服务。
但并不意味着要在 AWS 控制台上进行开发,而是需要从适当的服务器访问并操作 EKS。
我们有一个用于通过命令操作 Kubernetes 的工具,叫做 kubectl,我们会在开发中使用它。
对于熟悉 Kubernetes 的人来说,不需要额外学习,可以顺利迁移到 EKS,这个规范非常方便。
在过去的期间中,通过命令进行开发,在易于预测的控制台上创建资源等的确认是最佳的使用方式吧。
控制平面和工作者
Kubernetes由控制平面和多个工作节点组成。
控制平面和工作节点一起被称为集群。
工作节点用于容纳容器,而控制平面用于管理工作节点和容器。
比如,假设将同一个应用作为容器部署给多个工作人员。
当有多个人访问应用时,如果始终都运行在同一工作人员(服务器)的容器中,那么该服务器的资源很快就会耗尽。
这时,控制平面会帮助合理分配资源。
此外,在更新此类应用程序时,还可以使用滚动更新技术。
通过逐个按顺序更新容器而不是同时更新多个工作人员的容器,可以实现无需停止应用程序即可进行更新。控制平面负责这种控制。
收费
我想大家都对这个问题开始感到担忧,那就是费用体系。为了比较,我也会附上AWS中同样是编排工具的ECS的价格。
EKS创建一个集群每小时需要0.10美元,大约一个月需要74美元。
然而,ECS是零美元,非常慷慨。
由于工作人员最终成为服务器,因此将使用AWS上的服务器功能。根据使用哪个服务器,费用将有所不同。
维护
对于考虑引入EKS的人来说,以下是绝对需要了解的事项。
由于EKS使用Kubernetes,其功能取决于Kubernetes的版本。
由于版本管理的问题,在EKS中,每三个月左右会发布一个次要版本。但是,每个版本的支持期限只有一年,在该期限到来之前,需要更新集群和worker的EKS版本。
考虑到当前正在使用的功能规格突然发生变化或无法使用等可能性,可以认为这将给我们带来相当大的维护成本。
然而,ECS没有版本的概念。
在ECS中,没有功能会因为更新而无法使用,也不需要定期更新。
由于不知不觉地添加了方便的功能,我们可以尝试使用新功能。
网络设置
听到这些,可能会有人说:“如果是EKS和ECS的话,ECS似乎更好…” 但是这样的观点还为时过早。
当工人部署容器并协同工作时,需要根据情况搭建网络。相比之下,EKS会自动处理这些设置,而ECS则需要自行进行适当的配置。随着集群规模和服务复杂性的增加,网络管理工作量也会不断增加。
考虑到以上情况,如果服务更加复杂,选择EKS是比较安全的;如果服务更简单,选择ECS是比较明智的。
这篇文章的目的是什么?
我突然想到了EKS这个词,所以我打算先试着实现一个跨多个节点的应用程序。
因此,我打算先部署一个我平常使用熟悉的React应用程序。
我打算将React App + Nginx容器化并部署在多个节点上,作为最终的解决方案。
步驟
准备事项
由于需要从适当的服务器访问和操作,所以首先要准备好该服务器。
作为我的操作终端,我选择了先在本地安装了WSL Ubuntu20.04。
安装AWS CLI
$ pip install awscli
AWS命令行界面的设置
aws configure
AWS Access Key ID [None]: <アクセスキー>
AWS Secret Access Key [None]: <シークレットキー>
Default region name [None]: ap-northeast-1
Default output format [None]: json
安装 eksctl(用于连接 AWS EKS 的自有工具)
$ curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
$ sudo mv /tmp/eksctl /usr/local/bin
eksctl版本检查
$ eksctl version
0.31.0
以下是以自然中文翻译的选项:
kubectl的安装
截至2023年9月,由于EKS的最新版本为1.27,将根据该版本进行安装。
无法使用与EKS版本不匹配,且比其版本更高的kubectl。
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
sudo apt update
sudo apt install -y kubectl=1.27.5-00
构建网络角色
为了使用KES,需要事先准备一个网络作为容器。另外,还需要创建角色以拥有运行集群和工作者的权限。
使用以下的YAML和JSON,在CloudFormation控制台上创建堆栈,并创建网络角色。
EKS-Vpc的堆栈名称
※请在VPC中检查IP地址,根据需要进行适当的实施。
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Sample VPC - Public subnets only'
Parameters:
VpcBlock:
Type: String
Default: 10.10.0.0/16
Description: The CIDR range for the VPC. This should be a valid private (RFC 1918) CIDR range.
Subnet01Block:
Type: String
Default: 10.10.0.0/24
Description: CidrBlock for subnet 01 within the VPC
Subnet02Block:
Type: String
Default: 10.10.1.0/24
Description: CidrBlock for subnet 02 within the VPC
Subnet03Block:
Type: String
Default: 10.10.2.0/24
Description: CidrBlock for subnet 03 within the VPC. This is used only if the region has more than 2 AZs.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "Worker Network Configuration"
Parameters:
- VpcBlock
- Subnet01Block
- Subnet02Block
- Subnet03Block
Conditions:
Has2Azs:
Fn::Or:
- Fn::Equals:
- {Ref: 'AWS::Region'}
- ap-south-1
- Fn::Equals:
- {Ref: 'AWS::Region'}
- ap-northeast-2
- Fn::Equals:
- {Ref: 'AWS::Region'}
- ca-central-1
- Fn::Equals:
- {Ref: 'AWS::Region'}
- cn-north-1
- Fn::Equals:
- {Ref: 'AWS::Region'}
- sa-east-1
- Fn::Equals:
- {Ref: 'AWS::Region'}
- us-west-1
HasMoreThan2Azs:
Fn::Not:
- Condition: Has2Azs
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcBlock
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC'
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Public Subnets
- Key: Network
Value: Public
Route:
DependsOn: VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
Subnet01:
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 01
Properties:
MapPublicIpOnLaunch: true
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: Subnet01Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-Subnet01"
- Key: kubernetes.io/role/elb
Value: 1
Subnet02:
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 02
Properties:
MapPublicIpOnLaunch: true
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: Subnet02Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-Subnet02"
- Key: kubernetes.io/role/elb
Value: 1
Subnet03:
Condition: HasMoreThan2Azs
Type: AWS::EC2::Subnet
Metadata:
Comment: Subnet 03
Properties:
MapPublicIpOnLaunch: true
AvailabilityZone:
Fn::Select:
- '2'
- Fn::GetAZs:
Ref: AWS::Region
CidrBlock:
Ref: Subnet03Block
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-Subnet03"
- Key: kubernetes.io/role/elb
Value: 1
Subnet01RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet01
RouteTableId: !Ref RouteTable
Subnet02RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet02
RouteTableId: !Ref RouteTable
Subnet03RouteTableAssociation:
Condition: HasMoreThan2Azs
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref Subnet03
RouteTableId: !Ref RouteTable
ControlPlaneSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Cluster communication with worker nodes
VpcId: !Ref VPC
Outputs:
SubnetIds:
Description: All subnets in the VPC
Value:
Fn::If:
- HasMoreThan2Azs
- !Join [ ",", [ !Ref Subnet01, !Ref Subnet02, !Ref Subnet03 ] ]
- !Join [ ",", [ !Ref Subnet01, !Ref Subnet02 ] ]
SecurityGroups:
Description: Security group for the cluster control plane communication with worker nodes
Value: !Join [ ",", [ !Ref ControlPlaneSecurityGroup ] ]
VpcId:
Description: The VPC Id
Value: !Ref VPC
EKS服务角色名称 EKS-Service-Role
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Amazon EKS Cluster Role'
Resources:
eksClusterRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- eks.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
Outputs:
RoleArn:
Description: The role that Amazon EKS will use to create AWS resources for Kubernetes clusters
Value: !GetAtt eksClusterRole.Arn
Export:
Name: !Sub "${AWS::StackName}-RoleArn"
EKS节点组角色的栈名称
AWSTemplateFormatVersion: "2010-09-09"
Description: Amazon EKS - Node Group Role
Mappings:
ServicePrincipals:
aws-cn:
ec2: ec2.amazonaws.com.cn
aws-us-gov:
ec2: ec2.amazonaws.com
aws:
ec2: ec2.amazonaws.com
Resources:
NodeInstanceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- !FindInMap [ServicePrincipals, !Ref "AWS::Partition", ec2]
Action:
- "sts:AssumeRole"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonEKSWorkerNodePolicy"
- !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonEKS_CNI_Policy"
- !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
Path: /
Outputs:
NodeInstanceRole:
Description: The node instance role
Value: !GetAtt NodeInstanceRole.Arn
创建集群
我在控制台上创建了一个集群。(大约需要10分钟)
设置是根据以下设置进行的。
名称:EKS集群
版本:1.27
集群服务角色:使用EKS-Service-Role创建的角色
虚拟私有云和子网:使用EKS-Vpc创建的虚拟私有云和子网
安全组:使用EKS-Vpc创建的安全组
附加组件:默认(VPC CNI、kube-proxy、CoreDNS)
需要创建一个kubeconfig文件来访问集群。在集群创建完成后执行此操作。
aws eks --region ap-northeast-1 update-kubeconfig --name EKS-cluster
连接测试
kubectl get svc
创建工作节点
在控制台上创建一个工作节点(大约需要5分钟)。
选择在计算中添加到节点组的节点组并创建。
配置使用以下设置完成。
名称:EKS-worker-nodes
创建角色 EKS-Nodegroup-Role 用于节点 IAM 角色
适当设置 EC2
AMI 类型为 “Amazon Linux (AL2_x86_64)”
实例类型为 “t3.small”
磁盘大小为 “10”
最小大小为 “2”
最大大小为 “2”
期望大小为 “2”
子网为在 EKS-Vpc 中创建的子网
允许对节点进行远程访问 ON
选择 SSH 密钥
允许来自下次的远程访问 全部
在这里,KES的基本准备已经完成。
接下来的步骤是将React应用程序和Nginx进行容器化、部署,并通过负载均衡器进行公开。
创建一个React应用程序
nvm use 18.16.1
npx create-react-app test-app
将Dockerfile存储在app目录的根目录下。
Dockerfile 的中文翻译:Dockerfile 文件
FROM node:18.16.1 as builder
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json", "./"]
RUN npm install
COPY [".", "./"]
RUN npm run build
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=builder ["/usr/src/app/build", "./"]
COPY ["nginx/default.conf", "/etc/nginx/conf.d"]
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
将default.conf存放在app目录下的/nginx文件夹中。
默认.conf
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm
try_files $uri /index.html;
}
}
创建Docker镜像
我们暂时将Kubernetes的Namespace和ECR的Repository名称设置为eks-test。
使用Docker构建
docker build -t eks-test .
如果执行以下命令后能够通过 localhost:3000 访问就可以了。
docker run -p 3000:80 eks-test
将容器存放在ECR上。
首先,需要在Elastic Container Registry(ECR)中创建容器的存储库。
在控制台上打开ECR(Elastic Container Registry),然后点击创建存储库。
输入名称为”eks-test”并进行创建。
点击eks-test的详细信息中的”显示推送命令”,执行其中的”授权、图片标记和推送命令”,将其存储到本地创建的存储库中。
部署容器映像并公开服务
为了集中管理容器映像的部署服务,我们将创建一个命名空间。
kubectl create namespace eks-test
请将用于部署和发布服务的配置文件存储在一个适当的目录中。
请适当更改EKS-Deployment.yaml文件中的ECR URI。
apiVersion: apps/v1
kind: Deployment
metadata:
name: eks-test
spec:
selector:
matchLabels:
app: eks-test
replicas: 2
template:
metadata:
labels:
app: eks-test
spec:
containers:
- name: eks-test
image: <ECRのURI>
imagePullPolicy: Always
ports:
- containerPort: 80
EKS-Service.yaml可以用以下方式进行本地化简化翻译:京东EKS服务.yaml
apiVersion: v1
kind: Service
metadata:
name: eks-test
spec:
type: LoadBalancer
selector:
app: eks-test
ports:
- protocol: TCP
port: 80
targetPort: 80
在储存的目录下执行以下命令进行应用部署。
kubectl apply -f EKS-Deployment.yaml -n eks-test
创建服务
kubectl apply -f EKS-Service.yaml -n eks-test
如果能够通过以下获取的EXTERNAL-IP访问应用程序,就算成功了。(大概等待发布约两分钟左右。)
kubectl get service -n eks-test
在以上步骤中,我们完成了使用EKS发布React应用程序的过程。
完全删除
以下是删除已创建资源的方法。记得好好打扫一下哦。
删除命名空间(关联的节点和服务也会被删除)
kubectl delete namespace eks-test
删除 ECR 仓库
aws ecr delete-repository --repository-name eks-test --force --region ap-northeast-1
删除节点组
aws eks delete-nodegroup --nodegroup-name EKS-worker-nodes --cluster-name EKS-cluster --region ap-northeast-1
删除集群
aws eks delete-cluster --name EKS-cluster --region ap-northeast-1
删除云形成
aws cloudformation delete-stack --stack-name EKS-Vpc
aws cloudformation delete-stack --stack-name EKS-Service-Role
aws cloudformation delete-stack --stack-name EKS-Nodegroup-Role
结束
非常感谢您阅读到这里。
在撰写本文时,我参考了以下网站。
如果您有兴趣写作的话,我们可以在那时见面。
-
- コンテナサービス「Amazon EKS」とは?実際に使用してみた
-
- Kubernetes超入門
- Dockerで起動できるReactアプリをEKSにデプロイしてみた