Slack フリープランが改悪したので Mattermost をインストールしてみた

# Mattermost
2022年9月1日から **Slack** の[フリープラン](https://slack.com/intl/ja-jp/pricing/free)におけるメッセージ履歴の制約が「1万件」から「過去90日間」に変更になると[アナウンス](https://slack.com/intl/ja-jp/blog/news/pricing-and-plan-updates)されました。[^pricing-and-plan-updates]

Slack クローンの **Mattermost** はセルフホストが可能で、これを利用すれば制約を気にする必要がなくなります。
![](https://mattermost.com/wp-content/uploads/2021/10/mattermost-platform-channels.webp)
そこで、[公式ドキュメント](https://docs.mattermost.com/install/install-rhel-8.html)に基づいて  Mattermost を RHEL クローンの Rocky Linux 8.6 にインストールする手順をまとめました。Slack からのデータ移行も記載しています。

:::note info
RHEL 8.x や CentOS 8 Stream、 AlmaLinux 8.x 等でも手順に大きな違いはないかと思います。
:::

:::note info
Docker 版のインストールは [Install Mattermost via Docker](https://docs.mattermost.com/install/install-docker.html) を参照してください。 Docker での運用に慣れていればこちらの方が早いでしょう。
:::

[^pricing-and-plan-updates]: <q>今回、フリープランの内容も変更し、ユーザーの皆さまが Slack のさまざまな新機能を試しやすくなります。いずれの変更も、2022 年 9 月 1 日から適用予定です。</q><q cite="https://slack.com/intl/ja-jp/blog/news/pricing-and-plan-updates">これまでフリープランでは、メッセージ数 10,000 件、ストレージ容量 5 GB という制限がありましたが、今後は過去 90 日間のメッセージ履歴とファイルストレージを無制限に利用できるため、チームはいつ上限に達するのか心配する必要はありません。</q> - <cite>[Slack 初の料金改定とフリープランの内容変更のお知らせ | Slack](https://slack.com/intl/ja-jp/blog/news/pricing-and-plan-updates)</cite>

## 環境
* VirtualBox 6.1.34 (2022-03-22) [^virtualbox]
* Rocky Linux 8.6 (minimal) (2022-05-16) [^rocky]
* MySQL Community Server 8.0.29 (2022-04-26) [^mysql]
* Mattermost Server 7.1.1 (2022-07-15) [^changelog-mattermost]

[^virtualbox]: <q>VirtualBox 6.1.34 (released March 22 2022)</q> - <cite>https://www.virtualbox.org/wiki/Changelog</cite>
[^rocky]: <q cite="https://rockylinux.org/ja/news/rocky-linux-8-6-ga-release/">MAY 16, 2022 Rocky Linux 8.6 Available Now</q> - <cite>[Rocky Linux 8.6 Available Now | Rocky Linux](https://rockylinux.org/ja/news/rocky-linux-8-6-ga-release/)</cite>
[^mysql]: <cite>[MySQL :: MySQL 8.0 Release Notes :: Changes in MySQL 8.0.29 (2022-04-26, General Availability)](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-29.html)</cite>
[^changelog-mattermost]: <q>v7.1.1, released 2022-07-15</q> - <cite>[Mattermost self-hosted changelog — Mattermost documentation](https://docs.mattermost.com/install/self-managed-changelog.html)</cite>

## インストール
### Rocky Linux 8
「[Rocky Linux 8 を VirtualBox に最小インストールする - Qiita](https://qiita.com/bezeklik/items/79f0d65e971157aef907)」を参考に。

### MySQL Server 8
#### MySQL Yum Repository の追加
```bash
dnf install https://repo.mysql.com/mysql80-community-release-el8.rpm
dnf module disable mysql
```
最新バージョンの MySQL Server を利用したい場合、デフォルトの mysql モジュールを無効にして [MySQL Yum Repository](https://dev.mysql.com/downloads/repo/yum/) を導入します。

:::note info
この作業は必須ではありません。
:::

#### インストールと起動
```bash
dnf install mysql-server
systemctl --now enable mysqld
```

:::note info
`--now` オプションで自動起動設定と同時に起動します。 [^systemctl-option-now]
:::

[^systemctl-option-now]: <q cite="https://man.kusakata.com/man/systemctl.1.html#cmdoption-now">**enable** で使用した場合、ユニットの有効化だけでなく起動も行われます。**disable** や **mask** で使用した場合、ユニットは停止されます。有効化・無効化の操作が成功したときのみ起動・停止操作も行われます。</q> - <cite>[`--now`](https://man.kusakata.com/man/systemctl.1.html#cmdoption-now) - [オプション](https://man.kusakata.com/man/systemctl.1.html#id4) - [systemctl(1) — Arch Linux マニュアルページ](https://man.kusakata.com/man/systemctl.1.html)</cite>

:::note warn
エラーが出る場合は <kbd>`rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022`</kbd> を実行します。
:::

#### root パスワードの変更
```bash
 DB_ROOT_PASSWORD=$(< /dev/urandom tr -dc 'A-HJ-NP-Za-km-z2-9!$%&()*+,-.:;<=>?[]^_{}~' | head -c 16; echo)
 echo ${DB_ROOT_PASSWORD}
awk '/password/ {print $13}' /var/log/mysqld.log
mysql_secure_installation
```
ログに出力された初期パスワードを確認し、 <kbd>`mysql_secure_installation`</kbd> コマンドで root パスワードの変更等のセキュリティ強化を行います。

:::note info
コマンドの先頭にスペースがあると history に記録されません。
この挙動は環境変数 `HISTCONTROL` に `ignorespace` または `ignoreboth` が指定されていると機能します。
:::

:::note info
パスワード生成には文字種指定のために tr コマンドを応用していますが、 pwmake コマンドがデフォルトでインストールされているので <kbd>`pwmake 80`</kbd> などと実行しても良いでしょう。他に expect パッケージに含まれる mkpasswd コマンド等もあります。
:::

#### データベースと DB ユーザーの作成
```bash
 DB_USER_USERNAME='mmuser'
 DB_USER_PASSWORD=$(< /dev/urandom tr -dc 'A-HJ-NP-Za-km-z2-9!$%&()*+,-.:;<=>?[]^_{}~' | head -c 16; echo)
 echo ${DB_USER_PASSWORD}
mysqladmin --user=root --password create mattermost
mysql --user=root --password --execute="CREATE USER '${DB_USER_USERNAME}'@'localhost' IDENTIFIED BY '${DB_USER_PASSWORD}';"
mysql --user=root --password --execute="GRANT ALL ON mattermost.* TO '${DB_USER_USERNAME}'@'localhost';"
mysql --user=mmuser --password mattermost
```
:::note
mysql にログインして実行する場合は以下の通り。
```bash
mysql --user=root --password
```
```sql
CREATE DATABASE mattermost;
CREATE USER 'mmuser'@'localhost' IDENTIFIED BY '<DB_USER_PASSWORD>';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, EXECUTE, REFERENCES, INDEX, ALTER ON mattermost.* TO 'mmuser'@'localhost';
SELECT Host, User, plugin FROM mysql.user;
SHOW GRANTS FOR 'mmuser'@'localhost';
\q
```
:::

### Mattermost Server
#### ダウンロード
```bash
RELEASE_NAME=$(curl -sL https://api.github.com/repos/mattermost/mattermost-server/releases/latest | awk '/"name"/ {print $2}')
VERSION=${RELEASE_NAME:2:-2}
curl --location --remote-name https://releases.mattermost.com/${VERSION}/mattermost-${VERSION}-linux-amd64.tar.gz
dnf install tar
tar --extract --directory=/opt/ --file=mattermost-${VERSION}-linux-amd64.tar.gz
mkdir --verbose /opt/mattermost/data
useradd --system --user-group mattermost --comment 'Mattermost' --shell /sbin/nologin
chown --recursive mattermost:mattermost /opt/mattermost
chmod --recursive g+w /opt/mattermost
restorecon -Rv /opt/mattermost
```
Mattermost Server のバイナリをダウンロードするために、[Deploy Your Self-Hosted Mattermost Server](https://mattermost.com/deploy/) ページでバイナリの URL を確認するか、 [GitHub の REST API](https://docs.github.com/ja/rest) の [Get the latest release](https://docs.github.com/ja/rest/releases/releases#get-the-latest-release) から [Mattermost リポジトリ](https://github.com/mattermost/mattermost-server)の最新リリース名で最新バージョンを取得し、ダウンロード URL を生成します。

:::note warn
最小構成では tar パッケージがインストールされていません。
:::

#### 設定の変更
```bash
cp --archive --verbose /opt/mattermost/config/config.json{,.org}
FQDN='192.168.56.100'
SITE_URL="http://${FQDN}/"
sed --in-place \
 --expression='/DriverName/ s/postgres/mysql/' \
 --expression='/"DataSource"/ s|postgres://||' \
 --expression="/\"DataSource\"/ s/mostest/${DB_USER_PASSWORD}/" \
 --expression='/"DataSource"/ s/localhost/tcp(localhost:3306)/' \
 --expression='/"DataSource"/ s/mattermost_test/mattermost/' \
 --expression='/"DataSource"/ s/sslmode=.*yes//' \
 --expression='/"DataSource"/ s/\?"/\?charset=utf8mb4,utf8\&writeTimeout=30s"/' \
 --expression="/\"SiteURL\"/ s|\"\"|\"${SITE_URL}\"|" \
 /opt/mattermost/config/config.json
diff /opt/mattermost/config/config.json{.org,}
```
config/config.json ファイルの `DriverName` `DataSource` `SiteURL` の値を変更します。

#### サービスの追加
```bash
systemctl edit --force --full mattermost.service
```
```systemd:mattermost.service
[Unit]
Description=Mattermost
After=syslog.target network.target mysqld.service

[Service]
Type=notify
WorkingDirectory=/opt/mattermost
User=mattermost
ExecStart=
ExecStart=/opt/mattermost/bin/mattermost
PIDFile=/var/spool/mattermost/pid/master.pid
TimeoutStartSec=3600
KillMode=mixed
LimitNOFILE=49152

[Install]
WantedBy=multi-user.target
```
`systemctl edit` コマンドに `--force` オプションを付けることで新しいユニットファイルを作成できます。 [^systemctl-command-edit]

[^systemctl-command-edit]: <q cite="https://man.kusakata.com/man/systemctl.1.html#id6">**--force** を指定して既存のユニットが存在しない場合、新しいユニットファイルが開かれます。</q> - `edit UNIT...` - <cite>[ユニットコマンド](https://man.kusakata.com/man/systemctl.1.html#id6) - [コマンド](https://man.kusakata.com/man/systemctl.1.html#id5) - [systemctl(1) — Arch Linux マニュアルページ](https://man.kusakata.com/man/systemctl.1.html)</cite>

#### Mattermost の起動
```bash
systemctl --now enable mattermost
```

### FirewallD
```bash
firewall-cmd --permanent --new-service=mattermost
firewall-cmd --permanent --service=mattermost --set-short='Mattermost Server'
firewall-cmd --permanent --service=mattermost --set-description='Mattermost is a Slack-like chat collaboration tool.'
firewall-cmd --permanent --service=mattermost --add-port=8065/tcp
firewall-cmd --permanent --add-service=mattermost
firewall-cmd --reload
firewall-cmd --list-services
```
firewall-cmd　コマンドの `--new-service` オプションで新しいサービスを追加できます。 [^firewall-cmd-new-service]
ポートを開放したらブラウザからアクセスします。

[^firewall-cmd-new-service]: <cite>[7.3.4. 新しいサービスの追加 Red Hat Enterprise Linux 8 | Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/securing_networks/adding-new-services_controlling-network-traffic-using-firewalld)</cite>

:::note info
`firewall-cmd --permanent --add-port=8065/tcp && firewall-cmd --reload` で直接ポート番号を指定することもできます。
:::

## Slack から Mattermost へのデータの移行
### Slack データのエクスポート
https://slack.com/intl/ja-jp/help/articles/201658943

1. デスクトップの画面左上にあるワークスペース名をクリックします。
2. メニューから「<kbd><samp>設定とその他管理項目</samp></kbd>」を選択し、「<kbd><samp>ワークスペースの設定</samp></kbd>」をクリックします。
3.  「<kbd><samp>データのインポート/エクスポート</samp></kbd>」をクリックします。
4.  「<kbd><samp>エクスポート</samp></kbd>」タブを選択します。
5.  「<kbd><samp>日付範囲をエクスポートする</samp></kbd>」のドロップダウンメニューを開き、オプションを選択します。
6.  「<kbd><samp>エクスポート開始</samp></kbd>」をクリックします。エクスポートファイルの準備ができるとメールが送付されます。
7. 届いたメールに記載されている「<kbd><samp>ワークスペースのエクスポートページにアクセスする</samp></kbd>」をクリックします。
8.  「<kbd><samp>ダウンロードを開始する</samp></kbd>」をクリックして zip ファイルにアクセスします。`<ワークスペース名> Slack export <開始年月日> - <終了年月日>.zip` というファイルがダウンロードされます。
9. ファイル名にマルチバイトやスペースが含まれていてコンソールでの処理が面倒になるので、ファイル名を `slack-bulk-export.zip` などに変更しておきます。

### データファイルの転送
```bash
scp slack-bulk-export.zip username@192.168.56.100:./
```
ローカル PC からサーバーにエクスポートデータを転送します。

### Slack アプリの登録
1. https://api.slack.com/apps にアクセスします。
2. 「<kbd><samp>Create New App</samp></kbd>」ボタンを押下します。
3. 「<kbd><samp>From scratch</samp></kbd>」を選択します。
4. 「<kbd><samp>App Name</samp></kbd>」に「<kbd>Slack Advanced Exporter</kbd>」など任意の名前を入力し、「<kbd><samp>Pick a workspace to develop your app in:</samp></kbd>」でワークスペースを選択したら「<kbd><samp>Create App</samp></kbd>」ボタンを押下します。
6. サイドバーから「<kbd><samp>OAuth & Permissions</samp></kbd>」を選択し、「Scopes」の「Bot Token Scopes」で「<kbd><samp>Add an OAuth Scope</samp></kbd>」ボタンを押下して <kbd><samp>`users:read`</samp></kbd> と <kbd><samp>`users:read.email`</samp></kbd> を追加します。
7. 同じ「OAuth & Permissions」ページの「OAuth Tokens for Your Workspace」にある「<kbd><samp>Install to Workspace</samp></kbd>」ボタンを押下します。
8. 「<kbd><samp>許可する</samp></kbd>」ボタンを押下します。
9. 「OAuth Tokens for Your Workspace」の「<kbd><samp>Bot User OAuth Token</samp></kbd>」欄の値をコピーします。

### Slack Advanced Exporter
https://github.com/grundleborg/slack-advanced-exporter/releases/
```bash
 SLACK_TOKEN=''
curl --location --remote-name https://github.com/grundleborg/slack-advanced-exporter/releases/download/v0.4.0/slack-advanced-exporter.linux-amd64.tar.gz
tar --extract --file=slack-advanced-exporter.linux-amd64.tar.gz
mv --verbose slack-advanced-exporter /usr/local/bin/
slack-advanced-exporter --input-archive slack-bulk-export.zip --output-archive export-with-emails.zip fetch-emails --api-token ${SLACK_TOKEN}
slack-advanced-exporter --input-archive export-with-emails.zip --output-archive export-with-emails-and-attachments.zip fetch-attachments
```
前段でコピーした「<kbd><samp>Bot User OAuth Token</samp></kbd>」欄の値を、変数 `$SLACK_TOKEN` に代入します。

### 管理ユーザーとチームの作成
```bash
TEAM_NAME='mynewteam'
DISPLAY_NAME='My New Team'
MM_EMAIL='user@example.jp'
MM_USERNAME='userexample'
 MM_PASSWORD=$(< /dev/urandom tr -dc 'A-HJ-NP-Za-km-z2-9!$%&()*+,-.:;<=>?[]^_{}~' | head -c 16; echo)
 echo ${MM_PASSWORD}
ln --symbolic --verbose /opt/mattermost/bin/mmctl /usr/local/bin/
mmctl user create --email ${MM_EMAIL} --username ${MM_USERNAME} --password ${MM_PASSWORD} --system-admin
mmctl auth login http://${FQDN}:8065/
mmctl team create --name ${TEAM_NAME} --display-name ${DISPLAY_NAME}
```
管理ユーザーと、インポート先のチーム（Slackで言うワークスペース）を作成します。
<kbd><samp>`Connection name:`</samp></kbd> は空欄のままエンターを押下すれば大丈夫です。

### Mattermost ETL (mmetl) のダウンロード
https://github.com/mattermost/mmetl/releases/

```bash
curl --location -o mmetl.linux-amd64.tar.gz https://github.com/mattermost/mmetl/releases/download/v0.1.1/linux_amd64.tar.gz
tar --extract --file=mmetl.linux-amd64.tar.gz
mv --verbose mmetl /usr/local/bin/
```
:::note info
<kbd>`go get -u github.com/mattermost/mmetl`</kbd> でもインストールできますが、かなり時間が掛かります。何度も使うコマンドでもないためバイナリ版をダウンロードした方が早いでしょう。
:::

### 変換
```bash
mmetl transform slack --team ${TEAM_NAME} --file export-with-emails-and-attachments.zip --output mattermost_import.jsonl
dnf install zip
zip --recurse-paths mattermost-bulk-import.zip data mattermost_import.jsonl
```

:::note warn
最小構成では zip パッケージがインストールされていません。
:::

### インポート
```bash
mmctl import upload ./mattermost-bulk-import.zip
mmctl import list available
mmctl import process <IMPORT FILE NAME>
mmctl import job show <JOB ID> --json
```

:::note warn
`mmctl import upload` コマンド実行時に <samp><kbd>`Error: failed to create upload session: : Unable to upload file. File is too large.,`</kbd></samp> というエラーが表示された場合は、 config/config.json ファイルの `MaxFileSize` の値を増やして <kbd>`systemctl restart mattermost`</kbd> で再起動して再実行すると回避できます。 [^MaxFileSize]
:::

[^MaxFileSize]: <q>As you have probably noticed the config setting `FileSettings.MaxFileSize` still applies. You could temporarily bump it to allow for the import upload to succeed.</q> - <cite>[mmctl import upload fails with "Error: failed to create upload session: : Unable to upload file. File is too large." · Issue #19042 · mattermost/mattermost-server](https://github.com/mattermost/mattermost-server/issues/19042)</cite>

ちなみに今回仮想環境で一度試した限りでは、以下の点が期待と異なりうまくいきませんでした。
* チャンネル名がランダムな文字列になってしまった（チャンネルの説明文などは正しく取り込まれている）
* 画像をインポートしたはずなのに表示されない
* 各ユーザーのメールアドレスが反映されない