オンプレ環境でのUnityインフラ整備書

この記事は、神戸電子専門学校 ゲーム技研部 Advent Calendar 2024の８日目の記事です。
(OB枠で参加します。よろしくお願いいたします。)

https://qiita.com/advent-calendar/2024/kdgamegiken

## はじめに

お久しぶりです。最近自作サーバーでなにかやることがトレンドなしろくまです。

今回は趣味でやっていた知識を生かした(多分実務でも使える)「オンプレ環境でのUnityインフラを整備」の手順をまとめてみました。

なお、読者の対象としては小中規模Unity開発向けとなっています。

### この記事で扱う内容

- サーバー本体、証明書関係
  - Ubuntu
  - Certbot
  - Unbound
  - Nginx
- Git、ドキュメント関係
  - GitLab
  - GitLab CI/CD
  - GitLab Pages
  - Docker
- Unity関係
  - Unity
  - UPM
  - Verdaccio
- ドキュメント関係
  - Python3
  - mkdocs

扱わないもの

- Test関係
- コメントからのドキュメント自動生成

## 目標

- Gitホスティング
- 非公開UPMレジストリ
- 非公開ドキュメント
- CI/CDで大体自動化

ざっくりですがこれらを作成していきます。またLAN上で完結しますがhttpsに対応しています。

なお、用語の説明などは省略しています。

## 環境構築

### 環境

- domain：shirokuma1101.com (Cloudflare Registrar)
- Host OS：Windows 11 Pro 23H2
- Guest OS：Ubuntu 24.04
- CPU：Ryzen9 3950X
- RAM：64GB
- Hypervisor：Hyper-V (3.0)

ドメインやユーザー名を利用する箇所はそのまま記述しているので、適宜読み替えてください。

### Ubuntu

今回UbuntuはHyper-V上に作成していきます。この記事ではVMとして構築していますが、必須ではないので環境によって読み飛ばしてください。

まず、以下のサイトからUbuntuのisoを取得します。

https://jp.ubuntu.com/download

現在のLTSは`Ubuntu Desktop 24.04.1 LTS`でした。

![0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/e80d1752-4661-24b0-e6af-acd2a099b2f2.png)

次にHyper-V上でVMを作成していきます。VMの設定は以下の通りにしています。

- 名前と場所の指定：Ubuntu 24.04 - Unity Infra
- 世代の指定：第２世代
- メモリの割り当て：起動メモリ：8192MB(静的)
- ネットワークの構成：外部仮想スイッチ
- 仮想ハードディスクの接続：なし(後で設定するため)

仮想ハードディスクは、初期セットアップ段階で作成すると可変になるため、`なし`を選択しました。今回は手動で仮想ハードディスクを容量固定(VHDX)の64GBで作成して割り当てています。

またVM作成後に設定を変更する箇所があります。

まず、セキュリティのセキュアブートは`Microsoft UEFI 証明機関`に変更する必要があります。

次に、DVDドライブにダウンロードしたisoを指定し、ファームウェアのブート順をDVDドライブを最優先にします。

仮想プロセッサやメモリは適宜変更してください。私は`8 vProcessor`、`8192 MB Static memory`にしました。

これで起動してみます。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/9060c09e-4bf4-22f4-a53b-21781c1e7411.png)

この画面が出ていれば起動構成は問題ないと思います。`Try or Install Ubuntu`でインストールしていきます。

基本的には画面の内容に従ってください。ここでは一部だけ紹介します。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/78f388f7-00f8-a9fb-190d-bb6a609c1af2.png)

キーボードのレイアウトがUSキーボードでない場合はJapaneseにしておきます。環境によってはデフォルトでアンダースコアやパイプが入力できなくなりますので注意してください。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/2ebe244e-d481-651e-73a6-931f828b3676.png)

ユーザー名は適当に`shirotest-unityinfra`としました。起動時にログインが求められるのは面倒くさいので`Require my password to log in`のチェックは外しています。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/a56b6259-31a3-f4c8-e2c4-0fe79be44301.png)

無事Ubuntuのインストールが完了しました！

また、いつものおまじないをしておきます。

```shell
$ sudo apt update
$ sudo apt upgrade -y
```

### Certbot

始めにLet's Encryptで無料の証明書を発行していきます。ここだけ外部のサービスを利用しますが、昨今のブラウザはオレオレ証明書でHTTPS化しても`保護されていない接続`と警告されてしまうため、正式なCA認証局の証明書を発行します。今回はサブドメインを複数利用するので、ワイルドカード証明書を発行します。また、レジストリはCloudflareを前提とします。

:::note info
windowsの場合は`Certbot`の代わりに`win-acme`を利用します。
https://github.com/win-acme/win-acme
:::

:::note info
今回発行するワイルドカード証明書は
`*.shirokuma1101.com`
`*.pages.shirokuma1101.com`
の二つとなります。pagesに対してワイルドカード証明書を発行する理由として、GitLab Pagesはサブドメインにグループ名が付与されるため、こちらに対応するためです。
例:`root.pages.shirokuma1101.com`
:::

ワイルドカード証明書を発行する際に、DNSチャレンジというものを行います。DNSのTXTレコードを変更し反映されていることを確認したら、ドメインを所持しているという判定方式となっています。DNSを編集するため、CloudflareでAPI Tokenを発行する必要があります。

:::note warn
API Keys(Global API Key)を利用する方法もありますが、すべてにアクセスできるAPI Keyとなっているためセキュリティ的におすすめしません。
:::

まずCloudflareのtokenを発行するサイトへアクセスします。

https://dash.cloudflare.com/profile/api-tokens

`Create Token`で作成を開始し、`API token templates`の中の`Edit zone DNS`の`Use template`を選択します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/3473709b-70dd-06bf-4b95-c7f35f396150.png)

設定は`Zone Resources`のドメイン部分を変更するだけでOKです。`Continue to summary`で最終確認を行います。
```
Edit zone DNS test API token summary
This API token will affect the below accounts and zones, along with their respective permissions
└ メールアドレス's Account
 └ shirokuma1101.com - DNS:Edit
```

`DNS:Edit`が表示されていればOKです。`Create Token`で発行します。また、発行されたTokenは一度しか表示されないのでどこかにメモしておきます。

最後に証明書を発行していきます。基本的にはリファレンスに従って進めます。

https://certbot-dns-cloudflare.readthedocs.io/en/stable/

まず、必要なパッケージをインストールします。

```shell
$ sudo apt install -y certbot python3-certbot-dns-cloudflare
```

発行したAPI Tokenを設定ファイルに記述します。

```shell
$ mkdir -p ~/.secrets/certbot
$ vi ~/.secrets/certbot/cloudflare.ini
```

```ini
dns_cloudflare_api_token = YOUR_TOKEN
```

```shell
$ chmod 600 ~/.secrets/certbot/cloudflare.ini
```

以下のコマンドで実際に証明書を発行します。発行する際に、証明書の有効期限やセキュリティ情報を配信するためにemailの指定を求められますが、不要な方は`--register-unsafely-without-email`を指定する必要があります。

```shell
$ sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d *.shirokuma1101.com -d *.pages.shirokuma1101.com --register-unsafely-without-email
```

利用規約に同意するかを求められます。

```
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You muse agree in
order to register with the ACME server. Do you agree?
```

同意後、デフォルトで10秒の待機したあとにDNSチャレンジが成功すれば発行されます。

```
Successfully received certificate.
```

無事に発行できました。証明書は以下の場所に保存されています。

`/etc/letsencrypt/live/shirokuma1101.com/fullchain.pem`
`/etc/letsencrypt/live/shirokuma1101.com/privkey.pem`

Letsencryptで作成した証明書は3カ月間のみ有効ですが、デフォルトで自動更新が有効になっています。
以下のコマンドで有効化されているか確認できます。

```shell
$ sudo systemctl list-timers | grep certbot
```

有効化されない場合は以下のコマンドで有効化できます。

```shell
$ sudo systemctl enable certbot.timer
$ sudo systemctl start certbot.timer
$ sudo systemctl status certbot.timer
```

`enabled`となっていればOKです。

また、手動更新したい場合は以下のコマンドで更新できます。

```shell
$ sudo certbot renew
```

### Unbound

UnboundとはUbuntuで動作するDNSサーバーです。LAN内でドメインアクセスできるようにするには、DNSサーバーを立てて自分で紐づけしてあげる必要があります。同じようなパッケージにbindがありますが、こちらは機能が多いので今回は利用していません。ここはニーズに合わせて変更してください。

```shell
$ sudo apt install -y unbound
```

パッケージをインストールします。

```shell
$ sudo vi /etc/unbound/unbound.conf.d/unbound.conf
```

設定ファイルを変更します。その他設定項目が書かれているサンプルは以下の場所にあります。

`/usr/share/doc/unbound/examples/unbound.conf`

```
server:
        interface: 192.168.1.123
        port: 53
        access-control: 127.0.0.0/8 allow
        access-control: 192.168.1.0/24 allow
        local-zone: "shirokuma1101.com." redirect
        local-data: "shirokuma1101.com. IN A 192.168.1.123"
forward-zone:
        name: "."
        forward-addr: 1.1.1.1
        forward-addr: 1.0.0.1
```

今回は全てこのVMでホスティングするため、ドメインにアクセスされたものはすべて`192.168.1.123`でリゾルブする運用にしています。またforward先にはcloudflareを指定しています。

```shell
$ unbound-checkconf
```

構文に問題ないかチェックします。問題ない場合は`unbound-checkconf: no errors in /etc/unbound/unbound.conf`と表示されます。

```shell
$ sudo service unbound restart
```

これで設定が反映されます。

このDNSサーバーを自動で割り振りたい方はDHCPをホスティングしてDNSを指定してください。今回は手動でDNSを設定します。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/808ce2fe-9ae0-47d4-2439-27d6b660bdb0.png)

設定が完了したら、正しく名前解決できているか確認します。

```shell
dig gitlab.shirokuma1101.com
```

このコマンドで`gitlab.shirokuma1101.com. 3600 IN A 192.168.1.123`と表示されていれば正しく設定できています。

### Nginx

全ての通信をサブドメイン&https化するので、リバースプロキシを行います。

:::note warn
GitLab自体にNginxを利用したリバースプロキシ機能が備わっておりデフォルトで有効化されていますが、後述するVerdaccioをサブドメインに対応させるためGitLabのNginxは無効化して、自分でNginxをホストし一括で管理するようにしています。
:::

```shell
$ sudo apt install -y nginx
$ sudo vi /etc/nginx/sites-available/reverse_proxy.conf
```

パッケージをインストールし、設定ファイルを編集します。

```
ssl_certificate /etc/letsencrypt/live/shirokuma1101.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shirokuma1101.com/privkey.pem;
upstream gitlab-workhorse {
    server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket fail_timeout=0;
}
server　{
    listen 80;
    listen [::]:80;
    server_name *.shirokuma1101.com;
    return 301 https://$host$request_uri;
}
server　{
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name gitlab.shirokuma1101.com;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://gitlab-workhorse;
    }
}
server　{
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name pages.shirokuma1101.com *.pages.shirokuma1101.com;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://localhost:8090/;
    }
}
server　{
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name packages.shirokuma1101.com;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://localhost:4873;
    }
}
```

ここでは必要最低限の設定しかしていないので、(暗号化指定とかTLSサポートとか)セキュリティを上げたい場合はさらに設定が必要です。

以下のサイトにGitLab公式サンプルがあるので参考になるかと思います。

https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/support/nginx

設定の編集が完了したら、以下のコマンドでエラーチェック&適用します。

```shell
$ sudo ln -s /etc/nginx/sites-available/test.com /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl reload nginx
```

まだアクセス先のサービスを開始していないので確認はできませんが、エラーが出ていなければOKだと思います。

最後に、nginxのユーザー名を取得します。これはGitLabの設定で必要になります。

```shell
$ grep "^user[^;]*;" /etc/nginx/nginx.conf
user www-data;
```

ユーザー名は環境によって`nginx`となる人もいるようです。

これでUbuntuと証明書関係のセットアップは完了です。

### GitLab

基本的には以下の公式のセットアップに従いますが、一部変更しています。

https://about.gitlab.com/ja-jp/install/#ubuntu

```shell
$ sudo apt install -y curl openssh-server ca-certificates tzdata perl

$ curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
```

必要なパッケージをインストールします。

```shell
$ sudo EXTERNAL_URL="http://gitlab.shirokuma1101.com" apt install gitlab-ee
```

GitLabをインストールします。`EXTERNAL_URL`にhttpsを指定すると勝手に署名が作られてしまうため一旦httpを指定します。

インストールが終わると、以下の場所にパスワードが生成されているのでメモしておきます。

```shell
$ sudo cat /etc/gitlab/initial_root_password
```

設定ファイルを直接編集していきます。リバースプロキシの対応+Pagesの有効化対応も行っています。

```shell
$ sudo vi /etc/gitlab/gitlab.rb
```

```diff_python
- external_url 'http://gitlab.shirokuma1101.com'
- # web_server['external_users'] = []
- # web_server['username'] = 'gitlab-www'
- # nginx['enable'] = true
+ external_url 'https://gitlab.shirokuma1101.com'
+ web_server['external_users'] = ['www-data']
+ web_server['username'] = 'www-data'
+ nginx['enable'] = false

- # pages_external_url "http://pages.example.com/"
- # gitlab_pages['enable'] = false
- # gitlab_pages['listen_proxy'] = "localhost:8090"
- # gitlab_pages['dir'] = "/var/opt/gitlab/gitlab-pages"
- # gitlab_pages['log_directory'] = "/var/log/gitlab/gitlab-pages"
+ pages_external_url "https://pages.shirokuma1101.com"
+ gitlab_pages['enable'] = true
+ gitlab_pages['listen_proxy'] = "localhost:8090"
+ gitlab_pages['dir'] = "/var/opt/gitlab/gitlab-pages"
+ gitlab_pages['log_directory'] = "/var/log/gitlab/gitlab-pages"
```

設定の変更を保存したら、以下のコマンドで再構築します。

```shell
$ sudo gitlab-ctl reconfigure
```

エラーが出ていなければ、さきほど指定したドメインでアクセスしてみます。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/8b6919db-cd30-4bfd-ddd3-f60878add92a.png)

無事にウェブページにアクセスでき起動できているのが確認できました。

初期のログインID/Passwordは

```
ユーザー名：root
パスワード：さきほどメモしたパスワード
```

となっています。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/05580c45-f44b-7ba5-c629-3218ac778163.png)

ログインが成功するとAdmin権限としてアクセスすることができます。先ほどのパスワードは24時間で有効期限が切れてしまうので、`Edit profile`→`Password`でパスワードを変更しておきます。

また、URLの横の鍵アイコンから、正しくhttps化されているのが確認できると思います。

ちなみにアクセスできない等があれば、GitLabが原因であれば以下のコマンドでエラー内容を確認できます。

```shell
$ sudo gitlab-ctl tail
```

SSHのセットアップ手順は割愛します。クライアントから疎通の確認をしてみます。

```shell
$ ssh -T git@gitlab.shirokuma1101.com
Welcome to GitLab, @root!
```

サブドメインでのアクセスとhttps化の確認ができました。これでGitLabのセットアップは完了です。

### Verdaccio

非公開npmレジストリを立てていきます。

```shell
$ sudo apt install -y nodejs npm
$ sudo npm install -g verdaccio
```

必要なパッケージをインストールし、npmからverdaccioをインストールします。

また、ここで設定ファイルを生成させるためにVerdaccioを一度起動します。

```shell
$ verdaccio
```

`localhost:4783`でアクセスできるかを確認します。確認できたら`Ctrl + C`で終了させます。

この状態ではReboot時に起動してくれないので、設定を変更します。

```shell
$ sudo cp /usr/local/lib/node_modules/verdaccio/systemd/verdaccio.service /etc/systemd/system/
$ sudo vi /etc/systemd/system/verdaccio.service
```

設定を以下のように変更します。ユーザー名は適宜変更してください。

```diff_python
- ExecStart=/usr/bin/verdaccio --config /etc/verdaccio/config.yaml
+ ExecStart=/usr/local/bin/verdaccio --config /home/shiorotest-unityinfra/.config/verdaccio/config.yaml
- User=verdaccio
```

デフォルトのままだと、Unityのレジストリを認識してくれないので設定でuplinkを追加します。

```shell
$ vi /home/shiorotest-unityinfra/.config/verdaccio/config.yaml
```

```diff_yaml
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
+ unity:
+   url: http://packages.unity.com/

packages:
+ 'com.unity.*':
+   # unity packages
+   access: $all
+   proxy: unity

  '@*/*':
    # scoped packages
    access: $all
    publish: $authenticated
    unpublish: $authenticated
    proxy: npmjs

  '**':
  access: $all
  publish: $authenticated
  unpublish: $authenticated
  proxy: npmjs
```

設定が完了したら、以下のコマンドで起動します。

```shell
$ sudo systemctl daemon-reload
$ sudo systemctl start verdaccio
$ sudo systemctl enable verdaccio
```

エラーが出ていなければ、指定したドメインでアクセスしてみます。

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/be15521a-4b79-21c3-c98c-9571c2d8726d.png)

こちらもウェブページが表示されました。

クライアント側でレジストリとユーザーの追加をしておきます。

```shell
$ npm set registry https://packages.shirokuma1101.com
$ npm adduser --registry https://packages.shirokuma1101.com
```

以上でVerdaccioのセットアップは完了です。

### Unity

UPMの詳細な説明は割愛します。今回は一つのリポジトリに複数のpackage.jsonを作成する形で進めていきます。

```
Assets
└Plugins
 └shirokuma1101-helloworld
  └package.json
 └shirokuma-hoge
  └package.json
 ```

始めにリポジトリを作成します。名前は`shirokuma-upm`としました。

まず、`Hello World!`と表示するだけの簡単なPackageを作成します。

```csharp:HelloWorld.cs
using UnityEngine;

public class HelloWorld : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Hello world!");
    }
}
```

```json:com.shirokuma1101.helloworld.asmdef
{
    "name": "com.shirokuma1101.helloworld",
    "rootNamespace": "",
    "references": [],
    "includePlatforms": [],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": true,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
```

```json:package.json
{
    "name": "com.shirokuma1101.helloworld",
    "displayName": "Hello world",
    "description": "Hello world!",
    "version": "0.0.1",
    "unity": "2021.3"
}
```

一度手動でpublishしてみます。
カレントディレクトリを`package.json`があるところへ移動し、以下のコマンドでpublishします。

```shell
npm publish --registry https://packages.shirokuma1101.com
```

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/a7d0cbc9-7f14-5788-5a53-62712c376c20.png)

ウェブページに先ほど作成したPackageが表示されました。次に別のUnityProjectを作成し、実際にPackageをInstallしてみます。

![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/2913ad27-b306-1ae0-42bb-5c25119695ef.png)

`Project Settings`->`Package Manager`->`Scoped Registries`からスコープを追加します。UPMではスコープはドメインになります。npmでよく利用される`@`を利用したスコープは認識されないので注意してください。

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/f0653cb2-22cc-8d95-872c-a60b17c857bc.png)

`Window`->`Package Manager`->`My Registries`に先ほど追加したレジストリが追加されているのが確認できました。

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/d4295af7-d3a0-27cc-4911-6eb62c0578ff.png)

実際にInstallしてスクリプトが動作しているのも確認できます。

別のPackageも作成しておきます。

```Hoge.cs
using UnityEngine;

public class Hoge : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Hoge Hoge");
    }
}

```

```json:com.shirokuma1101.hoge.asmdef
{
    "name": "com.shirokuma1101.hoge",
    "rootNamespace": "",
    "references": [],
    "includePlatforms": [],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": true,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
```

```json:package.json
{
    "name": "com.shirokuma1101.helloworld",
    "displayName": "Hello world",
    "description": "Hello world!",
    "version": "0.0.1",
    "unity": "2021.3"
    "dependencies": {
        "com.unity.inputsystem": "1.11.2"
    }
}
```

次にGitLab CI/CDを利用して特定条件で自動publishを行うようにしていきます。

### GitLab CI/CD

CI/CDで自動化するにあたり、Runnerを自前で用意する必要があります。今回はRunnerをGitLabと同じUbuntuにインストールし、ExecutorにはDockerを選択します。ここも環境によって変わってくるところかと思います。

:::note warn
今回はDocker Engineをインストールするので不要ですが、Docker Desktopをインストールする場合にはKVMサポートが必要なので注意が必要です。今回と同じ環境(Hyper-VのVM上であること)の場合のみ対象ですが、VM内でDockerなど仮想環境を利用するにはホストOS側で設定が必要です。一度VMを停止し、以下のコマンドをPowershellで管理者権限で実行する必要があります。

```shell
PS> Set-VMProcessor -VMName <VM Name> -ExposeVirtualizationExtensions $true
```
:::

こちらも基本的には以下の公式のセットアップに従いますが、一部変更しています。

https://docs.docker.com/engine/install/ubuntu/

```shell
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc
$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

インストールが終わったら、起動確認してみます。

```shell
$ sudo docker run hello-world
```

`Hello world`が表示されていればOKです。続いてGitLab Runnerをインストールします。

```shell
$ sudo curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
$ sudo apt install -y gitlab-runner
```

インストールが完了したら、RunnerをGitLabに登録します。登録にはTokenが必要なので、GitLabウェブページから作成したリポジトリを選択し、`Settings`->`CI/CD`->`Runners`からRunnerを追加します。ここでTokenが発行されるのでどこかにメモしておきます。次に以下のコマンドで対話的に設定していきます。

```
$ sudo gitlab-runner register
url> https://gitlab.shirokuma1101.com
token> 先ほどのToken
executor> docker
default image> ubuntu:latest
```

今回デフォルトのimageにUbuntuを指定していますが、もう少し軽量のimageのほうがいいかもしれないです。

また、現在の設定だとUbuntuで設定してあるDNS(今回の例でいうと`192.168.1.123`)を認識してくれないので、設定を変更します。まず、dockerのnetworkをリスト化します。

```shell
$ sudo docker network ls
bridge
host
local
```

私の環境の場合は、`host`がUbuntu(ホスト)のネットワークを利用する名前となっていました。

```shell
$ sudo vi /etc/gitlab-runner/config.toml
```

```diff_toml:config.toml
[runners.docker]
  # 省略
+ network_mode = "host"
```

これでDockerを実行するOSのDNSを認識してくれます。

次にサンプルを実行してみます。ここでタグは指定していないので、Runnerの設定から`Run untagged jobs`にチェックを入れる必要があります。

```yaml:gitlab-ci.yml
build-job:
  stage: build
  script:
    - echo "Hello, $GITLAB_USER_LOGIN!"
   
test-job1:
  stage: test
  script:
    - echo "This job tests something"
   
test-job2:
  stage: test
  script:
    - echo "This job tests something, but takes more time than test-job1."
    - echo "After the echo commands complete, it runs the sleep command for 20 seconds"
    - echo "which simulates a test that runs 20 seconds longer than test-job1"
    - sleep 20
   
deploy-prod:
  stage: deploy
  script:
    - echo "This job deploys something from the $CI_COMMIT_BRANCH branch."
  environment: production
```

この`gitlab-ci.yml`をリポジトリのルートに配置してpushします。しばらくするとpipelineが自動で走ります。

![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/dff2ec4a-ff40-1333-55fa-ad062548b723.png)

無事にpassしていました。次はいよいよ自動publishを行っていきます。

まず、クライアントで追加したユーザー情報を元にTokenを取得します。Windowsの場合は以下のコマンドで内容を表示できます。

```shell
> type %USERPROFILE%\.npmrc
```

表示された内容の`_authToken=`より後をメモしてください。ダブルクォーテーションは不要です。

次に、先ほどメモしたTokenを`Settings`->`CI/CD`->`Variables`に追加します。ここに追加することでソースコードに直接Tokenを公開することなく環境変数として設定することができます。今回Keyは`AUTH_TOKEN`としています。

```yaml:gitlab-ci.yml
stages:
  - publish

# npm publish job
publish:
  stage: publish
  image: node:23
  tags:
    - docker

  script:
    # extract module name from tag
    - MODULE_NAME=$(echo $CI_COMMIT_TAG | sed 's/-v[0-9]\+\.[0-9]\+\.[0-9]\+$//')
    # change directory
    - cd $CI_PROJECT_DIR/Assets/Plugins/$MODULE_NAME
    # npm login
    - npm config set //packages.shirokuma1101.com/:_authToken ${AUTH_TOKEN}
    # npm publish
    - npm publish --registry https://packages.shirokuma1101.com

  rules:
    # tag <module-name>-vX.X.X format only
    - if: '$CI_COMMIT_TAG =~ /^[a-zA-Z0-9_-]+-v[0-9]+\.[0-9]+\.[0-9]+$/'
      when: on_success

# env
variables:
  AUTH_TOKEN: ${AUTH_TOKEN}
```

内容としてはタグの内容から名前を抽出し、特定のフォルダのみをpublishするという内容になっています。ここでは`shirokuma-hoge-v0.0.2`というタグを発行してみます。

![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/d7167861-131a-ad56-fd6d-3ab5c3bd1e67.png)

こちらも無事にpublishできるようになりました！以降はタグの発行のみで自動的にpublishされていくようになっています。

### Gitlab Pages

ドキュメントの生成にはPythonのモジュールである`mkdocs`と`mkdocs-material`を利用します。こちらを利用すればMarkdown形式のドキュメントをいい感じのウェブページにしてくれます。詳しくは以下のサイトが参考になると思います。

https://squidfunk.github.io/mkdocs-material/

以下のサイトからPythonをインストールします。今の最新バージョンは`Python 3.13.1`でした。

https://www.python.org/downloads/

mkdocs、mkdocs-materialモジュールをインストールします。依存モジュールになっているので、mkdocs-materialのみ指定すれば大丈夫です。

```shell
PS> py -m pip install mkdocs-material
```

ドキュメントのファイル構成は以下の通りにしました。

```
mkdocs.yml
docs
└readme.md
└shirokuma1101-upm
 └helloworld.md
 └hoge.md
```

ドキュメントを生成するにあたり、設定ファイルが必要なのでプロジェクトのrootに設定ファイルを作成します。

```yml:mkdocs.yml
site_name: shirokuma1101-upm docs

docs_dir: 'docs'

theme:
  name: material

nav:
  - Home: README.md
  - Docs:
    - helloworle: shirokuma1101-upm/helloworld.md
    - hoge: shirokuma1101-upm/hoge.md
```

一度ローカルで以下のコマンドで生成してみます。カレントディレクトリを設定ファイルと同じ場所に移動してコマンドを実行します。ウェブサーバーを介さない場合は`--no-directory-urls`オプションを付ける必要があります。

```shell
PS> py -m mkdocs build --no-directory-urls
```

`./site/`下にドキュメントが生成されているので、`index.html`をウェブブラウザーで表示して確認します。次にこちらもCI/CDで自動化していきます。

```diff_yaml:gitlab-ci.yml
stages:
  - publish
+ - mkdocs

省略

+pages:
+ stage: mkdocs
+ image: python:3.13
+ tags:
+   - docker
+ script:
+   - pip install mkdocs-material
+   - cd $CI_PROJECT_DIR
+   - mkdocs build --site-dir public
+ artifacts:
+   paths:
+     - public

```

今回はpushしたタイミングで生成されるようになっています。こちらもプロジェクトによって設定を変更してください。`Deploy`->`Pages`にDeployURLが記載されているので、実際にアクセスしてみます。

![40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/30557173-b80e-80e1-667c-378ec2ef89c7.png)

こちらも無事にDeployされているのが確認できました。

## おわりに

久しぶりにそこそこな量の文章を書いた気がします。今回の内容は既存の記事では日本語/英語ともに少なかったり古かったりしていたので、今後誰かの参考になれば幸いです。