Weblog by shuuji3

Software Engineering

Gitpod上でHugoのサイトの環境構築を自動化する

このブログはHugoで構築されていますが、執筆はGitpod上で行っています。この記事では、このブログのリポジトリで使用している.gitpod.ymlファイルについて説明します。.gitpod.ymlを作成すると、Gitpodのワークスペースを開いたときの開発環境の構築を自動化できます。最終的に、Gitpodのワークスペースを開くだけで、Hugoでブログをビルドして、プレビューができる環境が用意されるようにします。

Dockerデーモンの起動が完了を待機するシェルスクリプトを作る

まずは、Dockerデーモンの起動が完了するのを待機するためのシェルスクリプトを作ります。これは、後にtasks:で必要になるものです。

Gitpod上では、sudo docker-upコマンドを実行すると、rootlessモードでDockerデーモン(dockerd)が起動されます(2021-02-13時点では、Experimentalな機能)。その結果、Dockerのソケットが/var/run/docker.sockに作成され、DockerのCLIはこのソケットを使用してdockerdと通信できるようになります。(rootlessモードについては、詳しくは、別の記事「DockerのRootlessモードとGitpod」を参照してください。)

したがって、Dockerのソケットとの疎通が確認できるコマンドを実行することで、dockerdが利用可能になったかどうかを判別できます。今回は、docker infoの終了コードで判断することにします。docker infoが失敗する場合と成功する場合で、終了コードはそれぞれ次のように10になります。

失敗する場合

gitpod /workspace/weblog $ docker info; echo exit: $?
Client:
 Debug Mode: false

Server:
ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
errors pretty printing info
exit: 1

成功する場合

gitpod /workspace/weblog $ docker info; echo exit: $?
Client:
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 1
 Server Version: 19.03.14

...
exit: 0

よって、終了コードが0になるまでpollingすることで、Dockerが利用できるまで待機することができます。

while [[ $(docker info > /dev/null; echo $?) != 0 ]]; do
   sleep 1
done

作成した.gitpod.ymlファイル

前のセクションの実験を踏まえて、以下のような.gitpod.ymlファイルを作成しました。

# List the ports you want to expose and what to do when they are served. See https://www.gitpod.io/docs/config-ports/
ports:
  - port: 3000
    onOpen: open-browser

# List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/config-start-tasks/
tasks:
  - command: |
      while [[ $(docker info > /dev/null; echo $?) != 0 ]]; do
          sleep 1
      done
      make serve
  - command: sudo docker-up

posts:の定義

ports:は、Gitpodに3000番ポートでサービスが公開されることを教えます。onOpen: open-browserというオプションを指定すると、サービスが公開されたタイミングで、新しいタブが開き、プレビューが表示されます。

tasks:の定義

tasks:では、commandキーを含むdictionaryの配列を定義しています。Gitpodのワークスペースが起動すると、初めにここで定義されたコマンドがシェルで実行されます。各dictionaryごとに独立したシェルが起動するため、ここでは2つのシェルが起動します。

1番目のシェルでは、前のセクションで作ったコードを使ってDockerが利用できるようになるまで待機し、その後、make serveを実行します。コマンドの実体はMakefileで定義されていて、以下のコマンドが実行されます。

docker run --rm --name "weblog-serve" -p 3000:1313 -v $(CURDIR):/src --volume /tmp/hugo-build-output:/output -w /src -e HUGO_WATCH=1 jojomi/hugo hugo serve --buildFuture --bind 0.0.0.0

HugoのDockerコンテナを使って、hugoコマンドでブログをビルドして、3000番ポートで公開するという内容です。

2番目のシェルでは、sudo docker-upコマンドを実行します。これは、ユーザースペースでRootlessモードのDockerデーモンを起動してDocker CLIから利用できるように環境構築してくれるヘルパーコマンドです。dockerdが利用できるようになると、1番目のシェルはwhileループから抜け出して、docker runでHugoコンテナを実行できるようになります。

まとめ: .gitpod.ymlで可能になったこと

この.gitpod.ymlを作成したことで、Gitpodでリポジトリのワークスペースを開くだけで、以下のことが自動で行われるようになりました。

  • Rootressモードでdockerdが起動して、開発環境でDockerが利用できるようになる。
  • Hugoコンテナでブログがビルドされ、記事を編集するとすぐに再ビルドされる執筆環境が準備される。
  • ビルドされたブログのプレビューが別のタブで表示される。

残されている課題

.gitpod.ymlとは関係ありませんが、現在、プレビューでテーマが正しく読み込まれていないため、CSSが正しく使われない問題があります。おそらくHugoを十分に理解していないときにこのリポジトリを作ったので、GitのSubmoduleやファイルの配置などに問題があるか、Dockerコンテナのマウント設定やHugoのオプションなどがどこか間違っているのではないかと思います。

また、リンクをクリックしてもホストのパスが違うため、手動でURLを書き換える必要があります。どちらも修正したいです。

参考文献