GitLab-CIで行う運用改善(AutoScaling編)

こんにちは

インフラの戸田です。

ブログのタイトルで(Autoscaling編)と言っていますが、そんなに続かないと思います(どなたか続いて)

では、本題にいきます。

経緯

とあるサービスのEC2をAutoscalingを使った構成に変更しました。

Autoscalingに変更して数日たったある日 「Autoscalingのグループの設定変えるの手順めんどくさいね」という話が隣の席から聞こえました。(たぶん)

最初の手順は、

  1. AMIの作成
    • インスタンス作成
    • 設定の変更はansibleで実行
    • その後AMIを作成
  2. 起動設定を作成
    • インスタンスイメージの選択
    • IAM Roleの設定
    • UserDataの設定
    • セキュリティグループの設定
  3. Autoscalingグループの変更
    • どのVPC、どのサブネットに置くか
    • ALBの紐づけ
    • スケーリングポリシーの設定
    • タグの設定

一見少ないように思う方もいるかもしれませんが、staging環境とproduction環境があるため、二回同じようなことをしなくてはなりません。(この時点でめんどくさい)

また、頻繁にインスタンスを入れ替えることになった際は、上記の手順を何度も繰り返さないといけません。(想像したくない)

調べると、Cloudformaitonで更新する時は、Update Policyという設定があります。

Update Policyとは、RollingUpdateやReplaceUpdateといった手法でリソースに対する更新を処理する方法を指定する属性です。
詳しくは公式リンク を見て頂けると幸いです。手動では設定することができないことがわかりました。

CloudFormationの情報を話すと「コード化して、運用を楽にしよう。」ということになりました!

使うもの

  • gitlab-ci.yml
  • CloudFormation
  • AWSのアクセスキーとシークレットキー
  • AMIID
  • PRIVATE_TOKEN

事前準備

AWSのアクセスキーとシークレットキー

IAMからアクセスキーとシークレットキーを作成し、GitLab-CIのVariablesに登録します。

GitLabのPRIVATE_TOKENの取得

APIの権限が付いたPersonal access tokenを作成します。

作成したトークンをAWSアクセスキー同様GitLab-CIのVariablesに登録します。

やること

今回は上記に書いた手順の2(起動設定を作成)と3(Autoscalingグループの変更)をCloudFormationで行います。

※1のAMI作成に関してはPackerを使って作成しています。こちらもGitLab-Ciを使って、自動で生成していますが、今回は一部の処理のみ紹介します。

更にGitLab-CIを使って、特定のブランチにMergeするとCloudFormationを実行して、インスタンスの入れ替えを行います。

フロー

フロー図

f:id:AdwaysEngineerBlog:20190708113913p:plain

例えば

  1. nginxのconfigファイルを変更
  2. Masterにマージ
  3. AMIを作成(Staging)
  4. cfnでインスタンスの切り替え(Staging)
  5. 問題ないか確認
  6. AMIを作成(Production)
  7. cfnでインスタンスの切り替え(Production)

といったフローになります。

AMIIDについて

上記で書いたPackerの一部の処理について紹介します。

PackerでAMIを作成した際、AMIIDをPacker自身は保持をしています。

その保持したAMIIDをこの後行うCloudFormationで使うため、GitLab-CI内でGitLab-CIのVariablesに変数として登録します。

gitlab-ci.ymlでの処理

- curl --request PUT --header PRIVATE-TOKEN:$PRIVATE_TOKEN "https://gitlab.example.com/api/v4/projects/<projectID>/variables/STAGING_AMIID" --form "value=$AMIID"

CloudFormation

実行するCloudFormationに関しては以下をご覧ください

CloudFormation (抜粋)

---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  AZ1:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  AZ2:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  IAMRole:
    Type: String
    Description: select IAMRole Arn
  WebInstanceType:
    Description: WebServer EC2 instance type
    Type: String
    ConstraintDescription: must be a valid EC2 instance type.
  WebAMIID:
    Type: AWS::EC2::Image::Id
    Description: AMI ID for Springboard Instance
  WebInstanceMaxSize:
    Type: String
    Description: AutoScaling Instance Max Size
  WebInstanceMinSize:
    Type: String
    Description: AutoScaling Instance Min Size
  WebHealthCheckGracePeriod:
    Type: String
    Description: AutoScaling Instance HealthCheckGracePeriod
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable RDP access to the instances
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  ProjectName:
    Type: String
    Description: Project name for tag
  Stage:
    Type: String
  WebSubnetAID:
    Type: AWS::EC2::Subnet::Id
    Description: select SubnetID_A
  WebSubnetCID:
    Type: AWS::EC2::Subnet::Id
    Description: select SubnetID_C
  TargetGroupArn:
    Type: String
    Description: select TargetGroup
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup::Id
    Description: select securitygroup

Resources:
  WebEC2InstanceLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !Ref WebAMIID
      InstanceType: !Ref WebInstanceType
      IamInstanceProfile: !Ref IAMRole
      KeyName: !Ref KeyName
      SecurityGroups:
        - !Ref WebSecurityGroup

  WebEC2InstanceAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AvailabilityZones:
        - !Ref AZ1
        - !Ref AZ2
      DesiredCapacity: !Ref WebInstanceMinSize
      HealthCheckGracePeriod: !Ref WebHealthCheckGracePeriod
      HealthCheckType: ELB
      LaunchConfigurationName: !Ref WebEC2InstanceLaunchConfiguration
      MaxSize: !Ref WebInstanceMaxSize
      MinSize: !Ref WebInstanceMinSize
      TargetGroupARNs:
        - !Ref TargetGroupArn
      Tags:
        - Key: environment
          Value: !Ref Stage
          PropagateAtLaunch: 'true'
        - Key: role
          Value: web
          PropagateAtLaunch: 'true'
      VPCZoneIdentifier: 
        - !Ref  WebSubnetAID
        - !Ref  WebSubnetCID
    CreationPolicy:
      ResourceSignal:
        Timeout: PT20M 
    UpdatePolicy:
      AutoScalingReplacingUpdate:
        WillReplace: 'true'
 
  ScaleOutPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref WebEC2InstanceAutoScalingGroup
      PolicyType: StepScaling
      StepAdjustments:
        - ScalingAdjustment: 1
          MetricIntervalLowerBound: 0
          MetricIntervalUpperBound: 30.0
        - ScalingAdjustment: 1
          MetricIntervalLowerBound: 30.0

  ScaleInPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: PercentChangeInCapacity
      AutoScalingGroupName: !Ref WebEC2InstanceAutoScalingGroup
      PolicyType: SimpleScaling
      ScalingAdjustment: -50

  CPUAlarmHigh:
    Type: AWS::CloudWatch::Alarm
    Properties:
      EvaluationPeriods: 1
      Statistic: Average
      Threshold: 60
      Period: 300
      AlarmActions:
        - !Ref ScaleOutPolicy
      Namespace: AWS/EC2
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref WebEC2InstanceAutoScalingGroup
      ComparisonOperator: GreaterThanOrEqualToThreshold
      MetricName: CPUUtilization

  CPUAlarmLow:
    Type: AWS::CloudWatch::Alarm
    Properties:
      EvaluationPeriods: 1
      Statistic: Average
      Threshold: 10
      Period: 300
      AlarmActions:
        - !Ref ScaleInPolicy
      Namespace: AWS/EC2
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref WebEC2InstanceAutoScalingGroup
      ComparisonOperator: LessThanOrEqualToThreshold
      MetricName: CPUUtilization

このCloudFormationでは、CPUの使用率をトリガーにして増減するAutoscalingになります。

例えば、CPU使用率が80%を超えた場合、一台インスタンスを追加する。

もし、CPU使用率が10%を下回った場合、稼働しているインスタンスの半分を削除する。

パラメーターに関して、変更可能なcloudformatinになります。

※削除している部分は、UserDataの記述になります。

GitLab-CI

gitlab-ci.ymlでCloudFormationを実行している部分のみ抜粋しています。

また、Cloudformatinを実行するシェルに関しては、Autoscalingに合わせた仕様のため省略させていただきます。

.gitlab-ci.yml(抜粋)

---
stages:
 - depoly

update_autoscaling_staging:
 stage: depoly
 image: python:latest
 before_script:
 - apt-get update
 - apt-get install -y jq
 - pip install awscli
 script:
 - echo $STAGING_AMI 
 - "aws cli をラッピングしたシェルを使ってデプロイをします。" 
 only:
 - release-cfn-staging

update_autoscaling_productiom:
 stage: depoly
 image: python:latest
 before_script:
 - apt-get update
 - apt-get install -y jq
 - pip install awscli
 script:
 - echo $PRODUCTION_AMI 
 -"aws cli をラッピングしたシェルを使ってデプロイをします。" 
 only:
 - release-cfn-production

まとめ

GitLabとInfrastructure as Codeを組み合わせすると運用が楽になります!!

Infrastructure as Codeはよきものです

以上となります。 最後までご覧いただきありがとうございます。