【R】Windows10のRStudio Desktop使うのをやめてVSCodeからrockerコンテナ立ち上げている話

[R Advent Calendar 2020](https://qiita.com/advent-calendar/2020/rlang)の9日目の記事です。

書くのを決めた段階（7日目）でアドベントカレンダーにクラウド含む環境構築記事が複数公開されていたものの、ローカルでDockerを使う方法については書かれていなかったので一定の価値があるのではないかと考えて急遽書きました。
色々試行錯誤しこの形で安定してきているものの内容に自信がないので、誤り等については指摘していただければ助かります。

2020/12/13　12/10に公開されたDocker Desktop 3.0.0に関する情報を追記。
2021/09/19　Remote-Containers拡張で追加できるR定義をベースにしたものに更新。

## 概要

Windowsマシンで良い感じにLinuxを動かすWSL2が、この半年間で大多数のWindows10から使えるようになっています。
そこで私はRおよびRStudioをWindows10にもUbuntuにもインストールせず、Dockerコンテナ内にインストールされたRとRStudio Serverを使うようになりました。
↓のようにブラウザからアクセスします。
![RstudioServer](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675583/ee923ce4-c1fb-4578-2c13-4c464c6524b3.png)
以下のようなソフトウェアをインストールし、Dockerイメージは[rocker/tidyverse](https://hub.docker.com/r/rocker/tidyverse)や日本語向けの[rockerjp/tidyverse](https://hub.docker.com/r/rockerjp/tidyverse)を使わせていただいています。

- Windows10
    - [Ubuntu](https://www.microsoft.com/ja-jp/p/ubuntu/9nblggh4msv6) (WSL2)
    - [Docker Desktop](https://www.docker.com/products/docker-desktop)
    - [Visual Studio Code](https://code.visualstudio.com/)
        - [Remote - Containers拡張](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
        - [Docker拡張](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)

## 動機

### rockerを使う動機

WindowsでR+RStudioを使っていてそれまでに幾度か、日本語版Windows版特有の（Shift-JIS由来の）以下のような問題に苦しめられました。

- 文字化け（ドキュメントフォルダがOneDrive上の「ドキュメント」だったせいで全角パスがRの環境変数に紛れ込んだ）
- 文字化け（コマンドライン、最近文字化けしなくなったらしいです→[朗報！　R 4.0.3からはcmdやPowerShellでRを起動しても文字化けしない](https://qiita.com/ocean_f/items/a0514ae56040d9dee0ad)）
- 文字化け（R Notebook、設定により直るらしいです→[r-wakarang slackの投稿](https://r-wakalang.slack.com/archives/C0ADVD13N/p1604587345019300?thread_ts=1602721611.013300&cid=C0ADVD13N)）

なのでWSLでRを使いたいとずっと思っていたのですが、WSL1では同じスクリプトを実行してもWindows側で実行するよりも目に見えて時間がかかってしまっていました。

今年の夏に自分のPCでもWSL2が使えるようになったので、Shift-JISから解放されると同時に環境のスクラップ＆ビルドも容易になるということで、rockerに乗り換えることにしました。

### VSCode DevContainerを使う動機

私はバージョン管理のためのGit操作はVSCodeのGUIから行っていたので（[Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph)オススメです）元々VSCodeとRStudioを併用していました。
rockerの使い方を試行錯誤している内にVSCodeの拡張機能である[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)の面倒見の良さ（？）に感激し、今の状態に至っています。

コンテナ上のワークスペースをローカルと同じようにVSCodeが記憶する（最近使用した項目）のでコンテナの立ち上げ直しが楽だったり、コンテナ立ち上げ時に`.gitconfig`を自動でコンテナ内にコピーしてくれるのでコンテナ内でも何も考えずにGitで変更をcommitできたりします。

## ソフトウェアのインストール

以下、それぞれの詳細なインストール方法はネット上にたくさん情報があるので省きます。

### WSL2の有効化とUbuntuのインストール

とりあえずUbuntuとしていますがもちろんUbuntu以外のディストリビューションで大丈夫です。

なおDockerホストのファイルをマウントする場合、Linux側のファイルシステム（Windowsからは`\\wsl$\Ubuntu`でアクセス）を使わないと読み書き速度が低下するため、作業フォルダはLinux側に置いた方が良いです。Windowsからのアクセスがしづらくなるのは悩み所ですが。

### Docker Desktopのインストール

~~注意点として、Windowsのバージョンが1903/1909の場合はEdge版を選ぶ必要がありました（2020年8月の情報、現在はどうなっているのか不明）。~~ →2020年12月10日に公開されたDocker Desktop 3.0.0でStable版とEdge版という二つのチャネルは廃止されたので、普通にインストールすれば大丈夫です。（[窓の杜の記事](https://forest.watch.impress.co.jp/docs/news/1294522.html)）

### Visual Studio Codeのインストール

VSCodeをインストールしたら、拡張機能の[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)と[Docker](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)もインストールします。

Docker拡張機能はあるとコンテナの様子をVSCode上から確認できるようになって便利ですがなくても良いです。本記事内でも使用しません。

## 設定ファイルの準備

以下のような設定ファイルとフォルダをワークスペース（プロジェクト）として用意します。worksフォルダをワークスペースとして使用します。

Remote-Containers拡張機能の`remote-containers.createDevContainerFile`コマンドによって追加できるR定義をベースに編集しています。

```none
.
├── .devcontainer
│   ├── Dockerfile
│   ├── devcontainer.json
│   └── library-scripts
│       └── common-debian.sh
├── .env
├── docker-compose.yml
└── works
```

### .devcontainer/devcontainer.json

VSCode Remote-Containersがコンテナを立ち上げるときに使うファイルです。今回は`docker-compose.yml`を参照してコンテナを立ち上げるように書かれています。Remote-Containers拡張機能で追加できる定義からdocker-composeを使用するように変更し、`remoteUser`を`rstudio`にしただけです。

```json:devcontainer.json
{
  "name": "${localWorkspaceFolderBasename}",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "rstudio",
  "workspaceFolder": "/home/rstudio/works",
  "settings": {
    "r.rterm.linux": "/usr/local/bin/radian",
    "r.bracketedPaste": true,
    "[r]": {
      "editor.wordSeparators": "`~!@#%$^&*()-=+[{]}\\|;:'\",<>/?"
    },
    "terminal.integrated.profiles.linux": {
      "radian": {
        "path": "/usr/local/bin/radian",
        "overrideName": true
      }
    }
  },
  "extensions": ["ikuyadeu.r"],
  "shutdownAction": "none",
  "remoteUser": "rstudio"
}
```

コンテナの設定やコンテナ内にインストールされるVSCodeの設定を記述できます。コンテナ内のVSCodeにインストールされる拡張機能も指定でき、上のものでは[R拡張](https://marketplace.visualstudio.com/items?itemName=Ikuyadeu.r)をインストールするようになっています。汎用的に使う拡張機能はsettings.jsonの`remote.containers.defaultExtensions`で指定すれば自分で作成したすべてのコンテナにインストールされるので便利です。
`devcontainer.json`の書き方については[公式ドキュメント](https://code.visualstudio.com/docs/remote/create-dev-container)に大量の解説があるので、設定に困ったらこれを読みましょう。

VSCodeの「フォルダーを開く」でこのファイルの含まれるフォルダをワークスペースとして開くと、下の画像のようにコンテナでワークスペースを開き直すか聞かれます。これで「Reopen in Container」を選択するとVSCodeがDockerを操作してコンテナを作り、vscode-serverをインストールし、拡張機能をインストールしワークスペースを開く……というところまで一気にやってくれます。
![reopen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675583/a636ea69-bc79-7714-758f-9fc6fbc64069.png)
一度開いたワークスペースは「最近使用した項目を開く」から開けるので、二回目以降は直接コンテナ内のワークスペースを開くこともできます。

### .env

コンテナ内に読み込まれる環境変数の内、個々人で別の値を設定したいものを書いておくためのファイルです。今回は`docker-compose.yml`から参照しています。

```env:.env
PASSWORD=pass
```

RStudioのログインパスワードを指定する環境変数`${PASSWORD}`を定義しています。このようにパスワードを定義してやると、「Username:rstudio、Password:pass」でRStudio Serverにログインできます。

自分だけが見るので、このファイルはGitの管理対象外にします。

### Dockerfile

Dockerイメージの作り方を記述するファイルです。Remote-Containers拡張機能で追加できる定義からベースイメージを`rocker/tidyverse:latest`に変更し`USERNAME`を`rstudio`に変更しただけです。

```Dockerfile:Dockerfile
FROM rocker/tidyverse:latest

# Use the [Option] comment to specify true/false arguments that should appear in VS Code UX
#
# [Option] Install zsh
ARG INSTALL_ZSH="true"
# [Option] Upgrade OS packages to their latest versions
ARG UPGRADE_PACKAGES="false"

# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies.
ARG USERNAME=rstudio
ARG USER_UID=1000
ARG USER_GID=$USER_UID
COPY library-scripts/*.sh /tmp/library-scripts/
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \
    && usermod -a -G staff ${USERNAME} \
    && apt-get -y install \
        python3-pip \
        libgit2-dev \
        libcurl4-openssl-dev \
        libssl-dev \
        libxml2-dev \
        libxt-dev \
    && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts \
    && python3 -m pip --no-cache-dir install radian \
    && install2.r --error --skipinstalled --ncpus -1 \
        devtools \
        languageserver \
        httpgd \
    && rm -rf /tmp/downloaded_packages

# VSCode R Debugger dependency, install the latest release version from GitHub.
RUN Rscript -e 'remotes::install_github("ManuelHentschel/vscDebugger@*release", dependencies = FALSE)'

# R Session watcher settings.
# See more details: https://github.com/REditorSupport/vscode-R/wiki/R-Session-watcher
RUN echo 'source(file.path(Sys.getenv("HOME"), ".vscode-R", "init.R"))' >> ${R_HOME}/etc/Rprofile.site
```

せっかくVSCodeを使うので、`devcontainer.json`でインストールするよう指定した[R LSP Client](https://marketplace.visualstudio.com/items?itemName=REditorSupport.r-lsp)を動かすために、rocker/tidyverseに含まれていないlanguageserverパッケージをコンテナ内にインストールするようにしています。

コンテナ内で使うパッケージが増えたら、このファイルを編集してイメージビルドの段階でインストールされるようにします。

### docker-compose.yml

コンテナを立ち上げるときに使われる設定ファイルです。`Dockerfile`と`.env`を参照しています。

```yml:docker-compose.yml
version: '3'
services:
    rstudio:
        env_file: .env
        build:
            context: ./.devcontainer
        environment:
            - TZ=Asia/Tokyo
        ports:
            - "8787:8787"
        volumes:
            - ./.rstudio_config:/home/rstudio/.config
            - ./works:/home/rstudio/works
```

`./.rstudio_config:/home/rstudio/.config`の部分でRStudioの設定を永続化しています。

今回の用途なら`.devcontainer/devcontainer.json`だけでも同等の設定を書くこともできますが、慣れているのでdocker-composeを使っています。
もちろんVSCodeを経由せずとも`docker-compose up -d`とPowerShellでコマンド実行すればRStudioを使えます。

## 使い方

1. VSCodeで設定ファイルの含まれた、ワークスペースとなるフォルダを開きます。
2. 「Reopen in Container」を選択してコンテナを立ち上げます（右下の通知 or Shift+Ctrl+Pで出てくるコマンドパレット）
3. コンテナ内でワークスペースが開かれるまで待ちます。ワークスペースが開かれるとVSCode左下の謎の緑のマークの隣に「Dev Container: (フォルダ名)」と表示され、その右隣に「使用可能なポートが1個あります」と表示されます。
4. `localhost:8787`をブラウザで開きます。
5. 「Username:rstudio、Password:pass」と、`.env`で指定したパスワードであるpassを入力してRStudioにログインします。
6. 右上の「Project: (None)」→「Open Project」で「work」を開きます。
7. これでVSCodeもRStudioもコンテナ内で使えるようになったので、Shift-JISのことは忘れて好きなように作業します。
8. RStudioでの作業を終えたら、右上の電源ボタンを押してセッションを終わらせます。
9.  作業の最後に、VSCodeで左下の謎の緑のマークを押して出てくるメニューの一番下の「リモート接続を終了する」を選択し、リモートワークスペースを閉じます。

## おわりに

Linuxを使うことでShift-JISから解放されたと思いきや、「Excelでグラフ作るからcsvファイルくれ」と要求され直接Excelで開くのではなくcsvをインポートする方法を説明するのもめんどくさくてShift-JISなcsvファイルを作って渡したりしており、結局Shift-JISから逃れきれてはいません。

戦いは続く……。

## 参考記事

- [「tidyな分析環境つくるのがたいぎぃ」からDocker使う](https://qiita.com/kazutan/items/1af4d0c4defb4de4b7fb)
- [rocker で使える便利コマンド install2.r とその仲間たち powered by littler](https://blog.atusy.net/2019/04/16/littler-on-rocker/)
- [ローカルのRStudioの設定をDockerコンテナ上のRStudio Serverに反映する](https://qiita.com/nozma/items/cae323708ff60cc2f67a)
- [renv と Docker の相互運用パターン](https://terashim.com/posts/renv-docker-patterns/)
- [VS CodeでDocker開発コンテナを便利に使おう](https://qiita.com/Yuki_Oshima/items/d3b52c553387685460b0)
- [【venv不要】Remote Containersで作る最強のPython開発環境【VSCode/Docker】](https://qiita.com/0MeO/items/1fc3995e568d2c95569e)