musicLineのテストサーバーをローカル環境に構築してデバッグを簡単にした話。
WindowsでDockerとVSCodeを使って、サーバーをデバッグしたい時に役立つ内容です。
課題
musicLineでは、コミュニティ(クラウド)にデータをアップロード・ダウンロードする際は、Herokuのサーバーを経由してデータをやり取りしています。
Herokuサーバーでは、DBでデータを管理しており、Androidの端末からサーバーへAPIを投げてDBでデータ操作をしています。
現在、デバッグ用のテストサーバーもHerokuにあるため、デバッグする際はコードを修正するだけではなく、Herokuサーバーにも修正したコードを適用する必要があります。
このデプロイ作業が地味に手間であり、コードを修正する度に発生するため、デバッグがしづらい状況でした。
また、2022年秋くらいにHerokuのプランが変更となり、今まで無料プラン内に収まっていたデバッグ用のテストサーバーが有料になってしまいます。
これを機会に、サーバーのデバッグ環境をローカルに移行することを考えました。
環境構築
サーバー環境をローカルのPCに作るために、Dockerを使用しました。
そのまま開発環境をローカルのPCに作ってもいいですが、様々な開発環境が混同すると思わぬエラーとかに引っ掛かったり、インストールしたパッケージもよくわからなくなります。
かといってインストールしたパッケージを管理するのも面倒くさい。。
なのでDockerのコンテナーを使用します。
Dockerで環境を作ることのメリット
www.brain-gate.net
また、VSCodeの開発環境をDockerの仮想環境に作れるように、VSCode拡張のDev Containerを使用します。
1. Docker環境
以下のURLを参考にDocker環境を作ります。
ポイント
2. VSCode環境
以下のURLを参考にVSCode環境を作ります。
ポイント
3. Android側の設定
APIに使用していたIPアドレスをHerokuからローカルホストに変更します。
ローカルホストはデバック環境によって異なるため、動的に取得できるようにします。
以下のコードはデバッグ時のみ(BuildConfigで)、ローカルホストを参照できるように、build.gradleに記載してます。
productFlavors { develop { ... // REF:[Network]ローカルホストアドレスの動的取得 def localHostAddress = InetAddress.getLocalHost().getHostAddress() buildConfigField "String", "HOST_ADDRESS", "\"http://${localHostAddress}:4567/\"" } }
また、デバッグ時は平文(http)で通信を行いたいので、AndroidManifestにandroid:usesCleartextTraffic="true"
を追記します。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <application android:usesCleartextTraffic="true" /> </manifest>
4. DBをローカルへ移行
HerokuにあるDBをローカルへ移行する時は、TablePlusの機能を使いました。
TablePlusでHerokuのDBをBackupして、ローカルにRestoreすれば簡単に移行できました。
問題発生
環境構築ですんなり成功することはないですよね。
私の環境で発生した問題と解決方法を載せておきます。
1.Dockerを起動できない
WSL2とDockerDesktopをインストールしたのに、Dockerがスタートできない!
DockerDesktopのメニュー下は黄色い表示で一時停止状態でした。
Ubuntuは前にインストールしたもので、UbuntuのWSLバージョンを2にできていなかったことが原因でした。
wsl --set-version Ubuntu 2
コマンドでWSLバージョンを2に変更して解決しました。
2.Dev Containerで開けない
Dev Containerのコンテナーでファイルを開くとエラーが発生。
[7639 ms] Error: An error occurred setting up the container. [7639 ms] at loe (c:\Users\*****\.vscode\extensions\ms-vscode-remote.remote-containers-0.266.1\dist\spec-node\devContainersSpecCLI.js:1832:2820) [7639 ms] at process.processTicksAndRejections (node:internal/process/task_queues:96:5) [7639 ms] at async Poe (c:\Users\*****\.vscode\extensions\ms-vscode-remote.remote-containers-0.266.1\dist\spec-node\devContainersSpecCLI.js:1899:2301) [7639 ms] at async Zf (c:\Users\*****\.vscode\extensions\ms-vscode-remote.remote-containers-0.266.1\dist\spec-node\devContainersSpecCLI.js:1899:3278) [7640 ms] at async aue (c:\Users\*****\.vscode\extensions\ms-vscode-remote.remote-containers-0.266.1\dist\spec-node\devContainersSpecCLI.js:2020:15276) [7640 ms] at async oue (c:\Users\*****\.vscode\extensions\ms-vscode-remote.remote-containers-0.266.1\dist\spec-node\devContainersSpecCLI.js:2020:15030) [7652 ms] Exit code 1 [7657 ms] Command failed: C:\Users\*****\AppData\Local\Programs\Microsoft VS Code\Code.exe --ms-enable-electron-run-as-node c:\Users\*****\. ... [7658 ms] Exit code 1
VSCodeのDevContainerを開くときに表示されたエラーでは原因がわからず。。
まずはDockerのコンテナーが適切に起動できるかを試してみました。
PowerShellでdocker compose -f .devcontainer/docker-compose.yml build
コマンドをして、Dockerのymlファイルをビルドします。
=> ERROR [6/6] RUN bundle install 0.8s ------ > [6/6] RUN bundle install: #0 0.717 Your Ruby version is 2.7.7, but your Gemfile specified 2.7.6 ------ failed to solve: executor failed running [/bin/sh -c bundle install]: exit code: 18
これでエラーの原因が分かりました。
HerokuとDockerのコンテナ環境でRubyのバージョンが異なっていたようです。
GemfileでRubyのバージョン指定をruby "~> 2.7.6"
に変更することで解決しました。
3. Android端末からAPIを投げても反応がない
Dockerのコンテナーログを見ても、APIが投げられた痕跡がない。。
結果、セキュリティに引っ掛かってました。
Windows Defenderファイアーウォールの規則で特定のポートを解放することで解決!
と安心すると、しばらくしてまた反応がなくなりました。
結果、セキュリティに引っ掛かってました。
今度はESETの方
ローカルPCでセキュリティソフトを入れている場合は、設定を要チェックです。
設定 > ネットワーク保護 > トラブルシューティングウィザード
にブロックされたデバイスが表示されます。
セキュリティに引っ掛かってそうな時に確認するといいかも。
ファイアウォールやセキュリティソフトの設定を確認することで解決しました。
4. Android端末からAPIを投げても反応がない2
環境構築 > 3. Android側の設定
でAPIを投げるローカルのIPアドレスはAndroidプロジェクトをビルドする時に、動的に設定するようにGradleファイルに記述しました。
でもそのIPアドレスの取得方法では、複数のネットワークがある場合は間違ったIPアドレスが取得される可能性があることがわかりました。
Dockerの起動時にプロジェクトをビルドすると、Linuxの仮想環境が立ち上がる為、ローカルホストを取得する時に、WifiのIPアドレスではなくWSLのIPアドレスを取得されることが原因でした。
// REF:[Network]Wifiローカルホストアドレスの動的取得 def wifiNetwork = NetworkInterface.getNetworkInterfaces().toList().stream() .filter(network -> network.name == "wlan0") .findFirst().get() def wifiHostAddress = wifiNetwork.inetAddresses.toList().stream() .filter(net -> net instanceof Inet4Address) .findFirst().get().hostAddress
適切なローカルIPアドレスを取得することで解決しました。