Podmanと格闘した話(本題:自宅鯖でウェブ版VSCodeを動かしたい)

Red Hat

動いた手順しか書かないと何をどう格闘したのか分からない訳だが、色々遠回りをしただけな気もするので「これで動いた」というのをまとめておく。おっと、苦労して何をやったのかというのを書かないとアカンかった。

VSCodeが動かない

VSCode、便利ですね?
息子ちゃんがPythonに興味を持ったのは良いけれど、学校で買わされたChromebookだとVSCodeは動かない、と。ChromebookはLXCでターミナルが動くのでVimでPythonのコードを書き始める健気な息子ちゃん…。いや、VimとPythonを同時に学習するのはちょっとコストが高すぎるでしょ。

じゃあ、先日リリースされたvscode.devにするかとブラウザで開いてコードを書いてみたのは良いけれど、vscode.devではターミナルがまだ無いからコードが実行出来ないんですわ。んで、Jupyter Notebookにするかな、いやvscode.devがあるんだからvscodeのレポジトリにWUI化するコードが入ってるんじゃないかと調べてみたりしてたら、code-serverを見つけたと(ここから格闘が始まった)。

何で格闘するハメになったか

code-serverのRPMパッケージはある。自宅鯖のRHEL8にもインストール出来る(出来た)。しかしRPMパッケージで入れてサービスとして立ち上げると、vscodeのターミナルでサーバの/ディレクトリも見えちゃう。つまり

$ sudo systemctl start code-server@user

してても、/home/user/以外も見えちゃうし、/tmpにファイルが作れちゃったり、あるいは/home/user/Maildir/を消せたりする。あかん、完全にセキュリティホール作成サービスやんけw

じゃあ、chrootすりゃ良いんじゃね?と思ってsystemdのunitファイルに、

RootDirectory=/usr/lib/code-server/
RootDirectoryStartOnly=yes

とかやってみたけど、まあ色々アクセス出来なくなっちゃうから動かない、と。code-serverそのものがchrootされる環境を想定して設計・実装されてないから無理も無い。RHEL8だとsystemd-nspawnは無いしねぇ…。

Dockerで動くって書いてある

RPMがあるんだけども、Install手順にはDockerも当然書いてあって、

mkdir -p ~/.config
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
  -v "$HOME/.config:/home/coder/.config" \
  -v "$PWD:/home/coder/project" \
  -u "$(id -u):$(id -g)" \
  -e "DOCKER_USER=$USER" \
  codercom/code-server:latest

で動くことになってる(動くとは言ってない)。実際、動かない。

一見、動くんだけど、マウントしてる~/.config~/projectが両方ともroot:rootになっちゃうから、code-serverが書き込めない。code-serverのDockerイメージはDockerfileで

USER 1000

しちゃってるのでEACCESが出ちゃう(そりゃそうだ)。書いたコードをそのままgithubに上げちゃうなら問題ないけれど、Dockerホストのディレクトリに保存出来ないわけ。おまけにenv DOCKER_USERするとsudoers.d/にファイルを放り込んで、とかentrypointでやってるけどこれも上手く動いて無い。たぶんちゃんと検証してないんじゃないかなぁ…。このディレクトリの問題は、

docker volume create config

とかやって、それをマウントすりゃ良いだけなんだけども。

Podmanで動かす

さて、そんなわけでどうやったらPodmanで動くかなんだけど、以下のようにやっていく。なお環境はRHEL8.5だけど、たぶん8.1以降なら同じ手順で動く(はず)。

コンテナーツールをインストールする

まずモジュールのインストール。これはコマンド一発で終わる。

$ sudo dnf module install -y container-tools

ついdockerというコマンドを叩いてしまって毎回怒られが発生するのを避けるために以下を追加で。

$ sudo dnf install -y podman-docker

あと、ルートレスコンテナーを使えるようにしたいので、以下も。

$ sudo dnf install -y slirp4netns

既存のユーザについてもsubuid/subgidを設定してくれるようだ。

$ grep rio /etc/subuid
rio:100000:65536

$ grep rio /etc/subgid
rio:100000:65536

で、おそらくなんだけど、ここで一度サーバを再起動した方が良い。container-toolsをインストールした際に何かやってて、それが反映されるのは再起動後、というのがあるっぽい(ちゃんと調べてないけど)。

ルートレス固有の事柄

  • rootユーザなら/var/lib/containers/だけど、root以外は~/.local/share/containers/に色々入る。
  • その他詳細はRed Hatのマニュアルを読もう

コンテナレジストリの設定

以下の内容で~/.config/containers/registries.confを作る、とマニュアルには書いてあるんだけど、実は作らなくてもpullできる。

[registries.search]
registries = ['registry.access.redhat.com', 'registry.redhat.io', 'docker.io']

[registries.insecure]
registries = []

[registries.block]
registries = []

registry.access.redhat.comはサブスクリプション登録が必要なので、ここに書いただけではpull出来ないので注意。

code-serverのイメージをpull

普通にpullするだけ。

$ podman pull codercom/code-server:latest
✔ docker.io/codercom/code-server:latest
Trying to pull docker.io/codercom/code-server:latest...
Getting image source signatures
Copying blob f2992a38f428 done  
Copying blob 955615a668ce done  
Copying blob 1b7eaec48458 done  
Copying blob f78b6250e9f8 done  
Copying blob 4d402e4b2b85 done  
Copying blob e0f780da324e done  
Copying blob be75cc27e711 done  
Copying blob 225329f6c59c done  
Copying config 08823d7380 done  
Writing manifest to image destination
Storing signatures
08823d73807e2a522c69bb67acc20d68052bb794b0a6f7776593fa756d5b6ac7

マウントするボリュームを作成する

code-serverの設定と書いたコードのファイルを格納するボリュームを作成する。

$ podman volume create config
$ podman volume create projects

作成したボリュームはどこにあるかというと、

$ ls ~/.local/share/containers/storage/volumes/
config  projects

podman runする

あい、これでrunできます。

$ podman run -it --name code-server \
  -p 192.168.1.2:8080:8080 \
  -v "config:/home/coder/.config:Z" \
  -v "projects:/home/coder/projects:Z" \
  -e "PASSWORD=your_password" \
  codercom/code-server:latest

[2021-11-11T14:06:50.394Z] info  code-server 3.12.0 2661a690ac228872e8d1dc28ab6d33e8afc30add
[2021-11-11T14:06:50.395Z] info  Using user-data-dir ~/.local/share/code-server
[2021-11-11T14:06:50.404Z] info  Using config file ~/.config/code-server/config.yaml
[2021-11-11T14:06:50.404Z] info  HTTP server listening on http://0.0.0.0:8080 
[2021-11-11T14:06:50.404Z] info    - Authentication is enabled
[2021-11-11T14:06:50.404Z] info      - Using password from $PASSWORD
[2021-11-11T14:06:50.404Z] info    - Not serving HTTPS 
[2021-11-11T14:06:50.415Z] error Unauthorized HttpError: Unauthorized
    at ensureAuthenticated (/usr/lib/code-server/out/node/http.js:38:15)
[2021-11-11T14:06:50.415Z] error Unauthorized HttpError: Unauthorized
    at ensureAuthenticated (/usr/lib/code-server/out/node/http.js:38:15)
[2021-11-11T14:06:50.424Z] error Unauthorized HttpError: Unauthorized
    at ensureAuthenticated (/usr/lib/code-server/out/node/http.js:38:15)

-pのIPアドレス:ポートは適宜読み替えること。

-vpodman volume createで作成したconfigprojectsを、それぞれコンテナ内の/home/coder/.config/home/coder/projectsにマウントする。
その後に続いてる":Z"はSELinuxのラベルを修正するおまじない。複数のコンテナで共有するなら":z"(スモールゼット)で。

-eでcode-serverにブラウザでログインする時のパスワードを設定すると。

動作が確認できたら一旦ctrl-cして止めてpodman rm code-serverしても良いけど、そのまま次の手順にいった方が楽です。

systemdのUNITファイルを作る

コマンド一発でsystemdのUNITファイルが作成出来る。このコマンド、–nameで分かる通り、その名前で動いてるコンテナかポッドが無いとダメ。だから「そのまま次の手順」と書いた。

$ podman generate systemd --new --files --name code-server

コマンドの結果はcontainer-code-server.serviceというファイルになる。中身は以下。podman generateコマンドに上記の通りの引数を渡さないと、こうならないので注意。

# container-code-server.service
# autogenerated by Podman 3.3.1
# Thu Nov 11 21:26:31 JST 2021

[Unit]
Description=Podman container-code-server.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --sdnotify=conmon --cgroups=no-conmon --rm -d --replace -it --name code-server -p 192.168.1.2:8080:8080 -v config:/home/coder/.config:Z -v projects:/home/coder/projects:Z -e PASSWORD=your_password codercom/code-server:latest
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=multi-user.target default.target

さて、ここでRed Hatのマニュアルに不備があってちょっとハマった。このあと、この"container-code-server"systemctlenableするんだけども、マニュアルにコマンドが抜けていて、

$ sudo cp -Z ~/container-code-server.service /usr/lib/systemd/system/

するみたいに見えるけど、それ、違う。次に以下のコマンドを実行すると、そんなサービスは無い、って怒られが発生する。

$ sudo systemctl --user enable container-code-server.service

正しくは、以下を実行してから

$ sudo cp -Z ~/container-code-server.service /etc/systemd/user/
$ sudo chcon -u system_u /etc/systemd/user/container-code-server.service

systemctlenableする。サーバを再起動してもコンテナが起動するようになる。

$ sudo systemctl --user enable container-code-server.service

Created symlink /root/.config/systemd/user/multi-user.target.wants/container-code-server.service → /etc/systemd/user/container-code-server.service.
Unit /etc/systemd/user/container-code-server.service is added as a dependency to a non-existent unit multi-user.target.
Created symlink /root/.config/systemd/user/default.target.wants/container-code-server.service → /etc/systemd/user/container-code-server.service.

と思うじゃん?以下も必要。

$ sudo loginctl enable-linger root

コンテナを止める

ここまで出来たら、code-serverのコンテナを止める。ctrl-cして止めてpodman rm code-server

サービスを起動する

これはもういつも通り。

$ sudo systemctl --user start container-code-server

Firewalldの設定を忘れずに

この説明では8080/tcpなので、以下のように。

$ sudo firewall-cmd --permanent --add-port=8080/tcp

おわり

こうやって書くと簡単そうだけど、半日格闘したよ…。

では皆さん、良い「自宅vscode.devライフ」を。

で、終わりだと思うじゃん?

macOSのSafariだと、ターミナルの文字が表示されないんだこれがwww
ほら、真っ白。

GPU Accelerationが悪さをしているので、Settingsから

“GPU Acce..までタイプすれば出てくるのでoffにする。

これで完成。

タイトルとURLをコピーしました