この Gist は Cloud Foundry Advent Calendar 2017 の12日目の記事です。
アプリの実行環境の環境変数を知るには, cf ssh
でアプリのコンテナーに入って,以下のスクリプトを実行すれば(たぶん)OK:
cat /proc/$(ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } ' | sort -n | head -n 1)/environ | sed -e 's/\x0/\n/g'
この Advent Calendar の 12/6の私の記事 で,「task を実行する際は PATH
上に何があるのかを知ることが重要」ということを書いたが,今回は, PATH
も含め,アプリの環境変数をどうやって知ればいいのかという話。
以下,このアプリ を以下の manifest.yml を使ってデプロイしたものを例として用いる。
manifest.yml:
---
applications:
- name: nodetask
env:
MY_ENV: my-value
アプリの環境変数は, cf env <アプリ名>
で取得できるのでは,と思われるかもしれないが,これでわかるのはごく一部で, PATH
も HOME
も知ることはできない。
例:
$ cf env nodetask
Getting env variables for app nodetask in org example-org / space example-spc as nota...
OK
System-Provided:
{
"VCAP_APPLICATION": {
"application_id": "a5b6ab92-90b8-401f-b3b8-c51ebee8ef52",
"application_name": "nodetask",
"application_uris": [
"nodetask.example.org"
],
"application_version": "e4551ace-13c3-4a15-942b-3d268efad711",
"cf_api": "https://api.example.org",
"limits": {
"disk": 128,
"fds": 16384,
"mem": 128
},
"name": "nodetask",
"space_id": "930a3384-7b8c-4262-a083-8cc11db48f0f",
"space_name": "example-spc",
"uris": [
"nodetask.example.org"
],
"users": null,
"version": "e4551ace-13c3-4a15-942b-3d268efad711"
}
}
User-Provided:
MY_ENV: my-value
No running env variables have been set
No staging env variables have been set
それなら cf ssh <アプリ名>
で実際にコンテナーに入って調べれば…ということになりそうだが,これも実はそう単純ではない。
例:
$ cf ssh nodetask
$ env
CF_INSTANCE_ADDR=10.0.16.22:62543
TERM=xterm
VCAP_APP_PORT=8080
USER=vcap
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
CF_INSTANCE_INTERNAL_IP=10.254.0.2
VCAP_APPLICATION={"cf_api":"https://api.example.org","limits":{"fds":16384,"mem":128,"disk":128},"application_name":"nodetask","application_uris":["nodetask.example.org"],"name":"nodetask","space_name":"example-spc","space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","uris":["nodetask.example.org"],"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","version":"e4551ace-13c3-4a15-942b-3d268efad711","application_version":"e4551ace-13c3-4a15-942b-3d268efad711"}
CF_INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
PATH=/bin:/usr/bin
PWD=/home/vcap
LANG=en_US.UTF-8
MY_ENV=my-value
CF_INSTANCE_PORT=62543
CF_INSTANCE_IP=10.0.16.22
VCAP_SERVICES={}
CF_INSTANCE_INDEX=0
SHLVL=1
HOME=/home/vcap
CF_INSTANCE_PORTS=[{"external":62543,"internal":8080},{"external":62544,"internal":2222}]
LESSOPEN=| /bin/lesspipe %s
INSTANCE_INDEX=0
PORT=8080
INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
VCAP_APP_HOST=0.0.0.0
LESSCLOSE=/bin/lesspipe %s %s
MEMORY_LIMIT=128m
_=/usr/bin/env
一見大丈夫そうだが,Node.js に関係する情報が何も含まれていない。
なぜか。
理由は単純で,アプリの実行プロセスは cf ssh
で生成されたシェルのプロセスとは何の関係もないからである。
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:40 ? 00:00:00 /proc/self/exe init
vcap 13 0 0 14:40 ? 00:00:00 /tmp/lifecycle/diego-sshd --allowedKeyExchanges= --address=0.0.0.0:2222 --allowUnauthenticatedClients=false
vcap 14 0 0 14:40 ? 00:00:00 npm
vcap 66 14 0 14:40 ? 00:00:00 sh -c node server.js
vcap 67 66 0 14:40 ? 00:00:00 node server.js
vcap 124 13 0 14:42 pts/0 00:00:00 /bin/bash
vcap 421 124 0 14:56 pts/0 00:00:00 ps -ef
アプリの起動に関係するプロセス木は 14
=> 66
=> 67
であり,cf ssh
のシェルのプロセス木は 13
=> 124
である。前者と後者は互いに独立している。
アプリの実行環境の環境変数を知るためには,プロセス 14
の環境変数を調べればよい。
これは /proc/14/environ
に入ってるので, cat
で覗いてみる:
$ cat /proc/14/environ
CF_INSTANCE_ADDR=10.0.16.22:62543TMPDIR=/home/vcap/tmpWEB_MEMORY=512MEMORY_AVAILABLE=128VCAP_APP_PORT=8080USER=vcapCF_INSTANCE_INTERNAL_IP=10.254.0.2VCAP_APPLICATION={"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","application_name":"nodetask","application_uris":["nodetask.example.org"],"application_version":"e4551ace-13c3-4a15-942b-3d268efad711","cf_api":"https://api.example.org","host":"0.0.0.0","instance_id":"6ddbf04f-b643-4d87-7844-9fc6","instance_index":0,"limits":{"disk":128,"fds":16384,"mem":128},"name":"nodetask","port":8080,"space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","space_name":"example-spc","uris":["nodetask.example.org"],"version":"e4551ace-13c3-4a15-942b-3d268efad711"}CF_INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6PATH=/home/vcap/deps/0/bin:/usr/local/bin:/usr/bin:/bin:/home/vcap/app/bin:/home/vcap/app/node_modules/.binWEB_CONCURRENCY=1PWD=/home/vcap/appLANG=en_US.UTF-8MY_ENV=my-valueCF_INSTANCE_PORT=62543NODE_ENV=productionVCAP_SERVICES={}CF_INSTANCE_IP=10.0.16.22CF_INSTANCE_INDEX=0HOME=/home/vcap/appSHLVL=1DEPS_DIR=/home/vcap/depsCF_INSTANCE_PORTS=[{"external":62543,"internal":8080},{"external":62544,"internal":2222}]INSTANCE_INDEX=0PORT=8080INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6VCAP_APP_HOST=0.0.0.0MEMORY_LIMIT=128mNODE_HOME=/home/vcap/deps/0/node_=/home/vcap/deps/0/bin/npm
改行が入ってなくて見づらいので,NULL
文字を改行で置換:
$ cat /proc/14/environ | sed -e 's/\x0/\n/g'
CF_INSTANCE_ADDR=10.0.16.22:62543
TMPDIR=/home/vcap/tmp
WEB_MEMORY=512
MEMORY_AVAILABLE=128
VCAP_APP_PORT=8080
USER=vcap
CF_INSTANCE_INTERNAL_IP=10.254.0.2
VCAP_APPLICATION={"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","application_name":"nodetask","application_uris":["nodetask.example.org"],"application_version":"e4551ace-13c3-4a15-942b-3d268efad711","cf_api":"https://api.example.org","host":"0.0.0.0","instance_id":"6ddbf04f-b643-4d87-7844-9fc6","instance_index":0,"limits":{"disk":128,"fds":16384,"mem":128},"name":"nodetask","port":8080,"space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","space_name":"example-spc","uris":["nodetask.example.org"],"version":"e4551ace-13c3-4a15-942b-3d268efad711"}
CF_INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
PATH=/home/vcap/deps/0/bin:/usr/local/bin:/usr/bin:/bin:/home/vcap/app/bin:/home/vcap/app/node_modules/.bin
WEB_CONCURRENCY=1
PWD=/home/vcap/app
LANG=en_US.UTF-8
MY_ENV=my-value
CF_INSTANCE_PORT=62543
NODE_ENV=production
VCAP_SERVICES={}
CF_INSTANCE_IP=10.0.16.22
CF_INSTANCE_INDEX=0
HOME=/home/vcap/app
SHLVL=1
DEPS_DIR=/home/vcap/deps
CF_INSTANCE_PORTS=[{"external":62543,"internal":8080},{"external":62544,"internal":2222}]
INSTANCE_INDEX=0
PORT=8080
INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
VCAP_APP_HOST=0.0.0.0
MEMORY_LIMIT=128m
NODE_HOME=/home/vcap/deps/0/node
_=/home/vcap/deps/0/bin/npm
ちなみに, env
で得られた結果と, cat /proc/14/environ
で得られた結果の差分は,以下のようになっている:
diff --git env.sorted.txt proc-environ.sorted.txt
index 4d931bb..34ffbbe 100644
--- env.sorted.txt
+++ proc-environ.sorted.txt
@@ -5,23 +5,26 @@ CF_INSTANCE_INTERNAL_IP=10.254.0.2
CF_INSTANCE_IP=10.0.16.22
CF_INSTANCE_PORT=62543
CF_INSTANCE_PORTS=[{"external":62543,"internal":8080},{"external":62544,"internal":2222}]
-HOME=/home/vcap
+DEPS_DIR=/home/vcap/deps
+HOME=/home/vcap/app
INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
INSTANCE_INDEX=0
LANG=en_US.UTF-8
-LESSCLOSE=/bin/lesspipe %s %s
-LESSOPEN=| /bin/lesspipe %s
-LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
+MEMORY_AVAILABLE=128
MEMORY_LIMIT=128m
MY_ENV=my-value
-PATH=/bin:/usr/bin
+NODE_ENV=production
+NODE_HOME=/home/vcap/deps/0/node
+PATH=/home/vcap/deps/0/bin:/usr/local/bin:/usr/bin:/bin:/home/vcap/app/bin:/home/vcap/app/node_modules/.bin
PORT=8080
-PWD=/home/vcap
+PWD=/home/vcap/app
SHLVL=1
-TERM=xterm
+TMPDIR=/home/vcap/tmp
USER=vcap
-VCAP_APPLICATION={"cf_api":"https://api.example.org","limits":{"fds":16384,"mem":128,"disk":128},"application_name":"nodetask","application_uris":["nodetask.example.org"],"name":"nodetask","space_name":"example-spc","space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","uris":["nodetask.example.org"],"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","version":"e4551ace-13c3-4a15-942b-3d268efad711","application_version":"e4551ace-13c3-4a15-942b-3d268efad711"}
+VCAP_APPLICATION={"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","application_name":"nodetask","application_uris":["nodetask.example.org"],"application_version":"e4551ace-13c3-4a15-942b-3d268efad711","cf_api":"https://api.example.org","host":"0.0.0.0","instance_id":"6ddbf04f-b643-4d87-7844-9fc6","instance_index":0,"limits":{"disk":128,"fds":16384,"mem":128},"name":"nodetask","port":8080,"space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","space_name":"example-spc","uris":["nodetask.example.org"],"version":"e4551ace-13c3-4a15-942b-3d268efad711"}
VCAP_APP_HOST=0.0.0.0
VCAP_APP_PORT=8080
VCAP_SERVICES={}
-_=/usr/bin/env
+WEB_CONCURRENCY=1
+WEB_MEMORY=512
+_=/home/vcap/deps/0/bin/npm
PATH
と HOME
の値が異なり,また NODE_*
のような Node.js 固有の環境変数が追加で定義されている点が主な差分と言えよう。
実は冒頭に書いたスクリプトは,この cat /proc/<PID>/environ
周りの手順を ある程度の確度で 自動化したものである。
ある程度の確度で と書いたのは,実はどのプロセスがアプリの起動プロセスかを決定する方法を確立できていない 1 ためである。
冒頭のスクリプトでいうと,
ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } ' | sort -n | head -n 1
が「どのプロセスがアプリの起動プロセスか」を推定している部分にあたる。
順を追って説明する。
まず実行中のプロセスの一覧を取得:
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:40 ? 00:00:00 /proc/self/exe init
vcap 13 0 0 14:40 ? 00:00:00 /tmp/lifecycle/diego-sshd --allowedKeyExchanges= --address=0.0.0.0:2222 --allowUnauthenticatedClients=false
vcap 14 0 0 14:40 ? 00:00:00 npm
vcap 66 14 0 14:40 ? 00:00:00 sh -c node server.js
vcap 67 66 0 14:40 ? 00:00:00 node server.js
vcap 124 13 0 14:42 pts/0 00:00:00 /bin/bash
vcap 421 124 0 14:56 pts/0 00:00:00 ps -ef
そこから diego-sshd
のプロセスを除外:
$ ps -ef | grep -v diego-sshd
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:40 ? 00:00:00 /proc/self/exe init
vcap 14 0 0 14:40 ? 00:00:00 npm
vcap 66 14 0 14:40 ? 00:00:00 sh -c node server.js
vcap 67 66 0 14:40 ? 00:00:00 node server.js
vcap 124 13 0 14:42 pts/0 00:00:00 /bin/bash
vcap 516 124 0 15:01 pts/0 00:00:00 ps -ef
その中から,ユーザー vcap
によって実行されているものを取得:
$ ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } '
vcap 14 0 0 14:40 ? 00:00:00 npm
vcap 66 14 0 14:40 ? 00:00:00 sh -c node server.js
vcap 67 66 0 14:40 ? 00:00:00 node server.js
vcap 124 13 0 14:42 pts/0 00:00:00 /bin/bash
vcap 528 124 0 15:01 pts/0 00:00:00 ps -ef
vcap 530 124 0 15:01 pts/0 00:00:00 awk $1 == "vcap" { print $0 }
さらにそこから,親プロセスが存在しない(即ちPPIDが0の)プロセスのプロセス番号(PID)を取得:
$ ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } '
14
このケースでは,この時点で1つに絞られたので以後の処理は冗長だが,他の buildpack の事例を踏まえて,さらに以下の2つの処理を行う。
得られたPIDリストを数値順にソート:
$ ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } ' | sort -n
14
先頭の(=最も小さい)PIDを取得:
$ ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } ' | sort -n | head -n 1
14
こうして得たPIDのプロセスをアプリの起動プロセスとみなして,その環境変数を取得しているのが冒頭のスクリプトで,今の所これでうまくいっている。
$ cat /proc/$(ps -ef | grep -v diego-sshd | awk ' $1 == "vcap" { print $0 } ' | awk ' $3 == 0 { print $2 } ' | sort -n | head -n 1)/environ | sed -e 's/\x0/\n/g'
CF_INSTANCE_ADDR=10.0.16.22:62543
TMPDIR=/home/vcap/tmp
WEB_MEMORY=512
MEMORY_AVAILABLE=128
VCAP_APP_PORT=8080
USER=vcap
CF_INSTANCE_INTERNAL_IP=10.254.0.2
VCAP_APPLICATION={"application_id":"a5b6ab92-90b8-401f-b3b8-c51ebee8ef52","application_name":"nodetask","application_uris":["nodetask.example.org"],"application_version":"e4551ace-13c3-4a15-942b-3d268efad711","cf_api":"https://api.example.org","host":"0.0.0.0","instance_id":"6ddbf04f-b643-4d87-7844-9fc6","instance_index":0,"limits":{"disk":128,"fds":16384,"mem":128},"name":"nodetask","port":8080,"space_id":"930a3384-7b8c-4262-a083-8cc11db48f0f","space_name":"example-spc","uris":["nodetask.example.org"],"version":"e4551ace-13c3-4a15-942b-3d268efad711"}
CF_INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
PATH=/home/vcap/deps/0/bin:/usr/local/bin:/usr/bin:/bin:/home/vcap/app/bin:/home/vcap/app/node_modules/.bin
WEB_CONCURRENCY=1
PWD=/home/vcap/app
LANG=en_US.UTF-8
MY_ENV=my-value
CF_INSTANCE_PORT=62543
NODE_ENV=production
VCAP_SERVICES={}
CF_INSTANCE_IP=10.0.16.22
CF_INSTANCE_INDEX=0
HOME=/home/vcap/app
SHLVL=1
DEPS_DIR=/home/vcap/deps
CF_INSTANCE_PORTS=[{"external":62543,"internal":8080},{"external":62544,"internal":2222}]
INSTANCE_INDEX=0
PORT=8080
INSTANCE_GUID=6ddbf04f-b643-4d87-7844-9fc6
VCAP_APP_HOST=0.0.0.0
MEMORY_LIMIT=128m
NODE_HOME=/home/vcap/deps/0/node
_=/home/vcap/deps/0/bin/npm
しかし,上述の過程を読んでもらえればわかるように,これが100%確実かどうかは正直自信はない。
もしもっと確実な「アプリの起動プロセスのPID」の取得方法をご存知の方がいたら,ぜひ教えてほしい。
Footnotes
-
アプリのコンテナーがDEA上で実行されていた時は,
/home/vcap/run.pid
というファイルにアプリの起動プロセスのPIDが書かれていたので,それを読めば済んだのだが,Diegoになってそのファイルは廃止されたので,若干面倒な方法で推定せざるをえなくなった。 ↩