在Amazon EKS上运行Rails API应用程序

大致内容

我使用备受瞩目的Amazon EKS在街上运行了一个Rails应用程序。它就像是教程内容的加强版。

EKS 是什么?

目前在AWS上提供的托管式Kubernetes服务。
以下地区可用(截至2018年06月18日)。

    • 米国西部 (オレゴン) (us-west-2)

 

    米国東部 (バージニア北部) (us-east-1)

载入应用程序。

    • Rails : 5.2.0

 

    • Ruby : 2.5.1

 

    DB : PostgreSQL

这次,我们使用了RDS作为数据库。

然而,使用名为存储类的东西可以在Kubernetes上准备存储。
使用它可以运行需要持久化的服务,如数据库。

需要的东西 de

    • バージョン1.15.32以降のAWSCLI

Amazon EKSを触るのに必要
brew版は 1.15.32以降 の要件が満たせず、pipで再度インストールした(2018.06.18現在)

kubectl

Kubernetesクラスタに対してコマンドを実行するためのCLI
Homebrewでインストールした

heptio-authenticator-aws

IAMの情報を使ってKubernetesクラスタへの認証を行うためのツール

go get でgithubからインストールした

创建资源

借鉴了基本结构

我参考了这个cfn.yml文件,它是从https://github.com/y13i/aws-eks-example中拷贝过来的。

我借用了cfn.yml文件,并进行了自定义后使用。
非常感谢您的帮助和支持。非常感谢。

这个文件使用一个CloudFormation堆栈来创建EKS所需的所有资源,并且还将网络配置规划成特定的形式。

大致如此。

    • VPC

パブリックサブネット*2
プライベートサブネット*2

プライベートサブネット内にアプリ用のインスタンス設置
EKSクラスタの作成

定制化的部分

为了运行Rails应用程序,还需要以下内容,因此在yaml文件中进行了追加。

    • RDS

プライベートサブネット内に設置

ECRのリポジトリ作成
S3の設定

参数: 添加部分

  DBUsername:
    Type: String
  DBPassword:
    Type: String
    NoEcho: true

资源:附加的部分


  VPCEndpointForS3:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      RouteTableIds:
      - Ref: PublicRouteTable
      - Ref: PrivateRouteTable0
      - Ref: PrivateRouteTable1
      VpcId:
        Ref: Vpc
      ServiceName:
        Fn::Join:
        - "."
        - - com
          - amazonaws
          - Ref: AWS::Region
          - s3
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription:
        Ref: AWS::StackName
      SubnetIds:
      - Ref: PrivateSubnet0
      - Ref: PrivateSubnet1
      Tags:
      - Key: Name
        Value:
          Ref: AWS::StackName
  DBParameterGroup:
    Type: AWS::RDS::DBParameterGroup
    Properties:
      Family: postgres9.6
      Description:
        Ref: AWS::StackName
      Parameters:
        client_encoding: UTF8
        timezone: Asia/Tokyo
  DBInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      AllocatedStorage: 20
      DBInstanceClass: db.t2.micro
      DBName:
        Ref: AWS::StackName
      DBParameterGroupName:
        Ref: DBParameterGroup
      DBSubnetGroupName:
        Ref: DBSubnetGroup
      Engine: postgres
      EngineVersion: "9.6.6"
      MasterUsername:
        Ref: DBUsername
      MasterUserPassword:
        Ref: DBPassword
      MultiAZ: true
      PubliclyAccessible: false
      StorageType: gp2
      Tags:
      - Key: Name
        Value:
          Ref: AWS::StackName
      VPCSecurityGroups:
      - Fn::GetAtt:
        - DBSecurityGroup
        - GroupId
  DBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for rds
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId:
            Fn::GetAtt:
            - NodeSecurityGroup
            - GroupId
      VpcId:
        Ref: Vpc
      Tags:
      - Key: Name
        Value:
          Ref: AWS::StackName
  ContainerRepository:
    Type: AWS::ECR::Repository
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 365
      LogGroupName:
        Ref: AWS::StackName
  StorageBucket:
    Type: AWS::S3::Bucket
  StorageUser:
    Type: AWS::IAM::User
    Properties:
      ManagedPolicyArns:
      - Ref: StorageUserPolicy
  StorageAccessKey:
    Type: AWS::IAM::AccessKey
    Properties:
      UserName:
        Ref: StorageUser
  StorageUserPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: Allow
          Action: s3:*
          Resource:
          - Fn::GetAtt:
            - StorageBucket
            - Arn
          - Fn::Join:
            - ""
            - - Fn::GetAtt:
                - StorageBucket
                - Arn
              - /*
              - 

输出:新增的部分



  ContainerRepository:
    Value:
      Ref: ContainerRepository
  DBHostName:
    Value:
      Fn::GetAtt:
        - DBInstance
        - Endpoint.Address
  DBPort:
    Value:
      Fn::GetAtt:
        - DBInstance
        - Endpoint.Port

由于准备就绪,现在可以部署了。

目录结构大致如下。

api/
├ app/
├ config/
│ .
│ .
│ .
├ infra/
│ └ cfn.yml
└ Dockerfile

API/
├ 应用程序/
├ 配置/
│ .
│ .
│ .
├ 基础设施/
│ └ cfn.yml
└ Docker文件

在api目录下执行命令
※大约需要15到20分钟才能完成

$ aws cloudformation deploy --template-file infra/cfn.yml --capabilities CAPABILITY_IAM --stack-name development --parameter-overrides DBUsername=app DBPassword=hogehoge

一旦部署完成后,从管理控制台中选择CloudFormation > 堆栈 > [输出]选项卡,然后查看输出结果。

ss1.png

为了访问EKS集群,需要配置身份验证相关的设置。

创建.kube/

$ mkdir -p .kube/

创建 kubeconfig 文件

$ touch ./.kube/k8s-config.yml
apiVersion: v1
clusters:
- cluster:
    server: <CFnで出力された EKSClusterEndpoint>
    certificate-authority-data: <CFnで出力された EKSClusterCertificateAuthorityData>
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: aws
  name: aws
current-context: aws
kind: Config
preferences: {}
users:
- name: aws
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      command: heptio-authenticator-aws
      args:
        - "token"
        - "-i"
        - <CFnで出力された EKSClusterName>

设置环境变量KUBECONFIG

在执行kubectl命令时,添加路径以引用k8s-config.yml文件。

在direnv中追加或在.bash_profile中追加。

export KUBECONFIG=<k8s-config.ymlへのパス>

试着访问集群

$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   xxx.xx.x.x   <none>        443/TCP   8m

※ 指令:如果在heptio-authenticator-aws相关的地方出现错误,请将k8s-config.yml文件中的路径替换为指向heptio-authenticator-aws的相对路径。

例)../../../go/bin/heptio-authenticator-aws 可以在中国本地化吗?我只需要一种选项。

工作节点的设置

准备一个用于存放Kubernetes配置文件的位置

$ mkdir ./kubernetes/

创建配置文件

$ touch kubernetes/aws-auth-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: <CFnで出力された NodeInstanceRole>
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

让节点加入到集群中

$ kubectl apply -f kubernetes/aws-auth-configmap.yml
configmap "aws-auth" created

获取并确认节点列表。

$ kubectl get nodes
NAME                          STATUS    ROLES     AGE       VERSION
ip-XX-X-XX-XXX.ec2.internal   Ready     <none>    1m        v1.10.3
ip-XX-X-XX-XX.ec2.internal    Ready     <none>    51s       v1.10.3

如果STATUS不是Ready,就会一直温柔地等待它变成Ready。

$ kubectl get nodes --watch

设置环境变量

无论是使用direnv还是.bash_profile都可以,随你喜欢。

export AWS_ACCOUNT_ID=xxxxx
export AWS_DEFAULT_REGION=us-east-1
export AWS_REGION=us-east-1
export AWS_ACCESS_KEY_ID=xxxxx
export AWS_SECRET_ACCESS_KEY=xxxxxx
export CONTAINER_REPOSITORY=<CFnで出力された ContainerRepository>
export CONTAINER_IMAGE_TAG=<適当なタグ>

登录ECR,构建镜像,打标签,推送。

$ $(aws ecr get-login --no-include-email)
$ docker build -t "$CONTAINER_REPOSITORY:$CONTAINER_IMAGE_TAG" .
$ docker tag "$CONTAINER_REPOSITORY:$CONTAINER_IMAGE_TAG" "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$CONTAINER_REPOSITORY:$CONTAINER_IMAGE_TAG"
$ docker push "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$CONTAINER_REPOSITORY:$CONTAINER_IMAGE_TAG"

创建 Kubernetes 清单文件

创建秘密

为了使用环境变量,我在Pod中准备了它。

$ touch kubernetes/eks-secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: eks-secret
type: Opaque
data:
  HOGE: aG9nZQ==

数据是由键值对组成,值是经过base64编码的值。

我正在复制并粘贴通过命令生成的内容。

$ echo -n "hoge" | base64
aG9nZQ==

在这种情况下,Pod能够使用值为hoge的环境变量HOGE。

将创建的清单应用于集群。

$ kubectl create -f kubernetes/eks-secret.yml

创建部署

Pod和ReplicaSets的配置被整合在一起。

$ touch kubernetes/eks-deployment.yml

提前获取ECR的镜像信息

$ echo "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$CONTAINER_REPOSITORY:$CONTAINER_IMAGE_TAG"
xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx

将获取到的图像信息放入image中。

使用 AWS EC2 容器注册表时,需要完整指定从 URL 到标签的部分。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails-api
  template:
    metadata:
      labels:
        app: rails-api
    spec:
      containers:
      - name: rails-api
        image: xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx
        ports:
        - containerPort: 3000
        envFrom:
        - secretRef:
            name: eks-secret
        args:
          - rails
          - server
          - -p
          - '3000'
          - -b
          - '0.0.0.0'

这个句子的意思是以下这样的。

xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx イメージをもとにしたコンテナが入ったPodを作る。
そのPodにラベル app=rails-api を付与する。
ラベル app=rails-api がついたPodが3つになるようレプリケーションする。
Podは外部通信用に3000ポートを開ける。
コンテナ作成時に rails server -p ‘3000’ -b ‘0.0.0.0’ を実行する。

将创建的清单应用于集群。

$ kubectl create -f kubernetes/eks-deployment.yml

创建服务

放置Pod可以运行应用程序是一件好事,但是要如何访问该应用程序呢?解决这个问题的是服务。

需要这个来处理用户的请求。

$ touch kubernetes/eks-service.yml
kind: Service
apiVersion: v1
metadata:
  name: eks-service
spec:
  type: LoadBalancer
  selector:
    app: rails-api
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000

这句话的意思如下。

    80ポートへアクセスがあったとき、ラベルが app=rails-api のPodの3000ポートにつなぐ

将创建的清单应用到集群上。

$ kubectl create -f kubernetes/eks-service.yml

创建工作

不是像应用程序一样需要始终运行的东西,而是希望执行后消失的东西,可以使用“任务”。

比如在Rails的案例中,我认为像rails db:create或者rails db:migrate这样的命令只需要执行一次。

为此,我准备了一份工作。

$ touch kubernetes/eks-create-job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: eks-create-job
spec:
  backoffLimit: 3
  parallelism: 1
  completions: 1
  template:
    spec:
      containers:
      - name: rails-create
        image: xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx
        ports:
        - containerPort: 3000
        envFrom:
        - secretRef:
            name: eks-secret
        args:
          - rails
          - db:create
      restartPolicy: Never

Pod的组件与deployment相同,唯一不同的是arg(=执行的命令)。

从意思上来说

xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx イメージをもとにしたコンテナが入ったPodを作る。
Podは外部通信用に3000ポートを開ける。
コンテナ作成時に rails db:create を実行する。
実行が失敗したら3回までリトライする。
Podは最大1つ実行する。
1つのPodの処理が成功したらjobの成功とする。
コンテナの再起動は行わない。

将制作的清单应用于集群中。

$ kubectl create -f kubernetes/eks-create-job.yml

迁移也是相同的情况。

$ touch kubernetes/eks-migrate-job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: eks-migrate-job
spec:
  backoffLimit: 3
  parallelism: 1
  completions: 1
  template:
    spec:
      containers:
      - name: rails-migrate
        image: xxxxx.dkr.ecr.us-east-1.amazonaws.com/xxxxx:xxxxx
        ports:
        - containerPort: 3000
        envFrom:
        - secretRef:
            name: eks-secret
        args:
          - rails
          - db:migrate
      restartPolicy: Never

将已创建的清单应用于集群。

$ kubectl create -f kubernetes/eks-migrate-job.yml

访问应用程序

一旦创建了服务,EKS会自动创建负载均衡器。

我想要访问那里,所以为了确认,我会执行以下命令。

$ kubectl describe services

从中找到以下项目。

LoadBalancer Ingress: xxxxxxxxxx.us-east-1.elb.amazonaws.com

xxxxxxxxxx.us-east-1.elb.amazonaws.com成为终端点。

可以通过浏览器或Postman等工具进行访问。

请提供一个参考网址。

使用Amazon弹性容器服务(Amazon Elastic Container Service)为Kubernetes部署一个Kubernetes应用程序。

广告
将在 10 秒后关闭
bannerAds