今回はDocker Desktop for Linuxから画面を取るまでの試行錯誤、およびシンプルなdockerからの変更点などを解説します。4/15から4/24までの格闘日誌になります。ちょうど職場でのプレッシャが起因した休職に入ったのですが、今もすごく頭が痛いです。治るのかなこれ?

普通のDockerから画面を取る場合

普通のdockerから画面を取る場合は、下記3つが必要になります。

  1. /tmp/.X11-unix内のX0などのソケットファイルをコンテナ内に共有する
  2. ホストが画面表示を行うために使用している$DISPLAY変数を共有する
  3. ホストへの画面表示を他ユーザが行うことを許可する

まずは1つ目、ソケットファイルの共有から解説します。まずはホストにあるソケットファイルの実体ですが、こちらは簡単に見つけることができます。先頭がd(directory)ではなく、s(socket)になっていることが証拠です。

2つめの$DISPLAY変数ですが、こちらはホストでは下記のようになっています。

$DISPLAY変数の構成は(ホスト名):(ディスプレイ番号).(スクリーン番号)で表現されます。今回はローカルホストであるため、ホスト名は省略されており、理由は良くわかりませんがスクリーン番号も省略されています。コンテナから画面を取る際も、localhostの画面へ表示を行いたいので$DISPLAY変数は同じで良いということですね。
最後の画面表示の許可ですが、通常Xサーバへは決められたユーザしかログインできないのでそれを解除する作業になります。単純にxhost+と端末で入力するだけなので、非常に簡単な作業ではあります。ここまでの作業を自動でやってくれるプログラムはここで公開してあります。

DockerDesktopではうまく行かない

表題の通り、上記の手法をDockerDesktopで再現しようとしてみてもうまくいきません。エラーメッセージは下記の通りです。

このエラーは$DISPLAYで指定されている:0の画面へアクセスしようとしたら開けなかったというメッセージになり、通常はxhost + を行わなかった時に発生します。念の為xhost + を再度やってみても同じ回答が来るので、今回は違う原因で発生していると推測できます。幸いなことに、前回DockerDesktopは通常版とは違い、VM上で動作している説が発見できているので、今回は「別マシンから自マシン上へ画面を転送する」という技術で解決する必要がありそうです。

DockerDesktopからホストへのSSHを可能にする

さて、情報の転送といえばSSHですね。今まで横着してSSH関連についてあまり勉強してこなかったのですが、今回はOpenSSH[実戦]入門(河本安武さん著)を使ってしっかり活用して行こうと思います。まず、コンテナ側にあるアプリがssh-client, ローカルマシン側がssh-serverになるため、コンテナにopenssh-clientが入っているか確認しました。ここはデフォルトで入っていたので特筆する作業は無いです。一方でローカルホストの方にopenssh-serverが必要なのに入れていなくてかなり長い間格闘しました。ChatGPT先生の介抱もあってなんとか気づけたのですが、皆様もこのような初歩的なミスに引っかからないようにしましょう。ローカルホストへのssh-serverのインストールは下記の通りになります。

sudo apt install openssh-server

ホスト上のSSH開通実験

まずはsshが正しく可能かどうか確認しましょう。私の場合、最初にこの開通実験を行っていなかったため、問題の切り分けに時間がかかりました。反省を兼ねてここで当たり前の確認作業を記述します。このセクションでやっておくべきことは「ホスト端末」→「ホスト端末」へのSSHが成功するかどうかの確認になります。これ自体は下記の方法で実行できます。

ssh username@localhost

ホスト→コンテナへのSSH

ホスト→ホストのSSHが成功することが確認できたあとは、ホスト→コンテナのSSHを試してみましょう。ちなみに私はここで躓きました。まずはエラーメッセージから。

$ssh root@localhost -p 2222
kex_exchange_identification: Connection closed by remote host
Connection closed by 127.0.0.1 port 2222

このエラーメッセージが示しているのは鍵の交換が失敗しているという内容なので、次のことがわかります。

  1. ポートが空いていてアクセスには成功している
  2. なんらかの要因で公開鍵と秘密鍵のやりとりがうまくいかずに失敗している

さて、この問題を解決するためにOpenSSH実践入門を借りてきた次第です。OpenSSH実践入門のp9等によると公開鍵と秘密鍵のやりとり、またはそれに相当するセッション鍵のやりとりは通信の序盤に相当します。つまり、ほとんどSSHのプロセスは進んでいない状態で止まっていることがわかりました。これに対し私の立てた仮設は下記の通りです。

  1. 公開鍵が正しく配置されていない
  2. 公開鍵の権限が規定通りになっていない

結論から言うと、この2つは両方とも外れていました。配置も権限も完全に正しいことがすぐに確認できたからです。コンテナの中はユーザ名前空間が異なっているため、権限については100%確信を持って正しいとは言い切れないのですが、それでもコンテナ内で鍵を生成してもうまく行かなかったのでおそらく間違いなさそうです。さて、鍵の交換が失敗しているのに鍵は正しく設定されているのはどういうことだろう?

Pythonイメージではデーモンが動いていない罠

ここで大きく止まること一週間。ネット上を調べてみても、同じ悩みを抱えている人は全然いませんでした。また、「Docker – SSH を使ってコンテナ内に接続する方法」さんを参考にコンテナに色々細工をしてみてもうまくいきませんでした。しかし、ふて寝している際に上記サイトを完コピしたシンプルなコンテナを動かしてみたらどうなるのか?と気になりました。もし、この問題がDockerとDockerDesktopの違いに起因しているのであれば、上記サイトの完コピコンテナでも必ず失敗するはず。というかずっとその前提で動いてきたけど正しいのか?

結果・・・「Docker – SSH を使ってコンテナ内に接続する方法」さんのコンテナであればDockerDesktop4Linuxでも動作する!じゃあなんで同じ記述をした自作コンテナは動作しないんだろう?その原因はなんと

Python3.xイメージではsystemdが動いていない

今回はubuntuxx.04のイメージではなくpython3.xのイメージをベースにしてコンテナを作っていました。なんとこの場合、単純化と軽量化のためにsystemdを使用していないイメージが配置されます。冷静に考えればそちらのほうが最小構成として適切です。しかし、今回のように手動でSSHを行って画面を取るにはssh-daemonの動作は必須なのでpython3.xのイメージは使用できません。従って今回はubuntu22.04イメージをベースにpython3.9を入れることで解決しました。なお、Docker build中にtzdataで止まる問題が発生については、sudo apt install tzdataだと止まりますがUSER rootしたあとにapt install tzdataで成功することを確認しました。同じ苦しみを味わっている人がいれば参考にしてください!

DockerDesktopのコンテナから画像を転送する

“/tmp/X11-unix”の使用を諦めるよう調整

かつてプレーンなdockerコンテナに対して/tmp/.X11-unixを公開することによって画像を取る仕組みを発見(というか発掘)した際にはかなりテンションがあがりました。しかし、先述の通り今回はこの方法が使えないことがわかっています。また、原因を調べてみると結構不思議なことが発生していることがわかりました。通常、リモートで動作しているマシンの画面をホストで表示する際には「リモートサーバ上の Docker コンテナに X11 Forwarding する」さんのようにssh -X等でアクセスすることによってDISPLAY変数の設定を自動的に行うことができます。しかし、今回はssh -Xやssh -Yを使ってもDISPLAY変数が反映されないという状況になりました。また、コンテナ内で手動でDISPLAY変数を設定してもうまく映りませんでした。この状況からDISPLAY変数はdocker engineの入っている仮想空間のものが使用されていると推測しました(下図参照)。これについては、矛盾点が少々あるのでもう少し根拠がはっきりしてきたら更新します。結論としてはDISPLAY変数、および/tmp/X11-unix/X0.socketを用いた画面描画を諦める方針で進めることにしました。

VNCによる画面転送で解決

さて、X11serverへの転送を諦めたわけですが、幸いにもSSHは通っているので別の手段が使えます。それがVNCによる画面転送になります。これはコンテナ内にVNC serverが必要になりますが、ホストのVNC viewerからコンテナ内が見えるようになるので今回の問題解決には適しています。また、「VNCでDockerコンテナ内のGUIデスクトップにアクセスしてみた(かみのめも)」さんで具体的な手法をまとめてくださっていたので私にもすぐできました。

まとめ

今回の記事のまとめをここでやっておきます。

  1. コンテナ内にSSHをする場合は、python3.xイメージではなくubuntuイメージを使用すること
  2. DockerDesktop4Linuxを使っている場合はssh -Xが使用できないのでVNCを使って画面を共有すること

以上です。今回も見てくださってありがとうございました。

参考文献

北海道大学(X転送)
DockerからGUIを表示するためのプログラム(自作)
WordPressの構成を, DD4Lを使って学び直す
Docker – SSH を使ってコンテナ内に接続する方法
リモートサーバ上の Docker コンテナに X11 Forwarding する
VNC viewerの公式サイト
VNCでDockerコンテナ内のGUIデスクトップにアクセスしてみた(かみのめも)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です