0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Docker+Go+Airの開発環境がうまく動かないのでゴリ押しした

Last updated at Posted at 2023-02-15

ある日、Goを含むweb開発環境のDockerイメージを作ろうと思い立ちました。
実際に作成しようとしたのはnode.jsとGoの両方を含む開発環境なのですが、Goの部分でつまづいたところがあったので解決(したことにする)案を共有します。

想定する流れは以下の通りです。

  1. Dockerイメージを起動
  2. VS Codeでファイルを編集(Dockerコンテナの外側)
  3. Dockerコンテナ内側のAirでホットリロード

DockerもGoのセットアップも慣れているので、組み合わせるだけですぐできるだろうと思っていました。

つまづいたところ

Airがホットリロードしません。

docker execを使用して内側からecho "" >> main.goなどの変更を加えるとホットリロードします。
どうやらコンテナの外側からのファイル変更イベントがうまく走ってないようです。

環境

現象を確認したのは以下の環境です。
Macを使っている方やGo公式イメージを使用されている方の中にはAirがうまく動作している方もいるようなので、筆者の環境がよくない説はあります。

  • PC環境
    • OS
      • Windows 10 Pro 22H2
    • メモリ
      • 16GB
    • Docker
      • version 20.10.14
    • エディタ
      • VS Code 1.75.1
  • 仮想環境(Docker内)
    • OS
      • debian stable-20230202
    • Go
      • version 1.20
    • Air
      • version 1.41.0

解決(したことにする)案

Windows環境ではAirを使うのはやめて、PowerShellでファイルの変更を検知して、外側から通知するようにしました。

「それだと開発環境のセットアップをDockerで完結できるというメリットがなくなっちゃわない?」というド正論はあるのですが、以下二点の理由により容認できると考えています。

  • WindowsであればPoweshellは標準搭載なので、複雑な構成にはなるが環境移行の手順は増えない
  • 本番環境のイメージを作ってるわけでもないのに、時間をかけるのはもったいない

Docker内のLinuxに配置するシェル

Dockerfileのentrypointには以下のようなスクリプトを指定します。
環境変数DEVELOP_BUILDGORIOSHIが入ってきたら、Airを使わずにゴリ押しでホットリロードします。

entrypoint.sh
#!/bin/bash

export PATH=$PATH:/develop/app/go/bin
export GOPATH=/root/go/bin
export PATH=$PATH:/root/go/bin

if [ "$DEVELOP_BUILD" == "AIR" ]
then
  # Airでホットリロード
  echo Go Airで起動します
  echo ソースコードは自動でホットリロードされます
  air -c .air.toml
elif [ "$DEVELOP_BUILD" == "GORIOSHI" ]; then
  # ゴリ押しホットリロード
  # Airが意図したとおりに実行されない場合にはこれをつかう
  echo ゴリ押しデバッグモードで起動します
  echo ソースコードはビルドされ続けます
  for ((i=0; 1 == 1; i++)); do
      # ビルド&実行
      bash /develop/script/go/run.sh
      # 実行が終了すると1秒待って再度ビルド&実行しつづける
      sleep 1
  done
else
  echo "環境変数'DEVELOP_BUILD'の値'${DEVELOP_BUILD}'を識別できません"
  sleep 5
  exit 1
fi

プロセスを終了するスクリプトを配置します。

kill.sh
#!/bin/bash

# 実行しているプロセス「main」を終了するスクリプト

pid=`pidof main`
if [[ "$pid" != "" ]]; then
    echo "プロセスID$pidを停止しています..."
    kill $pid
fi

ソースコードをビルドして起動するスクリプトを配置します。
tmpフォルダの中にmainという名前の実行ファイルを生成して実行します。

run.sh
#!/bin/bash

# Goのソースコードをビルドして実行するスクリプト

#プロセスを終了
bash /develop/script/go/kill.sh

# フォルダ作成
if [ ! -d ./tmp ]; then
    mkdir ./tmp
fi

# ビルド
echo ビルドしています...
go build -o ./tmp/main .

echo 起動します...
./tmp/main

Windows側で使用するスクリプト

3秒ごとに拡張子.go.htmlの更新日時の変化を監視して、変化があった場合はDockerコンテナのkill.shを叩くように命令を送ります。
コンテナ内ではmainのプロセスが停止すると、entrypoint.shのループが一周して、再度run.shが実行されます。

※PowerShellならファイル変更イベントをトリガーにすることもできますが、以前に別件で使用した際スパゲティコードになったので今回は不採用

watch.ps1

$path = ${PSScriptRoot}
cd $path
$list = ''

while ($true) {
    $prelist = $list
    $list = (ls "${path}\src" -r | ?{$_.Extension -in '.go','.html'} | %{ "$($_.Name) $($_.LastWriteTime)" })
    if ("$list" -ne "$prelist") {
        $ErrorActionPreference = "silentlycontinue"
        docker-compose exec develop bash /develop/script/go/kill.sh
        $ErrorActionPreference = "continue"
    }
    sleep 3
}

完成

以下のようにホットリロードができるようになりました。

無題.png

Dockerイメージ

DockerHubにイメージをアップロードしたほか、GitHubにサンプル環境をアップロードしました。
git cloneしていただいて、serve.cmdを実行していただければホットリロード用のウィンドウとコンテナのウィンドウが開きます。
srcフォルダ内のコードを改変するとホットリロードします。

https://github.com/HoppingGanon/gorioshi-air

以上です。

本当にゴリ押しコードばかりになっているので、根本的な原因のわかる方はコメントで教えていただけると嬉しいです。
駄文にお付き合いいただき、ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?