一時ファイルを確実に削除するシェルスクリプト

途中で mktemp を実行するシェルスクリプトを運用していたら、SRE に「/tmp/temp.* がたくさんできているよ」とおこられた。

こういうイディオムがあるらしい。

SIGINT などのシグナルを受けたときにもきちんと一時ファイルを消せるようにするためのものだ。なるほどなあ。

「DevOps 先駆者 Mitchell Hashimoto との Meetup」の参加ログ

DevOps 先駆者 Mitchell Hashimoto との Meetup」に参加しました。

もともとは単なる「参加枠」で登録していたのですが、抽選に漏れてしまったので「Blogger 枠」で登録しなおして、イベントに参加をしました。そういう経緯で、まさに今、ブログを書いています。

どのようなイベントであったか?

いわゆる「ユーザー向けイベント」であるように感じました。ほとんどの参加者が HashiCorp プロダクトの使用者であったし、Mitchell Hashimoto 氏のプレゼンに関する質疑応答も、「HashiCorp の xx を使っているのですが…」ではじまるものが大多数でした。

hashicorp-1

どのようなプレゼンテーションであったか?

Mitchell Hashimoto 氏がプレゼンテーションの冒頭で “「HashiConf 2017」のサマリ的な内容” である、と言っていました。実際、何か新発表があったわけではなく、ある程度 HashiCorp の動向を追っている人なら聞き覚えのある内容がほとんどであったように思います。

話があったのは次の 6 つのプロダクトについてです。

  • Vagrant
  • Vault
  • Nomad
  • Packer
  • Consul
  • Terraform

hashicorp-2

時間的な制約もあり、それぞれのアップデートについて詳細に説明するというよりは、新機能について簡潔に述べることに終止していたように思います。

僕が個人的に面白いと感じたのは「Terraform Registry」と「Sentinel」です。

Terraform Registry は雑に言うと Terraform Module をシェアする仕組みで、Sentinel はポリシをコードの形で記述する仕組みです。

Terraform Registry を使うことで、「ありがちな」モジュールを再利用することができます。また、モジュールには「Verified Module」とされているものもあります。これは Hashicorp のメンバーによるレビューを経た「安心して使える」モジュールだという扱いです。質疑応答によると、Verified Module は「コードレベルでチェックしている」とのことです。

また Sentinel は “Policy as Code” を標榜しており、例えば次のようなポリシをコードで記述できるものです。

  • 営業時間外には構成変更 (e.g. terraform apply) させない。
  • 2048 ビット未満の鍵長の TLS 証明書は許可しない。
  • タグ付けされていない AWS インスタンスは許可しない。

例えば Terraform などでインフラの構成管理をするとき、コードレビューだけでポリシがきちんと守られていることを担保するのは困難です。Sentinel を使うことで、組織内のポリシの遵守がぐっと簡単になるのは確かだろうと思いました。

まとめ

HashiCorp は相変わらず勢いのある会社だなあと感じました。製品によっては強力な競合がいて (たとえば Nomad に対する Kubernetes) 苦戦することもあるだろうと思う一方で、ベンダー中立なことを活かした AWS や GCP のどちらもサポートする製品の開発であったり、 Consul に代表されるようなスケーラブルなミドルウェアの開発においては、他の追随を許さない魅力的な立ち位置にいるなあと改めて思いました。

“sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException” に対する対処

HTTPS でホストされている、あるウェブサイトをクロールする Java プログラムを動かしていたところ、次のような例外が表示された。

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

sun.security.validator.ValidatorException は、Java の持つルート証明書ではウェブサイトの SSL 証明書の正当性を検証できないときに投げられる例外である。

この問題を解決するためにできることはふたつある。

  • ウェブサイトの SSL 証明書に対応するルート証明書を Java にインストールする
  • Java のバージョンを更新して、Java が標準で使うルート証明書を更新する

後者の方が手軽なので、今回は単に Java をアップデートすることにした。なお、前者に興味がある場合は “java keytool” などで検索するとよい。

ただし、今回の僕のケースでは Java を最新版に更新しても問題は解決しなかった。調べてみると、ウェブサイトの側の SSL 中間証明書の設定に問題があるようであった。中間証明書の設定に問題があるかどうかは、たとえば Symantec が提供するサービスなどで確認できる。

ssl-intermediate-cert-verification

“Intermediate certificate missing.” とのことだ。ここで、対応する “GeoTrust SSL CA – G3” をダウンロードして Java の適切なパスに置いてやることで、当面の問題は解決する。

ただし、ウェブサイト側は中間証明書を置かなければ、古いブラウザのユーザーを切り捨てることになるし、BEAST攻撃に対して脆弱なまま放置することにもなる。

スナップショットから作成した EBS ボリュームをマウントする

この記事では EBS ボリュームのスナップショットを、新しい EC2 インスタンスでマウントする方法について記述する。すでに EBS ボリュームのスナップショットは作成されていると仮定する。まだであれば、ひとつ前の記事を参照してほしい。

AWS コンソールでぽちぽちと新しい EC2 インスタンスを作成し、途中の “Add Storage” のプロセスで /dev/sdf 辺りに前述の記事で作成したスナップショットIDを使ってボリュームを追加するとしよう。

最終的に作成されたインスタンスに ssh でログインし、lsblk を実行すると次のように表示される。ブロックデバイスは認識されているものの、マウントされていないことがわかる。

## /dev/xvdf はアタッチされているが、マウントされていない (MOUNTPOINT が空)
## (lsblk の NAME は "/dev/" が省略される)
$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0    8G  0 disk
└─xvda1 202:1    0    8G  0 part /
xvdf    202:80   0  512G  0 disk

## ちなみに xvdf は /dev/sdf のエイリアス
$ ls -l /dev/sdf
lrwxrwxrwx 1 root root 4 Jun 14 08:16 /dev/sdf -> xvdf

## /dev/xvdf にファイルシステムは存在することを確認する (ない場合は mkfs を使う)
$ sudo file -s /dev/xvdf
/dev/xvdf: Linux rev 1.0 ext4 filesystem data, UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (needs journal recovery) (extents) (large files) (huge files)

マウントするためには、次の順で作業をすればよい。

  1. マウントポイントを作成する (今回の例では /data/mydata をマウントポイントとする)
  2. /etc/fstab を変更する
  3. mount -a/etc/fstab の内容を反映する

具体的には、こうだ。

## マウントポイントを作成する
$ sudo mkdir -p /data/mydata

## (再起動でリセットされない) 永続的なマウントを実現するため /etc/fstab を変更
$ sudo cp /etc/fstab /etc/fstab.orig
$ sudo vi /etc/fstab  # (次の行を追加: `/dev/xvdf /data/mydata ext4 defaults,nofail 0 2`)

## /etc/fstab の内容を反映する
$ sudo mount -a

/etc/fstab に書けるオプションは正直なところ、かなりややこしい。AWS オフィシャルのドキュメントに次のようにある。

(/etc/fstab の) この行の最後の 3 つのフィールドは、ファイルシステムのマウントオプション、ファイルシステムのダンプ頻度、起動時に実行されるファイルシステムチェックの順番です。これらの値がわからない場合は、例の値 (defaults,nofail 0 2) を使用してください。

素直に「例の値 (defaults,nofail 0 2)」を採用しても多くの場合は問題ないだろう。

参考記事

AWS CLI で EBS のスナップショットを作成する

この記事は AWS CLI で EBS のスナップショットを作成するためのメモだ。

正直なところ、何も難しいことはない。ボリューム ID が “vol-xxxxxxxx” の EBS ボリュームのバックアップを取るなら aws ec2 create-snapshot --volume-id vol-xxxxxxxx --description "My Backup" とすればいいだけだ。

ただし、create-snapshot はオプションが少なく、スナップショットの作成と同時にタグ付ができない。例えば、スナップショットの Name タグを “MySnapshot” とし、Cost を タグを “MyService” としたければ、create-tags コマンドを別に叩く必要がある。

create-snapshot は作成するスナップショットの情報を JSON で STDOUT に出力する。この JSON にはスナップショット ID の情報も含まれているので、次のようにすることで目的の処理を達成できる。

$ SnapshotId=$(aws ec2 create-snapshot --volume-id vol-xxxxxxx --description "MySnapshot") | jq -r .SnapshotId)
$ aws ec2 create-tags --resources $SnapshotId --tags Key=Cost,Value=MyService Key=Name,Value=MySnapshot

なお、AWS では EBS ボリュームのスナップショットを連続で作成した場合、後から作られるスナップショットは前回のスナップショットからの増分スナップショットになる。

*nix が wait 絡みのデッドロックを防ぐ仕組み

引き続き「Working With Unix Processes」を読んでいる。

サンプルで「デッドロックが起こりそう だけど、起こらない」コードが掲載されていた。次のスニペットは、そのサンプルを少し書き換えたものだ。

#!/usr/bin/env ruby

# ふたつの子プロセスをつくり、すぐに殺す
2.times do
  fork do
    abort "おしまい"
  end
end

# 最初の子プロセスを待ち、その後は3秒ほど寝る
# 寝ている間にもうひとつの子プロセスも終了する
puts Process.wait
sleep 3

# もう子プロセスは終了しているので、これはデッドロックするはずでは? => 実際はしない
puts Process.wait

コードのコメントに書いた通りなのだが、最後の Process.wait はデッドロックになっても不思議ではない… けど、実際にはデッドロックにならない。

カーネルには exit したプロセスの情報をキューに詰めておく機構があり、親プロセスは死んだ子プロセスの順にその情報を取得できる。だから、サンプルでの最後の Process.wait は実際には子プロセスの終了を待つのではなく、キューから「既に死んだ」子プロセスの情報を取得している。

ただ、子プロセスが存在しない「かつ」前述のキューにもデータが存在しないとき、Process.waitErrno::ECHILD 例外を起こす。

まあ、それはそうだよね。

Ruby 2.0 (or later) の CoW

Working With Unix Processes」を読んでいる。

これによると、Ruby (MRI) では 2.0 まで CoW (Copy on Write) が実装されていなかったそうだ。理由は Ruby の GC がナイーブなマーク・アンド・スイープであったから、らしい。

バージョン 1.9 までの Ruby は、マークのタイミングでオブジェクト内に直接「使用中」フラグを立てていたそうだ。つまり、オブジェクトに変更を加えることになるので、このタイミングでオブジェクトのコピーが発生してしまう。

バージョン 2.0 からは、マーク・アンド・スイープのために専用のメモリ領域をつくるので、マークのタイミングでオブジェクト自身を変更する必要はない。これなら CoW を機能させることができる。

なるほどなあ。