Dockerで作る、最強の移動型開発環境

## まえがき

この記事は[富士通システムズウェブテクノロジー Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fsweb) 24日目の投稿です
（お約束）
記事の内容は全て個人の見解であり、執筆内容は執筆者自身の責任です。所属する組織は関係ありません。

## 目的

皆さん、コーディングは好きですか？私は好きです。
ただ、電車での移動中にPCを鞄から取り出して、IDEを立ち上げて・・・は電車の中などではちょっと難しいですし、
少し恥ずかしいですよね？

そんな時、スマホでコーディングができたらなぁ。
なんて思いませんか？

そんな時に役立つものをご紹介したいと思います

## 利用しているOSSの紹介

* [coder](https://coder.com/)
  * ブラウザ上で**ほぼそのまま**のVSCodeが使えるクラウドIDEサービス
    * 拡張機能もそのまま使えるものが多い
      * [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)は検索しても出てこなかったので、未対応？
  * [公式Dockeイメージ](https://hub.docker.com/r/codercom/code-server)が公開されており、手早く利用開始できます。

## 環境構成

ハイパー雑な構成イメージはこんな感じです。

![code-server.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/984ace1a-5c31-3cbf-0ea0-d747cd17d304.png)

* code-serverはメインのコンテナです。ここの上に開発環境をセットアップします
* other containerは、その他ミドル（DBなど）を動作させるコンテナです

### 環境を試したPCは以下

* macOS Catalina 10.15.2
* docker 19.03.5
* docker-compose 1.24.1

## 完成品

docker-composeを使用して、デプロイできるようにしてあります。

<details><summary>docker-compose.ymlの中身です</summary><div>

```yml
version: '3'

services:
  coder-server:
    build:
      context: .
      dockerfile: coder/Dockerfile
    volumes:
      - ./code-server/coder.json:/home/coder/.local/share/code-server/coder.json # code-serverの設定ファイル
      - ./code-server/languagepacks.json:/home/coder/.local/share/code-server/languagepacks.json # code-serverの設定ファイル
      - ./code-server/User:/home/coder/.local/share/code-server/User # code-serverの設定ファイル
      - ./code-server/extensions:/home/coder/.local/share/code-server/extensions # code-serverの拡張機能
      - ./project:/home/coder/project # projectディレクトリ 
      - /var/run/docker.sock:/var/run/docker.sock # docker sock
    ports: 
      - 8080:8080 # exported ports
    environment:
      PASSWORD: P@sw0rd # 認証パスワード
    entrypoint: "code-server --allow-http --no-auth"
  postgres:
    # アプリから使用するミドルなど
    image: postgres:9.6-alpine
    ports:
      - 15432:5432
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: P@s5w0rd
      POSTGRES_DB: pg_data
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
    restart: always
```

</div></details>


<details><summary>coder/Dockerfileの中身です</summary><div>

```Dockerfile
# 公式イメージをそのままベースイメージとして使用します。
# ここでビルドしたイメージに、自分の開発に必要なバイナリをインストールしていきます
FROM codercom/code-server AS baseImage

USER root

# DinDではないのですが、ホストのDocker daemonを共有して、自由にコンテナを建てられるようにします
# docker-cliのみインストールします
RUN apt-get update -y \
 && apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common \
 && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
 && add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable" \
 && apt-get update -y && apt-get install -y docker-ce-cli \
 && apt-get clean && rm -rf /var/lib/apt/lists/*

FROM baseImage

# ここから、アプリの開発環境に必要なものを入れていきます

# scalaの開発環境を作るため、Javaとsbtをインストールしていきます

ENV JAVA_HOME /usr/java/jdk1.8.232/
ENV SBT_HOME /usr/lib/sbt

# 毎回 sudo するのが面倒なので、rootで実行しちゃいます
USER root

# adoptOpenJDKをバイナリでダウンロードしてきてインストールします
RUN mkdir -p ${JAVA_HOME} \
 && wget -q -O - https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u232-b09/OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz | \
    tar zxf - -C ${JAVA_HOME} --strip=1
# sbtをバイナリでダウンロードしてきてインストールします
RUN mkdir ${SBT_HOME} \
 && wget -q -O - https://piccolo.link/sbt-1.3.5.tgz \
  | tar zxf - -C ${SBT_HOME} --strip=1

USER coder
# パスを設定して完成！
ENV PATH $PATH:${JAVA_HOME}/bin:${SBT_HOME}/bin
```

</div></details>

## 実行してみる

docker-composeを実行してみます！

```zsh
% docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_coder-server_1 ... done
Creating docker_postgres_1     ... done

% docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                     NAMES
c3655e1c8d6a        docker_coder-server   "code-server --allow…"   48 seconds ago      Up 47 seconds       0.0.0.0:8080->8080/tcp    docker_coder-server_1
4803094ccaef        postgres:9.6-alpine   "docker-entrypoint.s…"   48 seconds ago      Up 47 seconds       0.0.0.0:15432->5432/tcp   docker_postgres_1
```

postgresコンテナと、coder-serverコンテナが起動しましたね！
![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/4a89d419-5f9f-8a62-e25e-d5428935ffd0.png)

早速ブラウザからアクセスしてみましょう！

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/6e660e1a-5bdb-5c96-20b0-520a1133d229.png)


パスワードの入力が求められるので、**PASSWORD** に設定したものを入力して、Enterします

おめでとうございます。あなたの開発環境ができました！

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/33178ae1-e0ea-11fe-3e6d-0cd1126a57d9.png)

デフォルトでgitなどもインストールされているため、リポジトリをクローンしてきて、開発を開始することができます！

## iPhoneから確認

iPhoneからも確認できるか試してみましょう

<img width="375" alt="4.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/74c26e1c-9c2f-0c3d-192a-959fe4ca2c2b.png">

同様にしてログイン

以前プロジェクトやフォルダを開いていると、以下のようにワークスペースが自動的に開かれます

![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/b9c3696f-4701-c0c0-d2fe-644f601a81f6.png)


ターミナル操作などもできます

左下の歯車アイコン>Command Palette>Open New Terminal

![7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/904e61b9-79e9-fd9b-afae-ddea7f782017.png)

![8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537319/99fbbd99-37fe-a607-101f-fe8d25508ccd.png)

iPhoneのデフォルトのキーボードだと、ショートカットやタブが使用できないので、
外付けのキーボードでの使用をお勧めします。

## 最後に

* MavenやGradleを使った開発をされる方は、Dockerfilの**FROM baseImage** 以降から、sbtのインストールを除外すれば問題ないでしょう。  
  （リポジトリにwrapperがコミットされている場合）
  * wrapperがない場合は、ビルドツールも同時にインストールしてしまえばいいと思います
* その他開発言語の場合でも、Javaなどと同様にバイナリをダウンロードして展開・パスを通す方法が早いし確実だと思います。
  * [.NET Core](https://dotnet.microsoft.com/download)
  * [golang](https://golang.org/doc/install)
* UserLAndなどを使用して、開発環境を整えている方もいらっしゃったりするようですが、結局vimしか使えなかったり結構しんどそうなので、使い慣れたVSCodeがそのまま使えるっていうのはやっぱりいいなぁと。
  * 最悪このままクラウドのサーバ上にIDEを起動しておけばいつでも開発できる？！
  * ただし今の設定のままだとセキュリティがザルなので、絶対インターネット公開はしないでくださいね
* ほんとは、Remote Developmentを使って開発環境は別コンテナにしたかったんだけど、いったいなぜ使用できないのか・・・

```javascript
((h = (s = "めりくり", n = true, r = Math.floor(Math.random() * s.length), t1 = s.substr(r, 1)) => n ? (rp = 2) => (t1 + h(s.replace(t1, ""), false)).repeat(rp) : t1) => `${h()(1)}${h()(1)}`)()
```