前提
導入対象とするマシンは下記の通り。
- Hardware : CPU = i10900, GPU = GeForce RTX 2080Ti, Mem = 32GB, SSD 1 TB + HDD 2TB
- ベース OS : Windows 10 Pro バージョン 20H2 (ビルド 19042.685)
- 仮想 OS : Ubuntu 20.04 LTS on WSL2
- 環境構築メモ 2/4 を実施した状況
Docker 導入
Ubuntu 14.04 より apt-get
, apt-chache
ではなく apt
コマンドの使用が推奨されているため、参考記事に旧コマンドが含まれている場合は注意。特に Docker Package の導入では apt 新コマンド利用を強く推奨されている。
Docker のインストールと設定
前提パッケージのインストール
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
Docker 公式 GPG 公開鍵のインストール
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
apt レポジトリの設定
「Intel x86-64 だけど arch=amd64 でいいの?」と思うが、x86-64 (= AMD64) とは AMD が開発した 32bit 下方互換 64bit アーキテクチャという意味なのでこれでよい。AMD ではない方と誤って armhf にしてはいけない。
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
Docker Community Edition のインストール
sudo apt update sudo apt install -y docker-ce # 最後に更新 sudo apt update; sudo apt upgrade
インストールした Docker のバージョン確認
docker version
一般ユーザで Docker を起動するための準備
インストールした状態では root しか docker コマンドを実行できないため、起動させたいユーザを docker グループに追加する。現状のユーザをグループ追加するには次のコマンドを実行する。
sudo usermod -aG docker ${USER}
設定は再ログインまたは次のコマンドを実行した後に有効になる。
su - ${USER}
次のコマンドを実行し、指定したユーザが docker グループに所属したか確認する。
id -nG
これで Docker のインストールが完了し、デーモンが起動している、確認するため次のコマンドを実行する、と各解説サイトは説明する。
sudo systemctl status docker
が、実行結果は下記の通り。デーモンプロセスは動いていない。
System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down
systemd ≠ pid 1 問題とその解決
ここでデーモンが動かないのは、一般の Unix システムでカーネルから最初に起動され pid = 1 を割り振られる init プロセスが、仮想 OS である Ubuntu on WSL2 においてはカスタマイズされてしまっていることによる。Unix の場合 init = systemd (pid 1) だが、WSL2 の場合、init プロセスは systemd とは別にあり、systemd はインストールされているものの無効化されていて、有効化しても pid = 1 にはならない。という背景により systemctl
によるサービス管理が動作しない。
解決方法はいくつかあるらしいが、ここでは Genie というものを導入する方法を採る。
Genie 導入の準備
Genie が依存するパッケージをあらかじめインストールする。
sudo apt install -y daemonize dbus policykit-1
.NET Runtime が必要となるため、最新の .NET 5.0 for Ubuntu 20.04 LTS をインストールする。(参考記事は .NET も Linux も古いため、手順は少しカスタマイズが必要。)
# レポジトリの追加 wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb # 更新 sudo dpkg -i packages-microsoft-prod.deb sudo apt update # .NET Runtime 導入 sudo apt install -y apt-transport-https && sudo apt update && sudo apt install -y aspnetcore-runtime-5.0
Genie の導入
Genie のインストール
まず、下記の内容を記述した /etc/apt/sources.list.d/wsl-translinux.list
というファイルを新規作成する。書き込みには root 権限が必要。本家で説明されている導入手順であるため、解説サイトなどでは説明が端折られていることが多いが、このファイルを作成しないとパッケージが見つからず、インストールが途中で止まる。
deb [trusted=yes] https://wsl-translinux.arkane-systems.net/apt/ /
Genie をインストールする。
# リポジトリの追加 curl -s https://packagecloud.io/install/repositories/arkane-systems/wsl-translinux/script.deb.sh | sudo bash # Genie のインストール sudo apt install systemd-genie
getty@tty サービスの停止と systemd への差し替え
sudo systemd stop getty@tty
Genie の起動確認
# Genie の動作確認 (エラーが出なければ OK) genie -s
systemctl による Docker の状況再確認
あらためて systemctl
で Docker の状態を再確認する。.NET 入れたり Genie 入れたり、これらも一筋縄ではいかず、苦労したが、やりたかったのはこれ。ふぅ。
sudo systemctl status docker
Genie の自動起動設定
WSL2 起動時に Genie を自動起動させるために /etc/profile.d/genie.sh
に管理者権限で下記の設定をする。(参考記事と少し異なる理由は後述)
# systemd の PID を調べるコマンドにエイリアスをつける alias getsdpid='ps -eo pid,lstart,cmd | grep systemd | grep -v -e grep -e systemd- | sort -n -k2 | awk '\''NR==1 { print $1 }'\''' if [ -z "`getsdpid`" ]; then echo 'genie : initializing ...' genie -i &> /dev/null & echo 'genie : waiting for initialization ...' sleep 2s fi if [ "`getsdpid`" != "1" ]; then echo 'genie : launching bash ...' genie -s fi
Ubuntu 仮想環境初回起動時に systemd を待ち続ける問題
WSL2 起動時に Genie を自動起動させるために .bashrc
か /etc/profile.d/genie.sh
に下記を追記しておけとよく解説されているが、これでは PC 起動直後の Ubuntu 仮想環境初回起動時には Waiting for systemd ... !!!!!!!! (! が次第に増えていく) と出続け、しばらく待っていても機能しない。
if [ "`ps -eo pid,lstart,cmd | grep systemd | grep -v -e grep -e systemd- | sort -n -k2 | awk 'NR==1 { print $1 }'`" != "1" ]; then genie -s fi
この問題にかなり悩まされたがいろいろと調査した結果、わかったことを列挙する。原因は初期化プロセスでかなり待たされるようになっている Genie の作りにあるようだ。
genie -s
は、①初期化 (systemd 関連プロセス群の起動) を担うgenie -i
の部分と、②bash シェルを (Ubuntu 仮想環境直上の bash に重ねて) 起動する部分、から成るgenie -s
は、初期化されていれば①をスキップするgenie -i
は、systemd 関連プロセス起動をキックした後に Waiting for systemd ... !!!!!!!! と出力してコンソールを占有しつつ、数分間稼働し続ける- systemd 関連プロセス群が利用可能になるまでの時間は数秒だが、②の bash シェル起動は①のプロセス完了を待っている
- /init から systemd への pid = 1 移し替えは
genie -s
が担っている (genie -i
ではない) - systemd を pid = 1 にしているのはエミュレーションであり、
genie -s
が起動した bash シェル上でのみ有効である (Ubuntu 仮想環境直上で実際に systemd が pid = 1 となっているわけではない) genie -i
が起動した systemd 関連プロセス群はgenie -u
でクリーンアウトできる
したがって、回避策としては下記のようになる。
genie -s
の担う役割を、①初期化、と、②bash シェル起動 + pid エミュレート、とに分離する (初期化をgenie -s
に頼らない)- systemd がなければ
genie -i
を起動して初期化する genie -i
は、出力を捨てつつバックグラウンドで立ち上げる- 初期化開始後に
genie -s
を起動して bash シェルを起動する (genie -i
の完了を待たない)
genie.sh
の冒頭に記述した systemd の pid を返すエイリアス getsdpid
を使うと下記のように環境を調べることができる。
- 1 が返る →
genie -s
が起動した bash にいる - 1 以外が返る →
genie -i
が起動した systemd は生きているが、Ubuntu 仮想環境直上の bash にいて systemd の pid は 1 ではない - 何も返らない → Ubuntu 仮想環境直上の bash にいて systemd も生きていない
.bashrc
もレイヤー違いで重ねて走ってしまうため、重ねると害のある部分 (hwclock
など) は getsdpid
の返り値によってスキップさせるとよい。
Docker Compose のインストール
Docker Compose も使うことになるため、インストールしておく。
# update package list sudo apt update sudo apt upgrade # install sudo apt install docker-compose
Docker 利用の省力化
テキストタイプ省力化のためによく使う docker コマンドのエイリアスを .bashrc
に記述しておく。
alias dc='docker' alias dcc='docker-compose' alias dce='docker exec -it $(docker ps -lq) /bin/bash'