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の更新と被らないようにすること
githubのTrendingになっているchromelessをWindowsで試す
windowsでchromelessのexsampleを試すまで
下記を参考にnodistをインストール http://qiita.com/satoyan419/items/56e0b5f35912b9374305
nodist + v8.0.0 //node v 6.0.0とかだとasync functionが使えないのでv7.0.0以上にすること nodist v8.0.0 node -v //確認 npm install chromeless cd C:\Users\ryo-sato\AppData\Local\Google\Chrome SxS\Application //chromeのパス chrome.exe --romote-debugging-port=9222 --disable-gpu --headless
・C:直下にtmpフォルダを作る //スクリーンショット用 ・C:\nodeのフォルダを作る //スクリプトを置く場所 ・C:\node\testにexsampleスクリプト記載
cd C:\node node test
これでC:\tmpに下記の感じでchromeのスクリーンショットが保存される。
追記 なんていうことだ・・・、本家がpuppeteerなるものを・・・
ソシャゲのボックスガチャの当たるまでにかかる金額の期待値をプログラム化してみた
ソシャゲのボックスガチャでどれくらいお金賭けたら出るんだろうという興味でやってみました
期待値
参考サイト
【パワプロアプリ】BOXガチャのリセットタイミング:ぽて子のゲーム研究所 - ブロマガ
上記をプログラム化する
function math(){ var lottery_count = 100; //ボックス内全体数 var lottery_once_bet_amount = 300; //1回に掛かる金額 var lottery_hit_count = 2; //当たり数 var c = 0; var k = t(lottery_count,lottery_hit_count); for(var x = 1; x <= (lottery_count-(lottery_hit_count-1)); x++){ c += (1/k)*(x)*(t(lottery_count-x,lottery_hit_count-1)); } var ev = c * lottery_once_bet_amount; //当たるまでいくらかければいいかの期待値 } function t(n,c){ var l = 1; var m = 1; for(var x = 0 ; x < c; x++){ l = l*(n-x); m = m*(x+1); } return l/m; }
100個ある中で3個のあたりを狙い1回300円かかる場合=25.25300=7575円という期待値 100個ある中で1個のあたりを狙い1回300円かかる場合=50.5300=15150円という期待値
cronでdocker-composeが動かなかった件
問題
/var/www/dockerにdocker-compose.ymlが存在していてコンソール上でそのままのdocker-compose startは動くがcrontabでは動かない
例えば
cd /var/www/docker && docker-compose start
とか
cd /var/www/docker; docker-compose stop cd /var/www/docker; docker-compose start
で試してもcronのlogには出てくるのだが走ってる様子がない
解決策
直接にdocker-composeの/usr/local/bin/docker-composeスクリプトを指定する
cd /var/www/docker; /usr/local/bin/docker-compose start
参照:
Crontab can’t execute docker-compose commands · Issue #2293 · docker/compose · GitHub
外部から社内のパソコンへWake on lanを実装したときのまとめ(サーバー側)
外部からリモートデスクトップするためのwake on lanを実装したときのサーバー側のまとめ
前提資料
http://www.atmarkit.co.jp/ait/articles/0602/25/news014_2.html
必要事項
・外部から見れるサイトを社内にサーバーを立てれること
※Web UIとかログイン認証、sslは別途でやってね
概要
前提として下記の設定が必要になります。(マシンによって異なる)
今回はphpでWake on lanを実施する部分のみ
wakeonlanにはMAC Addressが必要だが、Ip Addressしかわからない人がいるのでコマンドを使って探す処理を実装
//ip address → mac address if(isset($ip_address && $ip_address != ""){ $mac = ""; $pcs = shell_exec("nmap -sP {$ip_address} "); $pcs = shell_exec("arp -a"); $pcss = explode("\n",$pcs); foreach($pcss as $value){ if(strripos($value,$ip_address !== false){ if(preg_match("/([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}/",$value,$match) === 1){ $mac = $match[0]; break; } } } if($mac != ""){ $ping = " {$ip_address} → MAC address {$mac}"; }else{ $ping = "get faild"; } }
その逆
//mac address → ip address if(isset($mac_address) && $mac_address != ""){ $ip = ""; #Need to change *.*.*. (192.168 or 172.16 or ....) $pcs = shell_exec("nmap -sP 192.168.6.* "); $pcs = shell_exec("arp -a"); $pcss = explode("\n",$pcs); foreach($pcss as $value){ if(strripos($value,$mac_address) !== false){ if(preg_match("/\(([a-zA-Z0-9.]+)\)/",$value,$match) === 1){ $ip = substr(substr($match[0],1),0,-1); break; } } } if($ip != ""){ $ping = "MAC address {$mac_address} → IP address {$ip}"; }else{ $ping = "get failed"; } }
また上記で参照できないことがあるので下記をcronで走らせる *.*.*.は192.168.1や192.168.2、172.16.3等に環境に応じて変える
for a in `seq 1 254`; do ping -c 1 -w 0.5 *.*.*.$a > /dev/null && arp -a *.*.*.$a | grep ether; done
MAC Addressがわかったら下記でバッチに引き数を渡す
※batchの実行ユーザーに気を付ける事、この場合はapacheのユーザーを使用して実行
#run user apache shell_exec("wakeonlan.sh ".$mac_address);
#!/bin/sh echo 'apache' | sudo -S ether-wake $1 echo "$1 start"
その後の起動確認は下記で実装
$res = shell_exec("ping -c 4 -W 5 ".$ip_address); $array = explode("\n",$res); if(strripos($res,'0 received') === false){ $ping = "boot success"; }else{ $ping = "boot failed"; }