Technical Articles
Cloud Foundry環境でのアプリの救助
これはメモ書き的なブログになります。ご意見やこういう方法があるという情報があればコメント欄に書いていただければ幸いです。
Cloud Foundryのアプリケーションを開発していてアプリをデプロイしたものの、ノートPCの故障でそのアプリのソースコードを消失させてしまうという事態が発生しました。
(だからまめにgitなりにcommit/pushしておけという当たりまえのことはさておき、)この状態から救助することを考えてみたいと思います。
SSH
Cloud Foundryではアプリケーションコンテナインスタンスに対してSSHでログインすることが可能です。いわばアプリケーションをインストールしたディレクトリに対してSSHで操作できるということになりますのでこれを使用することでデプロイしたファイル群にアクセス出来ることになります。
SSHでアクセスするにはスペースレベルでのSSH許可とアプリケーションレベルでのSSH許可を行う必要があります。アプリケーションがSSHでの接続を許可していてもそれが所属するスペースがSSHでの接続を許可していないと接続はできません。
これらはcfコマンドで行う必要がありますのでCloud Foundry Command Line Interface (CLI)のインストールが必要です。そして予めcli経由でログインしておきます。
$ cf api <endpoint> $ cf login |
スペースレベルでのSSH許可
スペースレベルでのSSH許可はSpace Managerの権限を持っているユーザーで行う必要があります。権限を持っていない場合は権限を持っているユーザーに依頼して作業を行ってもらう必要があるでしょう。スペース全体に関わることなので一時的にでしか許可してもらえないというケースもあるでしょう。
$ cf allow-space-ssh <SPACE NAME> |
許可を取り消すにはdisallow-space-sshとして同様のコマンドを実行します。
現在の状態の確認は
$$ cf space-ssh-allowed <SPACE NAME> ssh support is enabled in space ‘<SPACE NAME>’ |
で行うことができます。
アプリケーションレベルでのSSH許可
アプリケーションレベルでのSSH許可はSpace Developerの権限を持っているユーザーで行う必要があります。また、実際のログインもSpace Developerの権限を持ったユーザーでしか行なえません。
$ cf enable-ssh <APP NAME> |
<APP NAME>はアプリケーション名です。BTP Cockpitでアプリケーション一覧で表示されるアプリケーション名称を意味します。
許可を取り消すにはdisable-sshとして同様のコマンドを実行します。
現在の状態の確認は
$ cf ssh-enabled <APP NAME> ssh support is enabled for ‘<APP NAME>’ |
で行うことができます。が、重要なこととして、変更を反映させるにはアプリケーションの再スタートが必要です。私がテストした限りでは、enableに変更後、状態の確認でssh support is enabledと表示されていてもアプリケーションを再スタートさせないと変更は反映されないようで実際にはsshを使用することができませんでした。
SSHでログインしてみる
SSHを有効化した後、ログインしてみます。
Node.jsアプリの場合は以下のようになっていました。
% cf ssh nodejstestapp vcap@56dad81d-b04c-4283-6421-6ee8:~$ ls app deps logs profile.d staging_info.yml tmp vcap@56dad81d-b04c-4283-6421-6ee8:~$ cd app vcap@56dad81d-b04c-4283-6421-6ee8:~/app$ ls package.json server.js node_modules package-lock.json static |
記述したJavascriptのコードがそのままappディレクトリに入っていることが確認できます。
Javaアプリの場合は以下のようになっていました。
% cf ssh javatestapp vcap@7ef6de71-8eb6-4b87-60b7-0261:~$ ls app deps logs profile.d staging_info.yml tmp vcap@7ef6de71-8eb6-4b87-60b7-0261:~$ cd app vcap@7ef6de71-8eb6-4b87-60b7-0261:~/app$ ls index.html META-INF WEB-INF vcap@7ef6de71-8eb6-4b87-60b7-0261:~/app$ cd WEB-INF/ vcap@7ef6de71-8eb6-4b87-60b7-0261:~/app/WEB-INF$ ls beans.xml classes lib web.xml |
このアプリはJavaのWebアプリケーションでWarファイルを作成してデプロイしたものです。Warを解凍したものがappディレクトリ以下に入っている形になります。つまりはappディレクトリの内容をコピーできればWarファイルを再作成出来る形になるでしょう。
SCPは?
ファイルの在処がわかったのでこのファイル群をローカルにダウンロードしたいです。SSHが使えるのであればSCPも使えるはずです。が、上でSSHでログインしてみた際にはcfコマンドのSSHクライアント機能を使用しています。cfコマンドにSCPクライアントの機能があれば便利なのですが、現状はありませんので少々厄介な手順を取る必要があります。
まず接続先のエンドポイントを確認します。これは以下のコマンドで表示させることができます。
% cf curl /v2/info|grep app_ssh_endpoint “app_ssh_endpoint”: “ssh.cf.eu10.hana.ondemand.com:2222”, |
接続先のサーバー名とポート名がこれから取得できます。上記であれば接続先のサーバー名はssh.cf.eu10.hana.ondemand.com、ポートは2222です。
そして、接続時のユーザー情報に相当することになるアプリケーションのGUIDを取得します。
% cf app <APP NAME> –guid 16fa1e5e-de94-45f0-b7b1-21dca34baeec |
最後にパスワードを取得します。このパスワードはワンタイムパスワードで期限がありますので先に下のSCPコマンドを組み立てておいてから実行したほうが良いかもしれません。
% cf ssh-code uvufibcZSPt6eaYGtI3W |
scpコマンド実行に必要な情報が揃いました。scpコマンドを組み立てますが、ユーザー名は
cf:<アプリケーションGUID>/0
とする必要があります。最後の/0は0から始まるインスタンス番号です。もしアプリケーションが複数インスタンスで起動しており、2つ目のインスタンスに接続したいのであれば/0ではなく/1に返す必要があります。上記で取得したものからは
cf:16fa1e5e-de94-45f0-b7b1-21dca34baeec/0
となります。
scpコマンドの説明はmanやhelpを参照していただくとして、appディレクトリをまるごとローカルにコピーしたいということであれば上記のサンプル例からはscpコマンドは以下のようになります。
% scp -r -P 2222 -o User=cf:16fa1e5e-de94-45f0-b7b1-21dca34baeec/0 ssh.cf.eu10.hana.ondemand.com:/app ./ cf:16fa1e5e-de94-45f0-b7b1-21dca34baeec/0@ssh.cf.eu10.hana.ondemand.com’s password: (ここでワンタイムパスワードを入力) server.js 100% 21KB 34.5KB/s 00:00 mime 100% 149 0.6KB/s 00:00 mime.cmd 100% 271 1.0KB/s 00:00 mime.ps1 100% 478 1.8KB/s 00:00 …………(以下ファイルがローカルにコピーされていきます)…………. |
これでローカルにコピーできます。
私は試したことは有りませんが、GUI系のSCPクライアントでも(ものによっては)上記の情報で接続できると思います。
ソースコードを完全に救助出来るか?
これはそのアプリで使用している言語により異なると考えられます。上記の例でみたようにインタプリタ言語であるnode.jsやpython、あるいはHTML5アプリでであればデプロイしたアプリはそのコードとなるのでCloud Foundryのアプリケーションとしてそのコードがアップロードされていることになります。これらは救助出来るでしょう。
*ただ、実際はHTML5アプリ等の場合はトランスパイルされたものをデプロイすることがあります。このトランスパイルされたコードをどう捉えるかは別問題です。プログラマーが記述したオリジナルのコードではなく、非常に読みにくいコードになっている可能性は大ですが、ロジックなどは読めますので最悪の事態ではなくなるのではないでしょうか?
対してJavaなどのコンパイラ型言語を使用している場合、デプロイしたものはコンパイル後のファイルです。これはデコンパイラを使用することで人間が読めるコードに戻せることもありますが、先のトランスパイルされたコードと同じようにオリジナルの完全なコードとは異なるものになることが多いです。結果的にコンパイラ型言語を使用しているアプリの場合はコードの救助という面では難しいと言わざるを得ないと考えられます。但しアプリケーションを動作させる要素(DBの接続情報等)をコード中に静的に埋め込んでいなければ、救助したファイルを使用して違うサーバーに再デプロイするということには使えるはずです。
このようなことがないように
結局は「ちゃんとバックアップしろ」に尽きるのですが、このような状態になったときにも諦めないでください。
また、応用としてSSHでアクセスできるのでCloud Foundryアプリでファイルを出力し、それをSSHでアクセスして確認するということもできます。揮発してしまうという欠点があるので本番では難しいですが開発時には結構便利だったりします。