VSCode devcontainerを使って面倒な開発環境を作る

# 概要

ある案件で、opencvを使って映像を扱うサービスを構築した。
各開発者の開発環境をどう作るか頭を悩ませた。
opencvをビルドするのに2時間近くかかってしまうし、gstreamerのインストールやらもろもろのライブラリやら考えると、環境整備だけで気が遠くなりそう。

検索していると以下のような記事を見かけた。

https://qiita.com/d0ne1s/items/d2649801c6f804019db7

あらかじめ開発環境をdocker image化しておき、それをそれぞれの開発PC上で起動する事ができれば、開発参画が簡単になるのでは？
ということで、試してみた。

# 要件

* 開発言語はgolang1.15
* opencv4.5.1
* gocv0.26.0
* gstreamer
* Redis

gstreamerで映像を受け取ってgocvで画像処理。結果をRedisに格納。

# 手順

事前に必要なものは

module | desc
--|---
VSCode | IDE
VSCode Remote Container | VSCodeの拡張モジュール
github token | ghcr.ioにアクセスするためのtoken
Docker for windows | windows上での開発なので
VcXsrv | dockerコンテナ内で扱う映像を表示するため

https://github.com/microsoft/vscode-remote-try-go

試した内容は、上のブランチの内容を少し変更したもの。

```
project_root/
   .devcontainer/
      devcontainer.json
      (*Dockerfile)
      docker-compose.json
```
上記を作成する。開発環境用のコンテナのみでいいならdocker-compose.ymlは不要だが、どうせなら必要なサーバー一式全て立ち上げてしまいたいので、今回はDockerfileは不要。

## 1. 自前のdevcontainerを用意する

```docker
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.137.0/containers/go/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT}
```

vscode-vs-try-goに含まれるDockerfileにはこのように書かれている。
つまり、goのバージョンごとにdevcontainerの元となるimageが公開されている。これをもとに、自分専用の開発環境イメージを作成し、それをdockerリポジトリから各開発者の環境にpullできるようにする。

```docker
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.137.0/containers/go/.devcontainer/base.Dockerfile
ARG VARIANT="1.15"
# ARG OPENCV_VERSION="4.3.0"
# for gocv v0.26
FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT}
ENV OPENCV="4.5.1"

WORKDIR /src
# https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html
# Install minimal prerequisites (Ubuntu 18.04 as reference)
RUN apt update && apt install -y cmake g++ wget unzip libgtk2.0-dev pkg-config glib-2.0
# Install gstreamer
RUN sudo apt-get -y install \
    gstreamer1.0-dev \
    gstreamer1.0-tools \
    gstreamer1.0-doc \
    gstreamer1.0-libav \
    gstreamer1.0-plugins-ugly \
    gstreamer1.0-plugins-good \
    gstreamer1.0-plugins-bad \
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libgstreamer-plugins-bad1.0-dev

# Download and unpack sources
RUN wget -O opencv.zip https://github.com/opencv/opencv/archive/${OPENCV}.zip
RUN wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/${OPENCV}.zip
RUN unzip opencv.zip
RUN unzip opencv_contrib.zip
# Create build directory and switch into it
RUN mkdir -p build
WORKDIR /src/build
# Configure
RUN cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib-${OPENCV}/modules -D OPENCV_GENERATE_PKGCONFIG=YES -D CMAKE_BUILD_TYPE=Release -D WITH_GTK=ON -D WITH_GSTREAMER=ON ../opencv-${OPENCV}
# Buildpkg
RUN cmake --build -j4 . | tee error.log
RUN make install
```
そこで上記のようなDockerfileを作成。ここで作成したイメージをdocker repositoryにpushする。

* 今回はGithub Container Repositoryを使用した。
* 公開されているdevcontainer-go1.15のイメージにgstreamerを追加し、それからWITH_GSTREAMER=onオプション付きでopencvをビルドしている。

* gocvがopencvとリンクするために、opencvは`OPENCV_GENERATE_PKGCONFIG=YES`オプション付きでビルドし、`PKG_CONFIG_PATH`を環境変数に設定する必要があった。opencv単体をインストールするのではなく`go get gocv.io/x/gocv`してgocv配下でmakeすればこの辺りの手間はなくなるかもしれない。

```sh
docker push ghcr.io/my_organization/devcontainer-go1.15-opencv4.5:latest
```
でpush。

## 2. .devcontainer配下に必要なファイルを作成する

### devcontainer.json
```json
{
	"name": "Go",
	"dockerComposeFile": "docker-compose.yml",
	"service": "app",
	"workspaceFolder": "/app",
	"extensions": [
		"golang.Go"
	],
	"remoteUser": "vscode"
}
```
オリジナルから一部変更。imageではなくdocker-composeなので、buildを削除して
dockerComposeFileとserviceを追加している。

### docker-compose.yml
```yml
version: '3'
services:
  redis:
    image: redis:latest
    ports:
      - "6379:6379"
  app:
    image: ghcr.io/my_organization/devcontainer-go1.15-opencv4.5:latest
    environment:
      - "DISPLAY=host.docker.internal:0"
      - "LD_LIBRARY_PATH=/usr/local/lib"
      - "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig"
    volumes:
      - ../:/app:cached
    command: sleep infinity
    links:
      - redis
```
以上2つのファイルをプロジェクトに追加。

プロジェクトディレクトリ上でvscodeを開くとこんな画面が現れる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236681/0e40c023-c61b-6401-a078-3510b465c335.png)

**Reopen in Container** を選べば、docker-composeが起動してコンテナ内に切り替わる。

# 評価

dockerコンテナ内で開発するのは理想ではあるものの、敷居が高い印象がある。
だが、vscode devcontainerはあらかじめ開始するための各言語のキットが用意されており、簡単に開始する事ができるのが好印象だと感じた。特にContainer内外の切り替えが殆どシームレスな感覚で移動できるため、必要な時だけContainerに入るような運用もできた。

単純なWebアプリケーションとデータベース程度なら必要性は低いかもしれないが、環境を作るのが面倒なサービスなら、docker化するメリットは大きいのではないだろうか。

もちろん、それなりのPC性能が要求される事はお忘れなきよう。

## メリット

* 開発環境作成のハードルが高い場合、開発参画へのハードルを下げられる
* 全開発者が完全に同じ環境で開発できる
* 構成が変わっても、最初にイメージを作成する時間+pullする時間以外かからない
* ci環境やprod環境と大きく変わらない環境で開発できる

## デメリット

* 開発環境がVSCodeに限定される
* 開発者全員がDockerをインストールしなければいけない
* 開発者全員がDockerに慣れなければならない
* 素にインストールするのと比べると、それなりに重くはなる

結局、一番の障壁は「開発者のDocker慣れ」だと思う。重さの問題はお金で解決できるが、慣れの問題は価値観が絡むので、なかなか難しい。

もっとこうしたらいい、などご意見あればください。

https://qiita.com/kishibashi3/items/2b1e342d1ce3957b3dc8
へ続く