SSH接続でリモートサーバ上のDockerコンテナ内の作業をローカルのVSCodeから直接できる環境を構築してみた

NTTドコモの石川です。社会人2年目になりました。

最近いろいろなことがあって在宅での勤務や、在宅での研究を推奨する企業や学校が増えましたね。

そんな中で僕も在宅勤務をすることがとても増えて、この際なのでリモートで行う開発の環境構築に力を入れていたら、結構良い感じのができたのでよかったら参考にしてみてください。

まるでローカルで作業をしているような感覚で、リモートサーバ上のDockerコンテナ内での作業をすることができるようになります。

今回の環境構築の前提として、

- ローカルPCはMac
- MacへのVSCodeはインストール済み
- リモートサーバにはDockerが導入済み

とします。

また、DockerDesktopを使った方法もありますが、こちらは少し面倒に思ったので今回はそれを使用しない方法をご紹介します。


# 出来上がった環境

ローカルPCのVSCodeの画面上だけでリモートサーバに立てたDockerコンテナ内の下記の操作ができるようになります。

- 実行環境の整備
- プログラムファイルの編集
- プログラムの実行
- JupyterNotebookの実行

これに加えてCyberduckを使ったコンテナ内のファイルの編集、転送やGPUを使用した計算も可能です。


# コンテナの作成

それでは早速環境の作り方です。

まずはリモートサーバ上にSSH接続できるようにしておきましょう。

ここではRSA鍵認証による接続を使います。（※RSA鍵認証の設定の仕方については一般的なもので大丈夫なのでここでは省略）

リモートサーバへSSH接続でログインしたら、リモートサーバ上の任意の場所に下記のようなdockerfileを作成します。ファイル名は「dockerfile」です。

```dockerfile
# ベースのイメージ。下記のものは僕のGPU環境に合わせているので各自適切なものを選んでください。
# GPUを使わない人は"ubuntu:18.04"などで大丈夫です。
FROM nvidia/cuda:10.1-cudnn7-runtime-ubuntu18.04

# 作った人の名前です。
LABEL maintainer "dcm_ishikawa"

# 必要最低限のものやSSH接続などに必要となるものを取り込みます。
RUN apt-get update --fix-missing && \
    apt-get install -y apt-utils && \
    apt-get install -y software-properties-common vim curl unzip htop openssh-server wget procps

# SSH接続のための設定です。
# "password"のところを任意のパスワードへ変更してください。
# いろいろ設定しているのは、Ubuntu18.04でrootユーザでSSH接続するための作業です。
RUN mkdir /var/run/sshd
RUN echo 'root:password' | chpasswd
RUN echo '\nPermitRootLogin yes' >> /etc/ssh/sshd_config
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

# GPUの設定です。GPUを使用しない場合は不要です。
ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES utility,compute

# SSH接続のための設定です。
ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

# runしたときにSSH接続を始められるようにします。
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
```

dockerfileの作成が終わったら、`dockerfile`を保存したのと同じ場所で下記コマンドを実行しましょう。

```bash
docker image build . -t [imagename]
docker run --name [containername] -d -p 10000:22 -p 18888:8888 [imagename]
```
※nvidia-dockerを使用する人は`docker`のところを`nvidia-docker`へ変えてください。

`[imagename]`は任意のイメージ名、`[containername]`は任意のコンテナ名です。

`-p 10000:22`について、10000はリモートサーバにポートを通すときの任意のポート番号。

`-p 18888:8888`について、18888はブラウザ上でJupyterNotebookでアクセスするときのポート番号。JupyterNotebookを使用しない場合、またはVSCode上でJuoyterNotebookを使用するときは不要。

リモートサーバで下記コマンドを打ってコンテナ内に入れたら、コンテナの作成は成功です。

```bash
ssh root@127.0.0.1 -p 10000
```

接続の際にパスワードを求められますが、そのパスワードは`dockerfile`へ記述したものになります。

接続ができたらコンテナ内に`~/.ssh/authorized_keys`を作成し、ローカルPCで作成した公開鍵を記入しましょう。

これができたら`exit`でコンテナへのSSH接続を切断しましょう。

また、同様にリモートサーバ内でも`~/.ssh/authorized_keys`を作成し、ローカルPCで作成した公開鍵を記入しましょう。

これでリモートサーバでの作業はこれで完了です。リモートサーバへのSSH接続も切断しましょう。


# VSCodeの設定

それではローカルPCの設定です。

ローカルPCの`~/.ssh/config`へリモートサーバ、リモートサーバ上のコンテナへのSSHに関する下記の記述を書き足します。(このファイルがそもそもない場合は作成します)


```bash:~/.ssh/config
Host Server01                       # 任意の接続名
  HostName xxx.xxx.xxx.xxx          # リモートサーバのアドレス
  User username                     # リモートサーバにおけるユーザ名
  IdentityFile ~/.ssh/id_rsa        # 作成した秘密鍵のパス

Host Server01:Container             # 任意の接続名
  HostName 127.0.0.1
  User root
  IdentityFile ~/.ssh/id_rsa        # 同上
  Port 10000                        # コンテナをrunしたときに通したポート番号
  ProxyCommand ssh -W %h:%p Server01 # Server01のところは1行目のものと同一に
```

次に下記のURLから、VSCodeのSSH接続のための拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh-explorer

VSCodeを起動し、左側のリモート接続的なボタンを押します。

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491658/aeb72036-8adf-0588-554d-9e7af2827cfd.png)

その中の一覧に`~/.ssh/config`で指定したコンテナの接続名が現れると思います。(ここでは`Server01:Container`)

その接続名の右の方にあるフォルダマークをクリックすると、いよいよコンテナ内へのVSCodeでの接続が完了します。

![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491658/ef157349-4c6b-704d-4a71-5f34480bcc53.png)

現れた画面で「フォルダーを開く」と言うボタンがありますので、それを押すことでコンテナ内の任意の場所が選択できるので、ワークスペースに追加したい場所を選択しましょう。

お疲れ様でした。これで環境構築は完了です。
下記は使い方になります。ご参考ください。


# コマンドの実行

画像の順にボタンをクリックすることで、コンテナ上のコマンドラインを開くことができます。

![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491658/2733ce81-b2be-7548-ea1e-8124c60beb4e.png)

# Pythonのインストール

コンテナのコマンドラインからwgetを用いてAnacondaやMinicondaなどのインストーラをダウンロードし、インストールしましょう。

```bash:(例)
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
rm Miniconda3-latest-Linux-x86_64.sh
source ~/.bashrc
```

# JupyterNotebookの起動

まずコンテナのワークスペースでPythonの拡張機能をインストールします。**(※場合によっては初期状態ではインストールされていません！)**
インストール : https://marketplace.visualstudio.com/items?itemName=ms-python.python

次にコンテナ内のコマンドラインでJupyterをインストールします。

```bash
pip install jupyter
```

Jupyterを起動するコマンドを打ちます。

### VSCode上で使う場合

`command`+`shift`+`P`でコマンドパレットを開き、
`>Create New Blank Jupyter Notebook`を入力し`Enter`。
最近のアップデートによってこれだけで起動できるようになりました。すごい。

### ブラウザ上で使う場合

下記コマンドをコンテナ内のコマンドラインで実行します。

```bash
jupyter notebook --port 8888 --ip=0.0.0.0 --allow-root
```

**`jupyter notebook`コマンドのみではできないことがあるので注意。**
aliasで設定しておくと便利だと思います。

コマンドラインに下記のように返ってくるので`xxxxxxxxxxxxxxx`の部分をコピーしておきます。

```
http://127.0.0.1:8000/?token=xxxxxxxxxxxxxxx
```

ローカルのブラウザを開き、アドレスバーに
`[リモートサーバのIPアドレス]:18888`を入力し`Enter`。（`18888`はdocker run時に指定したポート番号。）
この後パスワードまたはトークンを求められるので、先ほどコピーしておいた`xxxxxxxxxxxxxxx`を入力するとJupyterNotebookをブラウザで使用できるようになります。


# Cyberduckでの接続

`新規接続`から下記の設定で接続できるようになります。

- SFTPを選択
- サーバ：リモートサーバのアドレス
- ポート：runのときに指定したポート番号（ここでは10000）
- ユーザ名：root
- パスワードを入力するか、もしくはSSH秘密鍵を選択


# 接続がうまくいかないとき

VSCodeやコマンドラインでのSSH接続がうまくいかないときは、大抵は何度かコンテナを作り直したりして、`known_hosts`で衝突が起こっていることが考えられます。

ローカルPCの`~/.ssh/known_hosts`で該当の記述の部分を削除する、もしくは名前を変更して`~/.ssh/known_hosts.old`などとすることで、解決することがあります。


# 終わりに

いかがでしたでしょうか。
この記事が少しでもみなさんの在宅勤務や研究などの手助けになれば幸いです。
これからもみんなで力を合わせて、頑張りましょう！