開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. 開発環境 >
  4. ZellijでDocker開発環境をちょっと自動化してみた

ZellijでDocker開発環境をちょっと自動化してみた

はじめに

こんにちは、ヨシです。

最近は大分暖かくなってきて気持ちよいの季節になってきましたね。その一方であれが到来しています。そう、花粉です。

現在は京都に住んでいるのですが、この間しばらく東京に行く機会がありまして、その際に花粉症が発症してしまいました。目薬や漢方が必須となりそうなので、そろそろ処方してもらおうかと思います。

さて、本日は Zellij というターミナルマルチプレクサのツールとそれを使った開発環境のオートメーションについての記事を書いていきたいと思います。

Zellijとは

まず、Zellij(ゼリッジ)とは、tmux などに代表されるターミナルマルチプレクサ(Terminal Multiplexer)ツールの一種で、ターミナルエミュレータ内でセッション、タブ、ペインといった単位で画面を分割して、その中で仮想ターミナルを起動させることができます。

https://zellij.dev

tutorial-1-opening-new-panes.png
https://zellij.dev/tutorials/basic-functionality/ より引用

Zellij は Rust 言語で開発されており、tmux に比べてモダンで、プラグインなどを導入しなくても最初から様々な機能が利用できる(batteries included)なものとして利用できます。

特徴的なのは初学者にとって非常にわかりやすく、ユーザーフレンドリーな UI になっている点です。こういったツールで大変なのはキーバインドを覚えるということですが、Zellij の場合には status-bar という画面下部の領域(下記画像の③の領域)に主なキーバインドを表示してくれて、モード変更に基づいてどのようなアクションが可能かを表示してくれます。実際、このキーバインドの表示によって簡単に Zellij の操作を覚えることができました。
 

overview-default-legend.png
 https://zellij.dev/documentation/overview より引用

status-bar は後から非表示にすることもできますが、このような初学者に非常に優しい作りとなっているため、これからターミナルマルチプレクサを使いたいという方は tmux よりも Zellij を使う方がおすすめです。

もちろん、tmux の方が歴史があるため、tmux でしか使えないような機能が多々あったりしますが、初学者にとっては使わないような機能もあるので、割り切って Zellij を使うという方法も全然アリだと思います。

逆に、tmux ではセッションの復活に特別にプラグインを導入しないといけなかったりしますが、Zellij では最初からそういった重要な機能が搭載されています。こういった点においても新しい Zellij を使い始めるのがおすすめです。

オートメーション

さて、今回の記事で紹介したいのは、Zellij の機能である、zellij run コマンドによる開発環境のちょっとしたオートメーションです。

Zellij はまだまだ発展途上なので、オートメーションについて一部欠けた機能もありますが、かなり簡単にターミナル上の開発環境のオートメーションができるのでここではそれについて解説したいと思います。

Zellij では手動(キーバインド)でタブ内のペイン分割を行うことが日常的ですが、CLI を通じてタブを新しく分割して、そのタブで特定のタスク(コマンド)を実行するという機能が付属しています。

よく使うコマンドは以下のコマンドで、それぞれ新しくタブを開いてタスクを実行できます。

  • zellij run 新しいペインを生成し、引数に渡したコマンドを実行する
  • zellij edit 新しいペインを生成し、引数に渡したパスをエディタで開く

今日紹介するのは、zellij run の方で、このコマンドを使うことでなにかしらの一連のタスクをタブ内で複数のタブを開きそれぞれ実行していくことが可能となります。

例えば以下のように -- の後に実行したいコマンドを渡すことで、このコマンドを実行したペインから別のペインを生成して echo 'hello' を実行できます。

zellij run -- echo 'hello'

zellij run の特徴として、引数のコマンドをペインと紐づけて実行することができ、その実行ペインでは、終了ステータスの取得と再実行などが簡単に行うことができます。

ユースケース

オートメーションのユースケースとしては、公式のチュートリアルで紹介されているとおり Rust 言語の開発において、Vim などのモーダルエディタを開いて、その横で cargo run, cargo test などのタスクの実行コマンドと結びついたペインを開くことが一つのコマンドで可能となります。

tutorial-2-preview.png
 https://zellij.dev/tutorials/layouts/ より引用

上記チュートリアルで紹介されているレイアウト機能(zellij --layout)を使ったオートメーションです。しかし、レイアウト機能を使うよりも zellij run の方が細かいコマンドの調整などができるので、本記事では zellij run によるオートメーションを紹介します。

なお、レイアウト機能を使わなくても上記のような環境の構築は可能です。※ただしレイアウトをきれいに作るのは少し難しいです。

docker コンテナの起動後のコマンドの連鎖実行

この記事のオートメーションのユースケースとして考えるのは Docker を使ったモノレポでの開発環境で、docker compose up によってコンテナを起動後に、コンテナ内で、複数のフロント画面(firstsecond の2つとします)について npm run dev を実行させる、ということをやりたいのですが、これを Zellij を使って一つのタブ内にそれぞれのコマンドを連鎖実行させていきます。

このモノレポでは以下のような proj というシェルスクリプトが定義されたファイルが用意されており、このスクリプトから各種のコマンドが実行できるとします。


#!/usr/bin/env bash

function up {
    docker compose up -d $@
}

function down {
    docker compose -f docker-compose.yml down
}

function front_npm {
    case ${1} in
      "first" | "second")
        type=${1}
        shift
        ;;
      *)
        type=first
    esac

    workdir="/app"
    container=node-${type}

    docker compose exec -w ${workdir} ${container} npm ${@}
}

function get_front_type() {
    case ${1} in
      "first" | "second")
        echo "${1}"
        ;;
      *)
        echo "${2}"
        ;;
    esac
}

function front_start {
    type=`get_front_type ${1}`
    echo "Starting ${type} server..."
    front_npm ${type} run dev
}

subcommand="$1"
shift

case $subcommand in
    up)
        up $@
        ;;
    down)
        down
        ;;
    front:start)
        front_start ${1}
        ;;
    front:npm)
        front_npm ${@}
        ;;
    *)
        echo "help"
        ;;
esac


シェルスクリプトを用意したら、以下のコマンドでファイルパーミッションを変更して実行できるようにします。


chmod +x proj

もちろん、このようなシェルスクリプトを用意せず、具体的なコマンドを実行するようにしても大丈夫です。

実装

さて、Zellij によるオートメーションコマンドの定義を行っていきます。実際、そこまで大したものではないので一つのコマンドを利用しているシェル用で定義してあげれば十分です。

※ もちろんシェルシェルスクリプト自体に以下のオートメーション用スクリプトを定義しておいてもよいですが、シェルスクリプトが共有されていたとしても開発チーム全員がそもそもターミナルマルチプレクサを使っている、そして Zellij を使っているという状況はありえなさそうなので、別途コマンドを外側で定義することを想定しています。

例えば、bash であれば以下のような関数をどこかに定義してあげて proj-start というようにコマンド実行できるようにします。


function proj-start {
  if [ ! -f "./proj" ]; then
    echo "this directory doesn't contain a script file"
    return
  fi

  ./proj up

  if [ $? -eq 0 ]; then
    zellij run -d down -- ./proj front:start first
    zellij run -d right -- ./proj front:start second
  else
    echo "fail"
  fi
}

 
  • まず、シェルスクリプトのファイルがあるかどうかを判断して、なければコマンドを終了します
  • そして、コンテナを起動するために docker compose up -d を実行するために ./proj up を実行します
  • コンテナ内で npm run dev を実行するためには、コンテナが正しく起動していることが重要なので ./proj up の結果としての終了ステータスをチェックし、コンテナが正常に起動できていれば、zellij run で新しいペインを開き各タスクを実行できます

さて、肝心の以下の部分ですが、まずペインで実行したコマンドは zellij run-- の後に実行したいコマンドを渡します。この -- という記号は以降の入力がオプションではないということを示すための文字列です。


zellij run -d down -- ./proj front:start first
zellij run -d right -- ./proj front:start second

-d オプションはペインを開くための方向で、-d down で下に開き、-d right で右に開きます。この場合には下に新しいペインを開いてから、そのペインについて右にまた新しいペインを開く、というようなことをやってます。

やることはこれだけです。このコマンドを実際に起動してみます。ペインが一つのタブで対象となるプロジェクトのディレクトリに入り、上記で定義した proj-start コマンドを実行してみます。

 

proj-start コマンドの実行によって、最初のペインで Docker コンテナの起動を行い、起動後に、コンテナ内での npm run devfirstsecond について実行します。これによって少しだけ効率化することができました。

なお、動画にあるように、Zellij のペインと実行したタスク用コマンドが紐づいているため、それらの終了ステータスの把握や、停止からの再実行が簡単に行うことができます。

まとめ

 

このようなちょっとしたオートメーションではありますが、これを機会にさらにターミナルマルチプレクサの使いこなしていきたいですね。また、Zellij はまだまだ発展途上なのでオートメーション機能はこの後も更に進化していくと思われます。ぜひキャッチアップしていきましょう。

TOPに戻る