0

JRE、いくつかの Java Web アプリケーション、およびjmxterm. 後者は、アドホックな管理タスクを実行するために使用されます。このイメージは、Web アプリケーション自体を実行するために、Docker 1.13 (かなり古いですが、ディストリビューションのリポジトリ経由で提供される最新バージョンです) を搭載した CentOS 7 サーバーで使用されます。

jmxtermすべて正常に動作しますが、 1.0.0 から最新バージョン (1.0.2) に更新した後、実行中のコンテナーに入って起動すると、次の警告が表示されjmxtermます。

WARNING: Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)

この後、jmxterm矢印キーに反応せず (コマンド履歴をナビゲートしようとするとき)、オートコンプリートも提供しません。

いくつかの簡単な調査では、CentOS 7 のクリーンな環境で問題が再現される可能性があることが示されています。たとえば、これは、必要なものすべてを備えたシステムとコンテナーをブートストラップする方法です。

$ vagrant init centos/7
$ vagrant up
$ vagrant ssh
[vagrant@localhost ~]$ sudo yum install docker
[vagrant@localhost ~]$ sudo systemctl start docker
[vagrant@localhost ~]$ sudo docker run -it --entrypoint bash openjdk:11
root@0c4c614de0ee:/# wget https://github.com/jiaqi/jmxterm/releases/download/v1.0.2/jmxterm-1.0.2-uber.jar

そして、これがコンテナに入って実行する方法ですjmxterm

[vagrant@localhost ~]$ sudo docker exec -it 0c4c614de0ee sh
root@0c4c614de0ee:/# java -jar jmxterm-1.0.2-uber.jar
WARNING: Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
root@0c4c614de0ee:/# bea<TAB>
<Nothing happens, but autocompletion had to appear>

いくつかの観察:

  • jmxtermどの画像を使用しても問題は発生しません。
  • jmxtermどの画像を使用しても問題は new で発生します。
  • 問題は私のラップトップ (新しいカーネルと Docker を搭載) では再現できません。
  • CentOS 7 のネイティブ バージョン 1.13 の代わりに、CentOS 7 サーバーで最新の Docker (外部リポジトリから) を使用すると、問題は再現できません。

何が起こり、特定の環境でのみエラーが再現できるのはなぜですか? これに対する回避策はありますか?

4

2 に答える 2

0

TLDR : 新しいjmxtermバージョンをそのまま実行java -jar jmxterm-1.0.2-uber.jar < /dev/ttyすることは、インタラクティブなコンテナー セッション内でオートコンプリートやその他のものを機能させるための、迅速で汚い、ハックな回避策です。


簡単なチェックでは、ユーティリティjmxtermを実行して、プロセスが使用する端末デバイスを特定しようとすることを示しています (おそらく後で端末機能を取得するため) 。tty

root@0c4c614de0ee:/# strace -f -e 'trace=execve,wait4' java -jar jmxterm-1.0.2-uber.jar
execve("/opt/java/openjdk/bin/java", ["java", "-jar", "jmxterm-1.0.2-uber.jar"], 0x7ffed3a53210 /* 36 vars */) = 0
...
[pid   432] execve("/usr/bin/tty", ["tty"], 0x7fff8ea39608 /* 36 vars */) = 0
[pid   433] wait4(432, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 432
WARNING: Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)

ユーティリティはステータス 1 で失敗します。これがエラー メッセージの原因である可能性があります。なんで?

root@0c4c614de0ee:/# strace -y tty
...
readlink("/proc/self/fd/0", "/dev/pts/3", 4095) = 10
stat("/dev/pts/3", 0x7ffe966f2160)      = -1 ENOENT (No such file or directory)
...
write(1</dev/pts/3>, "not a tty\n", 10not a tty
) = 10

ユーティリティには「tty ではありません」と表示されますが、確実に tty があります。シェルの標準ストリームは 1 つに接続されていますが、コンテナーには実際には PTY デバイスはありません。

root@0c4c614de0ee:/# ls -l /proc/self/fd
total 0
lrwx------. 1 root root 64 Jun  3 21:26 0 -> /dev/pts/3
lrwx------. 1 root root 64 Jun  3 21:26 1 -> /dev/pts/3
lrwx------. 1 root root 64 Jun  3 21:26 2 -> /dev/pts/3
lr-x------. 1 root root 64 Jun  3 21:26 3 -> /proc/61/fd

root@0c4c614de0ee:/# ls -l /dev/pts
total 0
crw-rw-rw-. 1 root root 5, 2 Jun  3 21:26 ptmx

最新の Docker で同じことを確認するとどうなるでしょうか。

root@c0ebd608f79a:/# ls -l /proc/self/fd
total 0
lrwx------ 1 root root 64 Jun  3 21:45 0 -> /dev/pts/1
lrwx------ 1 root root 64 Jun  3 21:45 1 -> /dev/pts/1
lrwx------ 1 root root 64 Jun  3 21:45 2 -> /dev/pts/1
lr-x------ 1 root root 64 Jun  3 21:45 3 -> /proc/16/fd

root@c0ebd608f79a:/# ls -l /dev/pts
total 0
crw--w---- 1 root tty  136, 0 Jun  3 21:44 0
crw--w---- 1 root tty  136, 1 Jun  3 21:45 1
crw-rw-rw- 1 root root   5, 2 Jun  3 21:45 ptmx

ビンゴ!これで、あるべき場所に PTY ができたのでjmxterm、最新の Docker でうまく機能します。

古い Docker では、プロセスが一部の PTY に接続されているのに にデバイスがないのはかなり奇妙に思えますが/dev/pts、Docker プロセスをトレースすると、これが発生する理由が説明されます。古い Docker は、他の設定を行うにコンテナーに PTY を割り当てます (新しいマウント名前空間を入力devptsしてそれにマウントするか、単にマウント名前空間を入力するなどdocker exec -it):

[vagrant@localhost ~]$ sudo strace -p $(pidof docker-containerd-current) -f -e trace='execve,mount,unshare,openat,ioctl'
...
[pid  3885] openat(AT_FDCWD, "/dev/ptmx", O_RDWR|O_NOCTTY|O_CLOEXEC) = 9
[pid  3885] ioctl(9, TIOCGPTN, [1])     = 0
[pid  3885] ioctl(9, TIOCSPTLCK, [0])   = 0
...
[pid  3898] unshare(CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWPID) = 0
...
[pid  3899] mount("devpts", "/var/lib/docker/overlay2/3af250a9f118d637bfba5701f5b0dfc09ed154c6f9d0240ae12523bf252e350c/merged/dev/pts", "devpts", MS_NOSUID|MS_NOEXEC, "newinstance,ptmxmode=0666,mode=0"...) = 0
...
[pid  3899] execve("/bin/bash", ["bash"], 0xc4201626c0 /* 7 vars */ <unfinished ...>

マウントがその PTY を排他的に所有し、それらを他のマウントと共有しないnewinstanceことを保証するマウント オプションに注意してください。devptsこれにより、興味深い効果が得られます。コンテナーの PTY デバイスはホストにとどまり、ホストのdevptsマウントに属しますが、コンテナー化されたプロセスは、既に開いているファイル記述子をその寿命の最初に取得したため、引き続きアクセスできます。 !

最新の Docker は、最初devptsにコンテナーをマウントしてから PTY割り当てます。そのため、PTY はコンテナーのdevptsマウントに属し、コンテナーのファイル システム内に表示されます。

$ sudo strace -p $(pidof containerd) -f -e trace='execve,mount,unshare,openat,ioctl'
...
[pid 14043] unshare(CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWNET) = 0
...
[pid 14044] mount("devpts", "/var/lib/docker/overlay2/b743cf16ab954b9a4b4005bca0aeaa019c4836c7d58d6073044e5b48446c3d62/merged/dev/pts", "devpts", 
MS_NOSUID|MS_NOEXEC, "newinstance,ptmxmode=0666,mode=0"...) = 0
...
[pid 14044] openat(AT_FDCWD, "/dev/ptmx", O_RDWR|O_NOCTTY|O_CLOEXEC) = 7
[pid 14044] ioctl(7, TIOCGPTN, [0])     = 0
[pid 14044] ioctl(7, TIOCSPTLCK, [0])   = 0
...
[pid 14044] execve("/bin/bash", ["/bin/bash"], 0xc000203530 /* 4 vars */ <unfinished ...>

問題は Docker の不適切な動作が原因ですがjmxterm、同じ環境で以前のバージョンがうまく機能したのはなぜでしょうか? 確認してみましょう (ここでは Java 8 イメージが使用されていることに注意してください。古いイメージはjmxtermJava 11 ではうまく機能しません)。

root@504a7757e310:/# wget https://github.com/jiaqi/jmxterm/releases/download/v1.0.0/jmxterm-1.0.0-uber.jar
root@504a7757e310:/# strace -f -e 'trace=execve,wait4' java -jar jmxterm-1.0.0-uber.jar
execve("/usr/local/openjdk-8/bin/java", ["java", "-jar", "jmxterm-1.0.0-uber.jar"], 0x7fffdcaebdd0 /* 10 vars */) = 0
...
[pid   310] execve("/bin/sh", ["sh", "-c", "stty -a < /dev/tty"], 0x7fff1f2a1cc8 /* 10 vars */) = 0

したがって、olderはデバイス名を尋ねる代わりにjmxterm使用するだけで、このデバイスはコンテナー内に存在するため、これは機能します。/dev/ttytty

root@504a7757e310:/# ls -l /dev/tty
crw-rw-rw-. 1 root root 5, 0 Jun  3 21:36 /dev/tty

のこれらのバージョンの大きな違いは、新しいツール バージョンでは、ターミナルとの対話を担当するライブラリであるjmxtermのメジャー バージョンが高いことです( C の世界の に似ています)。メジャーバージョン間の違いは の動作の違いにつながり、現在のバージョンは に依存しているだけです。jlinereadlinejlinejmxtermtty

この観察により、Dockerの更新もjline/jmxtermタンデムへのパッチ適用も必要としない、手早く汚い回避策にたどり着きます。これは、正式には常に正しいとは限りませんが、アドホックな使用には十分です):jmxterm/dev/ttyjline/proc/self/fd/0/dev/pts

root@0c4c614de0ee:/# java -jar jmxterm-1.0.2-uber.jar < /dev/tty
Welcome to JMX terminal. Type "help" for available commands.
$>bea<TAB>
bean    beans

これで、必要なオートコンプリート、履歴、およびその他のクールなものができました。

于 2021-06-03T23:38:24.890 に答える