SpotFleet(diversified/lowest)で起動するインスタンスを常にELBにアタッチする

目的

  • SpotFleetで起動しているインスタンスを常に同一のELBにアタッチする
  • 生存戦略はdiversified/lowestで機能する

前提

本構成ではインスタンス1台の運用を想定しているのでインスタンスが停止後、他プールからインスタンスが起動するまで〜2分かかるので、可用性を保つためにはオンデマンドを入れるなり、工夫が必要

方法

スポットインスタンスリクエスト時にユーザーデータを利用する

 イメージ

Untitled (5).png

設定

  1. ELBに対する権限を持つIAMRoleを作成
  2. IAMRoleを使用するスクリプト作成
  3. EC2Resourcesのaws_spot_fleet_requestの引数にuser_dataを記述
spot_fleet_request.tf
# Request a Spot fleet
resource "aws_spot_fleet_request" "aws-simple-console-spotfleet" {
  iam_fleet_role                      = "arn:aws:iam::xxxxxx:role/xxxxxx"
  spot_price                          = "0.0127"
  allocation_strategy                 = "diversified"
  target_capacity                     = 2
  terminate_instances_with_expiration = false

  launch_specification {
    instance_type               = "m3.medium"
    ami                         = "ami-xxxxxx"
    availability_zone           = "ap-northeast-1c"
    subnet_id                   = "subnet-xxxxxx"
    iam_instance_profile        = "xxxxxx"
    key_name                    = "xxxxxx"
    vpc_security_group_ids      = ["sg-xxxxxx"]
    monitoring                  = false
    associate_public_ip_address = true
    user_data                   = "${file("userdata.sh")}"

    root_block_device {
      volume_size = "10"
      volume_type = "gp2"
    }
  }
}
userdata.sh
#!/bin/bash
export INSTANCEID=`curl http://169.254.169.254/latest/meta-data/instance-id`
aws elb register-instances-with-load-balancer --load-balancer-name xxxxxx --instances $INSTANCEID --region ap-northeast-1

続きを読む

terraformでASGを作る時の注意点

概要

起動設定を更新した際に相関関係にあるASGで起動しているインスタンスが作り直されてしまうので、これを回避する。

方法

EC2Resourcesaws_launch_configurationを使用時、引数にnameではなく name_prefixを使用する。

launch_configuration.tf
resource "aws_launch_configuration" xxxxxxxx-autoscaling-conf" {
  name_prefix                 = "xxxxxxxx-autoscaling"
  image_id                    = "ami-xxxxx"
  instance_type               = "t2.medium"
  iam_instance_profile        = "xxxxxxxx"
  key_name                    = "xxxxxxxx"
  security_groups             = ["sg-xxxxxxxx"]
  associate_public_ip_address = true
  user_data                   = "${file("xxxxxxxx.sh")}"
  root_block_device  {
    volume_type           = "gp2"
    volume_size           = "50"
    delete_on_termination = "true"
  }
  lifecycle  {
    create_before_destroy = true
  }
}
  • IDのみがユニークなものに置換され、ASGは影響を受けない
aws_launch_configuration.xxxxxxxx-autoscaling-autoscaling-conf:
  id = xxxxxxxx-autoscalingxxxxxxxxxxxxxxxx

続きを読む

[JAWS-UG CLI] Amazon Kinesis Firehose re:入門 (1) 事前準備(Source(Kinesis Agent)およびDestination(S3)の作成)

この記事について

JAWS-UG CLI専門支部 #90 Kinesis Firehose 復習編で実施するハンズオン用の手順書です。

前提条件

必要な権限

作業にあたっては、以下の権限を有したIAMユーザもしくはIAMロールを利用してください。

  • 以下のサービスに対するフルコントロール権限

    • Kinesis Firehose
    • IAM
    • EC2
    • S3
    • CloudWatch Logs
    • STS
    • (Lambda)
      • データの変換を行う場合
    • (KMS)
      • データの暗号化を行う場合

0. 準備

0.1. リージョンを指定

オレゴンリージョンで実施します。(東京マダー?)

コマンド
export AWS_DEFAULT_REGION="us-west-2"

0.2. 資格情報を確認

コマンド
aws configure list

インスタンスプロファイルを設定したEC2インスタンスでアクセスキーを設定せずに実行した場合、以下のようになります。

結果
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************QSAA         iam-role
secret_key     ****************c1xY         iam-role
    region                us-west-2              env    AWS_DEFAULT_REGION

0.3. バージョン確認

コマンド
aws --version
結果
aws-cli/1.11.129 Python/2.7.12 Linux/4.9.38-16.33.amzn1.x86_64 botocore/1.5.92

0.4. バージョンアップ(必要に応じて)

コマンド
sudo pip install -U awscli

1. 管理対象の構築

CloudFormationを利用して、Source(Kinesis AgentをインストールしたEC2インスタンス)とDestination(S3バケット)を作成します。

1.1. KeyPairの作成

EC2インスタンス用にKeyPairを作成します。

KeyPairの名前を指定

コマンド
AWS_ID=$(aws sts get-caller-identity 
    --query "Account" 
    --output text) 
    && echo ${AWS_ID}
コマンド
KEY_PAIR_NAME="${AWS_ID}_firehose_jawsug_cli"
KEY_MATERIAL_FILE_NAME=${KEY_PAIR_NAME}.pem

同名KeyPairの不存在を確認

コマンド
aws ec2 describe-key-pairs 
    --query "KeyPairs[?KeyName==`${KEY_PAIR_NAME}`]"
結果
[]

KeyPairの作成

コマンド
aws ec2 create-key-pair 
    --key-name ${KEY_PAIR_NAME} 
    --query "KeyMaterial" 
    --output text 
    > ~/.ssh/${KEY_MATERIAL_FILE_NAME} 
    && cat ~/.ssh/${KEY_MATERIAL_FILE_NAME}

KeyPairの存在を確認

コマンド
aws ec2 describe-key-pairs 
    --query "KeyPairs[?KeyName==`${KEY_PAIR_NAME}`]"
結果
[
    {
        "KeyName": "XXXXXXXXXXXX_firehose_jawsug_cli",
        "KeyFingerprint": "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
    }
]

秘密鍵のPermissionを変更

コマンド
chmod 600 ~/.ssh/${KEY_MATERIAL_FILE_NAME}
ls -al ~/.ssh/${KEY_MATERIAL_FILE_NAME}
結果
-rw------- 1 ec2-user ec2-user 1671 Aug  5 18:33 /home/ec2-user/.ssh/788063364413_firehose_jawsug_cli.pem

1.2. CloudFormation テンプレートの生成

テンプレートの作成

コマンド
CF_TEMPLATE_FILE_NAME="firehose_jawsug_cli.yml"
コマンド
cat << EOF > ${CF_TEMPLATE_FILE_NAME}
AWSTemplateFormatVersion: "2010-09-09"
Description: JAWS-UG CLI Kinesis Firehose Hands-on

Parameters: 
  VPCNetworkAddress: 
    Type: String
    Description: "Network Address on AWS"
    MinLength: 9
    MaxLength: 18
    # AllowedPattern: "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})"
    Default: "10.0.0.0/16"
  PublicSubnetAddr: 
    Type: String
    Description: "Network Address on AWS"
    MinLength: 9
    MaxLength: 18
    # AllowedPattern: "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})"
    Default: "10.0.0.0/24"
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName


Resources:
  S3Bucket: 
    Type: "AWS::S3::Bucket"
  IAMRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "service-role-firehose"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "firehose.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
  IAMPolicy:
    Type: "AWS::IAM::Policy"
    Properties: 
      PolicyName: "service-policy-firehose"
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Action: 
              - "s3:AbortMultipartUpload"
              - "s3:GetBucketLocation"
              - "s3:GetObject"
              - "s3:ListBucket"
              - "s3:ListBucketMultipartUploads"
              - "s3:PutObject"
            Resource:
              - !GetAtt S3Bucket.Arn
              - Fn::Join: 
                - "/"
                - 
                  - !GetAtt S3Bucket.Arn
                  - "*"
          - 
            Effect: "Allow"
            Action: 
              - "logs:PutLogEvents"
            Resource: "*"
      Roles:
        - Ref: IAMRole

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCNetworkAddress
      Tags: 
        - 
          Key: "Name"
          Value: "KinesisFirehoseClient"
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - 
          Key: "Name"
          Value: "KinesisFirehoseClient"
  AttachIGW:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
          VpcId:
              Ref: VPC
          InternetGatewayId:
              Ref: IGW
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      CidrBlock: !Ref PublicSubnetAddr
      MapPublicIpOnLaunch: true
      VpcId: 
        Ref: VPC
      Tags: 
        - 
          Key: "Name"
          Value: "Public"
  PublicRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
      Tags: 
        - 
          Key: Name
          Value: Public
  PublicDefaultRoute:
    Type: AWS::EC2::Route
    Properties: 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: 
        Ref: IGW
      RouteTableId: 
        Ref: PublicRT
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PublicSubnet
      RouteTableId:
        Ref: PublicRT
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties: 
      GroupDescription: WebServer
      SecurityGroupEgress:
        - 
          IpProtocol: "-1"
          CidrIp: "0.0.0.0/0"
      SecurityGroupIngress:
        - 
          IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"        
        - 
          IpProtocol: "tcp"
          FromPort: 22
          ToPort: 22
          CidrIp: "0.0.0.0/0"
      VpcId:
        Ref: VPC    
  InstanceRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "instance-role"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "ec2.amazonaws.com"
                - "ssm.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonKinesisFirehoseFullAccess"
  InstanceProfile: 
    Type: "AWS::IAM::InstanceProfile"
    Properties: 
      Path: "/"
      Roles: 
        - !Ref InstanceRole
  Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      KeyName: 
        Ref: KeyPairName
      ImageId: ami-6df1e514
      InstanceType: t2.micro
      SecurityGroupIds: 
        - Ref: SecurityGroup
      SubnetId:
        Ref: PublicSubnet
      IamInstanceProfile:
        Ref: InstanceProfile
      BlockDeviceMappings: 
        - DeviceName: "/dev/sdm"
          Ebs: 
            VolumeType: "gp2"
            DeleteOnTermination: "true"
            VolumeSize: "8"
      UserData:
        Fn::Base64: |
          #!/bin/bash
          cd /tmp
          sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
          sudo yum install -y aws-kinesis-agent
          sudo chkconfig aws-kinesis-agent on
          sudo service aws-kinesis-agent start
          sudo yum install -y httpd
          sudo chkconfig httpd on
          sudo service httpd start
          sudo yum install npm --enablerepo=epel -y
          sudo npm install -g jsonlint

Outputs:
  S3BucketName:
    Value:
      Ref: S3Bucket
  IAMRoleARN:
    Value: !GetAtt IAMRole.Arn
  PublicIP:
    Value: !GetAtt Instance.PublicIp
EOF

cat ${CF_TEMPLATE_FILE_NAME}

CloudFormation テンプレートの検証

コマンド
aws cloudformation validate-template 
    --template-body file://${CF_TEMPLATE_FILE_NAME}
結果
{
    "CapabilitiesReason": "The following resource(s) require capabilities: [AWS::IAM::InstanceProfile, AWS::IAM::Role]",
    "Description": "JAWS-UG CLI Kinesis Firehose Hands-on",
    "Parameters": [
        {
            "NoEcho": false,
            "ParameterKey": "KeyPairName"
        },
        {
            "DefaultValue": "10.0.0.0/16",
            "NoEcho": false,
            "Description": "Network Address on AWS",
            "ParameterKey": "VPCNetworkAddress"
        },
        {
            "DefaultValue": "10.0.0.0/24",
            "NoEcho": false,
            "Description": "Network Address on AWS",
            "ParameterKey": "PublicSubnetAddr"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

1.3. CloudFormation Stackの作成

CloudFormation Stack名の指定

コマンド
CF_STACK_NAME="firehose-jawsug-cli"

同名CloudFormation Stackの不存在を確認

コマンド
aws cloudformation describe-stacks 
    --query "Stacks[?StackName==`${CF_STACK_NAME}`]"
結果
[]

CloudFormation Stackの作成

コマンド
aws cloudformation create-stack 
    --stack-name ${CF_STACK_NAME} 
    --template-body file://${CF_TEMPLATE_FILE_NAME} 
    --capabilities "CAPABILITY_NAMED_IAM" 
    --parameters ParameterKey=KeyPairName,ParameterValue=${KEY_PAIR_NAME},UsePreviousValue=false
結果
{
    "StackId": "arn:aws:cloudformation:us-west-2:XXXXXXXXXXXX:stack/firehose-jawsug-cli/8812e540-7a0e-11e7-aac3-50a68d01a68d"
}

CloudFormation Stackの作成完了を待機

5分程度で作成が完了すると思います。

コマンド
aws cloudformation wait stack-create-complete 
    --stack-name ${CF_STACK_NAME}
結果
(返値無し)

CloudFormation Stackの存在を確認

“StackStatus”が”CREATE_COMPLETE”になっていることを確認します。

コマンド
aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME}
結果
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-west-2:XXXXXXXXXXXX:stack/firehose-jawsug-cli/4043bde0-7a16-11e7-8701-50a686be73ba",
            "Description": "JAWS-UG CLI Kinesis Firehose Hands-on",
            "Parameters": [
                {
                    "ParameterValue": "XXXXXXXXXXXX_firehose_jawsug_cli",
                    "ParameterKey": "KeyPairName"
                },
                {
                    "ParameterValue": "10.0.0.0/16",
                    "ParameterKey": "VPCNetworkAddress"
                },
                {
                    "ParameterValue": "10.0.0.0/24",
                    "ParameterKey": "PublicSubnetAddr"
                }
            ],
            "Tags": [],
            "Outputs": [
                {
                    "OutputKey": "PublicIP",
                    "OutputValue": "54.191.102.113"
                },
                {
                    "OutputKey": "IAMRoleARN",
                    "OutputValue": "arn:aws:iam::XXXXXXXXXXXX:role/service-role-firehose"
                },
                {
                    "OutputKey": "S3BucketName",
                    "OutputValue": "firehose-jawsug-cli-s3bucket-134czh3hcofqz"
                }
            ],
            "CreationTime": "2017-08-05T19:42:44.440Z",
            "Capabilities": [
                "CAPABILITY_NAMED_IAM"
            ],
            "StackName": "firehose-jawsug-cli",
            "NotificationARNs": [],
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false
        }
    ]
}

1.4. パラメータの確認

以降の手順で必要になるパラメータを抽出します。

  • IAMロールARN
  • S3バケット名
  • パブリックIPアドレス
コマンド
OUTPUTKEY_ROLE_ARN="IAMRoleARN"
OUTPUTKEY_BUCKET_NAME="S3BucketName"
OUTPUTKEY_PUBLIC_IP_ADDRESS="PublicIP"
コマンド
ROLE_ARN=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_ROLE_ARN}`].OutputValue[]" 
    --output text) 
    && echo ${ROLE_ARN}
結果
arn:aws:iam::XXXXXXXXXXXX:role/service-role-firehose
コマンド
BUCKET_NAME=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_BUCKET_NAME}`].OutputValue[]" 
    --output text) 
    && echo ${BUCKET_NAME}
結果
firehose-jawsug-cli-s3bucket-134czh3hcofqz
コマンド
PUBLIC_IP_ADDRESS=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_PUBLIC_IP_ADDRESS}`].OutputValue[]" 
    --output text) 
    && echo ${PUBLIC_IP_ADDRESS}
結果
54.191.***.***

動作確認

パブリックIPアドレスにブラウザでアクセスします。

以上

続きを読む

AutoScaleを設定し、lambdaでAMIとAutoScaleの設定を自動更新する

今回はAuto Scaleの設定のまとめです。

・概要

負荷分散のためにAutoScaleを検討することはよくあることです。

ゲームのイベントなどでは負荷がかかる時間帯が決まっているので、常時起動しているサーバーを減らしAutoScaleのスケージュールで設定することで費用を抑えることが出来ます。

※AutoScaleは起動に時間がかかるので、スケーリングポリシーでは突発的な負荷に対応できないのでスケジュールを使っています。

・問題点

AutoScaleを設定した場合、設定しているAMIのassetやsourceが起動中のインスタンスと違うといった事が起こります。

そういった場合にAMIを更新し、自動的AutoScaleに割り当てる設定を行います。

・AutoScaleグループの作成

今回の設定は元々あるAutoScaleグループの設定を置き換えるので先にAutoScaleグループを作成しておきます。

・AWSコンソールの[AUTO SCALING]→[起動設定]→[起動の作成]

※ここの起動設定は最終的に使わないので適当でOKです

・AWSコンソールの[AUTO SCALING]→[AUTO SCALINGグループ]→[AUTO SCALINGグループの作成]

上記の起動設定を選択、下記を設定

ネットワーク
グループ名
ロードバランシング

※今回はスケジュールなのでスケーリングポリシーは設定しない
※通知を使用するとauto scaleの情報をlambda等に引き渡して色々出来ます。
※タグを使いauto scaleで作成されたインスタンスに適用できます。

※ここで作成したAUTO SCALINGグループの名前を控えておきます。

lambdaを設定

・[Blank Function]→リストから[CloudWatch Events]を選択
・ルール:新規のルール
・ルール名:任意
・ルール説明:任意
・ルールタイプ:スケジュール
・スケジュール式:cron(0 * * * ? *)
※cronの設定と同じ、上記は1時間毎
・トリガーの有効化にチェック

・名前:任意
・説明:任意
・ランタイム:python

・ロール:[カスタムロールの作成]を選択、IAMに下記をアタッチし作成、再度[既存のロールを選択]で作成したIAMを選択

・コード:下記を貼り付けて***の部分を自分の環境に置き換える

import boto3
import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.client('ec2')
autoscaling = boto3.client('autoscaling')

instance = "***" #コピー元のec2インスタンスID(例:i-22a9edc2ted27d2a1)
device_name = "***" #コピー元のec2のブロックデバイス(例:/dev/sda1)
image_prefix = "***" #amiの名前の識別子(任意※他のAMIで使っていない文字)
launch_prefix = "***" #auto scaleの起動設定の名前の識別子(任意※他の起動設定で使っていない文字)
ec2_volume_size = *** #ebsのボリュームサイズ(単位:GB)
ec2_volume_type = "***" #ebsのボリュームタイプ(例:gp2)
secrity_group = [***] #ec2インスタンスのセキュリティグループ
ec2_instance_type = "***", #ec2インスタンスタイプ(例:c4.2xlarge)
ec2_key_name = "***", #ec2インスタンスのキーペア名
auto_scaling_group_name ="***",#作成したAutoScaleGroup名


def lambda_handler(event, context):
    try:
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S_') + instance
        logger.warning("create ami:%s" % (dstr))
        new_ami = ec2.create_image(Name=image_prefix + dstr ,InstanceId=instance,NoReboot=True,DryRun=False)
        recv = ec2.describe_images(Owners=['self'])
        list=[]
        target = image_prefix
        for img in recv['Images']:
            if target in img['ImageLocation']:
                list.append(img)
        s_list = sorted(list,key=lambda h: h['CreationDate'])
        if len(s_list) > 0:
            del_target = s_list[0]
            logger.info("delete ami:%s" % (del_target['Name']))
            ec2.deregister_image(ImageId=del_target['ImageId'],DryRun= False)
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S')
        logger.info("create auto scale launch config:%s" % (dstr))
        device = {}
        device['DeviceName'] = device_name
        ebs = {}
        ebs['VolumeSize'] = ec2_volume_size
        ebs['VolumeType'] = ec2_volume_type
        ebs['DeleteOnTermination'] = True
        device['Ebs'] = ebs
        device_mapping = [device]
        launch_name = launch_prefix + dstr

        res = autoscaling.create_launch_configuration(
            LaunchConfigurationName = launch_name,
            ImageId = new_ami['ImageId'],
            InstanceType = ec2_instance_type,
            SecurityGroups = secrity_group, 
            KeyName = ec2_key_name,
            BlockDeviceMappings = device_mapping,
            AssociatePublicIpAddress = True
        );
        logger.info("update auto scale group name:%s" % (launch_name))
        res = autoscaling.update_auto_scaling_group(
            AutoScalingGroupName=auto_scaling_group_name 
            LaunchConfigurationName = launch_name
        );  
        target = launch_prefix
        recv = autoscaling.describe_launch_configurations()
        list=[]
        for launch in recv['LaunchConfigurations']:
            if target in launch['LaunchConfigurationName']:
                list.append(launch)
        s_list = sorted(list,key=lambda h: h['CreatedTime'])
        if len(s_list) > 2:
            del_target = s_list[0]
            logger.info("delete launch config:%s" % (del_target['LaunchConfigurationName']))
            autoscaling.delete_launch_configuration(
                LaunchConfigurationName=del_target['LaunchConfigurationName'],
            )
        logger.info("end ami update")

    except ClientError as e:
        logger.error( e )
    return 'end'

・auto scaleのスケジュールを登録する

負荷の時間に合わせて自由に

・注意事項

※最初はelbのhealthチェックだけじゃなくwebサーバーのログやawsコンソールで正常な起動を確認すること

※デプロイがAMIの更新と被らないようにすること

続きを読む

TerraformでAWSの構成を読み取るぞ(Terraform import)

前書き

Terraform import については、Qiitaでお2人も記載してくれていますので、参照ください。

tyasuさん:terraform importの使い方メモ
kt_higaさん:terraform importを試してみた

同じスタンスで記載してもしょうがないので、勉強している最中に違う観点でいくつかTerraform import について記載します。

環境

Windows にTerraform(2017/7/18時点:v0.9.11)をインストール
AWSには、EC2,ELB,RDS,S3 など以前手作業で作った環境があります。

準備

まずは、最低限Terraformの利用には、access_key, secret_keyが必要です。
こちらを参照してキーを確認しましょう。
Windowsの作業フォルダにファイルを作成して、確認したキーを記載します。
※ ファイル名は、<任意の名前>.tf としてください。
ここでは、c:work フォルダを作業フォルダとします。

provider "aws" {                                            ・・・・決め打ちです。
    region      = "ap-northeast-1"                          ・・・・リージョンを記載(ap-notheast-1:日本リージョン)
    access_key  = "XXXXXXXXXXXXXXXXX"                       ・・・・access_keyを登録
    secret_key  = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" ・・・・secret_keyを登録
}

※ ほとんどが決め打ちです。
   今回は、AWS環境のため"aws" となります。

インポート!

叩くコマンドは、簡単!

AWSのマネジメントコンソールから、インスタンスID(赤枠部分)を確認して、コマンドのオプションに指定します。
C:> cd work
C:work> terraform import aws_instance.<任意の名前> <インスタンスID>

題.png

ここでは、こんな感じでコマンドを叩いてみました。
もともとの環境では、タグ名を「batchserver01」という名前を付けていましたが、ここではわざと「batch01」に変更しています。
タグ名と合わせても勿論OKです。

import02.png

無事、terraform.tfstate ファイルが作成されました。

※ <> 部分は、修正しています。

{
    "version": 3,
    "terraform_version": "0.9.11",
    "serial": 0,
    "lineage": "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_instance.batch01": {
                    "type": "aws_instance",
                    "depends_on": [],
                    "primary": {
                        "id": "<INSTANCE_ID>",
                        "attributes": {
                            "ami": "<AMI_ID>",
                            "associate_public_ip_address": "false",
                            "availability_zone": "<AvailabilityZone_ID>",
                            "disable_api_termination": "false",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "<INSTANCE ID>",
                            "instance_state": "running",
                            "instance_type": "<INSTANCE_TYPE>",
                            "ipv6_addresses.#": "0",
                            "key_name": "<KEY_NAME>",
                            "monitoring": "false",
                            "network_interface.#": "0",
                            "network_interface_id": "<NW_INTERFACE_ID>",
                            "primary_network_interface_id": "<NW_INTERFACE_ID>",
                            "private_dns": "<DNS_NAME>",
                            "private_ip": "<PRIVATE_IP>",
                            "public_dns": "",
                            "public_ip": "",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "false",
                            "root_block_device.0.iops": "100",
                            "root_block_device.0.volume_size": "10",
                            "root_block_device.0.volume_type": "gp2",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "<SUBNET_ID>",
                            "tags.%": "1",
                            "tags.Name": "batchserver01",
                            "tenancy": "default",
                            "volume_tags.%": "0",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2148762998": "<SECURITY_GROUP_ID>"
                        },
                        "meta": {
                            "schema_version": "1"
                        },
                        "tainted": false
                    },
                    "deposed": [],
                    "provider": "aws"
                }
            },
            "depends_on": []
        }
    ]
}

ここから本題

★ EC2を1台しかimportしていないので、もう1台importしたらどうなるんだ?

import01.png

別に問題なくコマンド終了。
先ほど作成されたterraform.tfstate は、terraform.tfstate.backup にCOPYされ、terraform.tfstate に2台目が追記されました。
(省略)

★ S3をimport しても大丈夫だよね?

C:work> terraform import aws_s3_bucket.<任意の名前> <バケット名>

わざとバケット名を間違えて見ました。

import03.png

★ ALBをimport !!

C:work> terraform import aws_alb.<任意の名前> <ARN>

import04.png

結果、terraform.tfstate ファイルにはどんどんimport すればしただけ、追記されていきました。
どんどん下に追記されていくわけではなく、DataSource(aws_s3_bucket, aws_instance, aws_albなど)の名前で順に並んでいくようです。

参考サイト

対応しているDataSource をはじめ、オプション確認にはやっぱりここは外せません。

本家HashiCorp:AWS Provider

続きを読む

EC2スポットインスタンスでとても遅いマシンがあった時の話

がっつりハマったので備忘録として残します。

状況

  • AWS Lambdaからboto3を利用してスポットインスタンスを起動する仕組みを作成
  • ユーザーデータにシェルスクリプトを渡してサービスを起動
  • 時々ユーザーデータの処理がとても遅いマシンがいる

原因(と思われるもの)

EBSボリュームタイプが旧世代のstandardだった。

解決策

ボリュームタイプをgp2に変更

結果

今のところ問題は起きていない。

ハマりどころ

  • boto3でスポットリクエストを作成する際、ボリュームタイプを指定しないとデフォルトでstandardになる。

気付かなかったー

続きを読む

AWSCLIを使用してアンマウントせずにEBSボリュームを変更する

目的

  • EBSボリュームを活性変更する

環境

  • aws-cli/1.11.111以上で使用可能

    • aws-cli/1.11.112 Python/2.7.13 Darwin/16.5.0 botocore/1.5.75

      • $ pip install --upgrade --user awscliでupdate
SYNOPSIS
            modify-volume
          [--dry-run | --no-dry-run]
          --volume-id <value>
          [--size <value>]
          [--volume-type <value>]
          [--iops <value>]
          [--cli-input-json <value>]
          [--generate-cli-skeleton <value>]

設定

  • 100GB => 150GBに変更
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       99G   86G   14G  87% /
(snip)
$ aws ec2 modify-volume --volume-id <volume-id> --size 150 --volume-type gp2
  • 確認
$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  150G  0 disk
└─xvda1 202:1    0  100G  0 part /
  • パーティション拡張
$ sudo growpart /dev/xvda 1
CHANGED: disk=/dev/xvda partition=1: start=4096 old: size=209711070,end=209715166 new: size=314568670,end=314572766
  • ファイルシステム拡張
$ sudo resize2fs /dev/xvda1
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/xvda1 is mounted on /; on-line resizing required
old_desc_blocks = 7, new_desc_blocks = 10
The filesystem on /dev/xvda1 is now 39321083 (4k) blocks long.
  • 確認
$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  150G  0 disk
└─xvda1 202:1    0  150G  0 part /
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1      148G   86G   63G  58% /
(snip)

所感

  • 50GBの増設で約50分ほど必要でした。1TBだと6時間くらいだそうです。
    そもそも、容量追加なんてのは要件として緊急性がないので、時間がかかっても平気ですけど、商用環境以外ならumountした方がいいですね。

続きを読む

Terraformを利用して、AWS EC2を作成してみた

概要

Terraformを利用してAWSのEC2を作成してみました。
terraformを実行するのはIAM Roleで権限を付与したインスタンスからになります。

ドキュメントを見るといろいろと出来そうですね。
https://www.terraform.io/docs/

コードは下記になります。
同じ階層のディレクトリに変数の設定を入れてあげてください。

ec2_dev.tf

provider "aws" {
  region = "${var.region}"
}

resource "aws_instance" "dev" {
    ami = "${var.ami_id}"
    instance_type = "${var.instance_type}"
    key_name = "${var.key_name}"
    iam_instance_profile = "${var.iam_role}"
    vpc_security_group_ids = [
        "${var.sg_id}"
    ]
    subnet_id = "${var.subnet_id}"
    associate_public_ip_address = "true"
    root_block_device {
        volume_type = "gp2"
        volume_size = "8"
        delete_on_termination = "true"
    }
    tags {
        Name = "${var.tag_name}"
        env = "${var.tag_env}"
    }
}

https://github.com/handa3/study/blob/master/terraform/ec2/ec2_dev.tf

続きを読む

EC2でインスタンスストアを使う

i3などのストレージ最適化インスタンスには,NVMe SSDなどの一時ボリュームがついています.

インスタンスストアは、インスタンス用のブロックレベルの一時ストレージを提供します。このストレージは、ホストコンピュータに物理的にアタッチされたディスク上にあります。インスタンスストアは、頻繁に変更される情報 (バッファ、キャッシュ、スクラッチデータ、その他の一時コンテンツなど) の一時ストレージに最適です。また、インスタンスのフリート全体でレプリケートされるデータ (負荷分散されたウェブサーバープールなど) にも適しています。

from http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/InstanceStorage.html

i3.largeにインスタンスストアボリュームをマウントする

インスタンスストアボリュームは,起動時にはマウントされていない.
従って,以下のコマンドでフォーマットとマウントをする必要がある.

$ sudo mkfs.ext4 -E nodiscard /dev/nvme0n1
$ sudo mkdir /mnt/ssd
$ sudo mount -o discard /dev/nvme0n1 /mnt/ssd

簡易的な読み出し性能の測定

m4.large / EBS gp2(300 IOPS, 450Mbps bandwidth)

  • 71.26 MB/secc
$ sudo hdparm -t /dev/xvda1

/dev/xvda1:
 Timing buffered disk reads: 214 MB in  3.00 seconds =  71.26 MB/sec

i3.large / NVMe SSD

  • 580.10 MB/sec
$ sudo hdparm -t /dev/nvme0n1

/dev/nvme0n1:
 Timing buffered disk reads: 1906 MB in  3.29 seconds = 580.10 MB/sec

EBSとNVMe SSDでは,当然ながらこれだけの大差がつく.

続きを読む

EC2 Systems Manager で Ansibleを実行する【cloudpack大阪ブログ】

cloudpack大阪の佐々木です。
EC2 Systems Manager で Ansibleが直接実行できるらしいのでやってみました。
https://aws.amazon.com/jp/blogs/mt/running-ansible-playbooks-using-ec2-systems-manager-run-command-and-state-manager/

2017/05/26時点では、東京リージョンで AWS-RunAnsiblePlaybook のドキュメントが見つからないので、対応していないのかと思います。今回は、us-east-1で実行しています。

EC2の作成

OSはAmazonLinux(amzn-ami-hvm-2017.03.0.20170417-x86_64-gp2 (ami-c58c1dd3))でやってみます。
SSMが実行できるIAM Roleを適用します。
起動後にSSMエージェントと、ansibleのインストールが必要ですが、どうせならログインせずにやりたいので、UserDataをこんな感じにしときます。

#!/bin/sh
cd /tmp
curl https://amazon-ssm-$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//g').s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
yum install -y amazon-ssm-agent.rpm
/usr/bin/pip install ansible

インストールに成功していれば、マネージドインスタンスの一覧に表示されているかと思います。

Kobito.PjHL2B.png

関連付けの作成

一覧から該当の実行するインスタンスをチェックし、関連付けの作成 をクリックします。

ドキュメントを選択 から AWS-RunAnsiblePlaybook を選択します。
Kobito.6Ll3yi.png

インスタンス選択し、パラメーターのところのにAnsibleの設定をします。
Playbook に下記のような実行したいAnsibleのPlaybookを入力します。

- hosts: all
  become: true

  tasks:
  - name: yum install for Apache Web Server
    yum: name=httpd24

  - name: Start and Enable to Apache
    service: name=httpd state=started enabled=yes

ログを残す場合は S3 への書き込み にチェックを入れ、バケット情報を入力します。
Kobito.Ujt33a.png

Playbookurlhttps://〜s3://〜 でyamlファイルを指定して、外部のPlaybookを読み込むこともできます。

関連付けの作成 をクリックします。

実行

しばらくすると、自動的に実行されます。ステータスが 成功 になっていれば適用されていると思います。

Kobito.T8jToh.png

S3にログを保存しているとこんなログが残ります。

ansible 2.3.0.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Sep  1 2016, 22:14:00) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [yum install for Apache Web Server] ***************************************
changed: [localhost]

TASK [Start and Enable to Apache] **********************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0   

まとめ

今までもUserDataやRunCommandで実行していましたが、yamlを書くだけでできるので、簡単に利用できるようになっています。
ただ、外部から取り込む場合、1ファイルで完結するPlaybookしか読み込めないようなので、Role等ディレクトリを分けて管理している場合は使えないのではないのかな・・・。
そうなると簡単なものしか使えないような気がしますので、そのあたりが改善されればって感じです。

続きを読む