記事完成
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Rikuoh Tsujitani 2023-09-03 11:14:24 +09:00
parent a430eb128b
commit 903ae0cab0

View file

@ -1,30 +1,32 @@
--- ---
title: "Forgejo+WoodpeckerでCI/CD環境を所有する" title: "Forgejo+WoodpeckerでCI/CD環境を所有する"
date: 2023-09-03T08:39:29+09:00 date: 2023-09-03T11:13:29+09:00
draft: true draft: false
tags: ['tech'] tags: ['tech']
--- ---
逆になにができないのか判らないほど多機能と化したGitHubなどの大手Gitホスティングサービスだが、その機能性が仇となって限られた用途で利用するにはいささか煩雑な画面操作を要求されることが増えてきたように思う。`gh`コマンドは便利ではあるものの圧の強いUIと格闘するかCLIかの二択は少々極端なきらいが否めない。 逆になにができないのか判らないほど多機能と化したGitHubなどの大手Gitホスティングサービスだが、その機能性が仇となって限られた用途で使用するにはいささか煩雑な画面操作が増えたように思う。`gh`コマンドは確かに有用ではあるものの圧の強いUIと格闘するかCLIかの二択は少々極端なきらいが否めない。
そこで自前の計算資源を持っている人間にはリモートリポジトリのセルフホスティングが検討される。世の中には絶対にissueやPRを受け付ける気がない雑多なコード片や、ごく個人的、ないしは見知った少人数のみのプロダクトが存在する。そういったプライベート色の強いリポジトリを管理するには、いっそGitHubよりもミニマルに設計されたOSSの方が快適に用を足せると考えられる。 そこで自前の計算資源を持っている人間にはリモートリポジトリのセルフホスティングが検討される。世の中には絶対にissueやPRを受け付ける気がない雑多なコード片や、ごく個人的、ないしは見知った少人数のみのプロジェクトが存在する。そういった趣旨のリポジトリを管理するには、いっそGitHubよりもミニマルに設計されたサービスの方が快適に用を足せる見込みがある。
そもそもgitとは分散型志向のバージョン管理システムであり巨大資本のルールに縛られた一つの場所にコードを置くのは本来の設計理念に反するという考え方もある。もし持て余した計算資源がそこらに転がっているのなら、そこに個人的なリモートリポジトリを生やしてみるとこれまでに得られなかったときめきが感じられるかもしれない。本稿ではForgejoとWoodpeckerを連携させ統合的なCI/CD環境を構築する手法について記す。 そもそもGitとは自由なバージョン管理システムであり、巨大資本のルールに縛られた一つの場所にコードを置くのは十分に機能を活かせていないとも考えられる。もし持て余した計算資源がそこらに転がっているのなら、そこに個人的なリモートリポジトリを生やしてみるとこれまでに得られなかったときめきが感じられるかもしれない。本稿ではForgejoとWoodpeckerを連携させ統合的なCI/CD環境を構築する手法について記す。
## ソフトウェアの紹介 ## ソフトウェアの紹介
[**■Forgejo**](https://forgejo.org) [■Forgejo](https://forgejo.org)
ForgejoはGiteaを基にしたGitホスティングソフトウェアのコミュニティ主導フォークで、商業化に傾倒したGiteaから主要な開発者が離反する形で誕生した。昨年末に分派したばかりで見た目以外の目立った変更箇所はさほどないが、その代わりにGitea向けのソフトウェアや連携機能を利用することができる。 ForgejoはGiteaを基にしたGitホスティングソフトウェアのコミュニティ主導フォークで、商業化に傾倒したGiteaから主要な開発者が離反する形で誕生した。昨年末に分派したばかりで外観以外の目立った変更箇所はさほど見られないが、代わりにGitea向けのサービスや連携機能を利用できる。
[**■Woodpecker**](https://woodpecker-ci.org) 近い将来にはセルフホストされた各リモートリポジトリ同士をActivityPubで相互接続してissueやPRを投げられるようにする計画があるらしい。もし実現すれば従来の中央集権的な大手ホスティングサービスに縛られない自由なコミュニティを形成する余地が生まれる。
Woodpeckerも同様にDroneを基にしたコミュニティ主導フォークだが、こちらは特に喧嘩別れというわけではないらしい。エンタープライズ向けの有料エディションを持つDroneと違い生涯にわたる完全無料を確約している。
CI/CDとは「継続的インテグレーション/継続的デリバリー」の意で、コードの変更を起点に自動的にテストを行ったり、それを反映した最新バージョンを自動的に展開するための機能群を指す。GitHubのGitHub ActionはCI/CDの一つと言える。企業の提供する無料のCI/CD環境には制約が設けられている一方、セルフホストすれば所有する計算資源の限界まで使い倒せる利点がある。 [■Woodpecker](https://woodpecker-ci.org)
Woodpeckerも同様にDroneを基にしたコミュニティ主導フォークだが、こちらは特に喧嘩別れというわけではない。エンタープライズ向けの有料エディションを持つDroneと違い永久に渡る完全無料を確約している。
CI/CDとは「継続的インテグレーション/継続的デリバリー」の略称で、コードの変更を起点に自動的にテストを行ったり、ビルド結果を反映した最新バージョンを展開するための機能群を指す。GitHubの「GitHub Action」はCI/CDの一つと言える。企業の提供する無料のCI/CD環境には制約が設けられている一方、セルフホストすれば所有する計算資源の限界まで使い倒せる利点がある。
## ファイルの取得および編集 ## docker-compose.ymlの編集
`docker`および`docker-compose`はすでに導入済みと仮定する。ForgejoとWoodpeckerはそれぞれ別のソフトウェアで、前者がGitホスティング、後者がCI/CDの機能を提供しているが、互いに連携させる前提で構築するため今回は単一の`docker-compose.yml`でそれぞれをまとめる。 `docker`および`docker-compose`は導入済みと仮定する。ForgejoとWoodpeckerは別のソフトウェアだが、互いに連携させる前提で構築するため今回は単一の`docker-compose.yml`で両者をまとめる。
```docker ```docker
version: "3" version: "3"
@ -83,8 +85,8 @@ services:
- WOODPECKER_GITHUB=false - WOODPECKER_GITHUB=false
- WOODPECKER_GITEA=true - WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=あんたのURL - WOODPECKER_GITEA_URL=あんたのURL
- WOODPECKER_GITEA_CLIENT=指定された英数字 - WOODPECKER_GITEA_CLIENT=後述
- WOODPECKER_GITEA_SECRET=ランダム生成 - WOODPECKER_GITEA_SECRET=後述
- WOODPECKER_AGENT_SECRET=後述 - WOODPECKER_AGENT_SECRET=後述
networks: networks:
- forgejo - forgejo
@ -100,7 +102,7 @@ services:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
environment: environment:
- WOODPECKER_SERVER=woodpecker-server:9000 - WOODPECKER_SERVER=woodpecker-server:9000
- WOODPECKER_AGENT_SECRET=ランダム生成 - WOODPECKER_AGENT_SECRET=後述
networks: networks:
- forgejo - forgejo
@ -109,15 +111,13 @@ volumes:
woodpecker-agent-config: woodpecker-agent-config:
``` ```
`ports`の箇所はコロンの左側を他の数字に置き換えてもよい。この例では3000番ポートがすでに他のサービスに専有されているので代わりに3333番ポートを指定している。同様に、ForgejoとSSH通信で`git push`などを行うためのポートも標準の22番を避けている。`woodpecker-server`の8000番ポートももし空いていなければ他のに変える。 `ports`の箇所はコロンの左側を他の番号に置き換えてもよい。この例では3000番ポートが他のサービスに専有されている都合上、代替として3333番ポートを指定している。セキュリティ保護の観点からForgejoとSSH通信を行うポートも標準の22番を避けている。万が一、`woodpecker-server`の8000番ポートも空いていなければ他の番号に置き換える。
Woodpecker側の設定もここで指定する。`environment`の記述はブラウザからのアクセスやForgejoとの連携を図る上で必須となる。予めレジストラでそれぞれ任意のサブドメインを割り振っておくと話が早い。たとえばForgejoの方を`git.あんたのドメイン`、Woodpeckerの方を`cicd.あんたのドメイン`などに割り当てる。形式が`WOODPECKER_GITEA_*`なのはForgejoを内部的にGiteaと認識させているためだ。
Woodpecker側の設定もここで指定する。`environment`の記述はブラウザ経由のアクセスやForgejoとの連携を図る上で必須となる。予めレジストラでそれぞれ任意のサブドメインを割り振っておくと話が早い。たとえばForgejoの方を`git.あんたのドメイン`、Woodpeckerの方を`cicd.あんたのドメイン`などに割り当てる。定義が`WOODPECKER_GITEA_*`なのはForgejoを内部的にGiteaと認識させているためだ。
## リバースプロキシの設定 ## リバースプロキシの設定
nginx向けにリバースプロキシを書く。Forgejo用のものとWoodpecker用の二つを用意する。Cloudflareを利用していてオリジンサーバ証明書を取得していなければ[この記事](https://riq0h.jp/2023/07/22/204725/)の冒頭を参考に設置を行う。一度やれば使い回せるのでぜひおすすめしたい。 nginx向けにリバースプロキシを書く。Forgejo用とWoodpecker用の二つを用意する。Cloudflareを利用していてオリジンサーバ証明書を取得していなければ[この記事](https://riq0h.jp/2023/07/22/204725/)の冒頭を参考にSSL証明書を設置にされたし。一度やれば使い回せるのでぜひおすすめしたい。
```nginx ```nginx
# Forgejo用 # Forgejo用
@ -174,20 +174,20 @@ server {
} }
``` ```
記述を終えたら保存して`nginx -t`で文法をチェックする。問題がなければ`systemctl restart nginx`で再起動を行う。`docker-compose up -d`でコンテナを起動すると、この時点で指定したサブドメインにてForgejoの初期設定画面にアクセスできるはずである。ただし、WoodpeckerはForgejo上の連携作業が完了するまでは閲覧できない。 記述を終えたら保存して`nginx -t`で文法を検証する。問題がなければ`systemctl restart nginx`で再起動を行う。`docker-compose up -d`でコンテナを起動すると、この時点で紐づけたサブドメインからForgejoの初期設定画面にアクセスできるはずである。Woodpeckerの方はForgejoとの連携作業が済むまでは接続できない。
## Forgejoの設定 ## Forgejoの設定
ほとんどがすでに埋まっているので自ら記入する項目は少ない。むしろ下手にいじると初期設定に失敗する。強いて挙げるなら管理者アカウントを前もって作成するか、サイトタイトルなどのブランディングに関わる部分を書く程度と思われる。初期設定を進めると数秒でインストール作業が済んで他のページに遷移する。 ほとんどの欄がすでに埋まっているので自ら記入する項目は少ない。むしろ下手にいじると初期設定に失敗する。強いて挙げるなら管理者アカウントを事前に作成するか、サイトタイトルなどのブランディングに関わる部分を記す程度と思われる。初期設定を進めると数秒でインストール作業が完了して他のページに遷移する。
初期設定を変更または追記したい場合は`forgejo/gitea/conf/app.ini`という大層紛らわしいディレクトリの下に生成されるファイルで編集できる。Woodpeckerとの連携に際して下記の追加が求められる。 初期設定を編集したい場合は`forgejo/gitea/conf/app.ini`という大層紛らわしいディレクトリの下に生成されるファイルで変更できる。なお、Woodpeckerとの連携に際して以下の追記が求められる。
```conf ```conf
[webhook] [webhook]
ALLOWED_HOST_LIST = external,loopback ALLOWED_HOST_LIST = external,loopback
``` ```
また、一人で使用するなら新規登録は予め封じておいた方が無難。ただし、初期設定で管理者アカウントを作らず先に登録を無効化するとWeb上でログインする方法がなくなってしまうのでこの設定は後に行う。 また、一人で使用するなら新規登録機能は予め封じておいた方が無難。ただし、初期設定で管理者アカウントを作らず先に登録を無効化するとブラウザ経由でログインする方法がなくなってしまうのでこの設定は後に行う。
```conf ```conf
[service] [service]
@ -197,7 +197,7 @@ REGISTER_EMAIL_CONFIRM = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false ALLOW_ONLY_EXTERNAL_REGISTRATION = false
``` ```
一通りの設定が済んだら右上の+マークから「新しいリポジトリ」を作成して説明通りの`git clone`や`git push`が行えるか確認する。また、SSHキーの紐づけはユーザ設定の「SSH/GPGキー」から行える。`docker-compose.yml`の`ports`で指定したポート番号がSSH通信に用いられるので、それに応じたポートを解放しておかなければならない 一通りの設定が済んだらWebページの右上の+マークから「新しいリポジトリ」を作成して`git clone`や`git push`の実行を確認する。ちなみに、SSHキーの紐づけはユーザ設定の「SSH/GPGキー」から行える。`docker-compose.yml`の`ports`で指定したポート番号がSSH通信に用いられるので、それに応じたポートを開ける
```zsh ```zsh
ufw allow 4444 ufw allow 4444
@ -206,23 +206,19 @@ ufw reload
## Woodpeckerの設定 ## Woodpeckerの設定
Forgejoのユーザ設定から「アプリケーション」に進んで「OAuth2アプリケーションの管理」からキーを作成する。アプリケーション名は判別できればなんでも差し支えない。対してリダイレクトURIは`https://cicd.あんたのドメイン/authorize`の形式で明確に認証を指定する Forgejoのユーザ設定から「アプリケーション」に進んで「OAuth2アプリケーションの管理」でシークレットキーを作成する。アプリケーション名はなんでも差し支えない。対してリダイレクトURIは`https://cicd.あんたのドメイン/authorize`の形式で明確に指定しなければならない
![](/img/210.png) ![](/img/210.png)
ここで生成された`クライアントID`と`クライアントシークレット`の値がそれぞれ`docker-compose.yml`の`WOODPECKER_GITEA_CLIENT`と`WOODPECKER_GITEA_SECRET`に符合する。後者は一度きりしか表示されないので注意されたし。ついでに`WOODPECKER_AGENT_SECRET`の欄を`openssl rand -hex 32`で乱数を出力して埋める。 ここで生成された`クライアントID`と`クライアントシークレット`の値がそれぞれ`docker-compose.yml`の`WOODPECKER_GITEA_CLIENT`と`WOODPECKER_GITEA_SECRET`に符合する。後者は一度しか表示されない。ついでに`WOODPECKER_AGENT_SECRET`の欄を`openssl rand -hex 32`で乱数を出力して埋める。
`docker-compose down`で一旦コンテナを終了させてから`docker-compose up -d`で再起動して、Woodpecker用に割り当てたサブドメインにアクセスする。認証を要求されるのでForgejoで作成したアカウントでログインを行う。ログイン後、右上のアイコンから確認できる「Personal Access Token」の値を以下の要領で`forgejo/gitea/conf/app.ini`に書き込む `docker-compose down`で一旦コンテナを終了させてから`docker-compose up -d`で再起動して、Woodpecker用に割り当てたサブドメインにアクセスする。認証が要求されるのでForgejoで作成したアカウント情報を用いる。「Add repository」の項目をクリックしてForgejoのリポジトリを追加できたらひとまず成功と見てよい
```conf 以降は対象のリポジトリ直下に`.woodpecker/*.yml`のディレクトリ構造でコンテナファイルを作り、目的に適った内容のパイプラインを実行させる形となる。したがって、ForgejoとWoodpeckerの構築作業は以上で終了である。
INTERNAL_TOKEN = XXXXXXXXXXXXXXXXXXXX
```
Add repository」の項目をクリックしてForgejoのリポジトリを追加できたらひとまず成功を考えられる。以降は通常のCI/CDと同じように対象のリポジトリ直下に`.woodpecker/*.yml`の形式でコンテナファイルを作り、目的の用途に適ったパイプラインを記述していくこととなる。したがって、ForgejoとWoodpeckerの構築作業は以上で終了である。
## 実践例 ## 実践例
もっとも単純なパイプラインの実行例として静的サイトジェネレータのデプロイを挙げる。実は当ブログも先月よりCloudflare PagesではなくVPS上で稼働しており、ForgejoのリポジトリにPushした変更をWoodpeckerへ渡すことで同様のデプロイ環境を実現している。実際の記述例は以下の通り。 静的サイトジェネレータのデプロイを例にとる。実は当ブログも先月中旬からCloudflare PagesではなくVPS上で稼働しており、Forgejo上のリモートリポジトリにPushした変更をWoodpeckerへ渡すことで類似のデプロイ環境を再現している。実際の記述例は以下の通り。
```yml ```yml
clone: clone:
@ -252,16 +248,16 @@ steps:
target: /var/www/html target: /var/www/html
``` ```
上記の例では`hugo `コマンドを実行するコンテナによって吐き出された静的ファイルを`rsync`コマンドでVPSの`/var/www/html`に送信する形でデプロイを行っている。その際に利用されるSSH送信の秘密鍵やポート番号などは公開リポジトリに置くファイルに記述すべき情報ではないため、Woodpeckerの機能を用いて秘匿化する。各リポジトリ画面の右上の歯車から「Secrets」で設定できる。 上記の例では`hugo`コマンドを実行するコンテナによって吐き出された静的ファイルを`rsync`コマンドでVPS上の`/var/www/html`に同期する方法にてデプロイを行っている。その際に使用されるSSH通信の秘密鍵やポート番号などはコンテナファイルに直接記述すべきではないので、Woodpeckerの機能を用いて秘匿化してある。これは各リポジトリ画面の右上の歯車から「Secrets」で設定できる。
なお、余談だがWoodpeckerではパイプラインを実行するといい感じのアニメーションが見られる。僕はこれを目の当たりにした瞬間に一発でこのOSSが好きになった。機能的には不必要とはいえこういう遊び心って大切だ。30分くらいは余分に働いてもいい気分になる。 余談だがWoodpeckerではパイプラインを実行するとかわいいアニメーションが見られる。僕はこれを目の当たりにした瞬間に一発でこのソフトウェアが好きになった。機能的には不必要とはいえこういう遊び心ってすごく大切だ。30分くらいは余分に働いてもいい気分になる。
![](/img/211.gif) ![](/img/211.gif)
## おわりに ## おわりに
かつてGitやCI/CDはオンプレミス環境で実行する代物だったが、いつからかGitHubなどの営利企業が計算資源に幅を利かせてずいぶん使いやすくしてしまった。これらの無料枠は大半のユーザにとってあまりにも寛大すぎたため、あらゆるプロダクトが手軽なバージョン管理とCI/CDの恩恵に与る黄金時代が到来した。 かつてGitやCI/CDはローカルかセルフホスト環境で実行するサービスだったが、いつからかGitHubなどの営利企業が豊富な計算資源に幅を利かせてずいぶん手軽に使いやすくしてしまった。これらの無料枠は大半のユーザにとってとてつもなく寛大すぎたため、あらゆるプロジェクトがバージョン管理システムとCI/CDの恩恵に与る未曾有の黄金時代が到来した。
しかし、その一方で営利企業とは当然ながら自己利益優先で動く怪物である。昨日までは大盤振る舞いで使わせてくれたのに今日からはろくに使わせてもらえないなどという事態はいつでも起こりうる。利益率を重視するあまり本来は洗練されていた機能群が混沌の渦中に堕し、積み重なったベンダーロックインの依存性ゆえ抜け出すにも抜け出せない危険性は常に考慮しなければならない。 しかし、その一方で営利企業とは当然ながら自己利益優先で動く顔のない怪物だ。昨日までの最良の友が寝て起きたら人を食う魔物に変貌していたなどという事態はいつでも起こりうる。利益率を重視するあまり元々は洗練されていた機能が混沌の渦中に堕し、積み重なったベンダーロックインの依存性ゆえ抜け出すにも抜け出せない危険性は常に考慮しなければならない。
かといって、なにも完全に移行する必要はない。GitHubにしろ数多あるCI/CD環境にしろ、それはそれで確かに優れたサービスには違いない。ただ、いつでも代替となりうるソフトウェアを使い慣れておけば、用途に応じて賢く使い分けたり、最悪のハルマゲドンの到来に備えられる第二、第三の選択肢が皆さんの手のうちに残り続けるのである。 かといって、なにも完全に移行する必要はまったくない。GitHubにしろ数多あるCI/CD環境にしろ、それはそれで確かに優れたソーシャルコーディングサービスには違いない。ただ、平時より怠らず代替となりうるソフトウェアを使い慣れておけば、用途に応じて賢く使い分けたり、最悪のハルマゲドンに備えられる第二、第三の選択肢が皆さんの手のうちに残り続けるのである。