[Osaka/Yokohama] Looking for infrastructure/server side engineers!

[Osaka/Yokohama] Looking for infrastructure/server side engineers!

[Deployed by over 500 companies] AWS construction, operation, maintenance, and monitoring services

[Deployed by over 500 companies] AWS construction, operation, maintenance, and monitoring services

[Successor to CentOS] AlmaLinux OS server construction/migration service

[Successor to CentOS] AlmaLinux OS server construction/migration service

[For WordPress only] Cloud server “Web Speed”

[For WordPress only] Cloud server “Web Speed”

[Cheap] Website security automatic diagnosis “Quick Scanner”

[Cheap] Website security automatic diagnosis “Quick Scanner”

[Reservation system development] EDISONE customization development service

[Reservation system development] EDISONE customization development service

[Registration of 100 URLs is 0 yen] Website monitoring service “Appmill”

[Registration of 100 URLs is 0 yen] Website monitoring service “Appmill”

[Compatible with over 200 countries] Global eSIM “Beyond SIM”

[Compatible with over 200 countries] Global eSIM “Beyond SIM”

[If you are traveling, business trip, or stationed in China] Chinese SIM service “Choco SIM”

[If you are traveling, business trip, or stationed in China] Chinese SIM service “Choco SIM”

[Global exclusive service] Beyond's MSP in North America and China

[Global exclusive service] Beyond's MSP in North America and China

[YouTube] Beyond official channel “Biyomaru Channel”

[YouTube] Beyond official channel “Biyomaru Channel”

定期的にEC2インスタンスを削除するLambda関数をデプロイした話

インフラエンジニアの寺岡です。
今回は社内向けにLambda関数をデプロイした話をまとめてみたいと思います。

弊社では運用でお預かりしているお客様のサーバが稼働しているAWSアカウント以外にも
社内のエンジニアのメンバーが技術的な検証をするためのアカウントも管理しています。

このアカウント、ログイン周りのセキュリティ設定(IAMユーザー・ロール/MFA認証)はルールを定めて設定していますが
アカウント内での操作は権限を持っているメンバーであれば特に制約なく自由に扱えるようにしています。
検証用なのにあれこれルールが多すぎると扱いにくくなってしまいますからね。

検証の過程で最も多く作成されるリソースは弊社の場合はEC2インスタンスですが
自由に作成できる反面、停止忘れや削除忘れが割と多発している状況でした。

当然多発するとその分無駄なコストがかかってしまいます。
使い終わったら必ず停止か削除するという前提はありますが
忘れてしまった場合の予防線としてLambdaによって定期的にEC2インスタンスを自動削除する仕組みを導入しました。

ということで何をやったのかを下記にまとめます。

Lambda関数の作成

Pythonで書きました。
個人的にGolangの方が詳しいのでそっちで書けばよかった感。

import json
import boto3
import os
import requests

def terminate(event, context):
    
    EXCLUDE_TERMINATE_TAG = os.environ['EXCLUDE_TERMINATE_TAG']
    CHATWORK_API_KEY = os.environ['CHATWORK_API_KEY']
    CHATWORK_ROOM_ID = os.environ['CHATWORK_ROOM_ID']
    CHATWORK_ENDPOINT = os.environ['CHATWORK_ENDPOINT']

    ec2 = boto3.client('ec2')

    without_tag = ec2.describe_instances()
    with_tag = ec2.describe_instances(
        Filters=[{ 'Name': 'tag:' + EXCLUDE_TERMINATE_TAG, 'Values': ['true'] }]
    )
    
    without_tag_set = set(ec2['InstanceId'] for resId in without_tag['Reservations'] for ec2 in resId['Instances'])
    with_tag_set = set(ec2['InstanceId'] for resId in with_tag['Reservations'] for ec2 in resId['Instances'])
 
    target_instances = without_tag_set - with_tag_set
    list_target_instances = list(target_instances)
    
    if len(list_target_instances) != 0:
        terminateInstances = ec2.terminate_instances(
            InstanceIds=list_target_instances
        )
    
    notify_instances = ''
    
    if len(with_tag['Reservations']) != 0:
        for reservation in with_tag['Reservations']:
            for instance in reservation['Instances']:
                if len(instance['Tags']) != 0:
                    instance_name = ''
                    for tag in instance['Tags']:
                        if tag['Key'] == 'Name':
                            instance_name = tag['Value']
                
                instance_state_enc = json.dumps(instance['State'])
                instance_state_dec = json.loads(instance_state_enc)
                
                if instance_name != '':
                    notify_instances += instance['InstanceId'] + ' -> ' + instance_name + '(' + instance_state_dec['Name'] + ')'+ '\n'
                else:
                    notify_instances += instance['InstanceId'] + ' -> ' + 'None' + '(' + instance_state_dec['Name'] + ')'+ '\n'
    
        message = '[To:350118][To:1786285][info][title][Beyond POC] 退役除外されたインスタンスがあるよ(devil)[/title]' + notify_instances + '[/info]'
        PostChatwork(CHATWORK_ENDPOINT,CHATWORK_API_KEY,CHATWORK_ROOM_ID,message)

    response = {
        "TerminateInstances": list_target_instances
    }
    
    print (response)
    return response

def PostChatwork(ENDPOINT,API_KEY,ROOM_ID,MESSAGE):
    post_message_url = '{}/rooms/{}/messages'.format(ENDPOINT, ROOM_ID)
    headers = { 'X-ChatWorkToken': API_KEY }
    params = { 'body': MESSAGE }
    
    resp = requests.post(post_message_url,
                     headers=headers,
                     params=params)

コードを見ていただければわかるかなと思いますが

  • 除外タグが付いていないインスタンスを削除する
  • 削除対象から除外されたインスタンスをチャットワークに通知する

という処理をやってくれます。
問答無用で消されてしまうと困る場合があるので
その場合は除外タグ「EXCLUDE_TERMINATE:true」を付与してね
というルールで社内周知しています。

Serverless Frameworkの設定

書いたコードをどのようにLambdaにデプロイするのかというお話。

Serverless Frameworkというツールを利用しています。
https://serverless.com

これはLambdaなどのサーバレス系のサービスに対して
所定の設定ファイルに従って関数をデプロイしてくれるCLIツールになっていて
Lambdaの場合はCloudFormationと連携してデプロイを実行してくれます。

設定ファイルはserverless.ymlという名前で用意します。

# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: beyond-poc

plugins:
  - serverless-prune-plugin

provider:
  name: aws
  runtime: python3.8
  profile: ${opt:profile, ''}
  region: ap-northeast-1
  role: [IAM Role Name]
  environment:
    EXCLUDE_TERMINATE_TAG: EXCLUDE_TERMINATE
    CHATWORK_API_KEY: [ChatWark API KEY]
    CHATWORK_ROOM_ID: [ChatWark ROOM ID]
    CHATWORK_ENDPOINT: https://api.chatwork.com/v2
    timeout: 10

# my custom env
custom:
  prune:
    automatic: true 
    number: 5

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add packaging information here
#package:
#  include:
#    - include-me.py
#    - include-me-dir/**
#  exclude:
#    - exclude-me.py
#    - exclude-me-dir/**

functions:
  terminate-instance:
    handler: handler.terminate
    events:
      - schedule: cron(0 15 ? * * *)
    timeout: 120
    memorySize: 128
    reservedConcurrency: 1

environmentで環境変数として除外タグとChatWorkの認証情報の設定をしています。
functionsの部分で文字通り関数の設定をしています。
eventsの部分でスケジュールの設定をしているのですが
この設定だとCloudWatchEventsと連携して毎日15:00(UTC)に定期実行してくれます。
JSTだと深夜0:00になりますね。

この設定ファイルがあるディレクトリにデプロイするコードも一緒に保存して

$ sis deploy --profile [AWS Profile Name]

とすればコマンドラインからServerless Frameworkが自動作成した
CloudFormationのスタックを実行してデプロイすることができます。

最後に

ちゃっかりブログのネタにしていますが
社内で一番消し忘れが多いのは実は僕ですごめんなさい。。。

この記事がお役に立てば【 いいね 】のご協力をお願いいたします!
1
読み込み中...
1 票, 平均: 1.00 / 11
2,245
X facebook はてなブックマーク pocket
[2024.6.30 CentOS support ended] CentOS server migration solution

[2024.6.30 CentOS support ended] CentOS server migration solution

[2025.6.30 Amazon Linux 2 support ended] Amazon Linux server migration solution

[2025.6.30 Amazon Linux 2 support ended] Amazon Linux server migration solution

[Osaka/Yokohama] Actively recruiting infrastructure engineers and server side engineers!

[Osaka/Yokohama] Actively recruiting infrastructure engineers and server side engineers!

The person who wrote this article

About the author

Yuki Teraoka

Joined Beyond in 2016 and is currently in his 6th year as an Infrastructure Engineer
MSP, where he troubleshoots failures while
also designing and building infrastructure using public clouds such as AWS.
Recently, I
have been working with Hashicorp tools such as Terraform and Packer as part of building container infrastructure such as Docker and Kubernetes and automating operations, and I
also play the role of an evangelist who speaks at external study groups and seminars.

・GitHub
https://github.com/nezumisannn

・Presentation history
https://github.com/nezumisannn/my-profile

・Presentation materials (SpeakerDeck)
https://speakerdeck.com/nezumisannn

・Certification:
AWS Certified Solutions Architect - Associate
Google Cloud Professional Cloud Architect