オレオレ開発環境2017〜2019年版

この記事は[ひとり開発 アドベントカレンダー 2019年](https://qiita.com/advent-calendar/2019/dotfiles)の8日目の記事です。
開発環境の紹介 (VM->Docker with tmux, vim)と題して、ここ3年に渡る私のターミナル周りの開発環境の推移をまとめていく記事です。


# 2017年 dotfiles + git
[github - u1and0/dotfiles](https://github.com/u1and0/dotfiles)

dotfilesとはなんぞやについては今年のアドベントカレンダー1発目に詳しく書かれているので参照してみてください。
[ようこそdotfilesの世界へ ](https://qiita.com/yutakatay/items/c6c7584d9795799ee164)


## インストール方法

```shell-session
$ bash -c "$(curl -fsSL git.io/dotu1)"
```

ワンコマンドでインストールできます。ただし、インストール時に表示される注意書きにもあるように、インストール先のホームディレクトリ上のファイルを上書きするので、上書きされたくないファイルがあったら予めリネームするなどして退避してください。あくまで自分用にしか作っていないので、インストールするときはファイルを上書きするし、アンインストール方法は用意されていません。**ファイルが損失しても責任を取りかねます。**


ジョブ内容

* dotfilesをクローンしてファイルを上書きします。
* bacpacを使ってArchlinuxで使っていたのパッケージをインストールします。
* pyenvを使用してpython 環境を構築します　-> 今はdockerでpython環境を構築しているので基本的に使いません。
* デフォルトのshellをzshに変更します。

## bacpac
元はこれ[AUR - bacpac](https://aur.archlinux.org/packages/bacpac/)
フォークしたのが自分のGist [Gist - u1and0/.bacpac.md Secret](https://gist.github.com/u1and0/8bd32ade8d95988b52b03a1b08297b96/8b54d21bbe5727f3dce2e7a2a870317727bc71ca)

パッケージマネージャにpacmanを使っているOSなら使えます。
インストールしたパッケージをgitで管理するshell script。古いのかもしれないです。
元のソースと比べて`bacpac cat`, `bacpac diff`のサブコマンドと、zshの補完機能`_bacpac`を追加しています。


submoduleで管理しています。

```sh:.gitmodules
[submodule "bacpac"]
	path = bacpac
	url = https://gist.github.com/u1and0/8bd32ade8d95988b52b03a1b08297b96
	ignore = dirty  # dotfilesには関係ないパッケージの追加・削除による差分を感知しない
```

## zsh
* .bash_aliases
* .bash_functions
* .bashrc
* .zsh_aliases
* .zsh_functions
* .zsh_keybinds
* .zshrc
* .zplug.zsh

この辺で管理しています。
.zshrc呼び出すときに`.bashrc`をsourceしているので、bashの設定をオーバーライドしています。

```zsh:.zshrc
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
if [ -f ~/.zsh_aliases ]; then
    . ~/.zsh_aliases
fi
if [ -f ~/.zsh_functions ]; then
    . ~/.zsh_functions
fi
```

## zplug
[zplug](https://github.com/zplug/zplug)はzshに入れるプラグインマネージャです。
以下の設定を.zshrcに書いておくことでzplugをgithubからクローンしてzplug及びzplugで管理されるプラグインをインストールしてくれます。

```zsh:.zshrc
# 2回目以降は.zplug/init.zshを読み込んでロード
if [[ -f ${HOME}/.zplug/init.zsh ]]; then
    export ZPLUG_LOADFILE=${HOME}/.zplug.zsh
    source ~/.zplug/init.zsh

    # Auto installer
    if ! zplug check --verbose; then
        printf "Install? [y/N]: "
        if read -q; then
            echo; zplug install
        fi
    fi

    # コマンドをリンクして、PATH に追加し、プラグインを読み込む
    zplug load

# 初回はzplugをcurlでインストール
else; printf "Install zplug? [y/N]: "
    if read -q; then
        curl -sL --proto-redir -all, https\
            https://raw.githubusercontent.com/zplug/installer/master/installer.zsh |
    zsh && source $0  # .zshrc再リロード
    fi
fi
```

## tmux
* .tmux.conf
* .tmux/plugins/tpm

この辺で管理しています。
submoduleも入れています。

```sh:.gitmodules
[submodule ".tmux/plugins/tpm"]
	path = .tmux/plugins/tpm
	url = https://github.com/tmux-plugins/tpm
```

### 主な使い方
* セッション切り替え
  * <key>C-Space J</key>
  * <key>C-Space K</key>
* セッションデタッチ
  * <key>C-Space d</key>
* ウィンドウの新規作成
  * <key>C-Space c</key>
* ウィンドウの削除
  * <key>C-Space x</key>
* ウィンドウ切り替え
  * <key>C-Space L</key>
  * <key>C-Space H</key>
* ペイン切り替え
  * <key>C-Space j</key>
  * <key>C-Space k</key>
  * <key>C-Space l</key>
  * <key>C-Space h</key>
* ペインの新規作成(横分割)
  * <key>C-Space "</key>
* ペインの新規作成(縦分割)
  * <key>C-Space %</key>
* コピー(ヤンク)
  * <key>C-Space Space</key>でコピーモードにはいってから<key>Space</key>と移動キーで選択して<key>y</key>
* ペースト
  * <key>C-Space p</key>

## nvim
エディタは[neovim](https://github.com/neovim/neovim)です。
dotfilesでは以下のファイルで管理しています。
プラグインマネージャは[Shougo/dein.vim](https://github.com/Shougo/dein.vim)

* .config/nvim/init.vim: neovimが呼び出されるときに`source`する。これが親
* .config/nvim/keymap.rc.vim :neovimのキーバインド
* .config/nvim/options.rc.vim: neovimのオプション`set`で設定するもの
* .config/nvim/autocmd.rc.vim: `autocmd`で設定するもの
* .config/nvim/ftplugin/arduino.vim: arduino使うときに入れてたけど今使ってない
* .config/nvim/ftplugin/html.vim: htmlファイル開いたときのタブ設定など
* .config/nvim/ftplugin/javascript.vim: jsファイル開いたときのタブ設定など
* .config/dein/plugins.toml: 常に呼び出されるdein管理のプラグイン
* .config/dein/lazy.toml: 特定の条件下で呼び出されるdein管理のプラグイン
* .config/dein/lightline.toml: UIかっこよくする
* .config/dein/vim-airline.toml: UIかっこよくする。今は使っていない
* .config/dein/python.toml: python3+としてコンパイルされているneovimでないと使えないプラグイン


## python環境
[python環境を使い分ける〜pyenvとcondaのメモ〜](https://qiita.com/u1and0/items/80c7eeec1870423beb31)

pyenv+condaを使ってpython環境の構築をする記事です。


# 2018年 vagrant + Virtual Box
[Archlinux on Vagrant 日本語/GUI/docker セットアップスクリプト](https://qiita.com/u1and0/items/26be6344ced826ee4868)

この辺からドットファイルとパッケージだけではなくinfrastructure as code, 環境周りを一括して管理するようになりました。
これまでWindows機でCmder上でやっていたところ、watchコマンドが使えなかったりで本格的なLinuxを使いたいと思い、Windows10上にvagrant+VirtualBoxを使ってオレオレArchlinuxイメージを作って色々いじっていました。
Linuxマシンになってなるべく困難は調べものして解決するつもりでしたが、どうしてもうまく行かなくなったときは仮想マシンのスナップショット機能で過去に戻ればいいし、最悪新しい環境をもう一つ作ればいいやという軽い気持ちで使い始めました。


# 2019年 docker
この辺からWindows10を捨ててUbuntu18.04に移行しました。
VirtualBoxではパフォーマンス面でリソースを100%活かしきれていないことが気になったので、CPUやメモリをホストと共有できるコンテナ仮想環境dockerに開発環境を移行しました。ポータブルなので、一度イメージ構築してしまえば新しいマシンに移ってもイメージをpullするだけなので、引っ越しに抵抗がありませんね。
[オレオレdocker開発環境を作ってみた](https://qiita.com/u1and0/items/88e3dd5efbdb0a496959)にも自分用docker環境構築を書きました。
この記事ではさらにその続きで、docker-composeを使って各dockerコンテナをワンコマンドで立ち上げようという試みです。

```yaml:docker-compose.yml
# maintainer: u1and0 <e01.ando60@gmail.com>
# version 3.7 for docker version 18.06+
# see https://docs.docker.com/compose/compose-file/
# What for:
#    docker in docker dev.env
#    master: zsh & zplug env. Pairents for all docker image.
#    python: python env. It can use ipython & jupyter
#
# Usage:
# $ cd /path/to/composeset
# $ docker-compose start master
# Starting master ... done
# Starting jupyter ... done
# Starting vimgo ... done
# $ docker exec -it composeset_master_1 zsh -l
#
version: '3'
services:

    master:
        image: "u1and0/myenv:latest"
        tty: "true"
        volumes:
            - "${HOME}:/home/vagrant"
            - "${HOME}/.zsh_history:/root/.zsh_history"
            - "${HOME}/yankring_history_v2.txt:/root/yankring_history_v2.txt"
            - "/mnt/e/Users/U1and0:/mnt"
            - "/var/run/docker.sock:/var/run/docker.sock"
        working_dir: "/home/vagrant"
        environment:
            - "HOST=master"
        command: "zsh"

    jupyter:
        # jupyter tagはpasswordを設定したイメージ
        # [Jupyter on Dockerでパスワードの設定方法が分からないあなたへ](https://qiita.com/Yusuke-Shimizu/items/731ca71b3c4c3649ec7f)
        image: "u1and0/pyenv:jupyter"
        volumes:
            - "${HOME}:/home/vagrant"
            - "/mnt/e/Users/U1and0:/mnt"
        working_dir: "/home/vagrant/Dropbox/Program/python"
        environment:
            - "PYTHONPATH=/home/vagrant/Dropbox/Program/python"
        command: "bash -c 'source /root/.pyenvrc && source activate && jupyter notebook --allow-root'"
        ports:
            - "8888:8888"

    vimgo:
        image: "u1and0/vim-go:zplug1.2.0-r1"
        tty: "true"
        working_dir: "/root/go/src"
        volumes:
            - "${HOME}:/home/vagrant"
            - "${HOME}/.zsh_history:/root/.zsh_history"
            - "${HOME}/yankring_history_v2.txt:/root/yankring_history_v2.txt"
            - "/mnt/e/Users/U1and0:/mnt"
        environment:
            - "GO111MODULE=on"
            - "HOST=go"
        ports:
            - "8080:8080"
        cap_add:
            - "SYS_PTRACE" # for golang debugger
```

dotfilesでは .docker/config.json で<key>C-/ C-P</key>から<key>C-\ C-/</key>にデタッチキーを変更しています。

```.docker/config.json
{
    "detachKeys": "ctrl-\\,/"
}
```


| イメージ名                             | 使用用途  |
|----------------------------------------|-----------|
| image: "u1and0/myenv:latest"           |   DinD    |
| image: "u1and0/pyenv:jupyter"          |Python環境 |
| image: "u1and0/vim-go:zplug1.2.0-r1"   | Go環境    |


## 使い方
```
$ cd /path/to/composeset                            # 1
$ docker-compose start master                       # 2
Starting master ... done
Starting jupyter ... done
Starting vimgo ... done
$ docker exec -it composeset_master_1 zsh -l        # 3
```

1. docker-compose.ymlがある場所で
2. `docker-compose start`するだけで、docker-compose.ymlに記載している全環境をワンコマンドで立ち上げます。
ハマりどころとしては、DinDをするためにホストマシンの`/var/run/docker.sock`をボリュームにマウントすること。そして、`tty:true`としておかないと`command`がzshなどのshellを指定しているimageが一瞬で落ちてしまうことです。
3. `docker exec ...`で各環境にattachします。
とりあえずDinD(Docker in Docker)用のimage: masterにいつもログインして、tmuxを起動し、作業に入ります。

### Goで開発するなら

```shell-session
$ docker exec -it composeset_vimgo_1 zsh -l
```

### Pythonで開発するなら

```shell-session
$ docker exec -it composeset_jupyter_1 bash -c "source ~/.pyenvrc && source activate && ipython" 
$ docker exec -it composeset_jupyter_1 bash -c "source ~/.pyenvrc && source activate && nvim +PyenvActivate" 
```

commandにjupyter notebookを指定しているので`localhost:8888`をブラウザのURL欄に打てばJupyter Notebookに接続できます。
Pythonパッケージマネジャにpyenv+condaを使っているのでログイン時必ず`source activate`しないといけません。
`source activate`するためのパスが通っていないために`source ~/.pyenvrc`しないといけません。
.pyenvrcは自分で書いたものです。

```sh:.pyenvrc
#!/bin/sh
# pyenv config file

# pyenvのあるpath
export PYENV_ROOT="$HOME/pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
# pyenv実行
eval "$(pyenv init -)"
# bin/activate bin/deactivateを使うため

PATH="$PYENV_ROOT/versions/miniconda3-latest/bin:$PATH"
PATH="/usr/bin:$PATH"
export PATH

PYTHONPATH="${HOME}/Dropbox/Program/python/:$PYTHONPATH"
PYTHONPATH="${HOME}/home/python/:$PYTHONPATH"
export PYTHONPATH
```

このへんまだまだベストな状態ではありません。Dockerでパッケージ管理するなら、condaなんて使わずにpipだけでいいのでわー？condaが依存パッケージ集めてくれるから楽なのか？今は「今までcondaで管理してきたから」という理由だけでcondaを使い続けています。


### その他の開発するなら
masterイメージでshell scriptとかやったりします。qiita記事もこのイメージ内のneovimで作成しています。


# 2020年以降は
またVT-x系仮想環境に戻ろうと思います。
その上でDocker立ち上げようかと。
なぜなら、ホストマシンの電源を切ると開発の流れが途切れるからです。
常用ではホストマシンは就寝前にスリープモードにするのですが、ホストマシンのアップデートとかはいったりなんか調子おかしくなったときはリブートせざるを得ないときが必ずあります。

上で説明したdotfilesにはターミナルの復元ツールが多数含まれています。

* すべてのファイルはDropboxに保存して、万一ロールバックしたいときでも簡単に復元できる。
* そもそもgitが使えばコードの差分を見ながら過去に戻れたり別バージョンを残しておいたりできる。
* [tmux-plugins/tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect)でtmuxのセッションを復元できる。<kbd>Space C-r</kbd>に割り当てています。
* [tpope/vim-obsession](https://github.com/tpope/vim-obsession)でneovim/vimのセッションを復元できる。立ち上げるときに`nvim -S`というSオプションをつける。
* docker-composeもdocker-compose.ymlのディレクトリに飛んで`docker-compose start`するだけでコンテナ郡は一発復元する。

このように復元方法には困らないのですが、しかしながら一回途切れるものは途切れる。そのへんやっぱり仮想マシン用意してセッションを保存しておけばホストの電源落としてもかならずセッション復元するし、スナップショットも取れる。そしてDockerに移住したもののそんなにリソースを食う処理って滅多にやらない。

そんなわけでまたVirtualBoxかなんかに戻ろうと思います。

今注目しているのが[kvm](https://www.hiroom2.com/2018/05/11/ubuntu-1804-kvm-ja/)と[multipass](https://github.com/CanonicalLtd/multipass)。
multipassはこの記事「[仮想環境multipassを試す](https://qiita.com/u1and0/items/4926da3bef17853d33d1)」で少しいじってみた。VirtualBoxでやったところで、結局CUI環境はDockerで構築するわけなので、間に挟むイメージはなんでもいい、ということで安直にUbuntu一択になってしまうわけです。multipassは選べるイメージがUbuntuのみでシンプルに作られているので気に入りました。
しかしながら、ポートフォワーディングが今のところできないみたいなので、サーバー・ブラウザ・web使う系の開発に使えないのでまだ様子見です。
kvmはインストールがうまくいかないので、試行錯誤中です。
時間かかるようだったらvagrant+VirtualBoxで良いかなと思います。慣れているし。


# まとめ
開発環境構築は永遠に終わらない開発の一つです。[僕のdotfiles](https://github.com/u1and0/dotfiles)は一人でコミット数が719件になりました。
今後も快適な開発のための開発環境作りに勤しんで参りたいと思います。
今年もアドベントカレンダーに多くのCUIツールが紹介されていたので、試したくてウズウズしているところです。