はじめに
今年からDockerを使い始めた未熟者です。
- 運用をDockerコンテナで行うので、開発も似た環境で行った方が良いかな?
- 開発時はGUIのツールを使いたい。
そんな単純な動機で、Dockerで開発用デスクトップ環境の構築を試みることにしました。
以下の順に試しました
(1) ホストにX Serverをインストールして、DockerコンテナからホストにXを飛ばす
結果
- ホストがWindowsのとき(X ServerはMobaXterm):成功したが動きがやや重い。
- ホストがMacのとき(X ServerはXQuartz):Xを飛ばせたが、PyCharmを操作中にフリーズした。
(2) VNC接続
結果
成功したが動きがやや重い。
ホストとのクリップボード経由のコピペは、
- 「VNCデスクトップ→ホスト」は成功
- 「ホスト→VNCデスクトップ」は失敗
(3) XRDP
結果
成功したので採用。 動きも一番軽い。
クリップボード経由のコピペは両方向OK。
XRDPの参考ページ(感謝します)
Dockerでuid/gid指定可能かつsudo使用可能なデスクトップ環境を構築する(XRDP編)
参考ページではコンテナ環境を毎回使い捨てますが、環境をいったん保存して再開したいこともあると思います。
例えば、sudoを使ってホームディレクトリ以外を変更するような作業中に、
- リモートデスクトップの調子がおかしくなったので
docker restart
したい。 - 定時になったので、電源を落として翌日作業の続きを行いたい。
そこで、docker run
後にdocker stop
で停止しても、docker start
で再開できるように実装してみました。
Dockerfile
基本的に参考ページと同じです。変更したのは冒頭の3箇所のみですので、Dockerfile全体は再喝しません。
(1) Ubuntuのバージョンを18.04に上げた
FROM ubuntu:18.04
(2) apt-getをaptに変更した
RUN apt update \
&& DEBIAN_FRONTEND=noninteractive apt install -y \
(3) apt installにsudoのインストールを追加した(Ubuntu 18.04では必要でした)
RUN apt update \
&& DEBIAN_FRONTEND=noninteractive apt install -y \
sudo \
lxde \
endpoint.sh
docker run時にユーザ作成をendpoint.shで行う点は参考ページと同じですが、docker start時は既存ユーザをそのまま使うように実装してみました。
#!/bin/bash -eu
USER_ID=$(id -u)
GROUP_ID=$(id -g)
PASSWD=${PASSWD:-${DEFAULT_PASSWD}}
USER=${USER:-${DEFAULT_USER}}
# グループを作成する
echo "GROUP_ID: $GROUP_ID"
if [ x"$GROUP_ID" != x"0" ]; then # rootグループでないとき
# gidが既にあるか調べて、なければグループ作成
grep ":${GROUP_ID}:" /etc/group || groupadd -g $GROUP_ID $USER
fi
# ユーザを作成する
echo "USER_ID: $USER_ID"
if [ x"$USER_ID" != x"0" ]; then # rootでないとき
export HOME=/home/$USER
# uidが既にあるか調べる
set +e
grep ":${USER_ID}:${GROUP_ID}:" /etc/passwd
RET=$?
set -e
# uidがなければ(= docker run)ユーザ作成、あれば(= docker start)何もしない
if [ $RET -ne 0 ]; then
# docker runするときに、ホストのディレクトリ等をコンテナのホームディレクトリに
# マウントさせると、コンテナ内に既に${HOME}ディレクトリが存在して、マウントした
# ボリュームが見えるため、useradd -m すると以下の警告が出る。
# useradd: warning: the home directory already exists.
# Not copying any file from skel directory into it.
# その場合、まだ${HOME}に/etc/skel配下のファイルがなければ、
# ${HOME}にコピーし、あればそのまま利用する。
useradd -d ${HOME} -m -s /bin/bash -u $USER_ID -g $GROUP_ID $USER
[ ! -e ${HOME}/.xsession ] && cp -r /etc/skel/. ${HOME}
# ログインパスワードを設定する
echo "PASSWD: $PASSWD"
echo ${USER}:${PASSWD} | sudo chpasswd
fi
else
# ログインユーザ名をrootにする
USER=$(whoami)
echo "USER: $USER"
# ログインパスワードを上書きする
echo "PASSWD: $PASSWD"
echo ${USER}:${PASSWD} | chpasswd
fi
# パーミッションを元に戻す
sudo chmod u-s /usr/sbin/useradd
sudo chmod u-s /usr/sbin/groupadd
[ ! -e ${HOME}/.xsession ] && cp /etc/skel/.xsession ${HOME}/.xsession
echo "#############################"
echo "USER: $USER"
echo "HOME: $HOME"
# XRDPサーバを起動する
# 下の1行がないと、docker start時にxrdp接続が失敗した
[ -e /var/run/xrdp/xrdp.pid ] && sudo rm /var/run/xrdp/xrdp.pid
# docker start時のxrdp接続で、下の行でstart:失敗、restart:成功
sudo bash -c "/etc/init.d/xrdp restart && tail -F /var/log/xrdp-sesman.log"
docker build
Dockerfileとendpoint.shがあるディレクトリで、以下を実行します。
docker build -t lxde_xrdp:ubuntu18.04_ja .
docker run
通常は、(rootでなく)一般ユーザのユーザIDを渡してコンテナを立ち上げます。docker runのオプションの意味は、参考ページをご参照ください。
# コンテナのホームディレクトリにマウントするホスト側のディレクトリを作成する。
mkdir $HOME/container_lxde_xrdp_$(whoami)_home
# コンテナ立ち上げ例
docker run -it \
--privileged \
--name lxde_xrdp_$(whoami) \
--mount type=bind,src=${HOME}/container_lxde_xrdp_$(whoami)_home,dst=/home/$(whoami) \
-p 3389:3389 \
-u $(id -u):$(id -g) \
-e USER=$(whoami) \
-e PASSWD=p@ss \
lxde_xrdp:ubuntu18.04_ja
Webアプリ開発用に例えば5000番ポートを開けるには、下記のようにdocker runします。
docker run -it \
--privileged \
--name lxde_xrdp_$(whoami) \
--mount type=bind,src=${HOME}/container_lxde_xrdp_$(whoami)_home,dst=/home/$(whoami) \
-p 3389:3389 \
-p 5000:5000 \
-u $(id -u):$(id -g) \
-e USER=$(whoami) \
-e PASSWD=p@ss \
lxde_xrdp:ubuntu18.04_ja
このとき、ブラウザからは以下のアドレスにアクセスします。
http://localhost:5000/
docker stop、docker start
コンテナ停止
docker stop lxde_xrdp_$(whoami)
停止したコンテナを再開
docker start -i lxde_xrdp_$(whoami)