Open26

GitHub Codespaces を触ってみる

sezemi_adminsezemi_admin

モチベーション

研修環境を GitHub Codespaces でつくるのサイコーじゃんと思っていて、実際どうやってつくるのか試してみる

sezemi_adminsezemi_admin

受講者が 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 から停止する

    1. config ボタンからコマンドパレットを開く( or Windows なら ctrl + shift + p
    2. コマンドパレットに codespase と入力し始めると候補が出るので Codespases: Stop Current Codespase を選択
    3. しばらくして Codespase is Stopped と表示されれば停止
  • 自身の Codespace の一覧画面から停止する

    1. Codespaces を開く
    2. 使っている Codespace のメニューから Stop codespace を選択 → Stopped というメッセージが表示されれば停止完了

Codespace の削除

これも 30 日間動かしてなければ、通知の上、自動で削除されるのだけど、間違えて動かしてしまうのもアレなので削除しよう。

停止処理とほぼ同じだけど、念のため。

  1. Codespaces を開く
  2. 使っている Codespace のメニューから Delete を選択 → ダイアログがでるので yes を選択
sezemi_adminsezemi_admin

VScode から使う

ブラウザだけでなく VScode からも使えるのだけど、 Docker が必要なので、今回は遠慮する ... 。

はよ、 Docker にせねば、というところなんだけど、 Vagrant で作ってしまった Web メディアの開発環境があるので、それを移行するのがツラい。

sezemi_adminsezemi_admin

トレーナー(研修を実施する側)が Codespace を使う

こっちが本題。

ワンクリック・ノービルドで研修環境を構築できて、受講者には PC (スペックを問わない!) とブラウザだけ用意してもらえれば OK なので、トレーナーからしても魅力的。

template リポジトリをつくる

受講者には先程のようにブラウザから使えるのが理想なので、そうするには template リポジトリを作って公開するのが一番良い( private リポジトリでも OK なんだけど、その場合、いちいち invite したり、 Organization に入れたりメンドイ)。

今回は PHP / Laravel (フロントエンドまでやるかは不明) の環境を作る。

大きな手順はこんな感じで、以降は公式ドキュメントを参考にしながら進める。

Setting up a template repository for GitHub Codespaces - GitHub Docs

  1. 雛形となるテンプレートを選ぶ
  2. リポジトリをつくり .devcontainer ディレクトリを作成
  3. devcontainer.json を作成
    • core 数などの指定
    • 使用する設定ファイル (dockerfile など) を指定
sezemi_adminsezemi_admin

完全に Docker 環境で動く Laradock を忘れていた。

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 とほとんど変わらない。

sezemi_adminsezemi_admin

Laradock を fork してやってみる

ものは試しということで、一旦、 Laradock を fork して Codespaces を立ち上げてみる。

Laradock の .devcontainer.json を用意する

Laradock を fork する。 fork する際、わかりやすいよう -template という名前をつけた。

sezemiadmin/laradock-template

template-test というブランチを切る

template-test

すでに .devcontainer/devcontainer.sample.json があるので、それを devcontainer.json に変更してコミット。

Remove sample

ちなみにローカルで行っても意味がないので、画面ですべてやっている。 なんだか怖い ... 。

imagehostRequirements も追加しようと思ったんだけど、とりあえずこれで動かしてみてエラーが発生したなら追記しようと考え、とりあえずこれで Codespaces が動くかやってみる。

Codespaces で動かす

ブランチを開いて、 [<> code ▼] でメニューを開くと、 Create codespace on template-test メニューができているので、それを押す。

いつもの setting up 画面が表示され、無事に立ち上がった!

Laradock を Codespaces で動かす

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 を動かす手順が必要なんだろう。

sezemi_adminsezemi_admin

devcontainer.json を見ると、そもそも MySQL 使ってないやん ...

  "runServices": [
    "nginx",
    "postgres",
    "pgadmin"
  ],
sezemi_adminsezemi_admin

そもそも Laradock を動かす準備ができてなかった ...

cp .env.example .env
sezemi_adminsezemi_admin

Laradock の document を見ていると、 .env の APP_CODE_PATH_HOST もデフォルトに指定せい、ということなので、一応それも追加。

Update .env

sezemi_adminsezemi_admin

Laradock は docker (my PC) -> docker host (Codespaces) -> container (App host [developer]) -> App という構成になっているのか。

なので、 artisan は Codespaces で作ったあとに docker-compose exec で App host に入らないといけないということっぽい。

sezemi_adminsezemi_admin

一旦、これで codespace を起動してみよう。

log を眺めていると、 npm もやっているので、フロントも出来上がるっぽい。

が、エラー ... 。

sezemi_adminsezemi_admin

log を確認してみると、 host machine も指定されてた

Using image: mcr.microsoft.com/devcontainers/universal:2

sezemi_adminsezemi_admin

失敗しても VScode 風のエディタが開き、コンテナが無いというダイアログが表示される。

creation.log が表示されるので、これを保存。

次回はこれの解析から

sezemi_adminsezemi_admin

エラーの要因を探る

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

内容をみると、ワークアラウンドが複数ある様子。

  1. メモリの増設
  2. docker buildx install を使う

次回これの 1. から試してみよう。

sezemi_adminsezemi_admin

docker buildx を指定できるのか、念のため調べていたところ、全然別の話で、このスレッドに以前書いた

docker (my PC) -> docker host (Codespaces) -> container (App host [developer]) -> App という構成

これは docker in docker という構成だった。

sezemi_adminsezemi_admin

メモリの増設をするため、 devcontainer.json に GitHub のテンプレートを見ながら追加。

{
  "image": "mcr.microsoft.com/devcontainers/universal:2",
  "hostRequirements": {
    "cpus": 4
  },
}

ただし、実行すると image が見つからないよ、と言われるので、 image のプロパティを消して、 hostRequirements だけを指定すると、エラー無く、無事に Codespaces が動いた。

sezemi_adminsezemi_admin

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 で開いた。

sezemi_adminsezemi_admin

しかし、、、試しに php artisan mysql などのコマンドが無いと言われる。

そうか、 docker in docker だから、 codespace からさらにコンテナが立ち上がっているのかと思い、 docker exec を試してみるも、そもそも docker コマンドが無いと。。

sezemi_adminsezemi_admin

Laradock をベースにするのは断念

docker in docker で開いた codespace に docker をさらに入れることも考えたけど、初手で docker in docker で試すのは、ちょっと記事からすると趣旨が違うので( docker in docker を使えるようにするのではない)、 Laradock をベースにするのは断念。

同時に template リポジトリも削除。

他候補を探す

laravel codespaces devcontainer のようなクエリで GitHub 内なり、ググったりで、 探す -> 試す をやってみるも、 php artisanmysql がないと言われる。

で、そんなときに GitHub 内を探していると、 topics で template を扱っていたことを発見。

codespaces · GitHub Topics

php でフィルタして候補を探す。

新しいテンプレートに切り替え

topics で探したテンプレートのうち、 flemzord/codespaces-laravel を試したところ、 php artisan コマンドで無事に表示された!

flemzord/codespaces-laravel

result

というわけで、これをベースにする。

sezemi_adminsezemi_admin

PostgresSQL -> MySQL に切り替え

ベースにした codespaces-laravel では PostgresSQL を使っていたので、これを MySQL にする。

  1. .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
  1. .env を変更
# DB_CONNECTION=pgsql
# DB_HOST=db
# DB_PORT=5432
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306

Codespace を起動してみるも mysql コマンドが使えず

これで 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 は動いていた)。

なぜ!?

sezemi_adminsezemi_admin

前回のログから、試行錯誤しつづけ、というよりハマりまくり、はじめて環境構築をしたときと同じぐらいだった。 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 コンテナから別のコンテナが立ち上げることになるから、うーん、とググっていたところに出会ったのがこれ(検索クエリを忘れてしまった ... )。

Dev Container 環境で Docker を使う - Qiita

いや、マジで神記事でした ... 。 先人に感謝しかないです。

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_USERMYSQL_PASSWORD を入れておけば OK とあったのに ... 。

MySQLのコンテナを作成したのに再起動し続けるときの対応方法 - Qiita

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 に成功。

というわけで、これで原稿にするぞい。