ksaitoの日記

日々試したことの覚え書き

tomcatのオフシャルコンテナでスレッドダンプを取る

Tomcatのオフシャルコンテナのソースをcloneします。

git clone  https://github.com/docker-library/tomcat.git
cd tomcat/8.0/jre8

オフシャルコンテナのベースは、JREなのでJDKに変更します。

$ git diff
diff --git a/8.0/jre8/Dockerfile b/8.0/jre8/Dockerfile
index 973c8f3..be061ce 100644
--- a/8.0/jre8/Dockerfile
+++ b/8.0/jre8/Dockerfile
@@ -1,4 +1,4 @@
-FROM openjdk:8-jre
+FROM openjdk:8-jdk
 
 ENV CATALINA_HOME /usr/local/tomcat
 ENV PATH $CATALINA_HOME/bin:$PATH
$ docker build -t test .

起動します。

docker run --name tomcat -d test
$ docker exec -it tomcat bash
root@df763096e049:/usr/local/tomcat#

一番CPUを使っているスレッドを特定します。 今回は、特に何も使っていないのですが...

root@df763096e049:/usr/local/tomcat# top -n 1 -H -p `pgrep java` | head
top - 12:58:35 up 1 day,  7:45,  0 users,  load average: 0.06, 0.09, 0.12
Threads:  42 total,   0 running,  42 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.8 us,  0.8 sy,  0.0 ni, 98.3 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2046940 total,   305596 free,   368720 used,  1372624 buff/cache
KiB Swap:  1048572 total,  1038284 free,    10288 used.  1481004 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                  
    1 root      20   0 3015160  97540  19840 S  0.0  4.8   0:00.03 java                                                                                                                                     
    9 root      20   0 3015160  97540  19840 S  0.0  4.8   0:00.70 java                                                                                                                                     
   10 root      20   0 3015160  97540  19840 S  0.0  4.8   0:00.01 java                                                                                                                                     
root@df763096e049:/usr/local/tomcat#

CPUを一番使っているPIDを16進数に変換します。 今回は、意味ないですが...

root@df763096e049:/usr/local/tomcat# printf %x\\n 1
1
root@df763096e049:/usr/local/tomcat#

スレッドダンプを取ります。

root@df763096e049:/usr/local/tomcat# jstack `pgrep java` > thread.txt
root@df763096e049:/usr/local/tomcat# head thread.txt 
2018-07-02 13:02:13
Full thread dump OpenJDK 64-Bit Server VM (25.171-b11 mixed mode):

"Attach Listener" #43 daemon prio=9 os_prio=0 tid=0x00007f5c40001000 nid=0x51 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"ajp-apr-8009-AsyncTimeout" #41 daemon prio=5 os_prio=0 tid=0x00007f5c6c518000 nid=0x33 waiting on condition [0x00007f5c510d7000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.tomcat.util.net.AbstractEndpoint$AsyncTimeout.run(AbstractEndpoint.java:130)
root@df763096e049:/usr/local/tomcat#

スレッドのPIDを16進数に変換したものがスレッドダンプのnid=に対応します。 CPUを消費しているスレッドダンプが特定できるとスタックトレースからCPUを消費しているパッケージ、クラスが特定できます。

メモリ

メモリの枯渇が問題となる場合は、プロセスIDを指定してヒストグラムを取ります。

root@df763096e049:/usr/local/tomcat# jmap -histo 1 | head

 num     #instances         #bytes  class name
----------------------------------------------
   1:         34192        7276720  [C
   2:            37        1328696  [J
   3:          2801        1157408  [I
   4:         31998         767952  java.lang.String
   5:          4922         718224  [B
   6:         17622         563904  java.util.HashMap$Node
   7:          4450         391600  java.lang.reflect.Method
root@df763096e049:/usr/local/tomcat# 

ガベージは、下記のようにPID=1を1000ms毎に3回取得します。

root@df763096e049:/usr/local/tomcat# jstat -gc 1 1000 3
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
512.0  8704.0  0.0    0.0   33792.0   1730.6   30208.0    13429.3   15872.0 15252.9 1792.0 1669.2      6    0.027   2      0.068    0.095
512.0  8704.0  0.0    0.0   33792.0   1730.6   30208.0    13429.3   15872.0 15252.9 1792.0 1669.2      6    0.027   2      0.068    0.095
512.0  8704.0  0.0    0.0   33792.0   1730.6   30208.0    13429.3   15872.0 15252.9 1792.0 1669.2      6    0.027   2      0.068    0.095
root@df763096e049:/usr/local/tomcat#

dockerコンテナでnginxを起動する。

dockerのubuntuコンテナで、手軽にnginxを動かすコマンドです。

/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

gitリポジトリ全体でgrepする

gitリポジトリの過去のコミット含めて、全てのファイルに対してgrepします。

git rev-list --all | xargs git grep '文字列'

Ubuntuのopensshコンパイル

/etc/apt/sources.listの下記の行を追加します。

deb-src http://archive.ubuntu.com/ubuntu/ bionic main restricted

次のコマンドでソースパッケージを取得します。

apt update
apt-get source openssh

次のコマンドで、ビルドに必要な準備をします。

cd openssh-7.6p1
sudo apt-get build-dep openssh-server
autoconf
./configure

ビルドします。

make

以上

aws cliの結果をタグの値で絞り込むquery

aws cliの結果をタグの値で絞り込むqueryです。

例えば、Security Groupの中からNameタグに特定の値が設定されたデータを絞り込むには、下記のようにします。

aws ec2 describe-security-groups --group-ids <SecurityGroup ID> --query 'SecurityGroups[?Tags[?Key==`Name`].Value|[0]==`特定の値`]'

Kubernetes-dashboardに接続する

Kubernetes-dashboardをデプロイしましたが、接続するのにちょっと苦労しました。

リモートからは接続できない

マニュアル通りkubectl proxyを実行したところlocalhost以外からは接続できませんでした。

GUILinuxがあれば、これでも良いのですけどね。

$ kubectl proxy 
Starting to serve on 127.0.0.1:8001

表示をみてもIPアドレスが制限されているので、下記のような結果です。

$ curl http://ec2-XX-XX-XX-XX.ap-northeast-1.compute.amazonaws.com:8001
curl: (7) Failed to connect to ec2-XX-XX-XX-XX.ap-northeast-1.compute.amazonaws.com port 8001: 接続を拒否されました
$

IPアドレスの制限を解除してみる

$ kubectl proxy --address 0.0.0.0
Starting to serve on [::]:8001

アクセスできるようになりましたが、認証エラーとなりました。

$ curl http://ec2-XX-XX-XX-XX.ap-northeast-1.compute.amazonaws.com:8001
<h3>Unauthorized</h3>$

accept-hostsオプションを追加

0.0.0.0は、自分のIPアドレスに絞ったほうが良いです。

$ kubectl proxy --address 0.0.0.0 --accept-hosts '.*'
Starting to serve on [::]:8001

これで、アクセスできました。

$ curl http://ec2-XX-XX-XX-XX.ap-northeast-1.compute.amazonaws.com:8001 | head -5
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",

シェルでタイムアウトを設けて何かを待つ処理をワンライナーで書く

シェルでタイムアウトを設けて何かを待つ処理をワンライナーで書いてみました。

アプリケーションサーバの起動のあとに何か実行する必要がある場合、起動の完了を待つ必要があります。 同期呼び出しやコールバックなどが設定できない場合は、ログやポートを定期的に監視する必要があります。

ここでは、サンプルとして、時刻の秒が48秒になったら何か処理することを想定します。

下記のように実装しました。

$ timeout 30 bash -c 'while true; do date +'%S' | grep 48 &&  break; sleep 1; done'
48
$ echo $?
0
$ timeout 3 bash -c 'while true; do date +'%S' | grep 48 &&  break; sleep 1; done'
$ echo $?
124
$

詳細

ワンライナーの詳細です。

条件の判定

何かの状態になったかを確認する専用のコマンドが提供されていない場合は、grepdiffが便利です。これらのコマンドは、終了ステータスで条件を満すと0、満たさないと0以外を返します。

時刻が48秒かを判別するには、下記のようにします。

date +'%S' | grep 48
echo $?

判定条件を毎秒繰り返す

while文を使って、条件の判定を毎秒繰り返す事ができます。

$ while true
> do
>   date +'%S' | grep 48
>   sleep 1
> done

この処理では、条件を満たしても終了しないので、終了処理を追加します。

&&は、実行したコマンドが成功した時に実行するコマンドを指定できます。 コマンドが成功(=条件を満たした)場合にbreakコマンドを実行してループを抜けます。

$ while true
> do
>   date +'%S' | grep 48 &&  break
>   sleep 1
> done
48
$

タイムアウトを設ける

timeoutコマンドを使って一定時間経過しても条件を満たさない場合は、エラーとなるようにします。

条件を満たしたか、タイムアウトになったかは、終了ステータスで判別できます。

$ timeout 60 bash -c 'while true; do   date +'%S' | grep 48 &&  break;   sleep 1; done'
48
$ echo $?
0
$ timeout 2 bash -c 'while true; do   date +'%S' | grep 48 &&  break;   sleep 1; done'
$ echo $?
124
$