【初学者专用】尝试使用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月 : 74$EC2 or Fargate の料金ECS月 : 0$EC2 or Fargate の料金

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にデプロイしてみた
广告
将在 10 秒后关闭
bannerAds