EC2インスタンスにRaspberry Piで電源スイッチを付けてみた【cloudpack 大阪 BLOG】

 久しぶりにRaspberryPiを触ってみて思いついたので早速作ってみました。以前はLチカやったところで終わっていましたが、Pythonを少し覚えたので何かAPIと連携させてみようと考えていました。最近、EC2インスタンスを触ることが多くなりましたが、サーバの電源を入れるということを全てWeb上でやっているので物理スイッチでわざわざ起動、停止をやってみようと思い作ってみました。
今回やってみたのは以下のような内容です。

・RaspberryPiでスイッチを押すとEC2が起動もしくは停止
・起動中 or 停止中はLEDを点滅させる
・起動、停止が完了したらslackで自分に通知

準備

RaspberryPi B+ (OSはraspbian 今回使用したバージョンは「Linux raspberrypi 3.12.28+ 」)
ブレッドボード
LED
タクトスイッチ
抵抗 330Ω(LED用)、1kΩ(タクトスイッチ用)
ジャンパーワイヤ

AWSのアクセスキー、シークレットキーの取得しておく
SlackのAPIキーの取得しておく

モジュールのインストール

pipのインストール

apt-get install python-pip

boto3(AWS SDK FOR Python)のインストール

pip install boto3

部品の配置

下記の図のように部品を配置します。GPIO、GNDの配線は間違えないようにしてください。
f:id:cloudfish:20151117005956p:plain:w512
※タクトスイッチは1kΩ、LEDは330Ωの抵抗を配置

実際に配線すると下記のようになります。
f:id:cloudfish:20151117012549j:plain:w384

配線の確認

実際に部品が正しく配置されているか確認するため、下記のプログラムを実行して動作確認を行います。

LEDの動作確認

#! /usr/bin/env python
import RPi.GPIO as GPIO
import time

LEDPIN = 21

GPIO.setwarnings(False) # Suppress warnings

GPIO.setmode( GPIO.BCM )
GPIO.setup( LEDPIN, GPIO.OUT )

while 1:
        GPIO.output( LEDPIN, True )
        time.sleep( 1.0 )
        GPIO.output( LEDPIN, False )
        time.sleep( 1.0 )

実行すると一定間隔でLEDが点滅します。点滅しなければ部品の配置や配線が間違えてないか見直してください

タクトスイッチの動作確認

#! /usr/bin/env python

import RPi.GPIO as GPIO
import time

# define
INPUT_PIN = 4

# init
GPIO.setwarnings(False) # Suppress warnings
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(INPUT_PIN,GPIO.IN)

if __name__ == "__main__":
    Btn_Flag = False

    while True:
        print (GPIO.input(INPUT_PIN))
        time.sleep(0.2)

実行してボタンを押すとボタンが押されている間はコンソールに「1」が表示されます

EC2インスタンスの起動・停止プログラム

上記のテストプログラムが正常に実行されれば実際にボタンを押してEC2のストップ、スタートができるプログラムを作成していきましょう。
AWSのアクセスキー、シークレットキー及びSlackのAPIキーは取得したものに変更してください。また、Slackの送信先は自身のユーザー名(@ + ユーザー名)に変更してください。

#! /usr/bin/env python

import RPi.GPIO as GPIO
import time
import boto3
from boto3.session import Session
import sys
import urllib
import urllib2

# define
INPUT_PIN = 4
LEDPIN = 21

# init 
GPIO.setwarnings(False)	# Suppress warnings
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(INPUT_PIN,GPIO.IN)
GPIO.setup( LEDPIN, GPIO.OUT )

#################################################################
# Function 
#################################################################
def getResourceEC2 ():
    session = Session(aws_access_key_id='ACCESS_KEY',
                      aws_secret_access_key='SECRET_KEY',
                      region_name='ap-northeast-1')
     
    ec2 = session.resource('ec2')
    return ec2

def get_ec2_state(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)
    if(instance.state['Name']=='running'):
        return True
    else:
        return False

def start_ec2(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)
    if(instance.state['Name']=='stopped'):
        instance.start()
        # Flashing LED
        led_flag = False
        while instance.state['Name'] != 'running':
           GPIO.output( LEDPIN, not led_flag)
           led_flag = not led_flag
           instance.reload()
	   time.sleep( 1.0 )
    
        GPIO.output( LEDPIN, True)

        print('EC2 Start!')
        post_slack('EC2 Start!')
    else:
        print('EC2 already Started!')
        post_slack('EC2 already Started!')

def stop_ec2(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)

    if(instance.state['Name']=='running'):
        instance.stop()
        # Flashing LED
        led_flag = False
        while instance.state['Name'] != 'stopped':
           GPIO.output( LEDPIN, not led_flag)
           led_flag = not led_flag
           instance.reload()
	   time.sleep( 1.0 )
    
        GPIO.output( LEDPIN, False)

        print('EC2 Stop!')
        post_slack('EC2 Stop!')
    else:
        print('EC2 already Stopped!')
        post_slack('EC2 already Stopped!')

def post_slack(message):
    url = "https://slack.com/api/chat.postMessage"
    params = {'token'  :'SLACK_API_KEY',
              'channel':'@USER',
              'text'   : message
             }

    req = urllib2.Request(url)
    req.add_header('Content-Type', 'application/x-www-form-urlencoded')
    req.add_data(urllib.urlencode(params))

    res = urllib2.urlopen(req)


#################################################################
# Main Function 
#################################################################
if __name__ == "__main__":
    print('Switch Start')
    
    argvs = sys.argv
    Id = argvs[1]
    Btn_Flag = get_ec2_state(Id)
    GPIO.output( LEDPIN, Btn_Flag)

    while True:
        
        if(GPIO.input(INPUT_PIN)):
           Btn_Flag = not Btn_Flag

           if(Btn_Flag):
               start_ec2(Id)
           else:
               stop_ec2(Id)

        time.sleep(0.2)

※簡略化するため、ステータスチェックなど細かい処理は省いています。

実行

下記のコマンドで実行します。引数には起動させたいEC2のインスタンスIDをセットします。
Switch Startと表示されたら、タクトスイッチを押してみましょう。

root@raspberrypi:~# python input_switch.py [instanceid]
Switch Start

しばらくすると、RaspberryPiのLEDが点滅を始めます。

AWS Management Consoleを確認してみましょう。
おお、起動中になりましたね!
f:id:cloudfish:20151117020140p:plain

起動、停止完了後
Slackにも無事通知されました!
f:id:cloudfish:20151117020445p:plain

まとめ

今回はEC2の起動、停止を行ったため、起動後にLEDが光らないなどプログラムにバグがあった時にやり直しの時間が少しかかりました。RaspberryPiと何かを連携させる場合は、まずRaspberryPiの挙動部分と連携部分を分けて事前に動作確認したうえで、それぞれ統合を進めたほうがいいように思いました。EC2のCPU使用率などを監視してしきい値超えるとLEDで点滅表示など状態を表すこともできそうですね。
電子回路は詳しくないですが、RaspberryPiはすごく簡単に色々できるようになっているので、また何か思いついたら作ってみたいと思います。