Github Actionsにおけるvirtual-environments同等の実行環境を構築してみる



# サマリ（3行）
Github Actionsの素敵な点として、実行環境がGithub側で用意されている点があり、そのリソースは下のリポジトリにまとめられています。
https://github.com/actions/virtual-environments
これと同等の環境を手順に従って自前で導入してみます。

# Github Actionsとは

Github Actionsとは、Github公式が提供しているCI/CDを行う機能です。各リポジトリに紐付いた形で利用でき、シェル経由でのビルドや静的解析などの任意のコマンド実行のほか、Actionと呼ばれるモジュール化された処理を組み合わせることでワークフローを定義します。これらの処理はGithub側でホストされた仮想マシン上で動作するため、

 - 基本的に自前でjenkinsなどのCI/CDツールを用意する必要がない
 - マルチプラットフォームサポートである

等の利点があり、利用する全てのユーザがこの恩恵を受けることができます。
（ただし、プライベートリポジトリの場合は月あたりの利用時間が一定を超えると課金制となるそうです）

## self-hosted runner

Github側であらかじめ用意された実行環境（以下プリセット環境と呼称します）の他にも、自前で用意したサーバーをワークフローの実行環境として用いることもできます。この機能はself-hosted runnerと呼ばれています。用途としては、

 - 内製ツールに依存するワークフローを走らせたい場合
 - コスト的な都合（self-hosted runnerの場合Github側での課金は必要ない）

などが考えられます。
余談ですが、Enterprise Server（以降オンプレ版）の場合プリセット環境が存在しないため、self-hosted runnerを用いることが必須になります。

[self-hosted runnerについて（公式ドキュメント）](https://docs.github.com/ja/free-pro-team@latest/actions/hosting-your-own-runners/about-self-hosted-runners)

# プリセット同等のself-hosted runnerを構築する

プリセット環境にはいくつかのパッケージ類が標準でインストール済みとなっており、それらの内容およびイメージの構築手順は以下にまとめられています。
https://github.com/actions/virtual-environments
基本的にこちらを参考にすればself-hosted runnerの方でもプリセット同等の環境を用意することができるので、今回はそれをやってみようという趣旨になります。

## 構築手順(ざっくり)

提示されている構築手順はAzureのCLIツールを使ってPowerShell上でイメージのビルドからデプロイまでやる流れになっています。
Actionsの中身はAzure Pipelinesだと聞いたことがある（それ以上詳しくない）ので多分そのへんが関係しているのだと思われます。

以下、僕が実施した場合の環境構築の流れです。基本的に上のリポジトリの手順に沿いつつ、躓いたところのメモを残します。
 windows環境も構築できるようですが、とりあえずlinux環境にしておきます。

###用意したもの

- Windows 10
- PowerShell 5.1.0
    - ローカルマシンに環境作るのめんどいし、Azure Cloud Shell じゃだめかな？と思って試したら認証のポップアップが出なくて詰んだのでやめました。それでいいのか
- Azureのサブスクリプション（今回の範囲は無料試用版でやりました）
- [packer](https://www.packer.io/downloads)
    - 指示に従って `choco install packer` でインストール
- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?view=azure-cli-latest&tabs=azure-cli)
- [Azure Powershell module](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-5.1.0&viewFallbackFrom=azps-4.6.1)
    - PowerShell上でインストールできないときはリリースページにインストーラーがあるのでそこから導入します（僕はしました）
    
###イメージのビルド

上記リポジトリをcloneしてビルド用のスクリプトを呼び出します。

```PowerShell
git clone https://github.com/actions/virtual-environments.git
cd virtual-environments

Import-Module ".\helpers\GenerateResourcesAndImage.ps1"

GenerateResourcesAndImage -SubscriptionId "xxx" `
-GithubFeedToken "yyy" `
-ResourceGroupName "actionsTestGroup" `
-ImageGenerationRepositoryRoot "$pwd" `
-ImageType Ubuntu1604 `
-AzureLocation "Tokyo East"
```
SubscriptionId: デプロイ先のサブスクリプションID
GithubFeedToken: Githubのpersonal access token
ResourceGroupName: 新規作成するリソースグループの名前（デプロイ後のVMが属する）
適当に決めてよいのですが、長すぎると文字数制限があるので気をつけましょう。

リポジトリに書いてある手順ではGithubFeedTokenはオプションなのですが、実際は必須のようです（権限は適当でもよい？）。
[`ubuntu-latest`は現在18.04なので](https://github.com/actions/virtual-environments#available-environments)バージョンもそれに合わせたいのですが、16.04以外ではビルドが途中で失敗したので今回は一旦諦めました。
(エラーの内容的には`podman`が正常にインストールできていないようで、[16.04のイメージ](https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1604-README.md)にはそもそも`podman`が入っていないので回避できたのだと思いますが、深堀りはできていません)
僕の環境ではビルドにそれなりの時間が（4h33m）かかりました。スリープ設定は切っておきましょう。

### デプロイ

以下のスクリプトを叩いてデプロイします。

```PowerShell
Import-Module ".\helpers\CreateAzureVMFromPackerTemplate.ps1"

CreateAzureVMFromPackerTemplate -SubscriptionId "xxx" `
-ResourceGroupName "actionsTestGroup" `
-TemplateFile "C:\path\to\temporaryTemplate.json" `
-VirtualMachineName "testvm1" `
-AdminUsername "azure-user" `
-AdminPassword "Abcde0123" `
-AzureLocation "japaneast"
```

SubscriptionId, ResourceGroupName: ビルド時と同じ
TemplateFile: ビルド時に生成されたテンプレートファイル。ビルドログの末尾に記載されたURIから取得できるので、一旦ローカルに落としてからパスを指定します。
（バグでURIを直接指定できないらしい）
VirtualMachineName, AdminUsername, AdminPassword: 適当に決めましょう

パスワードを指定させられているのが微妙な気持ちですが、ともかくこれでデプロイが完了し、azureの[ポータル](http://portal.azure.com/)からもVMが見えるようになっています。
試しに接続してみます。

```sh
> ssh azure-user@xx.xx.xx.xx
ssh: connection to host xx.xx.xx.xx port 22: Connection timed out
```

できませんでした。あれ？

作成したVMの設定をポータルから眺めていると、セキュリティグループの設定が空だったので自力で設定する必要があるようです。
適当なセキュリティグループを新規作成し、受信はsshを許可、送信は全スルーで設定して割り当てます。

```sh
> ssh azure-user@xx.xx.xx.xx
(中略)
Welcome to Ubuntu 16.04.7 LTS (GNU/Linux 4.15.0-1098-azure x86_64)
(中略)
azure-user@testvm1:~$
```

で、出来た…

### Githubとの接続

ここまで来ればもうこちらのもので、通常のself-hosted runnerと同じ手順でgithubと連携します。
手順については[公式ドキュメント](https://docs.github.com/ja/free-pro-team@latest/actions/hosting-your-own-runners/adding-self-hosted-runners)を読むか、もしくはgithub上でもリポジトリのSettings -> Actions下部の「Add runner」を押すと詳細なガイドを表示してくれます。

最後に試しにやってみたリポジトリを貼って締めといきたいのですが、それは控えておきます。
何故ならself-host runnerでは実行環境にて任意コードの実行が可能だからです。
github.comでself-host runnerを使用する場合は必ずprivateなリポジトリで行いましょう。

# おわりに

やる前は情報がまとまっているならそれに従うだけだと思っていたのですが、思ったより苦労することとなりました。
一応環境の違いなどでAction間の互換性が無くなって困ることもあるかな、と思って始めたのですが、都度依存するパッケージを入れるかコンテナ用意して走らせるかしたほうが楽そうな気持ちも、といった感じです。

手順やテンプレートの中身を噛み砕いてAzureから剥がしたりできたらいいなと思ったのですが、とりあえず手順通りにデプロイまでできたところで一段落にしておきます。
self-hosted runner環境を構築している方の一助になれば幸いです。