本当に便利！direnv の 3つの活用パターン

今の時代、VM ベースのクラウド基盤だけでなく、Docker や Kubernetes (K8s) などのコンテナ基盤が広く使われています。
こんな時代だと、おうちクラウドを使っている人に限らず、他の人と環境を共有していたり、複数の環境を切り替えて作業しなければならない場面は多いのでは無いでしょうか？

例えば、複数の K8s クラスタを切り替えながらアクセスしないといけない、とか。

```text
PC ----> K8s Cluster A
    |
    +--> K8s Cluster B
    |
    +--> K8s Cluster C
    :         :
```

例えば、共有の検証環境が VPN 越しにアクセスできる踏み台の先にある、とか。

```text
PC ---(VPN)---> 踏み台 ---> 検証環境
```

意外とあるあるな状況だと思いますが、あるある過ぎて、この煩わしさを解消することを諦めていたり、そもそも解消する対象ですらなかったりしませんか？
今回は、こんな煩わしさを direnv というツールで解消してしまおう、というお話。

## direnv って何？

そもそも direnv ってどんなことができるツールなの？ということから。

(公式) [direnv/direnv: unclutter your .profile](https://github.com/direnv/direnv)

まず、一言で紹介すると、
「そのディレクトリに居る間だけ、設定したい環境変数を設定できるツール」
です。

超ざっくり説明なので、語弊はありますが、こんな↓ディレクトリ構造があり、`ChildDirB` で direnv を使った場合、

```text
ParentDir
├── ChildDirA
├── ChildDirB <--- ここで direnv を使う
└── ChildDirC
```

`ParentDir` など他のディレクトリから `ChildDirB` に移動したタイミングで、あらかじめ用意しておいた環境変数が読み込まれ、`ChildDirB` から他のディレクトリに移動したら、その設定を解除することができます。

「ん？？何が嬉しいの？？？」と思う方もいらっしゃるかと思いますが、これが！便利なんですよ！奥さん！！

環境変数って、実は色々な可能性を秘めていて、ご存知の通りではありますが、

* PATH ... (正確な説明は割愛しますが) 実行ファイルが格納されているフォルダのリスト。列挙することで、フルパス指定しなくても実行できるようになる。
* HTTP_PROXY/HTTPS_PROXY ... HTTP/HTTPS リクエストを発行する際のプロキシサーバーを指定。(少なくとも私の中では) 悪名高き環境変数で、こいつのせいで、どれだk...(げふんげふん
* KUBECONFIG ... 実は `kubectl` で利用する `kubeconfig` (デフォルトは `$HOME/.kube/config`) も指定できる。

などなど、ハックに使えそうな匂いが、もう、プンプンするものが沢山あるんです！

とは言え、まだまだ「え〜？`.bashrc` とか `.profile` とか書くアレでしょ〜？」という感じかと思いますので、簡単に direnv の使い方を見た後、タイトル通り、便利な活用例を 3つご紹介します。

## direnv の使い方

と言っても、そんなに難しくはありません。Unix ライクな OS であれば、一通りインストールできます。詳しくは、GitHub の公式ドキュメントをご参照ください。

(公式) [direnv/installation.md at master · direnv/direnv](https://github.com/direnv/direnv/blob/master/docs/installation.md)

私は、手元が Mac だったので、Homebrew でインストールしました。

```sh
brew install direnv
```

で、ここで**重要ポイント**です！！インストールしただけでは、direnv は動作しません！

お使いのシェル (bash とか zsh とか) に direnv をフック (仕込み) してあげる必要があります。

(公式) [direnv/hook.md at master · direnv/direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md)

私は手元のシェルが Mac のデフォルト zsh だったので、`~/.zshrc` の末尾に、下記を追記しました。

```sh
# ~/.zshrc の末尾に↓を追記

eval "$(direnv hook zsh)"
```

これで使う準備は整ったので、活用例を見ながら、動かして行きます。

## 活用パターン①: 複数 Kubernetes クラスタの切り替え

冒頭に触れた「K8s クラスタが複数あって、切り替えが面倒」への対策例です。

```text
PC ----> K8s Cluster A
    |
    +--> K8s Cluster B
    |
    +--> K8s Cluster C
    :         :
```

`kubectl` などを操作する端末は 1台ですが、管理対象の K8s が複数あるので、同じコマンドを発行するにも

1. `kubectl config use-context <K8s Cluster A>`
2. コマンド実行
3. `kubectl config use-context <K8s Cluster B>`
4. コマンド実行
5. `kubectl config use-context <K8s Cluster C>`
6. コマンド実行

とする必要があります。もちろん、これを解決する方法やツールはたくさんありますが、
「コマンドが 1回きりなんだよね」
「コマンド応答を確認しながら、試したいんだよね」
など、そこまで何か仕組みを使って実施すべきものでもなく、サクッとやりたい場面はあると思います。

この活用例①では、お馴染みの `cd` コマンドでディレクトリ移動するような感覚で、K8s クラスタを切り替えられるようにしてみたいと思います。

まず、全体の作業ディレクトリを用意しています。

```sh
% pwd
/Users/yusukei/work
```

K8s Cluster A 用のディレクトリと、その kubeconfig を格納するディレクトリを作成します。

```sh
% mkdir -p K8sClusterA/.kube
```

作成したディレクトリまで移動し、direnv の設定ファイルである `.envrc` に、「このフォルダにある kubeconfig を使え」という環境変数を書き込みます。

```sh
% cd K8sClusterA
% echo export KUBECONFIG=/Users/yusukei/work/K8sClusterA/.kube/config > .envrc
direnv: error /Users/yusukei/work/K8sClusterA/.envrc is blocked. Run `direnv allow` to approve its content
```

初回作成した `.envrc` なので、「まだ direnv に読み込み許可がないですよ」という警告が出ていますが、期待される動作なので、指示の通りに、`direnv allow` を実行していきます。

```sh
% direnv allow .
direnv: loading ~/work/K8sClusterA/.envrc
direnv: export +KUBECONFIG
```

これで、K8sClusterA のディレクトリまで移動した際には、kubeconfig の環境変数が読み込まれます。
確認として `env` コマンドを叩くと、その様子が確認できます。

```sh
% env
...(snip)...
KUBECONFIG=/Users/yusukei/work/K8sClusterA/.kube/config
...(snip)...
```

同じ容量で、他のクラスタについても用意すると、こんな↓感じです。

```text
work
├── K8sClusterA
│   ├── .envrc
│   └── .kube
├── K8sClusterB
│   ├── .envrc
│   └── .kube
└── K8sClusterC
     ├── .envrc
     └── .kube
```

これの準備が手間と言えれば手間ではありますが、一旦、用意さえしてしまえば、`cd` コマンドを使って、目的の K8s クラスタ名のディレクトリへ移動するだけで、kubeconfig を切り替えることができます。原始的な感じはしますが、個人的には、`cd` コマンドが「なんか移動してる！」感があり、直感的で気に入ってます。また、クラスタ毎にやりたい操作って、似ているようで微妙に違ったり、それぞれにログを残したい場面もあったりするので、ディレクトリに収まる感じも良いです。

ただ、あくまで好みなので、プロンプトに K8s Context を表示したり、`kubectx` などのツールを使うのも、全然アリだと思っています。

## 活用パターン②: リモートアクセス時の環境変数切り替え

こちらも冒頭に触れた「検証環境が踏み台の先にある」という状況を、(ちょっとだけ？) 便利にする方法です。

```text
PC ---(VPN)---> 踏み台 ---> 検証環境
```

前提として、踏み台に、または同じように手元の PC からアクセスできるところに、HTTP Proxy があることが条件になるので、「ちょっと用意できないよ」という場合には使えません...(すみません)

ですが、RDP や VNC のようにリモートのデスクトップ画面越しに操作するより、手元の PC のブラウザやツール (`kubectl` とか) で操作できる方が体感が良いので、個人的にはリモート環境には、HTTP Proxy を立てることが良いと思っています。
踏み台として Linux マシンを使ったり、あるいは入り口のルーターとして VyOS を使ったりする場面も多いかと思いますので、その場合、便利に HTTP Proxy を立てる方法を別の機会に記事にできればと思います。

ちょっと話が逸れましたが、この活用例では、こんな↓感じの構成を想定して、direnv を仕込んでいきます。

```text
PC ---(VPN)---> 踏み台 (HTTP Proxy) ---> 検証環境
```

まず、全体の作業ディレクトリを用意しています。

```sh
% pwd
/Users/yusukei/work
```

分かりやすく環境名を付けたディレクトリを作成し、そこに移動します。

```sh
% mkdir RemoteLab
% cd RemoteLab
```

リモート環境に立てている HTTP Proxy を環境変数として、`.envrc` に書き込んでいきます。

```sh:.envrc
export http_proxy=http://proxy:port
export https_proxy=http://proxy:port
```

活用パターン①の時と同様に、direnv 読み込みを許可してあげます。

```sh
% direnv allow .
direnv: loading ~/work/RemoteLab/.envrc
direnv: export +http_proxy +https_proxy
```

これにより、`~/work/RemoteLab` ディレクトリに移動すると、自動で HTTP Proxy の設定が入るようになりました。

```sh
% cd work/RemoteLab
direnv: loading ~/work/RemoteLab/.envrc
direnv: export +http_proxy +https_proxy
```

HTTP Proxy の設定が入ることで、`wget` や `curl` はもちろん、`kubectl` も！HTTP Proxy 経由で、叩けるので、リモートの検証環境の K8s であっても操作できます。
別の記事で紹介できればと思いますが、[Octant](https://octant.dev/) という K8s クライアントツールも、この HTTP Proxy 設定が入った状況で実行すれば、リモートであっても活用できるので、とても便利です。

(公式) [Octant - Visualize your Kubernetes workloads](https://octant.dev/)

とは言え、「いや、普通に HTTP Proxy 設定すれば良いじゃん」という意見もあるかと思います。少なくとも、Firefox などのブラウザに関しては、拡張機能を使って、HTTP Proxy を簡単にスイッチできちゃいます。それすら面倒なら、システム全体の設定として HTTP Proxy を入れてしまえば良いです。

しかし、話が社内環境だったりすると、
「このシステムへのアクセスには HTTP Proxy を Off にしたい」
「いやいや、こっちのシステムには、別の HTTP Proxy があって...」
「あっちの検証環境には、別の HTTP Proxy 立てたから...」
などなど、メインのブラウザ設定がある中、全面的に HTTP Proxy を設定するのが難しかったりもします。
そこので、「こんな技もあるんだな」くらいに見ていただければと思います。

## 活用パターン③: 実行バイナリの差し替え

こちらは冒頭のお困りごととは関係ないのですが、個人的に便利な使い方だと思っているので、紹介します。

どういうことかと申しますと、こんな↓シーンに対応したいと思います。
「ちょっとお試しで使いたいツールがあるけど、わざわざ Global には入れたくない」
「けど、フルパス指定 or `./` 始まりで指定も面倒」
「あ、あと、別バージョンのツールも裏で使うんだよねぇ」
と言った感じのシーンです。

仕組みは単純で、環境変数 `PATH` を弄ります。

まず、作業ディレクトリを用意します。

```sh
% pwd
/Users/yusukei/work
% mkdir sandbox
% cd sandbox
```

分かりやすく、このディレクトリに `bin` ディレクトリを作成し、そこにパスが通るように仕掛けていきます。

```sh
% mkdir bin
% echo export PATH=$PATH:/Users/yusukei/work/sandbox/bin > .envrc
direnv: error /Users/yusukei/work/sandbox/.envrc is blocked. Run `direnv allow` to approve its content
```

こうすることで、`bin` ディレクトリに入れたバイナリは、このディレクトリ (および配下のディレクトリ) に居る間だけ使えるようになります。なので、Global に入れるとセキュリティチェックが煩わしい Mac だったり、検証のために ver を切り替えたいツールがあったりする場合に、便利なサンドボックスが出来上がります。

私は kubectl とかを突っ込んで、ver 更新をバイナリ置き換えだけでできるようにしていたりします。

```sh
% ls bin
kubectl		kubectl-vsphere
```

ただし！！セキュリティ的には、全然サンドボックスでもなんでもなく、カレントディレクトリ (`.`) にパスを通しちゃうようなザルセキュリティとやってることは変わらないので、扱いにはご注意ください。

## まとめ

ツールとしては単に「`.profile` ファイルをディレクトリ毎に用意できる」というだけですが、環境変数と組合せるだけで、こんなこともできるので、ぜひぜひご活用ください！
「こんな活用方法もあるよ！」など、コメントいただけると嬉しいです！