右往左往ブログ

日々よりみち

serverspecでconfigファイルを複数行まとめてテストする

configファイルの記述の正当性をserverspecでテストするときは、ヒアドキュメントを使うといいかも、という話です。


configファイルを普通にテストする

serverspecで、configファイルの記述をテストするとき、普通はits(:content)matcherを使います。

describe file('/etc/httpd/conf/httpd.conf') do
  its(:content) { should match /ServerName www.example.jp/ }
end

が、configの記述が1行では意味をなさず、複数行にわたって「ブロック」として記載されていたとき、昔はits(:content)matcherをひたすら繋げて書いていました。

describe file('/etc/td-agent/td-agent.conf') do
  its(:content) { should match %r{^[[:blank:]]*<source>} }
  its(:content) { should match %r{^[[:blank:]]*type monitor_agent} }
  its(:content) { should match %r{^[[:blank:]]*bind 0.0.0.0} }
  its(:content) { should match %r{^[[:blank:]]*port 24220} }
  its(:content) { should match %r{^[[:blank:]]*</source>} }
end

例はtd-agent.confのテストです。 この記述のダメなところは、結局行単位で評価しているので、前後関係が全く意味を成さないところです。それぞれの行をconfig内のどこにどういう順番で書こうが、そのconfigが実際には無効な記述だったとしても、serverspecのテストは通ります。

素直にits(:content)をまとめて書く

一応、最初から最後までまとめて1つのmatcherで繋げて書くことはできます。

describe file('/etc/td-agent/td-agent.conf') do
  its(:content) { should match %r{<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n</source>} }
end

この記述の良くないところは、仮にテストがfailedしたときに、どこが間違っているのかわかりづらいところです。

  2) File "/etc/td-agent/td-agent.conf" content should match /<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n<\/source>/
     On host `localhost'
     Failure/Error: its(:content) { should match %r{<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n</source>} }
       expected "<match debug.**>\n  type stdout\n</match>\n\n<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24222\n</source>\n\ninclude /etc/td-agent/conf.d/*.conf\n\n" to match /<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n<\/source>/
       Diff:
       @@ -1,2 +1,12 @@
       -/<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n<\/source>/
       +<match debug.**>
       +  type stdout
       +</match>
       +
       +<source>
       +  type monitor_agent
       +  bind 0.0.0.0
       +  port 24222
       +</source>
       +
       +include /etc/td-agent/conf.d/*.conf

上記はport番号を24222とわざと間違って書いているのですが、ブロック全体が誤っていると捉えられます。というか、いちいちテストコードを書くのも、わざわざ改行を¥nで書かなければいけなかったりと面倒です。

ヒアドキュメントを使う

test-monitor_agent = <<"EOS"
<source>
  type monitor_agent
  bind 0.0.0.0
  port 24220
</source>
EOS

describe file('/etc/td-agent/td-agent.conf') do
  its(:content) { should match test-monitor_agent }
end

もしテストがfailedしても、その部分を判別してくれます。

  1) File "/etc/td-agent/td-agent.conf" content should match "<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n</source>\n"
     On host `localhost'
     Failure/Error: its(:content) { should match test }
       expected "<match debug.**>\n  type stdout\n</match>\n\n<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24222\n</source>\n\ninclude /etc/td-agent/conf.d/*.conf\n\n" to match "<source>\n  type monitor_agent\n  bind 0.0.0.0\n  port 24220\n</source>\n"
       Diff:


       @@ -1,6 +1,12 @@
       +<match debug.**>
       +  type stdout
       +</match>
       +
        <source>
          type monitor_agent
          bind 0.0.0.0
       -  port 24220
       +  port 24222
        </source>
       +
       +include /etc/td-agent/conf.d/*.conf

普通に書く

普通に書いても動きます。(これ昔はできなかったと思ったのですが…昔から動いてましたっけ?)

describe file('/etc/td-agent/td-agent.conf') do
  its(:content) { should match "
<source>
  type monitor_agent
  bind 0.0.0.0
  port 24220
</source>" }
end

当たり前ですがテストfailed時もヒアドキュメントと同じ表示になります。

一番簡単なのはこれですが、テストコードとconfigの記載が混在するので見づらいという問題はあります。好みですが、自分ならヒアドキュメントで変数化して使うかなという印象です。


そう考えると、configファイルをどこまで厳密にチェックするかは考えものです。

そこまでやるなら、もうconfigファイルの最初から最後までまるっとspecファイルに書いてテストすればいいんじゃないかとか、そもそもそれやるならもうserverspecじゃなくて、正しいconfigとのdiffでいいんじゃないかとか思ったりもします。configファイルのテストはある程度妥協して、あとは正しく動くのかというinfratasterの範疇になるかもしれません。

CheckInstallでruby 2.2.0のrpmパッケージを作成する

前回CentOS6にCheckInstallを導入したので、今回はCheckInstallを使ってruby-2.2.0をrpm化してみます。

以下を参考にしました。

CentOSにRubyをcheckinstallでRPMにしてインストールする。 - オープンソースこねこね

# wget http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.0.tar.gz
# tar -zxvf ruby-2.2.0.tar.gz
# cd ruby-2.2.0
# ./configure --enable-shared --prefix=/usr

注意点としては、configure時に--enable-shared--prefix=/usrを指定することです。

--enable-sharedは、指定しないとインストールの途中でライブラリが足りなくなってエラーになります。
libffi-develyumでインストールすればよいという記事もありましたが、自分の環境だとそれでもエラーになります。

また、--prefix=/usrについては、指定しないとデフォルトで--prefix=/usr/localを指定していることになり、 最終的にrpmパッケージをインストールするときに依存パッケージであるlibrubyが/usr/bin/rubyを参照しようとしてエラーになります。

# make 
# cp -p ruby bin/
# checkinstall --fstrans=no

rubyバイナリをbin/配下にコピーしている理由は、最初のリンクの通りです。
最終的なcheckinstallコマンド実行時は、最後に余計なディレクトリをパッケージングするか確認を求められるので、 含めないようにしてください。

あとはrpmパッケージができるので、インストールするだけ。

CentOS6にcheckinstallを導入する

ruby-2.2.0のrpmを作成したかったので、checkinstallをCentOS6 (64bit)に導入しました。

CheckInstallとは

ソースコードからmake, make installによってインストールするソフトウェアについて、rpmなどのパッケージングを自動で行ってくれるソフトです。
これがないと、specファイルを自分で書かなければならず、非常に手間がかかりますが、checkinstallによって比較的簡単にrpmパッケージを作成することができます。
(作成できるのはrpmパッケージだけではありませんが)

CheckInstall

導入手順

基本的に以下に従って導入しました。

CentOS 6.3 に checkinstall をインストールする - パンダのメモ帳
CentOSにcheckinstallをgitからインストールする - オープンソースこねこね

手順

基本的にrootユーザで実行しています。

必要パッケージ導入

# yum -y install git gcc make gettext rpm-build

CheckInstall導入前準備

# cd /var/tmp
# git clone http://checkinstall.izto.org/checkinstall.git
# cd checkinstall/
# sed -i 's:CONFDIR=$(PREFIX)/lib/checkinstall:CONFDIR=$(PREFIX):g' Makefile
# sed -i 's:EXCLUDE="":EXCLUDE="/selinux":g' checkinstallrc-dist
# sed -i 's:LIBDIR=$(PREFIX)/lib:LIBDIR=$(PREFIX)/lib64:g' installwatch/Makefile

CheckInstall rpmパッケージ作成

導入前に、CheckInstall自体のrpmパッケージを作成します。

# make 
# make install
# mkdir -p /root/rpmbuild/SOURCES
# checkinstall --install=no

途中でいろいろと質問がありますが、rpmを指定するくらいでほとんど気にする必要はありません。

checkinstall 1.6.3, Copyright 2010 Felipe Eduardo Sanchez Diaz Duran
           This software is released under the GNU GPL.

The checkinstallrc file was not found at:
/usr/local/lib/checkinstall/checkinstallrc

Assuming default values.


Please choose the packaging method you want to use.
Slackware [S], RPM [R] or Debian [D]? R


**************************************
**** RPM package creation selected ***
**************************************

This package will be built according to these values:

1 -  Summary: [ CheckInstall installations tracker, version 1.6.2 ]
2 -  Name:    [ checkinstall ]
3 -  Version: [ 20150207 ]
4 -  Release: [ 1 ]
5 -  License: [ GPL ]
6 -  Group:   [ Applications/System ]
7 -  Architecture: [ x86_64 ]
8 -  Source location: [ checkinstall ]
9 -  Alternate source location: [  ]
10 - Requires: [  ]
11 - Provides: [ checkinstall ]

Enter a number to change any of them or press ENTER to continue:

Installing with make install...

========================= Installation results ===========================

中略

Copying files to the temporary directory...OK

Stripping ELF binaries...OK

Compressing man pages...OK

Building file list...OK

Building RPM package...OK

NOTE: The package will not be installed

Erasing temporary files...OK

Writing backup package...OK

Deleting temp dir...OK


**********************************************************************

 Done. The new package has been saved to

 /root/rpmbuild/RPMS/x86_64/checkinstall-20150207-1.x86_64.rpm
 You can install it in your system anytime using:

      rpm -i checkinstall-20150207-1.x86_64.rpm

**********************************************************************

CheckInstall導入

# cd /root/rpmbuild/RPMS/x86_64
# rpm -ivh checkinstall-20150207-1.x86_64.rpm
Preparing...                ########################################### [100%]
   1:checkinstall           ########################################### [100%]

インストールの確認

# rpm -qi checkinstall
Name        : checkinstall                 Relocations: (not relocatable)
Version     : 20150207                          Vendor: (none)
Release     : 1                             Build Date: Sat 07 Feb 2015 08:00:05 PM UTC
Install Date: Sat 07 Feb 2015 08:01:58 PM UTC      Build Host: localhost
Group       : Applications/System           Source RPM: checkinstall-20150207-1.src.rpm
Size        : 460811                           License: GPL
Signature   : (none)
Packager    : checkinstall-1.6.3
Summary     : CheckInstall installations tracker, version 1.6.2
Description :
CheckInstall installations tracker, version 1.6.2

CheckInstall  keeps  track of all the files created  or
modified  by your installation  script  ("make install"
"make install_modules",  "setup",   etc),   builds    a
standard   binary   package and  installs  it  in  your
system giving you the ability to uninstall it with your
distribution's  standard package management  utilities.

インストールできました。
次回、CheckInstallruby-2.2.0をrpm化してみます。

consulをdockerで動かすときにホストからアクセスするときのメモ

以下の方法でconsulを検証していたのですが、1個だけハマったのでメモ。

Dockerを使って軽くConsulを触ってみる - さくらのナレッジ
基本的に上記方法に従います。

構成

Mac -> CoreOS(VM) -> docker container

docker container上でconsulを起動し、それをmacからアクセスして確認しました。

設定

  • Vagrantfile (途中略)
config.vm.network "forwarded_port", guest:28400, host:28400 # consul(remote rpc)
config.vm.network "forwarded_port", guest:28500, host:28500 # consul web api
config.vm.network "forwarded_port", guest:28600, host:28600 # consul dns
  • Dockerfile (途中略)
# consul
EXPOSE 8400
EXPOSE 8500
EXPOSE 8600
  • 起動時
core@core-01 ~ $ docker run -itd --name="consul-agent1" --hostname="consul-agent1" -p 20022:22 -p 28400:8400 -p 28500:8500 -p 28600:8600 test
core@core-01 ~ $ docker run -itd --name="consul-agent2" --hostname="consul-agent2" -p 30022:22 -p 38400:8400 -p 38500:8500 -p 38600:8600 test
core@core-01 ~ $ docker run -itd --name="consul-server1" --hostname="consul-server1" -p 40022:22 test
core@core-01 ~ $ docker run -itd --name="consul-server2" --hostname="consul-server2" -p 50022:22 test
core@core-01 ~ $ docker exec -it consul-server1 /bin/bash
[root@consul-server1 /]# consul agent -data-dir=/tmp/consul -server -bootstrap-expect 2 -client=`hostname --ip-address` &
[root@consul-server1 /]# exit
core@core-01 ~ $ docker exec -it consul-server2 /bin/bash
[root@consul-agent1 /]# consul agent -data-dir=/tmp/consul -server -join=10.1.0.3 -client=`hostname --ip-address` &
[root@consul-server2 /]# exit
core@core-01 ~ $ docker exec -it consul-agent1 /bin/bash
[root@consul-agent1 /]# consul agent -data-dir=/tmp/consul -join=10.1.0.3 -client=`hostname --ip-address` &
[root@consul-agent1 /]# exit
core@core-01 ~ $ docker exec -it consul-agent2 /bin/bash
[root@consul-agent2 /]# consul agent -data-dir=/tmp/consul -join=10.1.0.3 -client=`hostname --ip-address` &
[root@consul-agent2 /]# exit

ここでは、consul-server1IPアドレスは10.1.0.3としています。

これで、docker container上では8400,8500,8600のポートが、CoreOS(VM)上では28400,28500,28600へ変換され、さらにMac上ではPortForwardingにより28400,28500,28600へ変換されます。

接続

mac上から、以下で繋がります。

$ consul members -rpc-addr=127.0.0.1:28400
Node            Address        Status  Type    Build  Protocol
consul-agent1   10.1.0.2:8301  alive   client  0.4.1  2
consul-server2  10.1.0.4:8301  alive   server  0.4.1  2
consul-server1  10.1.0.3:8301  alive   server  0.4.1  2
consul-agent2   10.1.0.5:8301  alive   client  0.4.1  2
$ curl -s localhost:28500/v1/catalog/nodes | jq "."
[
  {
    "Address": "10.1.0.2",
    "Node": "consul-agent1"
  },
  {
    "Address": "10.1.0.5",
    "Node": "consul-agent2"
  },
  {
    "Address": "10.1.0.3",
    "Node": "consul-server1"
  },
  {
    "Address": "10.1.0.4",
    "Node": "consul-server2"
  }
]

ハマったこと

consul agentコマンド実行時に、-clientオプションを入れないと、localhostからのアクセスしか受け付けません。従って、mac上からipを叩いても結果を取得できません。

例えば、以下のように-clientオプションなしだと、localhost以外からconsulコマンドでアクセスしてもエラーになります。

[root@consul-agent1 /]# consul agent -data-dir=/tmp/consul -join=10.1.0.3 &

普通に、consul agentコマンドのオプションに書いてあるんですけどね…

  -client=127.0.0.1        Sets the address to bind for client access.
                           This includes RPC, DNS and HTTP

zabbixで運用していた人間がsensuを触ってみた感想

zabbixでシステム運用に関わっている人間が、sensuを使った運用にチャレンジした話です。
結論としては、運用に本格的に入る前に、sensuの導入を断念しました。

f:id:unchemist:20140518200938p:plain:medium

sensuとは

f:id:unchemist:20140518200929j:plain

sensuは、クラウド環境に親和性の高い監視・モニタリングソフトウェアです。
http://sensuapp.org/

大まかなポイントは以下だと思います。

  • Chefやpuppetで簡単に導入できる
  • community pluginが豊富
  • 設定が全てコードで記載されている。変更するときもjsonファイルの書き換え
  • クライアント側からサーバへ通知して監視が始まる(サーバ側にクライアントの定義が必要ない) → AWS EC2のようなImmutableに扱う環境で便利

詳細は以下の記事が詳しいです。
【DevOpsDays Tokyo 2013】クラウド時代のモニタリングツール、UNIXの思想が息づくsensuチュートリアル
監視ソフトをNagiosからSensuに切り替えて2ヶ月経ったのでまとめた

使ってみた状況

  • オンプレミス環境でzabbixを使い、数ヶ月の運用を行っていた
  • AWS環境を利用するにあたって、sensuの導入を検討した
  • 監視+モニタリングが要件 なので、必然的にgraphiteも導入した
  • AWS環境ではBlue Green Deploymentを導入。従って頻繁にEC2インスタンスを作り替える

感想

sensu(とgraphite)が物凄く使いづらい印象を受けました。以下、sensuを使ってみた感想です。

導入が容易じゃない

確かに、基本インストールは簡単です。chef-soloで導入しましたが、erlangのインストールトラブルはあったものの、割とすんなり入れることができます。
ただ、簡単なのはあくまで基本インストールのみで、実際に使うためにはほとんどの場合は以下を行うことになります。

  • community pluginの導入
  • subscribers (クライアントを役割別に分けるグループ名) の設定
  • モニタリング用にgraphiteの導入

特に面倒なのが後半2つです。

subscribersの設定は、クライアントごとにどうやって名前を定義するか?それをいかに自動的に設定するか?のベストプラクティスがまだ確立されていない気がします。そのため、色々な方法はあるものの、ある程度アーキテクチャを自分で考えて作りこむ必要があります。なおchef-serverで導入する場合は、role名が自動で割り当たるらしいので、それほど大変ではないみたいですが…。

graphiteはそもそもsensuではないので、自動的にはインストールされません。なので、chefに組み込むところから始まります。後述しますが、必要となるソフトウェアも多く、Djangoに馴染みがないならなおさら大変です。

メンテナンスが容易じゃない

sensuを導入すると、(自動でインストールされるとはいえ)以下の面倒を見る必要が生じます。

  • sensu本体
  • rabbitmq
  • redis

ということは、障害発生時は上記をチェックする必要があるということです。 そして、sensu-adminを導入すると、更に…

が増えます。
graphiteも導入すると、更に増えます。

sensuはRuby、sensu-adminはRuby on Rails、graphiteはpython (Django)というのもちぐはぐで、なんだかなぁという感じ。

ちなみにzabbixなら

  • zabbix本体 (zabbix-server, zabbix-agent)
  • データベース

で済みます。面倒を見なければならないソフトウェアは少ないほうがいいかと。

使用感もあんまり

sensuは監視ソフトウェア、graphiteはグラフ化のソフトウェアなので、監視とモニタリングでいちいち画面遷移しなければいけません。少なくとも素のsensu管理画面はかなり簡素なので、機能は必要最低限しかないように思います。シンプルという見方もできますが。

設定がjson形式だから見やすい、という見方もあるにはあるのですが、所詮ファイルなので、いろんな軸で見る、という多面的な見方はできません。

また、graphiteは完全なグラフ化ソフトウェアなので、"メトリクス取得のための"ソフトウェアではありません。そのため、いろんなサーバで横断的に見るとか複数のグラフを並べるとか、そんな気の利いたことはできません。これはsensuの問題ではありませんが。

ほとんどの機能はzabbixでもできる

もともとの触れ込みである"クライアント側からサーバへ通知して監視が開始"の仕組みも、zabbixではディスカバリ機能を使うことでほぼ同様のことができます。

sensuはapiを提供しているので独自の仕組みを作りやすい、という意見も、zabbixだってAPIを提供しています。

逆に、zabbixでできてsensuでできないことは多いです。sensu管理画面自体の認証とか、メンテナンス機能(一時的にアラートを抑止する機能)とか。後者はsensu-adminを導入すれば可能かもしれません。

community pluginが動かない…ものもある

1ヶ月くらい前に触ってみた当初は、elasticsearchのpluginが最新バージョンに対応していなかったり、別のプラグインで引数を用意しているくせに動かない(純粋なバグ)というものもありました。pull requestしろよっていう感じかもしれませんが…。幸いコード自体は簡単なので、修正することはできます。ただ、いちいちpluginくらいで信用しないことを前提にバグとか検証で時間を取っていられないし、動くことを前提としたかったなぁというのはあります。

ただ、zabbixだとユーザパラメータとか外部スクリプトとかでプラグイン相当の仕組みはあるにはあるのですが、communityとしてまとまっているわけではないので、その都度ネット上から探す(もしくは一から作る)必要があります。基本的なプロセス、ポート、リソース監視などだけで済むなら考える必要はありませんが、新しいソフトウェアがAPIを提供していて、API経由で新しいメトリクスを取得できるとすると、その仕組みに追従できるのはsensuの方が可能性があるかもしれません。

コードベースの変更管理

これは完全に志向の問題ですが、設定が全てjsonで記載されているということは、GUIからポチポチ設定を変更できないということです。変更履歴をコードベースで管理できるので、例えばgithubなんかで管理すると、いつ誰が設定を変更したかは分かりやすいと思います。
一方で、GUIでカジュアルに変更したいという志向のチームだと、かなり敷居が高いでしょう。設定を変更するだけで、その都度git pull → コード変更 → コミットしてpush → デプロイ となるので。変更履歴を取るか、カジュアルに設定変更できることを取るかはチームによると思います。

まとめ

最初に挙げた特徴の反対意見として、以下のように感じました。

  • Chefやpuppetで簡単に導入できる
    →ただし、簡単なのは基本インストールまでで、追加設定まで行うとそれなりに面倒
  • community pluginが豊富
    →community pluginでいろんなソフトウェアに対して簡単に監視が行えるし、今後もきっと追加されていくだろう
    ただし、バグが多い
  • 設定が全てコードで記載されている。変更するときもjsonファイルの書き換え
    →確かに1側面では見やすい。ただ、色々な軸で横断的に見るのには向いていない。
    設定変更履歴をgitなどで取ることができるのは便利
  • クライアント側からサーバへ通知して監視が始まる(サーバ側にクライアントの定義が必要ない)
    →確かにそうですがzabbixでもできる

さらに…

  • そもそもsensuを利用するために様々なソフトウェアを導入する必要があり、それらをメンテナンスする必要がある
  • 色々なソフトウェア、サービスと連携できるという触れ込みだが、例えばグラフ化はgraphiteという以外にそんなに選べるほど選択肢がある状況でもない

ちなみにリソースに関しては、長期間で運用したわけではないので評価できません。

sensuを利用するのに適した状況

なので、sensuを使いたいという場合は、以下になると思います。

  • sensu apiを利用して、一から手に馴染む監視サービスを作る覚悟がある
  • GUIで設定変更とか軟弱なことはしない、もしくは変更履歴をしっかり取ることが求められる
  • もともとrabbitmqやredis, graphiteなど関わりのあるソフトウェアを利用している
  • 今後も新しいソフトウェアをどんどん導入していく予定で、それを監視する予定

そう考えると、zabbixはやっぱり監視・モニタリング用によく考えられているのだなぁと思います。

外部(からの)アクセスに便利なsshポートフォワーディング

社内から社外へは接続できるけれども、社外から社内へは自由にアクセスできない…というのがよくあるセキュリティだと思いますが、この方法を使うことで、セキュリティ設定を変更せずに外部からアクセスが可能になります。

最初にこの方法を教えてもらった時に、感動しました。

条件

以下のマシンがあることが条件です。

  • 内部ネットワーク→インターネットへ接続できるサーバが内部にあり、インターネット側へsshできること(踏み台2とします)
  • 踏み台2は最終的に繋ぎたい内部システム(例ではinternal:80)などへアクセスできること
  • 最終的なアクセス元となる環境→インターネット上のサーバへssh接続できる環境があること(AWSなど)(踏み台1とします)

接続イメージ

説明する方法で、自分の端末の任意のポートが、内部ネットワークのアクセス先とマッピングされます。
localhost:20080が、まるで内部ネットワークのinternal:80に繋がるようなイメージです。

f:id:unchemist:20140322230439p:plain

方法

  1. 内部ネットワーク(踏み台2)→外部(踏み台1)へ接続する
    踏み台2から以下のコマンドを実行します。
ssh –N –R 10080:internal:80 user@x.x.x.x

これで、踏み台110080番ポートと踏み台2の先internal:80が繋がります。

f:id:unchemist:20140322224340p:plain

この時点で、x.x.x.x:10080にアクセスするだけで、外部から内部へアクセスできます。
が、この場合は外部から内部ネットワークが丸見えになる危険性があります。Public IPとポート番号さえ分かってしまえば、外部からアクセスできてしまいます。(AWSの場合はSecurityGroupなどで制御することも可能ですが…)
そこで、自分の端末からだけ内部ネットワークにアクセスできるようにします。

  1. 自分の端末→外部(踏み台1)へ接続する
    自分の端末から以下のコマンドを実行します。
ssh –N –L 20080:localhost:10080 user@x.x.x.x

これで、自分の端末の20080番ポートが踏み台1の10080番ポートと繋がりました。

f:id:unchemist:20140322224345p:plain

1.と2.を組み合わせることで、自分の端末の20080番ポートが、内部ネットワークのinternal:80に繋がることになります。
例えば内部ネットワーク上のwebシステムであれば、外部からでもhttp://localhost:20080/でアクセスできるようになります。

f:id:unchemist:20140322224350p:plain

仕組み

  • 踏み台2のコマンド:
ssh –N –R 10080:internal:80 user@x.x.x.x

-Rは、ssh接続先のポート番号と繋ぎたい先をマッピングするためのオプションです。
この場合は、x.x.x.x:10080internal:80マッピングしています。
-Nは、リモートコマンドを発行しないオプションです。ポートフォワーディング用に指定するものと思ってください。
cronなどで、万が一切れてしまったら自動的に繋ぎ直すようにしておけばより安心です。

  • 自分の端末のコマンド:
ssh –N –L 20080:localhost:10080 user@x.x.x.x

-Lは、自分自身のポート番号と繋ぎたい先をマッピングするためのオプションです。
この場合は、自分の端末の20080番ポートとx.x.x.x:10080マッピングしています。
-Rの時は、ssh接続先のポート番号を指定していましたが、-Lの場合はssh接続のポート番号を指定しています。
また、コマンド中に出てくるlocalhost:10080は、あくまで踏み台1(x.x.x.x)から見たlocalhostであることに注意してください。
ですので、ほぼ以下のコマンドと同義です。

ssh –N –L 20080:x.x.x.x:10080 user@x.x.x.x

最終的には、以下のようなイメージになります。

<自分の端末>:20080 → 踏み台1(x.x.x.x):10080 → 踏み台2 → internal:80

sshポートフォワーディングは、あくまでホスト:ポート同士をsshを通じてトンネリングしているだけなので、上記でいう踏み台1は、sshのポートしか開放する必要がありません。
ですので、ある程度のセキュリティをは担保できることになります。公開鍵による認証にすれば、より安心だと思います。

使い方によってはリスクになりうる方法ですので、理解した上でご利用ください。

Chefからsensuをインストールする時にerlang導入で失敗する

sensuをChefでインストールしようとすると、途中でエラーになって止まります。

Sensu | An open source monitoring framework

  • AWS EC2
  • CentOS 6.4
  • sensu-chef 0.8
  • sensu 0.12
Recipe: erlang::package
  * package[erlang] action install
================================================================================
Error executing action `install` on resource 'package[erlang]'
================================================================================


Chef::Exceptions::Exec
----------------------
 returned 1, expected 0


Resource Declaration:
---------------------
# In /root/chef-repo/cookbooks/erlang/recipes/package.rb

 46:   package 'erlang'
 47: end



Compiled Resource:
------------------
# Declared in /root/chef-repo/cookbooks/erlang/recipes/package.rb:46:in `from_file'

package("erlang") do
  action :install
  retries 0
  retry_delay 2
  package_name "erlang"
  version "R16B03-0.2.el6"
  cookbook_name :erlang
  recipe_name "package"
end

そこで、以下の記事を見つけたので、それを試してみました。

CentOS 6.5でChefからSensuをインストールしようとするとRabbitMQでコケる回避策 - さよならインターネット
こちらの記事ではRabbitMQ起動時に失敗するとのことでしたが、自分の環境ではインストール時に失敗します。

そして、上記の記事の通り、以下のようにrecipeを書き換えて実行…しても、止まってしまいました。

diff --git cookbooks/erlang/recipes/package.rb cookbooks/erlang/recipes/package.rb
index 19f9fce..6cec68f 100644
--- cookbooks/erlang/recipes/package.rb
+++ cookbooks/erlang/recipes/package.rb
@@ -43,5 +43,9 @@ when 'rhel'
     include_recipe 'yum-erlang_solutions'
   end

-  package 'erlang'
+  execute "yum install -y erlang-R16B02" do
+    user "root"
+    command "yum install -y erlang-R16B02"
+    not_if { File.exists? "/usr/bin/erl" }
+  end
 end

そこで結局取ったのは、上記erlangインストールに入る前に、そもそもインストールしてしまうという強引な方法…。
以下のコードをcookbooks/sensu/recipes/default.rbに記載します。

include_recipe "yum-epel"

package "erlang" do
  action :install
  options "--enablerepo=epel"
end

これで、Chefでインストールが通るようになりました。
これ、結局ryuzeeさんの方法なんですよね。

ryuzeeさんがなぜwrapperのcookbookを作って、わざわざerlangを先にインストールしているのかがようやく分かりました。
Sensuを使って自由度の高い監視システムの構築を行う方法 | Ryuzee.com
https://github.com/ryuzee-cookbooks/sensu-server-wrapper