同人誌の組版をGitLab CIを使ってやってみた

同人誌を執筆するにあたって、一番面倒なのが環境設定と反復ビルドである。
だるいので、自動化したい。じゃあCIすればいいよね、となる。このため、CI環境を構築してみた。

## 環境構築開始

環境構築は、今回は以下の通りとする。

- AWSのEC2インスタンスを使う
- GitLabでレポジトリ管理を行い、GitLab CIでビルドを行う

というわけで始めていこう

## EC2インスタンスの起動

まあ難しいことは何もない。注意しないといけないのは、GitLabはメモリを最低4GB以上必要とするため、あまり低いインスタンスだと動かないという点である。また、諸々考えると、こんな感じの設定にした。

- インスタンスサイズはt3a.medium T3 Unlimitedは無効
- IAMロールとしてS3の無制限アクセスがあるロールを割当
- ディスクサイズは20GB(6GBをswapに割り当てるので実質使えるのは10GB程度)
- セキュリティグループでは22・25・80・443を全IPアドレスに開放

永続的に起動するならElastic IPの設定をお忘れなく。

## DNSの設定

DNSは以下のように設定した。

- gitlab Aレコード インスタンスのIPアドレス
- *.gitlab CNAMEレコード gitlab
- *.pages.gitlab CNAMEレコード gitlab

## タイムゾーンの設定

タイムゾーンは`sudo timedatectl set-timezone Asia/Tokyo`で日本時間に設定できる。

## SWAP領域の設定

[ここ](https://aws.amazon.com/jp/premiumsupport/knowledge-center/ec2-memory-swap-file/)に書いてある通りだが、要するにこういうことである。

```shell
sudo dd if=/dev/zero of=/swapfile bs=1G count=6
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo vim /etc/fstab
```

fstabの中身は以下の通りとする(UUID=で始まる行は何も変更しないこと)。

```
#
UUID=disk-uuid / xfs defaults,noatime 1 1
/swapfile swap swap defaults 0 0
```

## パッケージのアップデート

SSHでログインする。`sudo yum update`でパッケージを更新する。その後、`sudo reboot now`でサーバを再起動する。再起動ではIPアドレスは変化しないのでご安心を。

## epelレポジトリの導入

epelレポジトリは`sudo amazon-linux-extras install epel`でインストールできる。これをインストールしたのは、Let's Encryptを導入するためである。

## Let's Encryptの導入と証明書取得

Let's Encryptのインストールは[MysteriousMonkey様の書いた文書](https://qiita.com/MysteriousMonky/items/4d3d857c0e68d4bfff39)の通りである。要するに、こういうことである(今回は、Nginx経由の認証はしないのでcertbotのみでよい)。

```shell
sudo yum install certbot
```

その後、[ラボラジアン様の書いた文書](https://laboradian.com/use-wildcard-with-letsencrypt/)にしたがって、以下のようなコマンドを入力する。

```shell
sudo certbot certonly --manual \
--preferred-challenges dns-01 \
--server https://acme-v02.api.letsencrypt.org/directory \
-m your@mail.tld
-d *.yourdomain.tld \
-d *.gitlab.yourdomain.tld \
-d *.pages.gitlab.yourdomain.tld
```

このあとは、指示に従いDNS設定を行い、浸透を待ってを繰り返して証明書の発行を行う。秘密鍵のパスと証明書のパスが出てくるので控えておくこと。

## Dockerのインストール

Dockerのレポジトリはデフォルトで有効になっている。ただ、docker-composeをそれ経由でインストールしようとすると依存関係で死ぬので、docker-composeは別口でインストールする。

```shell
sudo yum install docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -a -G docker ec2-user
sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

詳しくは[reoringさんの書いた文書](https://qiita.com/reoring/items/0d1f556064d363f0ccb8)を参照せよ

## GitLabのインストール

今回はGitLab CEを導入することにする。この場合の手順は以下の通り。ここでHTTP指定しているがあとでHTTPSにするのでご安心を。

```shell
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo EXTERNAL_URL="http://gitlab.yourdomain.tld" yum install gitlab-ce
```

まだ、これでは設定が終わってないので、設定をしていこう。

## GitLab設定ファイル

設定ファイルは/etc/gitlab/gitlab.rbにある。これを書き換えていく。

まず25行目近辺のこれを書き換える。

```ruby
# Before
external_url 'http://gitlab.yourdomain.tld'
# After
external_url 'https://gitlab.yourdomain.tld'
```

次に、57行近辺のこれを書き換える。

```ruby
# Before
# gitlab_rails['time_zone'] = 'UTC'

### Email Settings
# gitlab_rails['gitlab_email_enabled'] = true
# gitlab_rails['gitlab_email_from'] = 'example@example.com'
# gitlab_rails['gitlab_email_display_name'] = 'Example'
# gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'
# After
gitlab_rails['time_zone'] = 'Asia/Tokyo'

### Email Settings
gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = 'gitlab@yourmaildomain.tld'
gitlab_rails['gitlab_email_display_name'] = 'GitLab'
gitlab_rails['gitlab_email_reply_to'] = 'gitlab@yourmaildomain.tld'
```

次に、171行目近辺のこれを書き換える。メールサーバの設定は正規表現のエイリアスを定義してあげることで実現可能。[Lonely Mobilerさんの記事](https://loumo.jp/wp/archive/20160219120024/)を参考にせよ。

```ruby
# Before
# gitlab_rails['incoming_email_enabled'] = true

#### Incoming Email Address
####! The email address including the `%{key}` placeholder that will be replaced
####! to reference the item being replied to.
####! **The placeholder can be omitted but if present, it must appear in the
####!   "user" part of the address (before the `@`).**
# gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"

#### Email account username
####! **With third party providers, this is usually the full email address.**
####! **With self-hosted email servers, this is usually the user part of the
####!   email address.**
# gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"

#### Email account password
# gitlab_rails['incoming_email_password'] = "[REDACTED]"

#### IMAP Settings
# gitlab_rails['incoming_email_host'] = "imap.gmail.com"
# gitlab_rails['incoming_email_port'] = 993
# gitlab_rails['incoming_email_ssl'] = true
# gitlab_rails['incoming_email_start_tls'] = false
# After
gitlab_rails['incoming_email_enabled'] = true

#### Incoming Email Address
####! The email address including the `%{key}` placeholder that will be replaced
####! to reference the item being replied to.
####! **The placeholder can be omitted but if present, it must appear in the
####!   "user" part of the address (before the `@`).**
gitlab_rails['incoming_email_address'] = "gitlab+%{key}@yourmaildomain.tld"

#### Email account username
####! **With third party providers, this is usually the full email address.**
####! **With self-hosted email servers, this is usually the user part of the
####!   email address.**
gitlab_rails['incoming_email_email'] = "gitlab"

#### Email account password
gitlab_rails['incoming_email_password'] = "MailPass"

#### IMAP Settings
gitlab_rails['incoming_email_host'] = "mailserv.yourmaildomain.tld"
gitlab_rails['incoming_email_port'] = 143
gitlab_rails['incoming_email_ssl'] = false
gitlab_rails['incoming_email_start_tls'] = true
```

次に、562行目近辺のこれを書き換える。

```ruby
# Before
# gitlab_rails['smtp_enable'] = true
# gitlab_rails['smtp_address'] = "smtp.server"
# gitlab_rails['smtp_port'] = 465
# gitlab_rails['smtp_user_name'] = "smtp user"
# gitlab_rails['smtp_password'] = "smtp password"
# gitlab_rails['smtp_domain'] = "example.com"
# gitlab_rails['smtp_authentication'] = "login"
# gitlab_rails['smtp_enable_starttls_auto'] = true
# gitlab_rails['smtp_tls'] = false
# After
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mailserv.yourmaildomain.tld"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "gitlab"
gitlab_rails['smtp_password'] = "MailPass"
gitlab_rails['smtp_domain'] = "yourmaildomain.tld"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
```

次に、1085行近辺のこれを書き換える。ここで、先程作成した証明書と秘密鍵を使う。

```ruby
# Before
# nginx['ssl_certificate'] = "/etc/gitlab/ssl/#{node['fqdn']}.crt"
# nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/#{node['fqdn']}.key"
# After
nginx['ssl_certificate'] = "/etc/letsencrypt/live/yourdomain.tld/fullchain.pem"
nginx['ssl_certificate_key'] = "/etc/letsencrypt/live/yourdomain.tld/privkey.pem"
```

ここまで書き換えて保存したら`sudo gitlab-ctl reconfigure`したあと、Web上から初期設定をする。

## GitLab Runnerの設定

GitLab Runnerをまずインストールする。以下の通りである。

```shell
curl -LJO https://gitlab-runner-downloads.s3.amazonaws.com/latest/rpm/gitlab-runner_amd64.rpm
sudo yum install gitlab-runner_amd64.rpm
sudo usermod -aG docker gitlab-runner
```

ここまで実行したら、runnerを登録する。登録方法は、まずページからトークンを取得して以下のコマンドを入力する。事前に/etc/hostsでlocalhostに自分のドメインを設定しておくこと。

```shell
sudo gitlab-runner register -n --url https://yourdomain.tld/ \
--registration-token TOKEN -executor shell --description 'Runner'
```

これでrunnerのセットアップができたので、次にテスト用のビルドをしていく。

## プロジェクトを作成する

まずプロジェクトを作成する。その際、以下のようなディレクトリ構成にする。

```
/
|-files
| |-main.tex
| --(other content files)
|-image
| |-Dockerfile
| --build.sh
-.gitlab-ci.yml
```

files以下は普通のLaTeXの構成なので省略。

image/Dockerfileの中身は以下の通り。

```dockerfile
FROM ubuntu:19.10
RUN apt-get update && apt-get install -y software-properties-common
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y texlive-full \
latex-cjk-all fonts-ipafont
RUN kanji-config-updmap-sys ipaex
COPY build.sh /
RUN chmod a+x /build.sh
```

image/build.shの中身は以下の通り。

```shell
#!/bin/bash

# 1回通しのビルドしかしてないので相互参照がある場合は要調整
cd /files
platex main.tex
dvipdfmx main.dvi
```

.gitlab-ci.ymlの中身は以下の通り。

```yaml
before_script:
  - docker info

build_image:
  script:
    - docker build -t content-builder ./image
    - docker run --rm -v $(pwd)/files:/files content-builder /build.sh
  artifacts:
    paths:
      - files/main.pdf
```

あとは細かく調整すると良いだろう。