GitHub Codespaces を触ってみる
モチベーション
研修環境を GitHub Codespaces でつくるのサイコーじゃんと思っていて、実際どうやってつくるのか試してみる
受講者が Codespaces を使う
とりあえず研修受講する側の使い方から。
- ブラウザから使う
- VScodeから使う
ブラウザから使う
特に難しいことはなく、 GitHub で Dev Containers ( Codespaces 用の設定がある) に対応したリポジトリを開き、 [Use this template] > Open in a codespace を押す。
そうすると、セットアップ中と表示されたタブが開く。
待っていると数分で、 VScode そっくりの画面が開いたらオシマイ。 マジか ...
なお使ったのは、このテンプレート。 また起動中は自身の GitHub アカウントの Codespaces で開いている。
microsoft/vscode-remote-try-php: PHP sample project for trying out Dev Containers
Codespace の停止
ブラウザを閉じてから 30 分で自動終了するのだけど、 60 時間と限られた時間なので、終了処理を忘れずにやる。
-
ブラウザの VScode から停止する
- config ボタンからコマンドパレットを開く( or Windows なら
ctrl
+shift
+p
) - コマンドパレットに
codespase
と入力し始めると候補が出るのでCodespases: Stop Current Codespase
を選択 - しばらくして Codespase is Stopped と表示されれば停止
- config ボタンからコマンドパレットを開く( or Windows なら
-
自身の Codespace の一覧画面から停止する
- Codespaces を開く
- 使っている Codespace のメニューから Stop codespace を選択 → Stopped というメッセージが表示されれば停止完了
Codespace の削除
これも 30 日間動かしてなければ、通知の上、自動で削除されるのだけど、間違えて動かしてしまうのもアレなので削除しよう。
停止処理とほぼ同じだけど、念のため。
- Codespaces を開く
- 使っている Codespace のメニューから Delete を選択 → ダイアログがでるので yes を選択
VScode から使う
ブラウザだけでなく VScode からも使えるのだけど、 Docker が必要なので、今回は遠慮する ... 。
はよ、 Docker にせねば、というところなんだけど、 Vagrant で作ってしまった Web メディアの開発環境があるので、それを移行するのがツラい。
トレーナー(研修を実施する側)が Codespace を使う
こっちが本題。
ワンクリック・ノービルドで研修環境を構築できて、受講者には PC (スペックを問わない!) とブラウザだけ用意してもらえれば OK なので、トレーナーからしても魅力的。
template リポジトリをつくる
受講者には先程のようにブラウザから使えるのが理想なので、そうするには template リポジトリを作って公開するのが一番良い( private リポジトリでも OK なんだけど、その場合、いちいち invite したり、 Organization に入れたりメンドイ)。
今回は PHP / Laravel (フロントエンドまでやるかは不明) の環境を作る。
大きな手順はこんな感じで、以降は公式ドキュメントを参考にしながら進める。
Setting up a template repository for GitHub Codespaces - GitHub Docs
- 雛形となるテンプレートを選ぶ
- リポジトリをつくり .devcontainer ディレクトリを作成
- devcontainer.json を作成
- core 数などの指定
- 使用する設定ファイル (dockerfile など) を指定
devcontainer.json で指定できるのは docker-compose.yml で、 docker-compose.yml から Dockerfile を指定する。
Laravel で参考にできそうなのが、 Laravel Sail のリポジトリにある docker-compose.yml や Dockerfile 。
Laravel Sail 8.x Laravel
laravel/sail: Docker files for running a basic Laravel application.
完全に Docker 環境で動く Laradock を忘れていた。
devcontainer.json の sample もあったんだけど、なにか違う ... 。
{
"name": "Laradock",
"dockerComposeFile": "../docker-compose.yml",
"remoteUser": "laradock",
"runServices": [
"nginx",
"postgres",
"pgadmin"
],
"service": "workspace",
"workspaceFolder": "/var/www",
"shutdownAction": "stopCompose",
"postCreateCommand": "uname -a"
}
以下は GitHub が作った Rails の Template の devcontainer.json
{
"image": "mcr.microsoft.com/devcontainers/universal:2",
"hostRequirements": {
"cpus": 4
},
"waitFor": "onCreateCommand",
"updateContentCommand": "bundle install",
"postCreateCommand": "",
"postAttachCommand": {
"server": "rails server"
},
"customizations": {
"codespaces": {
"openFiles": [
"app/views/hello/index.html.erb"
]
},
"vscode": {
"extensions": [
"rebornix.Ruby"
]
}
},
"portsAttributes": {
"3000": {
"label": "Application",
"onAutoForward": "openPreview"
}
},
"forwardPorts": [3000]
}
Django の template も見てみたんだけど、 Rails とほとんど変わらない。
これを見ると docker-compose.yml や Dockerfile を指定すれば動くように書いてるなぁ。
Laradock を fork してやってみる
ものは試しということで、一旦、 Laradock を fork して Codespaces を立ち上げてみる。
Laradock の .devcontainer.json を用意する
Laradock を fork する。 fork する際、わかりやすいよう -template という名前をつけた。
template-test というブランチを切る
すでに .devcontainer/devcontainer.sample.json があるので、それを devcontainer.json に変更してコミット。
ちなみにローカルで行っても意味がないので、画面ですべてやっている。 なんだか怖い ... 。
image
や hostRequirements
も追加しようと思ったんだけど、とりあえずこれで動かしてみてエラーが発生したなら追記しようと考え、とりあえずこれで Codespaces が動くかやってみる。
Codespaces で動かす
ブランチを開いて、 [<> code ▼] でメニューを開くと、 Create codespace on template-test メニューができているので、それを押す。
いつもの setting up 画面が表示され、無事に立ち上がった!
git や PHP などのバージョン確認をしたところで今日は一旦おしまい。
- git git version 2.25.1
- php PHP 7.3.27 (cli) (built: Mar 18 2021 08:29:27) ( NTS )
mysql は動かず、 php artisan serve
も動かないので、この辺は次回確かめてみる。 たぶん、 Laradock を動かす手順が必要なんだろう。
停止する際に確認すると 2-core で動いていた模様。
devcontainer.json を見ると、そもそも MySQL 使ってないやん ...
"runServices": [
"nginx",
"postgres",
"pgadmin"
],
そもそも Laradock を動かす準備ができてなかった ...
cp .env.example .env
devcontainer.json の property を確認していると、やはり service
で動かすものを定義しているので、これを mysql に変えよう。
Laradock の document を見ていると、 .env の APP_CODE_PATH_HOST
もデフォルトに指定せい、ということなので、一応それも追加。
Laradock は docker (my PC) -> docker host (Codespaces) -> container (App host [developer]) -> App という構成になっているのか。
なので、 artisan
は Codespaces で作ったあとに docker-compose exec
で App host に入らないといけないということっぽい。
一旦、これで codespace を起動してみよう。
log を眺めていると、 npm もやっているので、フロントも出来上がるっぽい。
が、エラー ... 。
デバッグの方法を一応書いてるので終わったら確認する
log を確認してみると、 host machine も指定されてた
Using image: mcr.microsoft.com/devcontainers/universal:2
失敗しても VScode 風のエディタが開き、コンテナが無いというダイアログが表示される。
creation.log が表示されるので、これを保存。
次回はこれの解析から
エラーの要因を探る
creation.log を見たところ、以下のところでエラーが発生していた模様。
2022-12-26 06:05:58.640Z: cannot write /tmp/tmpbhotbre3 because server did not provide an image ID
2022-12-26 06:05:58.643Z: ERROR: Service 'workspace' failed to build : Build failed
2022-12-26 06:05:58.643Z: Stop (340631 ms): Run: docker events --format {{json .}} --filter event=start
2022-12-26 06:05:58.643Z: unexpected EOF
2022-12-26 06:05:58.643Z:
2022-12-26 06:05:58.643Z: Docker events terminated (code: 1, signal: null).
2022-12-26 06:05:58.728Z: Stop (335627 ms): Run: docker-compose --project-name laradock -f /var/lib/docker/codespacemount/workspace/laradock-template/docker-compose.yml -f /var/lib/docker/codespacemount/.persistedshare/docker-compose.codespaces.yml -f /var/lib/docker/codespacemount/.persistedshare/docker-compose/docker-compose.devcontainer.build-1672034423076.yml build nginx mysql pgadmin workspace
2022-12-26 06:05:58.729Z: Error: Command failed: docker-compose --project-name laradock -f /var/lib/docker/codespacemount/workspace/laradock-template/docker-compose.yml -f /var/lib/docker/codespacemount/.persistedshare/docker-compose.codespaces.yml -f /var/lib/docker/codespacemount/.persistedshare/docker-compose/docker-compose.devcontainer.build-1672034423076.yml build nginx mysql pgadmin workspace
2022-12-26 06:05:58.729Z: at hF (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1822:412)
2022-12-26 06:05:58.730Z: at processTicksAndRejections (internal/process/task_queues.js:95:5)
2022-12-26 06:05:58.730Z: at async Une (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1822:2381)
2022-12-26 06:05:58.730Z: at async jne (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1804:2396)
2022-12-26 06:05:58.730Z: at async oie (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1871:2266)
2022-12-26 06:05:58.730Z: at async qf (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1871:3239)
2022-12-26 06:05:58.730Z: at async Mse (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1995:16211)
2022-12-26 06:05:58.730Z: at async Lse (/usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js:1995:15965)
2022-12-26 06:05:58.730Z: Stop (344612 ms): Run: /usr/bin/node /usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js up --user-data-folder /var/lib/docker/codespacemount/.persistedshare --container-data-folder .vscode-remote/data/Machine --container-system-data-folder /var/vscode-remote --workspace-folder /var/lib/docker/codespacemount/workspace/laradock-template --id-label Type=codespaces --log-level info --log-format json --config /var/lib/docker/codespacemount/workspace/laradock-template/.devcontainer/devcontainer.json --override-config /root/.codespaces/shared/merged_devcontainer.json --default-user-env-probe loginInteractiveShell --mount type=bind,source=/.codespaces/agent/mount/cache,target=/vscode --skip-post-create --update-remote-user-uid-default never --mount-workspace-git-root false
2022-12-26 06:05:58.730Z: Exit code 1
====================================== ERROR ====================================
2022-12-26 06:05:58.753Z: Failed to create container.
=================================================================================
2022-12-26 06:05:58.758Z: Error: Command failed: /usr/bin/node /usr/lib/node_modules/@microsoft/vscode-dev-containers-cli/dist/spec-node/devContainersSpecCLI.js up --user-data-folder /var/lib/docker/codespacemount/.persistedshare --container-data-folder .vscode-remote/data/Machine --container-system-data-folder /var/vscode-remote --workspace-folder /var/lib/docker/codespacemount/workspace/laradock-template --id-label Type=codespaces --log-level info --log-format json --config /var/lib/docker/codespacemount/workspace/laradock-template/.devcontainer/devcontainer.json --override-config /root/.codespaces/shared/merged_devcontainer.json --default-user-env-probe loginInteractiveShell --mount type=bind,source=/.codespaces/agent/mount/cache,target=/vscode --skip-post-create --update-remote-user-uid-default never --mount-workspace-git-root false
2022-12-26 06:05:58.762Z: Error Code: 1302
最後の Error Code: 1302
はただ単にコンテナが生成できませんでしたよ、というエラーだった。
というわけで一番最初の
cannot write /tmp/tmpbhotbre3 because server did not provide an image ID
これに原因がありそうなので調べてみる。
調べてみると、以下に issue が上がっていた。
#2971 cannot write /tmp/tmp4phcyxn2 because server did not provide an image ID
内容をみると、ワークアラウンドが複数ある様子。
- メモリの増設
-
docker buildx install
を使う
次回これの 1. から試してみよう。
docker buildx を指定できるのか、念のため調べていたところ、全然別の話で、このスレッドに以前書いた
docker (my PC) -> docker host (Codespaces) -> container (App host [developer]) -> App という構成
これは docker in docker という構成だった。
メモリの増設をするため、 devcontainer.json に GitHub のテンプレートを見ながら追加。
{
"image": "mcr.microsoft.com/devcontainers/universal:2",
"hostRequirements": {
"cpus": 4
},
}
ただし、実行すると image が見つからないよ、と言われるので、 image
のプロパティを消して、 hostRequirements
だけを指定すると、エラー無く、無事に Codespaces が動いた。
Codespace のディレクトリがなぜか /var/www/
で開いてしまう。 確認したところ .env で
# Point to the path of your applications code on your host
APP_CODE_PATH_HOST=../project-z/
# Point to where the `APP_CODE_PATH_HOST` should be in the container
APP_CODE_PATH_CONTAINER=/var/www
で指定していたことが原因だった。
そこで、この前のコミットで追記した APP_CODE_PATH_HOST=
の値を消して、 ../
に変更すると、無事に /workspace で開いた。
しかし、、、試しに php artisan
mysql
などのコマンドが無いと言われる。
そうか、 docker in docker だから、 codespace からさらにコンテナが立ち上がっているのかと思い、 docker exec
を試してみるも、そもそも docker
コマンドが無いと。。
Laradock をベースにするのは断念
docker in docker で開いた codespace に docker をさらに入れることも考えたけど、初手で docker in docker で試すのは、ちょっと記事からすると趣旨が違うので( docker in docker を使えるようにするのではない)、 Laradock をベースにするのは断念。
同時に template リポジトリも削除。
他候補を探す
laravel codespaces devcontainer
のようなクエリで GitHub 内なり、ググったりで、 探す -> 試す をやってみるも、 php artisan
や mysql
がないと言われる。
で、そんなときに GitHub 内を探していると、 topics で template を扱っていたことを発見。
php でフィルタして候補を探す。
新しいテンプレートに切り替え
topics で探したテンプレートのうち、 flemzord/codespaces-laravel を試したところ、 php artisan
コマンドで無事に表示された!
というわけで、これをベースにする。
PostgresSQL -> MySQL に切り替え
ベースにした codespaces-laravel では PostgresSQL を使っていたので、これを MySQL にする。
- .devcontainer/docker-compose.yml の image を変更
app:
build: ./docker/app
# 中略
environment:
APP_ENV: local
PHP_EXTENSION_XDEBUG: 1
# PHP_EXTENSION_PGSQL: 1
# PHP_EXTENSION_PDO_PGSQL: 1
PHP_EXTENSION_MYSQLI: 1
PHP_EXTENSION_PDO_MYSQL: 1
# 中略
db:
image: mysql:5.7
restart: unless-stopped
ports:
# - 5432:5432
- 3306:3306
environment:
# POSTGRES_DB: laravel
# POSTGRES_USER: laravel
# POSTGRES_PASSWORD: laravel
MYSQL_DB: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: laravel
- .env を変更
# DB_CONNECTION=pgsql
# DB_HOST=db
# DB_PORT=5432
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
mysql
コマンドが使えず
Codespace を起動してみるも これで Codespace を起動してみると、無事に動きました。
試しに artisan migrate でテーブルを作成してみると、これも無事に通りました。
$ php artisan migrate --database
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (84.84ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (37.35ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (60.87ms)
mysql 動いてるやん! と心躍りながら、 mysql --version
を実行してみると ... 、 mysql コマンドは無いとのこと。 えっ、 migrate できたのに !?
ps a
でも mysql はなく( apache2 は動いていた)、 service mysql status
でも mysql は認識されていないとのこと( apahe2 は動いていた)。
なぜ!?
前回のログから、試行錯誤しつづけ、というよりハマりまくり、はじめて環境構築をしたときと同じぐらいだった。 3 日間溶かしてしまった ... 。
というわけで、ハマったポイントなどをつらつら残しておく。
Docker Compose 知ってるつもりで知らなかった問題
前回のログでも試してみていたように、 codespace で開いたコンテナに mysql が入っているものと思っていたのが、そもそもの大きな間違い。
docker-compose.yml を見てみると、
services:
app:
# 中略
db:
# 中略
redis:
# 中略
mailhog:
とあるので、 app / db / redis / mailhog がそれぞれ別コンテナになっていて、 default ネットワークで繋がっているのだった。
なので、codespace の workspace として開くようになっていたのが app で、そこで mysql を探しても当然ながら無いのだった。 orz
app コンテナから他コンテナが見れない問題
通常の Docker compose なら開発者 PC なりの host が各種コンテナを立ち上げ、 host から docker ps
などのコマンドでコンテナの状態を知ったり操作するのだが、 Codespaces の場合、この host は GitHub のコンテナであり、これに入って作業するわけではなかったのだった ... ( app コンテナに Codespace の workspace があった)。
というわけで、 app コンテナからは db などのコンテナが動いているのかどうかもわからず、なんとか Codespace の creation log を追って、 create db -> done してるやん、とわかるぐらいだった。 なので、 db コンテナが出来上がったのはわかるけど、中がどうなっているのかはわからんという状態だった。
これが致命的で、 php artisan migate
の実行エラーが起こっても、
php_network_getaddresses: getaddrinfo for db failed: Name or service not known ( # 略
そのエラーメッセージからだけしか状態がわからず、ググっては試し -> エラー、ググっては試し -> エラー の循環をただただ繰り返すことになってしまった ... 。 ツラかった。
ちなみに最終的にわかった原因は、
-
db
というホスト名がついてなかった -
laravel
という DB が作れていなかった
この 2 つだった。
docker-from-docker に救われる
手探りで db ホストを正しく立てられるようにするのは諦め、 app コンテナから host もしくは db の状態がわかる手段を探す方に切り替えた。
app コンテナに docker 入れればいいんじゃね、と思ったけど、それだと app コンテナから別のコンテナが立ち上げることになるから、うーん、とググっていたところに出会ったのがこれ(検索クエリを忘れてしまった ... )。
いや、マジで神記事でした ... 。 先人に感謝しかないです。
docker-in-docker ではなく、 docker-from-docker なら app コンテナから host の docker を動かせるというもので、「まさしく、求めていたのはこれ!」。
というわけで、 devcontainer.json に docker-from-docker を入れてみたところ、
app コンテナで docker version
が動く!動くぞ!
docker ps
が動いた!コンテナちゃんたち、ちゃんといたのね!
docker exec
で db コンテナ入れるやん!
となったのだった。
MySQL 特有の問題?
docker-from-docker で db の状態がわかるようになり、 docker exec -it conatainer_id bash
で db コンテナに入ろうとすると、
Error response from daemon: Container 3f16c721fd2315181363585fc74e06d6d6a26e42946573d3a98042edfa0b1313 is restarting, wait until the container is running
というエラーで入れず。 調べてみると MYSQL_ROOT_PASSWORD
環境変数を入れてないからだった。 MYSQL_USER
と MYSQL_PASSWORD
を入れておけば OK とあったのに ... 。
MYSQL_ROOT_PASSWORD
を入れると、 db コンテナに無事に入れた。
これで php artisan migrate
が通るかと思いきや、引き続き同じエラーが続く。
hostname がちゃんと指定された db になっているか db コンテナに入ると、コンテナ ID が振られただけの状態だった。 えっ、 MYSQL_HOST=db
って指定しているのに!
事実は事実なので、 docker-compose.yml で、
db:
image: mysql:5.7
hostname: db
にすると、無事に hostname が db になった。 そんなん外からは絶対わからんやん。
今度こそ php artisan migrate
が通るだろうと思っていたら、またまた同じエラー。
port がちゃんと開いてるのか、と思って確認すると、ちゃんと開いてるし、なぜじゃ、とこれまた悩む。 links プロパティが必要なのか -> 動かん、というのを繰り返す。
ふと、そもそも database ができてるのか、と db コンテナで確認してみると、なんと空っぽ。
えーー。
MYSQL_DB=laravel
って docker-compose.yml には書いてるのに!
というわけで、何度か確認してた mysql の公式イメージのドキュメントを改めて見ていると ... 、
MYSQL_DATABASE
This variable is optional and allows you to specify the name of a database to be created on image startup. If a user/password was supplied (see below) then that user will be granted superuser access (corresponding to GRANT ALL) to this database.
ん? MYSQL_DATABASE
??
アホアホ、 MYSQL_DB
にしてるやん!
というわけで、 docker-compose.yml を書き換えて、 php artisan migrate
を実行!
$ php artisan migrate
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (205.04ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (187.86ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (209.16ms)
ヤッターーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー!
「推測するな、計測せよ」という言葉どおりの結末で、なんとか postgresql -> mysql に成功。
というわけで、これで原稿にするぞい。