あまり知られていない~/.ssh/configのIdentityFileの挙動
あまり知られてなさそうなのと、自分が完全に忘れていたのでメモっておきます。
IdentityFile
の挙動
man ssh_config
でみてみると
It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives).
すなわち
IdentityFile
は複数書ける- 書いた順に試行される
とのこと。
Host github.com HostName github.com User git IdentityFile ~/.ssh/github-foo.pem IdentityFile ~/.ssh/github-bar.pem
と書くと、github-foo.pem, github-bar.pemの順でログインできるか試行する挙動になるよう。
どういう場面で使える?
追記あり:Jenkinsにdeploy keysをリポジトリ毎に設定するのが面倒くさい - なっく日報 の方がいいかも
JenkinsでGithubと連携させるためにプロジェクト毎にdeploy keyを設定するときとか。
↑の回答にあるようにJenkinsサーバの~/.ssh/configにリポジトリ毎にエイリアスを設定して
Host a.github.com HostName github.com User git IdentityFile ~/.ssh/project-a-id_rsa Host b.github.com HostName github.com User git IdentityFile ~/.ssh/project-b-id_rsa
git clone git@a.github.com:yukidarake/project-a.git git clone git@b.github.com:yukidarake/project-b.git
みたいに書けます!という例をよく見かけますが、これは
Host github.com HostName github.com User git IdentityFile ~/.ssh/project-a-id_rsa IdentityFile ~/.ssh/project-b-id_rsa
こう書けると。
プロジェクト毎に秘密鍵を作って、~/.ssh/configに追加して、deploy keyに設定するという手順自体は変わりませんが、
git clone
時にa.github.comみたいなエイリアスを使うという気持ち悪さからは開放されます。
まとめ
自分はこの書き方を使っていこうかと思ってますが、
何か指摘等あれば、コメントいただければ幸いです。
Google Cloud Deployment Managerの設定ファイルのJinja2形式での書き方TIPS
特に細かい説明はしないので、分かる人がググったときにたどり着いてもらえればと・・・(雑)
env
を利用してインスタンス名とかを生成
ロール毎のテンプレートとかにname
を動的に生成するために使えそうな技。
↓のように書けます。
{% set INSTANCE_NAME = 'foo-{deployment}-{name}'.format(**env) %}
この辺、普段からPython使っている人には常識なのかもしれないですが
- 文字列のフォーマットに
%
よりも、format
使えとどっかに書いてたからそうした - dictionary型の変数をformatに渡す際にはアンパックするべし
アンパックとか初めて知りました(ES2015で書いている人には{...props}
的なといえば伝わるかも)
なお、envについては、
https://cloud.google.com/deployment-manager/configuration/adding-templates#environment_variables
のドキュメントを読むべし。
最後の手段としてマクロがある
↑のやり方で済んだので、使ってないですが、最終兵器ということで。
{% macro diskName(diskObj) -%}{{ env["deployment"]}}-disk-{{ diskObj["name"] }}{%- endmacro %} # ... {% for diskObj in properties["disks"] %} - deviceName: {{ diskName(diskObj) }} type: PERSISTENT source: $(ref.{{ diskName(diskObj) }}.selfLink) autoDelete: true {% endfor %}
な書き方ができます。ほぼほぼ関数。
まとめ
まとめるほどのものはないですが。
Jinja2の公式ドキュメントをみながら、ちゃんとPythonを書ければ特に何もいうことはありません!
Google Cloud Deployment Managerでimportsを使ったときの嵌りどころ
Cloud Deployment Managerで
imports: - path: other.jinja
みたいに書いて、他の設定ファイル(.pyや.jinja)をインクルードできる機能があるのですが、
エントリポイントにあたるファイルはYAMLにしないといけません(ドキュメントのどこにも書いてない)
結構嵌って、↓のstackoverflowでわかりました。
事例が少ないから嵌ったときキツイなぁ・・・
確かに、
https://github.com/GoogleCloudPlatform/deploymentmanager-samples/tree/master/examples/v2/single_vm
とかのシンプルな例を見ても必ずエントリポイントのYAMLファイルを置いてます。
ご注意ください。
やっぱりCloud Deployment Managerの方がいい気がしてきた
妻から犬を買ったという連絡が来て、それなんてエイプリルフールネタ?と思ったのですが、本当っぽいです。
それはさておき、通勤途中に色々考えたのでメモっておきます。
昨日のエントリの補足的なメモになります。
AWSのCloudFormationとTerraformを比較すると?
GoogleのCloud Deployment Managerに対して、AWSにはCloudFormationがありますが、ちょっとググった感じ
- 記述がJSONなのが辛い
- dry-runがないのが辛い
という声がチラホラと。
これならAWSでTerraformを使いたくなる理由がわからなくはないです。
GoogleのCloud Deployment ManagerとTerraformを比較すると?
昨日も書きましたが、やっぱりCloud Deployment Managerの方が、Terraformより機能的にはイケていると思いました。
にプラスして、昨日書き忘れましたが、
- ◎ .tfstateをどこに保存するんだ?問題で悩まなくて良い(状態ファイル的なモノはGoogle側で管理してくれる)
- 最新機能もすぐ使える
- 公式の安心感
もあります!
なので無理やりまとめると
自分の場合(とくに会社のしがらみ等なければw)
という方針になりそうです。
とはいえ、今後も各所をウォッチして、最適解を考えていきたいと思います・・・
Terraformを使っていてGrunt/Gulpのことを思い出してもやもやする話
ちょっと今日もやもやしたことがあるので書き残しておきます。
Terraformが何かの説明はしないので、わかる人は読んでくださいということで。
Cloud Deployment Managerの方が良さそう
最近GCPをTerraformで触ってみているのですが、
の存在を知り、GCPに限って使うならこっちの方が高機能でいいなと。
- 全リソース(GCEとかNetworkとか)の設定をファイルとして管理できる
- プレビュー機能(dry-run的な)あり(WebのUIからも変更点が見られる)
- YAML/Python/Jinja2で設定が書ける
それでも、Terraformを使い続ける理由があるとすれば、AWSとかAzureとか複数のサービスにまたがって統一的なインターフェースを提供してくれていることかなと思ったんですが、そこでなぜか去年のGrunt/Gulpの話が頭に浮かびました。
タスクランナー!
去年あたりはフロントのJSを結構触っていたので、↓みたいなGrunt/Gulpのようなタスクランナーの話で頭を悩ましてました。
- Terraform → 統一的なインターフェースで、いろんなサービスのリソースを操作できる
- Grunt/Gulp → 統一的なインターフェースで、いろんなタスクを操作できる
的な抽象化レイヤーであるところに共通点があるなと感じました。
すると当然、TerraformにもGrunt/Gulp同様の↓の問題が起きます。
- TerraformのProviderで定義されているリソースの設定で、全てを設定できるわけではない
- サービス提供者側の機能が増えたとき追従するのにタイムラグがある
- バグったときに、原因究明の難易度があがる
安心と信頼のHashiCorpだから大丈夫でしょう!
Grunt/Gulpと比較して、Terraformの代替ツールが雨後の筍のようにポコポコ生まれてくることはなさそうだし、そもそもHashiCorpプロダクトだから大丈夫では? という話はあるでしょう。
が、インフラというクリティカルな領域で、抽象化レイヤーを挟んだことが原因でバグったら辛すぎるなとも思います。
実際、なんやかんやで嵌ったりしているという話はチラチラと聞きます(最新バージョンでは諸々解消されているかもしれませんが)
まとまらない
もやもやを書いただけなので、結論はありません。
AWSとか別サービスでTerraformを使ってはいないので、そちらだとまた話が違うかもしれません。
Expressのapp.useが記述した順で動かない?
基本的には設定した順に動く
v4からは公式(http://expressjs.com/en/4x/api.html)にある通り、
Middleware functions are executed sequentially, therefore the order of middleware inclusion is important.
つまり、設定した順序でmiddlewareが適用されるということになってます。
// this middleware will not allow the request to go beyond it app.use(function(req, res, next) { res.send('Hello World'); }); // requests will never reach this route app.get('/', function (req, res) { res.send('Welcome'); });
だと、Welcomeは出力されません。
しかし注意点
app.use
の間に一個でもapp.(all|get|post|put|delete)
が出てきたらその後のapp.use
は全て無効になってしまいます。
例えば
app.use('/foo', (req, res, next) => { console.log(1); next(); }); app.all('/foo/*', (req, res, next) => { console.log(2); next(); }); app.use('/foo', (req, res, next) => { console.log(3); next(); }); app.get('/foo/bar', (req, res) => { console.log(4); res.send('yo'); });
これだと、/foo/bar
にアクセスしても3が出力されません。
app.use('/foo', (req, res, next) => { console.log(1); next(); }); app.all('/baz/*', (req, res, next) => { console.log(2); next(); }); app.use('/foo', (req, res, next) => { console.log(3); next(); }); app.get('/foo/bar', (req, res) => { console.log(4); res.send('yo'); });
app.all('/baz/*'
みたいにパスが違っていてもダメ。
とにかく間に出てきたらダメ。
どうすればよいか
- 特別な理由がない限り
app.use
はapp.(all|get|post|put|delete)
よりも全て先に書く。 - middleware大杉な場合はrouterの分割を検討する -> http://expressjs.com/en/4x/api.html#router
あたりを守っていけばいいんじゃないでしょうか。
大したことないように思いますが、意外と嵌ったのでご注意を。
Expressで特定のパスだけ静的ファイルを配信する方法
本日APIを眺めていて、たまたま気づいたのでメモ。
何も考えずにやるなら、fs.readFile
で読み込んでres.send
なんですが、最初からAPIがありました。
それはこちらのres.sendFile
http://expressjs.com/en/4x/api.html#res.sendFile
使い方(APIまんま)
app.get('/file/:name', function (req, res, next) { var options = { root: __dirname + '/public/', dotfiles: 'deny', headers: { 'x-timestamp': Date.now(), 'x-sent': true } }; var fileName = req.params.name; res.sendFile(fileName, options, function (err) { if (err) { console.log(err); res.status(err.status).end(); } else { console.log('Sent:', fileName); } }); });
ちなみに、v4.8.0より前はres.sendfile
とCamelでないメソッド名なのでご注意ください。