Rubyのoptparseの挙動について
もう知ってる人にとっては、当たり前じゃん何言ってるのみたいな内容かもしれませんが、私は最近知ったので。。
Rubyには optparse
というコマンドライン引数を読み取るライブラリが標準でついてくる。
これを使ってよくバッチ処理とかに使うスクリプトを書いてたりする。
この前書いたコードで以下のようなオプションを書いた。
OPTIONS = {} OptionParser.new do |opt| opt.on('-n', '--no-daemon') {|v| OPTIONS[:no_daemon] = v} opt.parse!(ARGV) end
デーモン状態で動くほうが通常モードだったアプリケーションなので、デーモン状態でない時を明示的にしたいときにと思い、 --no-daemon
とした。
当初はこの引数を以下のように使用していた。
unless OPTIONS[:no_daemon] # :no_daemonがfalseだったときの処理(デーモン状態で動かしたいときの処理) end
しかしどうも期待どおりの挙動を示してくれない(常にデーモン状態になる)のでドキュメントを見た。library optparse
--[no-]...などとすることで、否定型のオプションを指定することができます。
require 'optparse' opt = OptionParser.new opt.on('-a', '--foo') {|v| p v } opt.on('--[no-]bar') {|v| p v } opt.parse!(ARGV) p ARGV ruby sample.rb -a foo bar --bar baz --no-bar # => true true **false** # <- --no-bar の指定による。 ["foo", "bar", "baz"]
noってつけると否定形になって、falseが返ってくるのか
いつもTrueの感覚で書いていたので普通にミスった。
つわけで当初書いたここのコードブロックは
--no-daemonオプションあり => false --no-daemonオプションなし => nil => false
となるので、常にデーモン状態となったわけだった。
最終的に以下のように修正した。
if OPTIONS[:no_daemon].nil? # :no_daemonがfalseだったときの処理(デーモン状態で動かしたいときの処理) end
こっちのほうが素直な実装だとは思う。
corretto8をAnsible経由でインストールしてAmazonLinuxで使う
前回書いたブログの日にちが1月2日だって。もう引くレベル。
個人用途ではOracleのJavaのライセンス問題はそこまで気にする必要ないけれども、会社でJavaを使ってサービス作って運営する業務をしている身としては、頭の片隅にいつもいる感じで、ちょっと気持ち悪かったのも事実。
そんなときに登場した Corretto
AWSありがてえ。。
以外の何者でもない感じであった。(まあOpenJDKでいこうってプロジェクトもあるので完全ではないけど)
Jenkinsを立てる必要が出てきて、どうせなら使ってみようと思って、Ansible経由でインストールしてみたので、その過程を忘備録的に残しておく。
※Jenkinsのことを脳死で嫌う人いるけれども、別にそれをCircleCIやCodeShip変えたからモダンだみたいなのも個人的にはよく分からない。まあ確かにCircleCIチョー便利なのは納得しますが。
使用環境はAmazonLinux2。いきなりAnsibleのtask貼っちゃう。
はい。これだけ。AmazonLinuxならこれだけでインストール完了します。
ついでにJnekinsをインストールするtaskも貼っておく。
これらのインストールが完了して、初期設定とか済ませて、本当にcorrettoが使われているのかをJenkinsの管理画面から確認する。
問題なさそう。
ターミナルをカラフルに表示してみる
12月があっという間で本当に光陰矢の如しとはこのとこだなと思いましたまる
一ヶ月強ぶりの更新となった。一ヶ月ぶりの更新なのにまさかの小ネタなのはお許し下さい
ログ出力時のターミナルの表示をもっとリッチにしたいと思い、どの色が使用可能なのかを確認したかったので作った突貫スクリプト
こちらのブログを参考にした
以下gistにまとめた
実行するとこんな感じで表示される
次回はもっとまともな記事を書こう
JMeterのクラスタをゆるく立ててみる
仕事でJMeterのクラスタを使う必要が出てきたので、割と簡単に立てられる方法無いかなと思い、実際にやった方法を忘備録的に残しておく。(まあ会社のwikiにちゃんとあるんだけれど)
GatlingやSaasなどが既に沢山ある状況で、なんで昔ながらのJMeterかというと、既にあるシナリオを使いたいというのが最大の理由。 とはいえJMeterが悪いわけでは全く無いので新しい何かにするモチベーションもなかったのも事実。
構築は全部AWS上。スレッド数がそこそこ多いので、Master-Slave構成とした。
東京リージョンを使用して建てたが、負荷が1つのAZに依るのはテスト的にも基盤的にも良くないだろうと考え、2つのAZに均等にまたがって建てるようにした。
Slave側をstop/startする度にパブリックDNSが変わるのも不便かと思い、本当はEIPをアタッチすべきであるが、aws shellを使ってPublicDNS名引けるよなと思ったこと、将来的にはクラスタサイズを自由に変えたいという思いがあったことなどから敢えてEIPは使用していない。
tag:Nameを適切に設定していれば、aws cliとjqを使用して以下のように取得することが可能である。
以下はtag:Nameを load-test-jmeter-slave-1c
に設定していた場合の例。
$ aws ec2 describe-instances --filters="Name=tag:Name, Values=load-test-jmeter-slave-1c" | jq -r ".Reservations[].Instances[].PublicDnsName" ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com
これが分かればMasterからSlaveに接続する際に困らないはず。
JMeterとJava8のインストールはAnsibleを使用。 JMeterインストールの主要部分を切り出したロールはこんな感じ。
--- - name: download Apache JMeter shell: > wget -O {{ jmeter.src_path }} {{ jmeter.pkg_url }} - name: Unarchive Apache JMeter unarchive: src: "{{ jmeter.src_path }}" dest: "{{ jmeter.dst_path }}" remote_src: yes
vars配下には以下の内容を記したmain.ymlを配置してある。
## vars for Apache Jmeter --- jmeter: src_path: /usr/local/src/source/apache-jmeter-3.3.tgz dst_path: /usr/local/src/script pkg_url: http://ftp.yz.yamagata-u.ac.jp/pub/network/apache//jmeter/binaries/apache-jmeter-3.3.tgz
読むと分かるのだがこれはJMeterのインストールにしか使えない。メモリのアロケーションとかその他confをいじった状態で再度Ansibleを流してしまうと綺麗サッパリなくなってしまうので注意。
(本当はちゃんと主要ファイルをテンプレート化して撒く処理を追加するなりすれば良いのだけれど、ゆるくクラスタを立ててみるの趣旨からはそれてしまうので、今回はなしの方向で)
ここまで出来ればとりあえずJMeterをMaster-Slave構成で起動できるようになる。
Masterから実際に繋いでみる。コマンドは以下のようになるはず。
$ ./jmeter -n -t test.jmx -e -l log.jtl \ -R ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com,ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com,ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com
一応オプションを説明すると、
-n cliモードで起動(本テストでは基本的にGUIを起動しないように) -t シナリオファイルの指定 -e 実行後のレポート出力 -l 実行後のログファイルの出力 -R 使用するSlaveのエンドポイント
もっとちゃんと設定すると、 このページのような GatlingライクなHTMLでグラフィカルなレポートも出力できたりするが、今回はとりあえず動かしたかったので。 今回はここまで。
CloudFormation忘備録2
前回はただ単体のセキュリティグループを作っただけなので、今回は複数のセキュリティグループを連携させてみる。
例えば図のような経路に対するセキュリティグループを作りたくなったとする。
このときに、それぞれのコンポーネントに対してセキュリティグループを充てる必要が出てくる。 今回の場合ではリクエストを一番最初に受け付けるロードバランサと、そのロードバランサから来たリクエストを処理するAPサーバーの2つとなる。 ロードバランサはSSL終端をしているためHTTPS(443)のみ。APサーバー達はロードバランサからのリクエストしか受け付けないようにする。 (SSH出来ないよとかはとりあえず無視で。それは別のセキュリティグループを作って充てましょう)
前回書いたセキュリティグループのYAMLファイルを流用する。
以下のようにディレクティブを加えた。
--- AWSTemplateFormatVersion: "2010-09-09" Description: > Here is the area of description of this CloudFormation template. Resources: WebAPServersSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: SecurityGroup of webservers SecurityGroupIngress: - IpProtocol: tcp FromPort: '3000' ToPort: '3000' SourceSecurityGroupId: !Ref ApplicationLoadbalancerSecurityGroup Tags: - Key: Name Value: dev-web-ap-sg VpcId: vpc-XXXXXXXX ApplicationLoadbalancerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: SecurityGroup of Application Load Balancer SecurityGroupIngress: - IpProtocol: tcp FromPort: '443' ToPort: '443' CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: dev-alb-sg VpcId: vpc-XXXXXXXX
別段変わったところは無く、ALB用のセキュリティグループが増えた形となる。ただしAPサーバー達に充てるセキュリティグループ dev-web-ap-sg
には変更が入っている。
SecurityGroupIngress
のアクセス元を表す表記が CidrIp
から SourceSecurityGroupId
へ変更となっている。
このように設定することによって、 dev-alb-sg
を充てたロードバランサからのみのリクエスト受け付けるように出来る。
ちなみに今回は参照先・参照元共に一緒に生成しているため !Ref
を利用して未知のセキュリティグループIDに対応出来るようにしているが、
既存のセキュリティグループを参照したい場合は sg-xxxxxxxx
を書いてあげればおk。
前回のCloudFormationのスタックを利用してupdate-stackを実行。
無事にできていた。
CloudFormation忘備録
前のエントリーから非常に時間が開いてしまった。。
前のエントリーの投稿日60日前とか死ぬしかない。
本当に意識的に書いていかないと全然続かないな。 忙しかったんだけど、まあ酒飲んでる時間とかちょっと削れば1エントリーくらい書けるわけで。 まあ良い訳ですね。
業務で積極的にCloudFormationを使うようにしており、セキュリティグループやらロードバランサもCloudFormationで作っている。 以外とこの辺をCloudFormationで作っている人が居ないようなので、忘備録がてらブログに記しておく。
AWSのドキュメント読めば正直解決するのであるが、デバッグしずらいところでもあるので、このエントリーが誰かの役に立てばと。
いきなりソースコードから。 (not ステーキ)
--- AWSTemplateFormatVersion: "2010-09-09" Description: > There is the area of description of this CloudFormation template. Resources: WebAPServersSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: SecurityGroup of webservers SecurityGroupIngress: - IpProtocol: tcp FromPort: '80' ToPort: '80' CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: dev-web-ap-sg VpcId: vpc-XXXXXXX
最小構成のセキュリティグループのCloudFormationテンプレートを書いた。
おおまかな構成としては
AWSテンプレートフォーマットのバージョン このテンプレートの説明 テンプレートで定義しているリソース
となっている。意外と構成要素少ない。なので、リソース配下にどのようなリソースをおけばよいかを書けば基本的には完成する。
一つずつ説明。
WebAPServersSecurityGroup
ここは正直なんと書いても問題ない。これ自体が変数みたいなもの。 今回は例として、WebAPサーバーにアタッチするセキュリティグループとしたので、このように書いているだけ。
Type: AWS::EC2::SecurityGroup
これはどのようなリソースをこのディレクティブ内部で使用するかを定義するもの。これはAWSのドキュメントを調べないとわからないので各自で。
Properties:
この下にネストさせて、色々構成物を定義していく。
GroupDescription: SecurityGroup of webservers SecurityGroupIngress: - IpProtocol: tcp FromPort: '80' ToPort: '80' CidrIp: 0.0.0.0/0
GroupDescription
セキュリティグループの説明文。事業部では英語で書くことが決まりとなっているが、別に日本語でもエラーにはならないと思う。
SecurityGroupIngress
インバウンドのルールのこと。ちなみにアウトバウンド(EC2 => ネット)の方は SecurityGroupEgress
。だた、EC2にアタッチするセキュリティグループでアウトバウンドを制限する要件はそれほど高くないと思われるので、今回は割愛。
複数定義したいときは次のように書くことも出来て、
- IpProtocol: tcp FromPort: '80' ToPort: '80' CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: '443' ToPort: '443' CidrIp: 0.0.0.0/0
ちなみに更に一気に範囲を指定してあげたいときは
- IpProtocol: tcp FromPort: '2000' ToPort: '4000' CidrIp: 0.0.0.0/0
とすることで 2000~4000番ポートを一気に開けることが出来る。
完成したCloudFormationテンプレートを実際に使用したい時は以下のコマンドを使用する。
(AWS CLIの基本的な使い方はマスターしている前提で話しています。そもそもAWS CLIってなんぞやって方はこちらを参照ください)
$ aws cloudformation create-stack --stack-name=dev-web-application-security-group --template-body=file://dev-web-ap-sg.yml
YAMLファイルを dev-web-ap-sg.yml
として保存して、この構成名を dev-web-application-security-group
として作成する感じ。
実行すると以下の結果が返ってくる。
/Users/dorian/sandbox aws cloudformation create-stack \ --stack-name=dev-web-application-security-group \ --template-body=file://dev-web-ap-sg.yml | jq -r { "StackId": "arn:aws:cloudformation:ap-northeast-1:0123456789:stack/dev-web-application-security-group/8d7bf440-c6e9-11e7-931f-500c44f24c1e" }
マネコンを開いてみると
ててーん。あっという間に完成。実際にセキュリティグループを見てみると
確かにできている。ちなみにCloudFormationの画面を確認してみるとこんな感じで出ていた。
最初に頑張って書いてしまえば、インフラ構成をgitでバージョン管理出来たりするので変更履歴置いやすいし、インフラエンジニアじゃなくてもどのような構成になっているのかを分かってもらえるので変なコミュニケーションが発生せず快適だったりする。
inode を枯渇させてみる
毎週書くとか言っておきながら、すごく日が開いてしまった。。よくない。。
とか言っておきながら、今回も小ネタ的なものですみません。 inode を枯渇させてみる。
業務中にルートボリュームがおかしくなったサーバーがあり、調査の過程でinodeも調べたので、興味本位がてらやってみた。 (結局そのサーバーは別の原因でルートボリュームがおかしくなっていたのだけれども)
そもそもinodeってなんぞや、全く聞いたことが無いで、という方は以下を読んでおけばなんとなくわかるかと思います。
inode番号とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
数に上限があるので、細かいファイルをガンガン保存していたりすると普通に枯渇する。そんなに無いとは思うけれどJSONをファイルとして 大量に保存していたりすると大変そう。
枯渇させるのは簡単で、ファイルをひたすらtouchしてあげればおk。 計測がてら、以下のようなスクリプトを組んでみた。
。。。と実行してみたことろ、SSH越しでコマンドを実行していため、生成速度が遅い。
1時間程度回してみたが、0.3%くらいしか使用率が上がらなかったorz
inode枯渇のタイミングで挙動がスクリプトの挙動がおかしくなって、正確な測定にならないだろなとか考えてSSH越しで別サーバーを立てて叩くようにしたが、そもそも測定が終わらなさそうなので、考えを改めて、普通にローカルで回せるように変更。
本当は別のドライブアタッチしてあげれば、そんな心配もしなくて良いのだけれども、まあ面倒なので
最終的に以下スクリプトを利用。
これを回すと以下のログが出力されるようになっている。
I, [2017-09-10T12:41:28.910920 #20048] INFO -- : Used: 86386 Remain: 3753614 Percentage: 97.750 I, [2017-09-10T12:41:28.946672 #20048] INFO -- : Used: 86396 Remain: 3753604 Percentage: 97.750 I, [2017-09-10T12:41:28.981453 #20048] INFO -- : Used: 86406 Remain: 3753594 Percentage: 97.749 I, [2017-09-10T12:41:29.016357 #20048] INFO -- : Used: 86416 Remain: 3753584 Percentage: 97.749 I, [2017-09-10T12:41:29.050865 #20048] INFO -- : Used: 86426 Remain: 3753574 Percentage: 97.749 I, [2017-09-10T12:41:29.085021 #20048] INFO -- : Used: 86436 Remain: 3753564 Percentage: 97.749 I, [2017-09-10T12:41:29.119606 #20048] INFO -- : Used: 86446 Remain: 3753554 Percentage: 97.748 I, [2017-09-10T12:41:29.155900 #20048] INFO -- : Used: 86456 Remain: 3753544 Percentage: 97.748 I, [2017-09-10T12:41:29.190215 #20048] INFO -- : Used: 86466 Remain: 3753534 Percentage: 97.748
出力されるものは現在のinodeの使用率、残り、残りの%表記となっている。
最終的に15時間ほど掛かって完全に枯渇した。
枯渇すると以下のログが吐き出される。
ubuntu@ip-172-31-3-220:~$ ruby run_local.rb /usr/lib/ruby/2.3.0/fileutils.rb:1157:in `initialize': No space left on device @ rb_sysopen - ./inode/1/e28c7144-aff1-4ddf-97cd-f93abaee09f7 (Errno::ENOSPC) from /usr/lib/ruby/2.3.0/fileutils.rb:1157:in `open' from /usr/lib/ruby/2.3.0/fileutils.rb:1157:in `rescue in block in touch' from /usr/lib/ruby/2.3.0/fileutils.rb:1153:in `block in touch' from /usr/lib/ruby/2.3.0/fileutils.rb:1151:in `each' from /usr/lib/ruby/2.3.0/fileutils.rb:1151:in `touch' from run_local.rb:18:in `block (2 levels) in <main>' from run_local.rb:15:in `times' from run_local.rb:15:in `block in <main>' from run_local.rb:12:in `times' from run_local.rb:12:in `<main>' ubuntu@ip-172-31-3-220:~$
容量的には余裕。
ubuntu@ip-172-31-3-220:~$ df -h Filesystem Size Used Avail Use% Mounted on udev 488M 0 488M 0% /dev tmpfs 100M 6.8M 93M 7% /run /dev/xvda1 30G 1.5G 28G 5% / tmpfs 496M 0 496M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 496M 0 496M 0% /sys/fs/cgroup tmpfs 100M 0 100M 0% /run/user/1000
しかしinodeは確かに枯渇している。
ubuntu@ip-172-31-3-220:~$ df -i Filesystem Inodes IUsed IFree IUse% Mounted on udev 124865 381 124484 1% /dev tmpfs 126832 469 126363 1% /run /dev/xvda1 3840000 3840000 0 100% / tmpfs 126832 1 126831 1% /dev/shm tmpfs 126832 3 126829 1% /run/lock tmpfs 126832 16 126816 1% /sys/fs/cgroup tmpfs 126832 4 126828 1% /run/user/1000
この状態ではボリュームに対する、書き込みが発生するいかなる処理も実行不可能となる。 もちろんファイルの新規作成は無理であるし、
ubuntu@ip-172-31-3-220:~$ touch test.txt touch: cannot touch 'test.txt': No space left on device
入力補完といった、一見ファイル作成とは無関係のコマンドも、以下のように使用することが出来ない。
ubuntu@ip-172-31-3-220:~$ less -s re-bash: cannot create temp file for here-document: No space left on device -bash: cannot create temp file for here-document: No space left on device
これらに対処する一番簡単は方法は、不要ファイルを削除すること。 以下のように、ちょっとでもinodeに余裕ができれば、
ubuntu@ip-172-31-3-220:~/inode$ df -i Filesystem Inodes IUsed IFree IUse% Mounted on udev 124865 381 124484 1% /dev tmpfs 126832 469 126363 1% /run /dev/xvda1 3840000 3830454 9546 100% / tmpfs 126832 1 126831 1% /dev/shm tmpfs 126832 3 126829 1% /run/lock tmpfs 126832 16 126816 1% /sys/fs/cgroup tmpfs 126832 4 126828 1% /run/user/1000
ファイルの新規作成も可能。
ubuntu@ip-172-31-3-220:~$ touch test.txt ubuntu@ip-172-31-3-220:~$ ls test.txt
入力補完も問題なく効く。