CentOS7+Apacheサーバに GitLab + Mattermost をインストールする

2017.11.9 Update:  
GitLab 10.1 + Mattermost 4.2 でうまく動かない部分があったため手順を見直しました。本文およびスクリーンショットは GitLab 9.0 + Mattermost 3.7 のもので現行とは少し異なる場合があります。

---

## はじめに

会社の担当内ツールサーバーに GitLab および Mattermost を導入した際の備忘録です。

むかし Redmine のセットアップに挫折し、すっかり Rails (Ruby) 恐怖症になり、ファイルを置くだけで動くPHPの偉大さに毎日手を合わて拝む日々でしたが、最近はパッケージが整備され手軽に導入できるようになりつつあるようです。

GitLab も例に漏れず[オムニバス版](https://about.gitlab.com/downloads/)とよばれるオールインワンパッケージが提供されるようになったため、今回勇気を出してインストールを試みました。

同パッケージは、Nginx、PostgreSQL、Redis が自動でインストール＆セットアップされますが、サーバーにはすでにApacheが動いているので、バンドルの Nginx は使用せず、既存の Apache をリバースプロキシにして使います。





## 目的・目標

* 最新の GitLab のインストール手順を確認したい（ググると旧バージョン向けの古い情報が混ざっていて戸惑ったので整理したい）
* Proxy環境下でも正常にインストール・運用できるか検証したい
* すでに Apache が動いているサーバーにリバプロを導入して複数のWebアプリを共存させたい
* ドメインネームを使って `http://10.0.0.1:8000/` みたいなIPアドレス＋ポート番号のカッコ悪いURLをやめたい
* いろいろと便利になるし、アドレスバーがグリーンになって気持ちよいので常時SSLにしたい





## 条件

* OS: CentOS 7.3
* HTTPサーバ: Apache 2.4.20
* サーバーは社内の閉域網に接続されておりインターネットへの接続は不可。インストール時など、どうしても必要な場合は一時的にプロキシサーバー経由でゴニョゴニョする

また今回の説明で用いるドメインネーム(架空)は次の通り。

* インストール先サーバーのFQDN: `centos.ipnoc.net`
* GitLab の公開URL: `https://gitlab.ipnoc.net/`
* Mattermost の公開URL: `https://mattermost.ipnoc.net/`
* インターネット接続用Proxy: `http://proxy.ipnoc.net:3128/`





## IUSリポジトリ インストール

CentOS 標準のリポジトリでは、なにかと古いバージョンが入ってしまうため、[IUS: Inline with Upstream Stable](https://ius.io) というサードパーティーのリポジトリをインストールし使います。

`yum install -y https://centos7.iuscommunity.org/ius-release.rpm`

古いのが嫌なら Fedora か Ubuntu の最新版使えよ、というツッコミはその通りだと思います。




## Git クライアントインストール

標準リポジトリでは Git 1.8 という遙か古のバージョンが入ってしまうので、さっそく IUS を利用します。標準のものと衝突しないように "git" ではなく "git2u" という名前で登録されていますので、`yum install -y git2u` でインストールします。

```
# 2017.3時点
$ git --version
git version 2.12.0
```



## オレオレ証明書発行

前段階として HTTPS のための証明書を準備します。ナウなヤングにとって SSL と言えば Let's Encrypt ですが、閉鎖網だとインターネットへリーチャビリティーも無ければグローバルアドレスもありません。そこでみんな大好きオレオレ証明書を使います。

[オレオレ認証局とオレオレ証明書](http://qiita.com/softark/items/15a5280bd38c5dd97b48)

上記エントリを参考にオレオレ認証局(CA) および、オレオレ証明書を作ります。

* オレオレCA証明書(cacert.pem)をクライアント端末に「信頼するルート証明機関」の証明書としてインポート
* オレオレCA証明書(cacert.pem)を `/usr/share/pki/ca-trust-source/anchors/` へコピーし、`update-ca-trust` コマンドを実行
* 各秘密鍵ファイル(*.key) は `chmod 600 *.key` で root 以外で開けないようにしておく






## Apache セットアップ

### Apache インストール

標準だと Apache 2.4.6 という微妙に古いものが入ってしまうので、IUS を使って最新版をインストールします。

1. `yum install -y httpd24u httpd24u-mod_ssl httpd24u-mod_proxy_html` でインストール
2. `systemctl enable httpd` で自動起動設定
3. `systemctl start httpd` で起動
4. `firewall-cmd --permanent --add-service=http --add-service=https` でファイアウォール設定
5. `systemctl reload firewalld` ファイアウォール設定有効化
6. 外からWebブラウザで本ホストにアクセスし、"It Works!" が表示されることを確認


### SSLセットアップ

`/etc/http/conf.d/ssl.conf` を編集します

```/etc/http/conf.d/ssl.conf
Listen 443 https

<VirtualHost _default_:443>
  ServerName centos.ipnoc.net:443 #サーバーのFQDNを設定
  SSLEngine on
  SSLProtocol all -SSLv2 -SSLv3 -TLSv1 #古いプロトコルはしまっちゃおうね〜
  SSLCipherSuite HIGH:!ADH:!SSLv2:!MD5:!3DES:!DSS:!PSK #古いプロトコルはしまっちゃおうね〜
  SSLCertificateFile /etc/pki/tls/certs/centos.ipnoc.net.crt #サーバー証明書
  SSLCertificateKeyFile /etc/pki/tls/private/centos.ipnoc.net.key #サーバー秘密鍵
  SSLCACertificateFile /etc/pki/tls/certs/IPNOC-CA.crt #CA証明書
</VirtualHost>
```

終わったら、`systemctl restart httpd` でサーバーを再起動し、Webブラウザでアクセスしてください。

![オレオレ証明書によるHTTPS](https://qiita-image-store.s3.amazonaws.com/0/72336/73260c0f-2bb4-7fb8-da94-e1d487ce5329.png "オレオレ証明書によるHTTPS")

~~なお Safari, Firefox ではOKだったのですが、Chrome では NET::ERR_CERT_COMMON_NAME_INVALID というエラーが出てしまいました。いや、FQDNあってるじゃん･･･。謎です。~~ ← SubjectAltName(SAN) の設定が漏れていたためでした。CommonName(CN) だけじゃダメゼッタイ。


### 設定ファイル置き場作成

既存の設定ファイルに継ぎ足していくと、設定が散らばり、どこに書いたかわからなくなったり、読み込まれる順番によって意図しない誤動作を起こす場合があります。（特に VirtualHost は最初に読まれる設定がデフォルト設定になるためトラブルになりやすい）

うちでは独立したディレクトリを作り、そこにツールごとに設定ファイルを作って httpd.conf で読み込むようにしています。

1. `mkdir /etc/httpd/conf.user.d`
2. /etc/httpd/conf/httpd.conf の末端に `IncludeOptional conf.user.d/*.conf` を追加







## GitLab CE Omnibus セットアップ

### GitLab Omnibus インストール

[公式ドキュメント](https://about.gitlab.com/installation/#centos-7?version=ce)を参考にします。

1. `yum install -y curl policycoreutils openssh-server openssh-clients` で依存パッケージをインストール(すでに入ってると思いますが)
2. `curl -x proxy.ipnoc.net:3128 -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash` でGitLab公式リポジトリをインストール
3. `EXTERNAL_URL="https://gitlab.ipnoc.net" yum install -y gitlab-ce` で GitLab CE Omnibus をインストール


### オレオレCA証明書インストール

さきほど作成したオレオレ認証局の証明書を `/etc/gitlab/trusted-certs/` へコピー


### 設定ファイル編集

`/etc/gitlab/gitlab.rb` を編集

```/etc/gitlab/gitlab.rb
external_url 'https://gitlab.ipnoc.net' #外から見たGitLabのURL

gitlab_rails['time_zone'] = 'Asia/Tokyo' #JSTにする

gitlab_workhorse['listen_network'] = "tcp" #デフォルトはSocket接続だがリバプロできないのでTCPに変更
gitlab_workhorse['listen_umask'] = 000
gitlab_workhorse['listen_addr'] = "127.0.0.1:8181"
gitlab_workhorse['auth_backend'] = "http://127.0.0.1:8080"

nginx['enable'] = false #Apacheを使うので Nginx はOFF
```

編集が終わったら `gitlab-ctl reconfigure` && `gitlab-ctl restart` で設定を反映。

また、`gitlab-rake gitlab:check` を実行し怒られないかチェック。


### Apache リバースプロキシ設定

`/etc/httpd/conf.d/gitlab.conf` を編集

[GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/)の [gitlab-omnibus-ssl-apache24.conf](https://gitlab.com/gitlab-org/gitlab-recipes/blob/master/web-server/apache/gitlab-omnibus-ssl-apache24.conf) をコピペ。

* `YOUR_SERVER_FQDN` をよしなに書き換え
* `SSLCertificateFile` にサーバーの公開鍵ファイルを指定
* `SSLCertificateKeyFile` にサーバーの秘密鍵ファイルを指定
* `SSLCACertificateFile` にオレオレCAの公開鍵ファイルを指定
* `/var/log/httpd/logs/` というパスは存在しないので、該当のディレクトリを作成するか、設定を `/var/log/httpd/` に修正


編集が終わったら `systemctl restart httpd` で設定を反映


### 動作確認(Web)

![GitLab初期画面](https://qiita-image-store.s3.amazonaws.com/0/72336/5ec8f0ef-92b0-7f08-9b18-ecd72b6b0ab4.png "GitLab初期画面")

OKですね。


### 動作確認(Git)

適当にグループ・プロジェクトを作って、まずはHTTPS経由でアクセスします。

```
$ git clone https://gitlab.ipnoc.net/ipnoc/test-project.git
Cloning into 'test-project'...
Username for 'https://gitlab.ipnoc.net': miyahan
Password for 'https://miyahan@gitlab.ipnoc.net': 
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
```

つぎに SSH 経由のアクセスを試します。

1. `ssh-keygen -t rsa -C <メールアドレス>` で鍵生成 (毎回入力するのが面倒なので passphrase は空にすることをおすすめ)
2. `~/.ssh/config` を下記内容で作成しつつ、パーミッションを 600 に変更
3. GitLab ＞ User Settings ＞ SSH Keys で公開鍵(id_rsa.pub）の中身を貼り付けて登録
4. リモートリポジトリへアクセス: `git clone git@gitlab.ipnoc.net:ipnoc/test-project.git`

```~/.ssh/config`
Host gitlab.ipnoc.net
  User git
  Port 22
  HostName gitlab.ipnoc.net
  TCPKeepAlive yes
  identitiesonly yes
  identityFile ~/.ssh/id_rsa
```

```
$ git clone git@gitlab.ipnoc.net:ipnoc/test-project.git
Cloning into 'test-project'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.

$ echo "Hello World\!" > README.md

$ git add README.md

$ git commit -m "first commit"
[master f85316b] first commit
 1 file changed, 1 insertion(+), 4 deletions(-)

$ git push -u origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 276 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To gitlab.ipnoc.net:ipnoc/test-project.git
   ed9904d..f85316b  master -> master
Branch master set up to track remote branch master from origin.
```

SSH/公開鍵認証方式でもアクセスできましたね。ちなみにWebブラウザで GitLab のWebページは開けるのに、git コマンドでのリポジトリへのアクセスができない場合は、Gitlab Workhorse が正しく動作していない可能性があります。



## Mattermost セットアップ

GitLab には Slack ライクなチャットソフトウェア: Mattermost が同梱されています。Slackクローンとして大変出来が良く、GitLab との相性も抜群とくれば使わない手はないでしょう。




### 本体インストール

GitLab CE Omnibus に同梱されているので追加の作業は要りません。


### GitLab APIキー払い出し

今回 Mattermost は自身でのユーザー登録・認証はさせず、GitLab OAuth のみを許可します。GitLab に管理者アカウントでログインし、Admin Area(右上のスパナアイコン) ＞ Applications で New Application を追加してください。

![GitLab Applicaitons登録画面](https://qiita-image-store.s3.amazonaws.com/0/72336/70c3b932-f60f-8218-b71e-a267bd85fae9.png "GitLab Applicaitons登録画面")

Redirect URL には次の２つを登録します

* ＜Mattermost 公開URL＞/login/gitlab/complete
* ＜Mattermost 公開URL＞/signup/gitlab/complete

![GitLab Applicaitons登録画面](https://qiita-image-store.s3.amazonaws.com/0/72336/74a48e71-28b3-fc4c-9d01-7728c3be0fef.png "GitLab Applicaitons登録画面")


ID と Secret Key が生成されるのでメモしておきます。


### コンフィグファイル編集

また `/etc/gitlab/gitlab.rb` を編集します。

```/etc/gitlab/gitlab.rb
gitlab_rails['mattermost_host'] = "https://mattermost.ipnoc.net"

mattermost_external_url 'https://mattermost.ipnoc.net'

mattermost['service_use_ssl'] = false
mattermost['service_site_url'] = 'https://mattermost.ipnoc.net'

mattermost['gitlab_enable'] = true 
mattermost['gitlab_id'] = "c79de97835280339d3202cb7f46042d7405ca94826ec5496542e40bdf26ef65d"
mattermost['gitlab_secret'] = "9b9b87e3db550361df18f56de1f0934e9564fc97b7b736bdec0292b831b406e3"
mattermost['gitlab_scope'] = ""
mattermost['gitlab_auth_endpoint'] = "https://gitlab.ipnoc.net/oauth/authorize"
mattermost['gitlab_token_endpoint'] = "https://gitlab.ipnoc.net/oauth/token"
mattermost['gitlab_user_api_endpoint'] = "https://gitlab.ipnoc.net/api/v3/user"

mattermost['email_enable_sign_up_with_email'] = false #GitLab の OAuth のみにする 
mattermost['email_enable_sign_in_with_email'] = false
mattermost['email_enable_sign_in_with_username'] = false
mattermost['email_send_email_notifications'] = true #設定しないとうるさいのでON
mattermost['email_require_email_verification'] = false
mattermost['email_send_push_notifications'] = false
mattermost['email_enable_batching'] = false

mattermost['localization_server_locale'] = "ja"
mattermost['localization_client_locale'] = "ja"
mattermost['localization_available_locales'] = "ja,en,es,fr,pt-BR"

mattermost_nginx['enable'] = false #Apacheを使うので Nginx はOFF
```

で、さきほどと同じく `gitlab-ctl reconfigure` && `gitlab-ctl restart` します。



### Apache 設定

VirtualHost および Reverse Proxy を設定します。VirtualHost は最初に読まれた設定がデフォルトになる仕様のため、いまから作る設定ファイルは、基本設定ファイル(`/etc/httpd/conf.d/ssl.conf`) より**後にインクルード**されるようにしてください。

```/etc/httpd/conf.user.d/mattermost.conf
<VirtualHost *:80>
  ServerName mattermost.ipnoc.net
  ServerAdmin admin@ipnoc.net
  ServerSignature Off

  RewriteEngine on
  RewriteCond %{HTTPS} !=on
  RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [NE,R,L]
</VirtualHost>

<VirtualHost _default_:443>
  ServerName mattermost.ipnoc.net:443
  ServerAdmin admin@ipnoc.net
  ServerSignature Off

  SSLEngine On
  SSLProtocol all -SSLv2 -SSLv3 -TLSv1
  SSLHonorCipherOrder on
  SSLCipherSuite HIGH:!ADH:!SSLv2:!MD5:!3DES:!DSS:!PSK
  Header add Strict-Transport-Security: "max-age=15768000;includeSubdomains"
  SSLCompression Off
  SSLCertificateFile /etc/pki/tls/certs/mattermost.ipnoc.net.crt
  SSLCertificateKeyFile /etc/pki/tls/private/mattermost.ipnoc.net.key
  SSLCACertificateFile /etc/pki/tls/certs/IPNOC-CA.crt

  # This setting is required to forwarding Websocket
  ProxyPreserveHost On

  # The following two lines fix the problem that page is reloaded multiple times whenever switching the channel
  RequestHeader unset If-Modified-Since
  RequestHeader unset If-None-Match

  RequestHeader set X-Forwarded-Proto 'https'
  RequestHeader set X-Forwarded-Ssl 'on'

  RewriteEngine On
  RewriteCond %{REQUEST_URI} ^/api/v3/users/websocket [NC,OR]
  RewriteCond %{HTTP:UPGRADE} ^WebSocket$             [NC,OR]
  RewriteCond %{HTTP:CONNECTION} ^Upgrade$            [NC]
  RewriteRule .* ws://127.0.0.1:8065%{REQUEST_URI}    [P,QSA,L]

  <Location />
    Require all granted
    ProxyPass http://127.0.0.1:8065/
    ProxyPassReverse http://127.0.0.1:8065/
    ProxyPassReverseCookieDomain 127.0.0.1 mattermost.ipnoc.net
  </Location>

  LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b" common_forwarded
  ErrorLog /var/log/httpd/mattermost.ipnoc.net_error.log
  CustomLog /var/log/httpd/mattermost.ipnoc.net_forwarded.log common_forwarded
  CustomLog /var/log/httpd/mattermost.ipnoc.net_access.log combined env=!dontlog
  CustomLog /var/log/httpd/mattermost.ipnoc.net.log combined
</VirtualHost>
```


### 動作確認

![Mattermost ログイン画面](https://qiita-image-store.s3.amazonaws.com/0/72336/cced2fb6-69c5-01dd-8188-e97c875fc26e.png "Mattermost ログイン画面")

"GitLab でログインする" というボタンが表示され、かつメールアドレスでのサインインフォームが表示されなければ成功です。

![Mattermost ログイン画面](https://qiita-image-store.s3.amazonaws.com/0/72336/fcaf5459-d8a5-cb97-09aa-17d9c7d2d95b.png "Mattermost ログイン画面")

さらに GitLab で認証を承認し、Mattermost にリダイレクトして戻ってくるかどうか確認してください。

ようこそ！あとは Hubot を住まわせるなり、GitLab で Webhook を設定するなりしてイケてるチャットライフをお楽しみください。





## トラブルシュート

### Mattermost がサブディレクトリで運用できない

実は当初、https://centos.ipnoc.net/gitlab/ および https://centos.ipnoc.net/mattermost/ といったようにサブディレクトリにデプロイしようとしていました。

GitLab は設定できたものの、Mattermost は gitlab_rails['mattermost_host'] = "https://miyadev.ipnoc.net/mattermost" としたところ reconfigure がエラーで止まってしまいました。どうやら Mattermost は(まだ?)**サブディレクトリに対応していない**ようです。結局、VirtualHost を使ったサブドメインに作戦変更しました。まあ後述の通り GitLab もトラブル続きでとても使えたものじゃなかったのでちょうどよかったのですが･･･。


### gitlab-ctl reconfigure 時にDBロック解放待ちが発生し続けてしまう

gitlab-ctl reconfigure した際に下記のようなDBロックエラーが出てしまうことがありました。

```
    [execute] createdb: database creation failed: ERROR:  source database "template1" is being accessed by other users
              DETAIL:  There is 1 other session using the database.
```

このときは、何回か `gitlab-ctl restart` してから再度実行したところ直りました。なにかのセッションが持ちきっていたんですかね？


### Error compling CSS asset エラーが出て何もできなくなる

当初GitLabをサブディレクトリ（https://centos.ipnoc.net/gitlab/）で運用しようと設定していたところ、どこにアクセスしても "Error compiling CSS assert" というエラー画面がでて何も動かなくなってしまいました。

```
$ gitlab-rake assets:precompile --trace
** Invoke assets:precompile (first_time)
** Invoke assets:environment (first_time)
** Execute assets:environment
** Invoke environment (first_time)
** Execute environment
** Execute assets:precompile
I, [2017-03-24T01:09:32.947634 #13329]  INFO -- : Writing /opt/gitlab/embedded/service/gitlab-rails/public/assets/katex-0f6b15d9e75a27a42599be41c6dd27deae957093901bb29c0254f5ce67dffb6b.css
I, [2017-03-24T01:09:32.948279 #13329]  INFO -- : Writing /opt/gitlab/embedded/service/gitlab-rails/public/assets/katex-0f6b15d9e75a27a42599be41c6dd27deae957093901bb29c0254f5ce67dffb6b.css.gz
rake aborted!
ExecJS::Error: ExecJS disabled
/opt/gitlab/embedded/lib/ruby/gems/2.3.0/gems/execjs-2.6.0/lib/execjs/disabled_runtime.rb:18:in `compile'
/opt/gitlab/embedded/lib/ruby/gems/2.3.0/gems/execjs-2.6.0/lib/execjs/module.rb:27:in `compile'
/opt/gitlab/embedded/lib/ruby/gems/2.3.0/gems/uglifier-2.7.2/lib/uglifier.rb:171:in `initialize'
```

トレースを見てみると "ExecJS::Error: ExecJS disabled" なるエラーが。ググってみると似たような issue: [Frontend broken after update to 8.17 in Scientific Linux 7.3](https://gitlab.com/gitlab-org/gitlab-ce/issues/28630) があり、そこに書かれているワークアラウンドを実行したところ、エラーがでなくなりました。

```
rm /opt/gitlab/etc/gitlab-rails/env/EXECJS_RUNTIME
chown -R git:git /opt/gitlab/embedded/service
gitlab-rake gitlab:assets:compile
chown -R git:git /opt/gitlab/embedded/service
gitlab-rake webpack:compile
chown -R git:git /opt/gitlab/embedded/service
gitlab-ctl reconfigure
gitlab-ctl restart
```

ただそれでもサイドメニューが開かない(必要なJSが生成されない)などフロントエンド周りに不具合が残っていてお手上げでした。。。どうやらサブディレクトリでの運用がトリガーになっているらしく、ルートディレクトリに変更して再セットアップしたところ不具合は治まりました。



## さいごに

いろいろとつまずき、完全に動くまでに2日かかってしまいましたが、総括すると「足を踏み外さなければ何も起きない。しかし･･･」という感触です。これはバージョンアップとか辛そう･･･。

まあともかく GitLab は立ったので、これからちゃんとソース・issue管理してツールつくるぞー！！