【大阪 / 横浜 / 徳島】インフラ / サーバーサイドエンジニア募集中!

【大阪 / 横浜 / 徳島】インフラ / サーバーサイドエンジニア募集中!

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【CentOS 後継】AlmaLinux OS サーバー構築・移行サービス

【CentOS 後継】AlmaLinux OS サーバー構築・移行サービス

【WordPress 専用】クラウドサーバー『ウェブスピード』

【WordPress 専用】クラウドサーバー『ウェブスピード』

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

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

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

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

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

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

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

Lambda関数の作成

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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という名前で用意します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 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になりますね。

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

1
$ sis deploy --profile [AWS Profile Name]

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

最後に

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

この記事がお役に立てば【 いいね 】のご協力をお願いいたします!
1
読み込み中...
1 票, 平均: 1.00 / 11
2,487
X facebook はてなブックマーク pocket
【2026.6.30 Amazon Linux 2 サポート終了】Amazon Linux サーバー移行ソリューション

【2026.6.30 Amazon Linux 2 サポート終了】Amazon Linux サーバー移行ソリューション

この記事をかいた人

About the author

寺岡佑樹

2016年ビヨンド入社、現在6年目のインフラエンジニア
MSPの中の人として障害対応時のトラブルシューティングを行いながら
AWSなどのパブリッククラウドを用いたインフラの設計/構築も行っている。
最近はDockerやKubernetesなどのコンテナ基盤の構築や
運用自動化の一環としてTerraformやPackerなどのHashicorpツールを扱うことが多く
外部の勉強会やセミナーで登壇するEvangelistの役割も担っている。

・GitHub
https://github.com/nezumisannn

・登壇経歴
https://github.com/nezumisannn/my-profile

・発表資料(SpeakerDeck)
https://speakerdeck.com/nezumisannn

・所有資格
AWS Certified Solutions Architect - Associate
Google Cloud Professional Cloud Architect