はじめての AWS Lambda python で boto3 から ec2 を起動する
はじめての AWS Lambda で boto3 から ec2 を起動する
いまさら感ありありですが表題のことを Management Console からやってみます。
初期画面
bluprint
Get Started
で進むとたくさんのサンプルから選ぶことができますが、今回は Skip
します
Configure function
設定画面が表示されますので、さっそく Lambda Function を定義していきます。
- Name
- Lambda Function の名前。
- Description
- 説明を記載します。必須ではありません。
- Runtime
今回は python2.7 にします。 (というか java も node.js もさっぱりです。。)
Lambda function code
デプロイ方法は 3 種類あります。
- Edit Code inline
- 画面のエディタに直接記述
- Upload a .ZIP file
- zip ファイルを upload
- cli からやるときは zip でやるとよさそうです
- Upload a .ZIP from Amazon S3
- S3 にコードをアップロードしておき、その S3 URL を記載します
今回は Edit Code inline でやってみます。
test
タグが True
で stopped
なインスタンスを起動するという単純なスクリプトを用意しました。
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import boto3 ec2 = boto3.resource('ec2') def get_specified_tagged_instance_ids(target_tag): """ get target tagged instance-id """ try: instances = ec2.instances.filter( Filters=[ {'Name': 'tag:%s' % target_tag, 'Values': ['True']}, {'Name': 'instance-state-name', 'Values': ['stopped']}]) except Exception, e: print(e) sys.exit(2) if not instances is None: i = [] for instance in instances: i.append(instance.instance_id) else: print("not found target tagged instances") sys.exit(0) return i def start_instances(target_tag): """ start instances """ try: instances = get_specified_tagged_instance_ids(target_tag) except Exception, e: print(e) sys.exit(2) for instance in instances: res = ec2.instances.filter(InstanceIds=[instance]).start() status_code = res[0]["ResponseMetadata"]["HTTPStatusCode"] if status_code == 200: print("operation complete. target -> %s" % instance) else: print("operation failed. target -> %s" % instance) sys.exit(1) def lambda_handler(event, context): target_tag = "test" start_instances(target_tag) sys.exit(0)
lambda_hanlder()
を __main__
的に使うようにしてみました。
なお、lambda_hanlder()
という名前は好きなものに変えることができます。
Lambda function handler and role
- Handler
- デフォルトは
lambda_function.lambda_handler
- 例えば test.py の
hello_world()
を実行したい場合はtest.hello_world
とすることができます
- デフォルトは
- Role
- 適切な IAM Role を付与します。
Role について、今回は basic vpc から作成してみます。 デフォルトの policy は以下のようなものでした。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "ec2:CreateNetworkInterface", "ec2:DescribeNetworkInterfaces", "ec2:DetachNetworkInterface", "ec2:DeleteNetworkInterface" ], "Resource": "*" } ] }
ここに ec2:DescribeInstances
と ec2:StartInstances
を追加して保存します。
Advanced settings
- Memory
- 128MB から 1536MB まで選択できます
- Timeout
- 5min まで設定できます
- VPC
- VPC を選択すると、lambda が起動する Subnet / Secrurity Groupを指定できます
確認画面
こんな感じになりました。 Create function に進みます。
Function の定義
今回は一日一回実行することにしますので Event Source のみ使います。
Add event source から CloudWatch Events Schedule を選択します。
- Rule Name
- 名前をつけます
- Rule Description
- 説明を記載します
- Schedule Expression
- rate (*) は繰り返し実行する時に使います
- cron も書けますのでとても便利です
- cron の時間は UTC のみです
指定したら submit します。Enable now の場合、少し待つと lambda が起動します。 Monitoring タブと CloudWatch Logs で稼働状況を確認できます。
CloudWatch Logs
Monitoring タブの View logs in CloudWatch
からこの function の log stream に移動できます。
print 文の内容が表示されていますね。
awscli
一連の流れを awscli で行うとしたら以下の様な感じかと。
- コードを zip にする
- iam role を作成しておく(手抜き)
- function を作成する
- zip ファイルなので
file://
ではなく、fileb://
なことに注意
- zip ファイルなので
aws lambda create-function \ --function-name auto_start_test \ --runtime python2.7 \ --role arn:aws:iam::**********:role/lambda_basic_vpc_execution \ --handler auto_start.lambda_handler \ --zip-file fileb://auto_start.zip \ --timeout 30 \ --memory-size 128 \ --vpc-config SubnetIds=subnet-*******,SecurityGroupIds=sg-****** \ --region ap-northeast-1
- event を作成する
aws --profile lk-staging events put-rule \ --name test \ --schedule-expression "rate(5 minutes)" \ --state ENABLED { "RuleArn": "arn:aws:events:ap-northeast-1:**********:rule/test" }
ただ、function と event の関連付ける方法がわかりませんでした。。。
ご存知の方、いらっしゃれば教えて下さい!
CentOS7 on vagrant に docker をインストールして nginx で hello world するまで
CentOS7 (vagrant) に docker をインストールして nginx で hello world するまでの記録です。 CentOS7 が vagrant up されていることが前提です。
docker インストール
公式 にあったワンライナーでインストール後、起動します。
なお、このワンライナーは /etc/yum.repos.d/docker.repo
を自動で作成してくれます。
$ curl -fsSL https://get.docker.com/ | sh $ systemctl start docker.service
↓ インストール中のログ
[vagrant@localhost ~]$ curl -fsSL https://get.docker.com/ | sh + sudo -E sh -c 'sleep 3; yum -y -q install docker-engine' Delta RPMs disabled because /usr/bin/applydeltarpm not installed. warning: /var/cache/yum/x86_64/7/docker-main-repo/packages/docker-engine-selinux-1.10.0-1.el7.centos.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID 2c52609d: NOKEY docker-engine-selinux-1.10.0-1.el7.centos.noarch.rpm の公開鍵がインストールされていません Importing GPG key 0x2C52609D: Userid : "Docker Release Tool (releasedocker) <docker@docker.com>" Fingerprint: 5811 8e89 f3a9 1289 7c07 0adb f762 2157 2c52 609d From : https://yum.dockerproject.org/gpg Created symlink from /etc/systemd/system/sysinit.target.wants/lvm2-lvmpolld.socket to /usr/lib/systemd/system/lvm2-lvmpolld.socket. If you would like to use Docker as a non-root user, you should now consider adding your user to the "docker" group with something like: sudo usermod -aG docker vagrant Remember that you will have to log out and back in for this to take effect!
非 root ユーザで docker コマンドを使うにはコマンドを叩くように言われているので、ついでに実行しておきます。
$ sudo usermod -aG docker vagrant
docker のバージョンは以下のようになりました。
[vagrant@localhost docker]$ docker version Client: Version: 1.10.0 API version: 1.22 Go version: go1.5.3 Git commit: 590d5108 Built: Thu Feb 4 18:34:50 2016 OS/Arch: linux/amd64 Cannot connect to the Docker daemon. Is the docker daemon running on this host?
Dockerfile の作成
こちらを参考にしながら、適当なディレクトリを作り、Dockerfile を作成します。
[vagrant@localhost docker]$ tree . ├── Dockerfile └── index.html 0 directories, 2 files
Dockerfile
の中身はこんな感じです。
FROM centos MAINTAINER hoge hoge@hoge.hoge RUN yum install -y epel-release RUN yum install -y nginx ADD index.html /usr/share/nginx/html/ EXPOSE 80 ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]
GPGキーでWARNING出ましたが、ここでは一旦無視。
FROM
はイメージ名。ローカルになければ dockerhub から取ってくるMAINTAINER
はメンテナの名前とメールアドレスを記入RUN
はイメージ起動後に実行するコマンドADD
はファイルを指定したパスに配置EXPOSE
は Listen するポートENTRYPOINT
は docker run 時に実行するコマンド
index.html
には hello world とだけ書いておきました。
docker build
build します。
$ sudo docker build -t centos/nginx:1.0 .
-t
は image にタグを打つオプションcentos/nginx
は REPOSITORY 名:1.0
は TAG 名
- 最後の引数に Dockerfile のあるディレクトリを指定
build が成功すると image ができあがります。
[vagrant@localhost docker]$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos/nginx 1.1 80d766bb9338 About an hour ago 356 MB centos/nginx 1.0 d1345ae2d9a6 9 hours ago 356 MB centos latest 61b442687d68 6 weeks ago 196.6 MB hello-world latest 690ed74de00f 3 months ago 960 B
docker 実行
build したイメージを実行します。
$ sudo docker run -d -p 80:80 centos/nginx:1.1 753d92d2bf360a529e881efaee564f86840e8d2d649f20825a7132093b45683e
-d
は docker をバックグラウンド実行-p
はポートフォワード設定。ローカル側:docker側
の順番- 実行後の標準出力はコンテナID
sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 753d92d2bf36 centos/nginx:1.1 "/usr/sbin/nginx -g '" 3 minutes ago Up 3 minutes 0.0.0.0:80->80/tcp determined_tesla
コンテナの停止、削除はこのコンテナIDを利用します。
コンテナの中に入ってシェル操作するには以下のオプションで docker run
します。
$ sudo docker run -it --entrypoint "/bin/bash" centos/nginx:1.2 [root@920ea5c72a47 /]#
--entrypoint
を指定することで Dockerfile の ENTRYPOINT を上書き
起動中のコンテナに入るには以下のようにします。
$ sudo docker exec -i -t *コンテナID* /bin/bash
確認
これで nginx が起動したので、index.html が見えるはずです。
$ curl -s localhost hello world
見えました。
ブラウザから確認する場合は vagrant の ip アドレスを指定するとよいでしょう。
コンテナの停止・削除
コンテナを停止するには以下のコマンドを実行します。
$ sudo docker stop *コンテナID*
コンテナを削除するには以下のコマンドを実行します。
$ sudo docker rm *コンテナID*
コンテナが増えると停止がめんどうなので、for 文でぐるぐるしたり。
$ for p in $(sudo docker ps -a -q); do sudo docker rm -f ${p}; done
image の削除
image の削除は以下のコマンドを実行します。
$ sudo docker rmi *イメージID*
コンテナ実行中は削除できないので注意。
以上、ここまでdesu。
参考
参考にさせていただきましたm(__)m
https://docs.docker.com/engine/installation/linux/centos/ http://www.atmarkit.co.jp/ait/articles/1407/08/news031.html http://qiita.com/hihihiroro/items/d7ceaadc9340a4dbeb8f http://heartbeats.jp/hbblog/2014/07/3-tips-for-nginx-on-docker.html http://qiita.com/kasaharu/items/d4654193d75c67d65226 http://qiita.com/hnakamur/items/afddaa3dbe48ad2b8b5c
apache で X-Forwarded-For をログに記録する
記事としてよくありますが、ちょっとハマったので記録として。
<VirtualHost *:80> ServerName hoge.com DocumentRoot "/var/www/html" LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined_x CustomLog "/var/log/httpd/access_hoge.com.log" combined_x ErrorLog "/var/log/httpd/error_hoge.com.log" </VirtualHost>
ELB 配下で使っているため、 %h
は除去しました。
また、 combind_x
としているのは /etc/httpd/httpd.conf
にある LogFormat のデフォルト設定に上書きされてしまうことを回避するためです。
218 # 219 # Load config files from the config directory "/etc/httpd/conf.d". 220 # 221 Include conf.d/*.conf 493 # 494 # The following directives define some format nicknames for use with 495 # a CustomLog directive (see below). 496 # 497 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
ログは以下のようになります。
52.68.214.11 - - [10/Feb/2016:15:58:36 +0900] "GET /index.html HTTP/1.1" 200 47 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36"
以上です。
ssh で rake spec する時に sudo password が渡せない
追記
serverspec の実行時や、ローカルPCの環境変数に sudo_password を設定すればデフォルトのままで問題ありませんでした。
Sudo password support
If you log into servers as non-root user, Serverspec add "sudo" in front of the command. You can specify sudo password like this.
$ SUDO_PASSWORD=xxxxxxxx rake spec
Or display prompt for sudo password if you run Serverspec like this.
$ ASK_SUDO_PASSWORD=1 rake spec
tutorial を先に読まないとこういうことになりますね。猛省します。
追記ここまで
.ssh/config や /etc/hosts をごにょごにょして、
デフォルトの spec_helper.rb を使って鍵認証でもパスワード認証でも ssh ログインまでできたのですが、sudo できずにエラーで終了してしまいます。
$ rake spec (in /home/vagrant/serverspec) /home/vagrant/.rbenv/versions/2.2.3/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.0/lib:/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/lib /home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/test-001/\*_spec.rb Package "postgresql-9.3" hoge@test-001's password: Wrong sudo password! Please confirm your password on test-001. Finished in 6.7 seconds (files took 0.37223 seconds to load) 1 example, 0 failures /home/vagrant/.rbenv/versions/2.2.3/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.0/lib:/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/lib /home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/test-001/\*_spec.rb failed
必要な環境変数が null
なことが原因のようです。
if ENV['ASK_SUDO_PASSWORD'] begin require 'highline/import' rescue LoadError fail "highline is not available. Try Installing it." end set :sudo_password, ask("Enter sudo password:") { |q| q.echo = failse } else set :sudo_password, ENV['SUDO_PASSWORD'] end
対象は ASK_SUDO_PASSWORD
と SUDO_PASSWORD
の両方の様で、
上記 if 文の後に puts ENV['ASK_SUDO_PASSWORD']
と、 puts ENV['SUDO_PASSWORD']
したら結果は null 。
なので、highline
がインストールされているかどうかのチェックもできず落ちていた模様。
(highline が未インストールだったのでパスワード認証の時に、標準入力にパスワード丸見えになってしまった・・)
なので if 文を削って強制的に sudo password を入力させることにしました。
(環境変数を追加するのはちょっと抵抗あるので)
また、ssh が成功した時に set :request_pty, true
と書くように怒られたので、追記しています。
require 'serverspec' require 'net/ssh' set :backend, :ssh set :request_pty, true begin require 'highline/import' rescue LoadError fail "highline is not available. Try installing it." end set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false } host = ENV['TARGET_HOST'] options = Net::SSH::Config.for(host) options[:user] ||= Etc.getlogin set :host, options[:host_name] || host set :ssh_options, options
これでパスワードでも鍵認証でも実行できました。が、本当にこれでいいんだろうか。。。
apache で elb からのヘルチェックをログに記録しない
apache のバージョン
# httpd -V Server version: Apache/2.2.15 (Unix) Server built: Aug 13 2013 17:29:28 Server's Module Magic Number: 20051115:25 Server loaded: APR 1.3.9, APR-Util 1.3.9 Compiled using: APR 1.3.9, APR-Util 1.3.9 Architecture: 64-bit Server MPM: Prefork threaded: no forked: yes (variable process count) Server compiled with.... -D APACHE_MPM_DIR="server/mpm/prefork" -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=128 -D HTTPD_ROOT="/etc/httpd" -D SUEXEC_BIN="/usr/sbin/suexec" -D DEFAULT_PIDLOG="run/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_LOCKFILE="logs/accept.lock" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"
ELB-HealthChecker/1.0 からのアクセスログ
***.***.***.*** - - [11/Dec/2015:14:36:42 +0900] "GET /check.html HTTP/1.1" 200 20 "-" "ELB-HealthChecker/1.0" 373 ***.***.***.***
これをログに記録させないようにします。
config
<VirtualHost *:80> DocumentRoot /var/www/html ServerName hoge.com SetEnvIf Request_URI "^/check.html$" dontlog CustomLog /var/log/httpd/vhost-access.log combined_customized env=!dontlog ErrorLog /var/log/httpd/vhost-error.log </VirtualHost>
CustomLog
に env=!dontlog
が肝ですね。
反映は reload で OK です。
PostgreSQL で UPDATE REPLACE する時にちょっと怖かった
こんな感じのテーブルがあるとします。
select count(*) from table ; count ------ 1000
a
が含まれているレコードが 10 あるとします。
select count(*) from table where column like '%a%'; count ------ 10
replace してみます。
BEGIN; UPDATE table SET column = REPLACE(column, 'a', 'b'); UPDATE 1000
a
にマッチするのは 10 件のはずなのに、全件 UPDATE しています。
select count(*) from table where column like '%a%'; count ------ 0
目的は果たせているようですが、他に影響でていたらいやなので慌てて rollback します。
select count(*) from table where column like '%a%'; count ------ 10
結論としては普通に WHERE 句を指定していないから全件走査するようでした。(そりゃそうか)
BEGIN; UPDATE table SET column = REPLACE(column, 'a', 'b') WHERE column like '%a%'; UPDATE 10
WHERE句なしの時は全カラム走査して、検索条件にマッチすれば更新するという挙動のようです。
UPDATE 1000 と表示されても実際に影響があったのは a
が含まれている行のみでした。
Serverspec のセットアップ (rbenv / bundler)
Serverspec を使うために ruby の環境をセットアップします。 環境は CentOS 7 です。 O'REILLY を参考に進めていきます。
rbenv のインストール
rbenv は ruby のバージョン切替ができるツールです。 以下の手順でセットアップします。
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ rbenv --version rbenv 0.4.0-192-g825de5d
インストールできる ruby のバージョン確認
$ rbenv install --list
ruby のインストール
今回は 2.2.3 をインストールします。
$ rbenv install 2.2.3 Downloading ruby-2.2.3.tar.gz... -> https://dqw8nmjcqpjn7.cloudfront.net/df795f2f99860745a416092a4004b016ccf77e8b82dec956b120f18bdc71edce Installing ruby-2.2.3... BUILD FAILED (CentOS Linux 7 using ruby-build 20151028-11-gbd22205) Inspect or clean up the working tree at /tmp/ruby-build.20151119040352.4827 Results logged to /tmp/ruby-build.20151119040352.4827.log Last 10 log lines: The Ruby openssl extension was not compiled. The Ruby readline extension was not compiled. The Ruby zlib extension was not compiled. ERROR: Ruby install aborted due to missing extensions Try running `yum install -y openssl-devel readline-devel zlib-devel` to fetch missing dependencies. Configure options used: --prefix=/home/vagrant/.rbenv/versions/2.2.3 LDFLAGS=-L/home/vagrant/.rbenv/versions/2.2.3/lib CPPFLAGS=-I/home/vagrant/.rbenv/versions/2.2.3/include
エラーになってしまいました。パッケージが足りないようです。
$ sudo yum install -y openssl-devel readline-devel zlib-devel
もう一度インストールします。
$ rbenv install 2.2.3 Downloading ruby-2.2.3.tar.gz... -> https://dqw8nmjcqpjn7.cloudfront.net/df795f2f99860745a416092a4004b016ccf77e8b82dec956b120f18bdc71edce Installing ruby-2.2.3... Installed ruby-2.2.3 to /home/vagrant/.rbenv/versions/2.2.3
できました!
インストールした ruby を local 環境に設定します。
$ rbenv local 2.2.3 $ rbenv local 2.2.3 $ ruby --version ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
bundler のインストール
Gemfile に記載したパッケージをインストールしてくれる bundler をインストールします。
$ gem install bundler
bundler を使って Serverspec をインストール
serverspec ディレクトリを作成して作業ディレクトリとします。 また、Gemfile を用意します。
$ pwd /home/vagrant/serverspec $ cat Gemfile source 'https://rubygems.org' gem 'serverspec'
インストールします。
$ bundle install Fetching gem metadata from https://rubygems.org/....... Fetching version metadata from https://rubygems.org/.. Resolving dependencies... Installing diff-lcs 1.2.5 Installing multi_json 1.11.2 Installing net-ssh 2.9.2 Installing net-scp 1.2.1 Installing net-telnet 0.1.1 Installing rspec-support 3.4.0 Installing rspec-core 3.4.1 Installing rspec-expectations 3.4.0 Installing rspec-mocks 3.4.0 Installing rspec 3.4.0 Installing rspec-its 1.2.0 Installing sfl 2.2 Installing specinfra 2.44.2 Installing serverspec 2.24.3 Using bundler 1.10.6 Bundle complete! 1 Gemfile dependency, 15 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed.
インストールできました。
サンプルの生成
postgresql の構築テストを行いたいので、target host name を test-db としてみます。
$ serverspec-init Select OS type: 1) UN*X 2) Windows Select number: 1 Select a backend type: 1) SSH 2) Exec (local) Select number: 1 Vagrant instance y/n: n Input target host name: test-db + spec/ + spec/test-db/ + spec/test-db/sample_spec.rb + spec/spec_helper.rb + Rakefile + .rspec
spec
の配下にホストごとのディレクトリが生成され、それぞれにテストコードがあるようです。
ホストごとでは大規模になったときに大変とどこかに書いてあった気がします。。
とりあえずはこれで進めてみようと思います。
一旦ここまで。