VSCode Remote Containers で --privileged と /sbin/init を渡したコンテナで開発する方法

# はじめに
VSCode Remote Containers を使って開発する場合、Dockerfile を書いて「Reopen in Container」とすると Docker コンテナが起動しますが、この時 VSCode が `docker run` を実行します。
このため、何も気にせず[公式の手順](https://code.visualstudio.com/docs/remote/remote-overview)などを参考に起動すれば、`--privileged` オプションを付加できず、ENTRYPOINT に `/sbin/init` も渡せないため、コンテナで `systemctl` を実行できません。
※実行すると「Failed to get D-Bus connection: Operation not permitted」と表示されエラーとなります。
※このあたりの仕組みについては、既に「[CentOS 7のDockerコンテナ内でsystemdを使ってサービスを起動する](https://qiita.com/yunano/items/9637ee21a71eba197345)」などの記事で語られているため、本記事では割愛します。

本記事では、題記の通り、VSCode Remote Containers で使うコンテナで、`docker run` に `--privileged` と `/sbin/init` を渡すための調査経緯と、実現した方法を紹介します。

# 0. 序章: 調査編
まずは正攻法で行けないかと調査してみると、設定ファイルの devcontainer.json には「runArgs」というパラメータがあり、ここで `docker run` する時のパラメータを追加できることが分かりました。
参考: [devcontainer.json reference
](https://code.visualstudio.com/docs/remote/devcontainerjson-reference)
しかし、この方法では `--privileged` を追加できても、`--entrypoint` を上書きできませんでした。
VSCode Remote Containers で 「Reopen in Container」 して docker コンテナを起動すると、`docker run` の引数に **`--entrypoint /bin/sh <各自のDocker Image> -c echo Container started ; trap "exit 0" 15; while sleep 1 & wait $!; do :; done`** が付加されてしまい、devcontainer.json に書いたパラメータではこれを上書きできない仕様となっているようです。

起動時にどんなコマンドが流れているかは、起動中に表示される以下ポップアップのリンクを押すことで見えるログから把握できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/52582b29-d941-4032-b348-40344e0aef0a.png)

また、次のリンク先で解説されているように、Dockerfile に書いた ENTRYPOINT と CMD よりも、引数で指定された `--entrypoint` が優先されるため、Dockerfile に書いても上書きできません。
VSCode が必ず `docker run` に引数で `--entrypoint` を付加するためこれが最優先されてしまいます。
参考: [Dockerfileによるビルド](http://www.tohoho-web.com/docker/dockerfile.html)

# 1. 実現手順
そこで、以下の手順を辿ることにしました。

## (1) まずは手順通り 「Reopen in Container」 を実行
この手順により Docker イメージがビルドされ、コンテナが起動します。
この時、先ほどのポップアップのリンクからログを開き、ログの中から以下のコマンドを探してコピーします。

```bash
docker run -a STDOUT -a STDERR --mount type=bind,source=<各自の環境のパス>,target=<各自の環境のパス>,consistency=cached -l vsch.quality=stable -l vsch.remote.devPort=0 -l vsch.local.folder=<各自の環境のパス> --entrypoint /bin/sh <各自のDocker Image> -c echo Container started ; trap "exit 0" 15; while sleep 1 & wait $!; do :; done
```

## (2) VSCode でコンテナとの接続を解除
(1) で起動したコンテナは `--privileged` されていませんので、VSCode で一旦接続を解除します。

## (3) コピーしたコマンドを一部変更
コピーしたコマンドに `--privileged` を追記し、`--entrypoint` を書き換えます。

```bash
docker run -a STDOUT -a STDERR --mount type=bind,source=<各自の環境のパス>,target=<各自の環境のパス>,consistency=cached -l vsch.quality=stable -l vsch.remote.devPort=0 -l vsch.local.folder=<各自の環境のパス> --privileged --entrypoint /sbin/init <各自のDocker Image>
```

## (4) 上記コマンドをローカルのターミナルで実行
実行すると (3) のオプション通りに docker コンテナが起動します。
`docker ps` で起動状態を確認できます。

## (5) VSCodeで「Attach to Running Container」を実行
次に、(4) で起動したコンテナを VSCode にアタッチします。
まず、以下画像のように「Attach to Running Container」を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/8ca46365-bd83-d8cd-39c5-bc098e0185b9.png)

次に起動中のコンテナを選ぶ画面で、コンテナを選ぶだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/a128c6fb-455f-7d8e-d98d-a5231c89b4c0.png)

ここまでで、VSCode からコンテナに接続して、開発環境を開けます。

# 2. 動作確認
VSCode でコンテナのターミナルを開いて `systemctl` を打ってみます。

```bash
bash-4.2# systemctl
  UNIT                          LOAD   ACTIVE     SUB       DESCRIPTION
  dev-vda1.device               loaded activating tentative /dev/vda1
  -.mount                       loaded active     mounted   /
  dev-hugepages.mount           loaded active     mounted   Huge Pages File System
  dev-mqueue.mount              loaded active     mounted   POSIX Message Queue File Syste
  etc-hostname.mount            loaded active     mounted   /etc/hostname
  etc-hosts.mount               loaded active     mounted   /etc/hosts
(以下長いので略)
```

このようにエラーが解消され、打てるようになっています。

# 最後に
このように、起動したコンテナに VSCode から接続する手順を踏むことで、VSCode Remote Containers でも `systemctl` が実行できるようになります。
今回、少々面倒ですが `docker build` までは VSCode で実行して、各種オプションが含まれる `docker run` コマンドを VSCode のログから発掘する方が早いと判断しました。
(もっと良い方法があるのかもしれませんが、もし見つかったら追記します)

なお `--privileged` はその名の通り特権という意味なので、ご利用は計画的に、とすべきです。

# 参考文献
* [VSCode Remote Containers](https://code.visualstudio.com/docs/remote/remote-overview) ※公式
* [devcontainer.json reference
](https://code.visualstudio.com/docs/remote/devcontainerjson-reference) ※公式
* [DockerとRemote Containersでの開発環境が最高過ぎる](https://www.keisuke69.net/entry/2020/06/04/145719) ※Dockerfileで起動する手順が掲載されています
* [VSCode Remote Containerが良い](https://qiita.com/d0ne1s/items/d2649801c6f804019db7)
* [VSCode Remote Containers を利用して最強のローカル開発環境を作りたい](https://qiita.com/sabure500/items/a117b8a1733193be455f)
* [CentOS 7のDockerコンテナ内でsystemdを使ってサービスを起動する](https://qiita.com/yunano/items/9637ee21a71eba197345)
* [Dockerfileによるビルド](http://www.tohoho-web.com/docker/dockerfile.html)