Spring Cloud on ECS

背景

如果只是在一臺(tái)主機(jī)或者虛擬機(jī)上面運(yùn)行一個(gè)容器,那么Docker命令行就足夠了,e.g: docker run xxximage。 但是如果需要管理一個(gè)100X的容器集群,就比較有挑戰(zhàn)了。
在AWS眾多服務(wù)中,就有提供集群資源管理和服務(wù)調(diào)度的服務(wù),比如EKSECS, 由于EKS還沒有進(jìn)入中國(guó)區(qū),本篇博客重點(diǎn)介紹在ECS上面運(yùn)行基于Spring Cloud全家桶的微服務(wù)架構(gòu)。

ECS的工作原理

ECS-picture.png

我們將基于上面的ECS架構(gòu)圖,簡(jiǎn)單介紹下ECS的一些重要組件和工作原理。

  • ECS agent, 在每一個(gè)實(shí)例,它都有一個(gè)運(yùn)行的docker進(jìn)程,它就是ECS agent, 其主要負(fù)責(zé)接收ECS的命令,并且將這些命令轉(zhuǎn)化為docker對(duì)應(yīng)的命令;所以它能夠控制EC2實(shí)例,什么時(shí)候啟動(dòng)、停止容器并且監(jiān)控已經(jīng)使用的和空閑的資源情況。
  • ECS pause, 第一啟動(dòng)的容器,協(xié)調(diào)以后容器進(jìn)行協(xié)議的分配,防止沖突。
  • Cluster mangement Engine負(fù)責(zé)協(xié)調(diào)整個(gè)集群的實(shí)例,它可以被認(rèn)為是虛擬的資源池,比如內(nèi)存、存儲(chǔ)、CPU、網(wǎng)絡(luò)等;所以通過它,能夠知道整個(gè)集群的資源現(xiàn)狀。
  • Scheduler負(fù)責(zé)調(diào)度容器或者tasks的執(zhí)行;它能知道每個(gè)task的運(yùn)行狀態(tài),是否是alive或者dead,是否需要rescheduled等。
  • Kev-Value Store, 負(fù)責(zé)整個(gè)集群狀態(tài)的管理。
  • API, ECS另一個(gè)比較獨(dú)特的地方,在于它將容器的調(diào)度從集群管理中解耦出來,并且提供一些的API,以供使用者使用。

演示介紹

基于Spring Cloud全家桶的微服務(wù)架構(gòu),如何運(yùn)行在ECS容器云平臺(tái)上面,本文準(zhǔn)備了三個(gè)服務(wù),用于演示:

  • 服務(wù)一: simple-application, 使用https://start.spring.io/進(jìn)行創(chuàng)建,主要用于從配置服務(wù)器獲取動(dòng)態(tài)配置。
  • 服務(wù)二: simple-config-server, 主要用于配置服務(wù)器,進(jìn)行微服務(wù)配置的管理。
  • 服務(wù)三: simple-eureka, 主要用于服務(wù)注冊(cè)與發(fā)現(xiàn)。

每個(gè)服務(wù)基于docker,需要通過./mvn clean package的方式,進(jìn)行打包和構(gòu)建新的docker鏡像,然后通過下面類似的命令,將最新的鏡像發(fā)布到ECR鏡像注冊(cè)服務(wù)器上面。

  • 在ECR創(chuàng)建對(duì)應(yīng)的鏡像庫(kù)。
$(aws ecr get-login --no-include-email --region ap-southeast-1)
aws ecr create-repository --repository-name simple-cluster/simple-application
  • 將最新的鏡像,發(fā)布到ECR對(duì)應(yīng)的鏡像庫(kù)。

docker tag simple-cluster/simple-application:latest 613175009525.[dkr.ecr.ap-southeast-1.amazonaws.com/simple-cluster/simple-application:latest](http://dkr.ecr.ap-southeast-1.amazonaws.com/simple-cluster/simple-application:latest)

docker push 613175009525.[dkr.ecr.ap-southeast-1.amazonaws.com/simple-cluster/simple-application:latest](http://dkr.ecr.ap-southeast-1.amazonaws.com/simple-cluster/simple-application:latest)
  • 發(fā)布成功之后,更新Cloudformation對(duì)應(yīng)的鏡像倉(cāng)庫(kù)地址:
 Sample:
      cpu: 512
      mem: 1024
      javaopt: "-Xms512m -Xmx1024m -Xss512k"
      port: 3000
      hostport: 9050
      name: simple-sample
      context: /simple-application
      taskcount: 1
      minhealthcount: 50
      maxhealthcount: 200
      HealthCheckGracePeriodSeconds: 300
      logPrefix: simple-application
      image: '613175009525.dkr.ecr.ap-southeast-1.amazonaws.com/simple-cluster/simple-application:latest'
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: /simple-application/actuator/info
      HealthCheckPort: traffic-port
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 5

ECS集群:

可以通過命令行工具ecs-cli up進(jìn)行創(chuàng)建,或者使用Cloudformation進(jìn)行創(chuàng)建,本文演示基于Cloudformation的創(chuàng)建方式。

$ aws cloudformation create-stack --stack-name simple-ecs-cluster-cf-template --template-body file://simple-ecs-cluster-cf.yaml --region ap-southeast-1 --parameters ParameterKey=EcsInstanceType,ParameterValue=t2.medium,ParameterKey=KeyName,ParameterValue=aws-singapore-key,ParameterKey=VpcId,ParameterValue=vpc-0cbbd87895dbbfc5b
{
    "StackId": "arn:aws:cloudformation:ap-southeast-1:613175009525:stack/simple-ecs-cluster-cf-template/008ff110-2685-11e9-94ea-064b1a6199c8"
}

創(chuàng)建成功界面如下:


image.png

請(qǐng)指定正確的VPC,Subnet,SG和指定EC2的類型,數(shù)量,AMI,EBS和key pair,之后ECS會(huì)自動(dòng)創(chuàng)建EC2,并且為其配置如下信息:

 "#!/bin/bash\n","echo ECS_CLUSTER=",!Ref EcsClusterName," >> /etc/ecs/ecs.config\n" 

創(chuàng)建公共的ALB, 并且根據(jù)path進(jìn)行微服務(wù)的分發(fā),對(duì)外提供接口。

image.png

部分相關(guān)Cloudformation配置:

###############   ALB Default Target-Group ####################
  DefaultALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Condition: CreateSpringCloud
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref DefaultTargetGroup
      LoadBalancerArn: !Ref PublicApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

  DefaultTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Condition: CreateSpringCloud
    DependsOn: PublicApplicationLoadBalancer
    Properties:
      HealthCheckIntervalSeconds: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthCheckIntervalSeconds ]
      HealthCheckPath: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthCheckPath ]
      HealthCheckPort: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthCheckPort ]
      HealthCheckProtocol: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthCheckProtocol ]
      HealthCheckTimeoutSeconds: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthCheckTimeoutSeconds ]
      HealthyThresholdCount: !FindInMap [ SpringCloudMiddlewares, Eureka, HealthyThresholdCount ]
      Matcher:
        HttpCode: 200
      Name: default-target-group
      Port: 80
      Protocol: HTTP
      TargetGroupAttributes:
        -
          Key: deregistration_delay.timeout_seconds
          Value: 300
        -
          Key: slow_start.duration_seconds
          Value: 0
        -
          Key: stickiness.enabled
          Value: false
      VpcId: !Ref VpcId

##############   Spring Cloud Sample Resources start ####################
  SpringCloudSampleECSALBListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Condition: CreateSampleTask
    DependsOn: DefaultALBListener
    Properties:
      Actions:
      - Type: forward
        TargetGroupArn: !Ref SpringCloudSampleTargetGroup
      Conditions:
      - Field: path-pattern
        Values: [/simple-application*]
      ListenerArn: !Ref DefaultALBListener
      Priority: 3

指定服務(wù)運(yùn)行環(huán)境:

方法一: 通過ECS task配置環(huán)境變量

environment": [
        {
          "name": "JAVA_OPTS",
          "value": "-Dspring.profiles.active=aws"
        }
      ]

方法二: 通過Cloudformation配置環(huán)境變量

 Environment:
            -
              Name: JAVA_OPTS
              Value: !FindInMap [ SpringCloudMiddlewares, ConfigServer, javaopt ]
            -
              Name: EUREKA_URL
              Value: !Join [ "" , [ "http://", !GetAtt PublicApplicationLoadBalancer.DNSName, "/simple-eureka/eureka"]]
            -
              Name: SPRING_PROFILES_ACTIVE
              Value: aws

確保端口動(dòng)態(tài)綁定(非靜態(tài)綁定),防止端口沖突問題:

方法一: ECS的task配置,hostPost設(shè)置為0.

"portMappings": [
        {
          "hostPort": 0,
          "protocol": "tcp",
          "containerPort": 8080
        }
      ]

方法二: 通過Cloudformation配置動(dòng)態(tài)端口綁定

 -
   ContainerPort: !FindInMap [ SpringCloudMiddlewares, ConfigServer, port ]
   HostPort: 0

確保SG安全組,允許在動(dòng)態(tài)端口范圍內(nèi)的訪問:

image.png

創(chuàng)建Task的時(shí)候,確保每個(gè)task里面都開啟了日志記錄:

"logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "simple-ecs-cluster-log",
          "awslogs-region": "cn-northwest-1",
          "awslogs-stream-prefix": "simple-application"
        }
      }
###############  CloudWatchLog  start ####################
  ECSCloudWatchLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join [ "-" , [ !Ref EcsClusterName, !Ref ECSCloudWatchLogGroupName]]
      RetentionInDays: !Ref ECSCloudWatchLogGroupRetentionInDays
###############  CloudWatchLog  end ####################

創(chuàng)建Task的時(shí)候,確保每個(gè)task里面都擁有足夠多的訪問權(quán)限,比如你的服務(wù)需要訪問SQS,Redis,S3等。

集群部署
部署成功后,可通過下面Eureka界面,查看注冊(cè)成功的服務(wù):

ecs-demo-dashbroad.png

查看ECS集群運(yùn)行狀態(tài):
ecs-cluster.png

時(shí)區(qū)的一致性:

  • EC2上面的時(shí)區(qū)一致性:

for ec2 in 10.208.195.1xx 10.208.194.xx 10.208.194.1xx 
do
echo $ec2;
ssh -i ~/.ssh/yourkey.pem ec2-user@$ec2  << sshoff
    sudo yum -y erase 'ntp*'
    sudo yum -y install chrony
    sudo service chronyd start
    sudo chkconfig chronyd on
    sudo sed -i 's/ZONE=\"UTC/ZONE=\"Asia\/Shanghai/g' /etc/sysconfig/clock
    sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
    exit
sshoff
done
  • RDS上面的時(shí)區(qū)一致性:

    設(shè)置Parameter Group,并且重啟實(shí)例。

  • Docker上面的時(shí)區(qū)一致性:

    重新構(gòu)建滿足條件的base鏡像,并更新響應(yīng)的配置文件pom.xml或者dockerfile

  • 編碼上,保證時(shí)區(qū)的方法:

System.setProperty("user.timezone","Asia/Shanghai");

  • 通過傳遞參數(shù)的方式,保證時(shí)區(qū)的方法:
-Duser.timezone=GMT+8 

源代碼:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容