なっく日報

技術やら生活やらのメモ

あまり知られていない~/.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を設定するときとか。

stackoverflow.com

↑の回答にあるように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を書ければ特に何もいうことはありません!

http://jinja.pocoo.org/docs/dev/templates/

Google Cloud Deployment Managerでimportsを使ったときの嵌りどころ

Cloud Deployment Managerで

imports:
- path: other.jinja

みたいに書いて、他の設定ファイル(.pyや.jinja)をインクルードできる機能があるのですが、

エントリポイントにあたるファイルはYAMLにしないといけません(ドキュメントのどこにも書いてない)

結構嵌って、↓のstackoverflowでわかりました。

stackoverflow.com

事例が少ないから嵌ったときキツイなぁ・・・

確かに、

https://github.com/GoogleCloudPlatform/deploymentmanager-samples/tree/master/examples/v2/single_vm

とかのシンプルな例を見ても必ずエントリポイントのYAMLファイルを置いてます。

ご注意ください。

やっぱりCloud Deployment Managerの方がいい気がしてきた

妻から犬を買ったという連絡が来て、それなんてエイプリルフールネタ?と思ったのですが、本当っぽいです。

それはさておき、通勤途中に色々考えたのでメモっておきます。

昨日のエントリの補足的なメモになります。

yukidarake.hateblo.jp

AWSのCloudFormationとTerraformを比較すると?

GoogleのCloud Deployment Managerに対して、AWSにはCloudFormationがありますが、ちょっとググった感じ

  • 記述がJSONなのが辛い
  • dry-runがないのが辛い

という声がチラホラと。

これならAWSでTerraformを使いたくなる理由がわからなくはないです。

GoogleのCloud Deployment ManagerとTerraformを比較すると?

昨日も書きましたが、やっぱりCloud Deployment Managerの方が、Terraformより機能的にはイケていると思いました。

  • 全リソース(GCEとかNetworkとか)の設定をファイルとして管理できる
  • プレビュー機能(dry-run的な)あり(WebのUIからも変更点が見られる)
  • YAML/Python/Jinja2で設定が書ける

にプラスして、昨日書き忘れましたが、

  • ◎ .tfstateをどこに保存するんだ?問題で悩まなくて良い(状態ファイル的なモノはGoogle側で管理してくれる)
  • 最新機能もすぐ使える
  • 公式の安心感

もあります!

なので無理やりまとめると

自分の場合(とくに会社のしがらみ等なければw)

  • GCPのみ使う場合、Cloud Deployment Manager
  • AWSのみ使う場合、Terraform
  • GCPAWSハイブリッドな場合
    • AWSメインで、GCPも特に凝った使い方しないならTerraformにまとめてしまうかも
    • GCPメインなら、Cloud Deployment ManagerとTerraform併用するかも

という方針になりそうです。

とはいえ、今後も各所をウォッチして、最適解を考えていきたいと思います・・・

Terraformを使っていてGrunt/Gulpのことを思い出してもやもやする話

ちょっと今日もやもやしたことがあるので書き残しておきます。

Terraformが何かの説明はしないので、わかる人は読んでくださいということで。

Cloud Deployment Managerの方が良さそう

最近GCPをTerraformで触ってみているのですが、

cloud.google.com

の存在を知り、GCPに限って使うならこっちの方が高機能でいいなと。

  • 全リソース(GCEとかNetworkとか)の設定をファイルとして管理できる
  • プレビュー機能(dry-run的な)あり(WebのUIからも変更点が見られる)
  • YAML/Python/Jinja2で設定が書ける

それでも、Terraformを使い続ける理由があるとすれば、AWSとかAzureとか複数のサービスにまたがって統一的なインターフェースを提供してくれていることかなと思ったんですが、そこでなぜか去年のGrunt/Gulpの話が頭に浮かびました。

スクランナー!

去年あたりはフロントのJSを結構触っていたので、↓みたいなGrunt/Gulpのようなタスクランナーの話で頭を悩ましてました。

qiita.com

d.hatena.ne.jp

t32k.me

  • 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/*'みたいにパスが違っていてもダメ。

とにかく間に出てきたらダメ。

どうすればよいか

あたりを守っていけばいいんじゃないでしょうか。

大したことないように思いますが、意外と嵌ったのでご注意を。

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でないメソッド名なのでご注意ください。