GitBucket の運用サーバーを HTTPS 対応で建てたいという動機からスタート。Tomcat と Apache のような組み合わせではなく、もっとライトに建てたかったので、GitBucket のビルトインサーバーでも採用されている Jetty を試した。
HTTP サーバー兼 Java サーブレットを一つで提供してくれて、先進機能も豊富、設定項目の自由度、ポータビリティと言った利点に惹かれた。
注意: だいたい設定できたけどデーモン化して起動しようとすると java.lang.NoClassDefFoundError
というエラーで失敗する問題が未解決です。助けて。
tl;dr
サーバーの構成は次のように定義した:
環境変数 | 値 | 説明 |
---|---|---|
JETTY_HOME | /opt/jetty |
Jetty 実行ディレクトリのパス。 |
JETTY_BASE | /var/jetty |
Jetty データディレクトリのパス。 |
JETTY_USER | jetty |
Jetty を実行する Linux ユーザー名。 |
GITBUCKET_HOME | /var/gitbucket |
GitBucket データディレクトリのパス。 |
N/A | /opt/gitbucket |
GitBucket 実行ディレクトリのパス。 |
- 上記ディレクトリは
jetty
ユーザーの所有とした。 -
JETTY_HOME
は実ディレクトリ/opt/jetty-distribution-9.3.7.v20160115
のシンボリックリンクとした。 - この他 GitBucket の実行ファイルパスは
/opt/gitbucket/gitbucket.war
としている。
Amazon Linux
セットアップ
Security Group で 80 と 443 ポートを開放する。
iptables
で Jetty 標準の HTTP ポート 8080
と HTTPS ポート 8443
をポートフォワーディングして、80
と 443
に設定する:
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
sudo service iptables save
-
80
と443
に Jetty のポートを変更するには実行ユーザーをroot
にする必要がある。それを避けるためにポートフォワーディングを使用する。1
Jetty の実行ユーザー jetty
を作成する:
sudo adduser \
--comment 'User running Jetty server' \
--home /home/jetty \
--create-home \
--system \
--shell /bin/bash \
--user-group \
jetty
Java
インストール
Amazon Linux は OpenJDK を yum
コマンドからインストールできる。しかし、HTTPS 暗号化通信のアルゴリズムの一部が実装されていないとかで、Oracle 純正 JDK をダウンロードして使用する。
現状最新の jdk-8u74-linux-x64.rpm
をダウンロードしてインストールする:
curl -L \
http://download.oracle.com/otn-pub/java/jdk/8u74-b02/jdk-8u74-linux-x64.rpm \
| sudo rpm -Uvh -
alternatives
コマンドでデフォルトの Java 環境をインストールしたものに変更する:
sudo alternatives --config java
There is 3 program that provides 'java'.
Selection Command
-----------------------------------------------
1 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
2 /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java
*+ 3 /usr/java/jdk1.8.0_74/jre/bin/java
Enter to keep the current selection[+], or type selection number: 3
GitBucket
インストール
GitBucket は手動インストールのみ。
最新の gitbucket.war
をダウンロードして設置する:
# /opt/gitbucket 以下にダウンロード
sudo mkdir -p /opt/gitbucket
cd /opt/gitbucket
sudo curl -LO https://github.com/gitbucket/gitbucket/releases/download/3.12/gitbucket.war
# 所有者は jetty ユーザーに
sudo chown jetty:jetty -R /opt/gitbucket
GITBUCKET_HOME
として /var/gitbucket
ディレクトリを作成する:
# GITBUCKET_HOME ディレクトリを作成
sudo mkdir -p /var/gitbucket
# 所有者は jetty ユーザーに
sudo chown jetty:jetty -R /var/gitbucket
Jetty
インストール
Amazon Linux は yum
コマンドで Jetty のインストールに対応していないので、手動で導入する。
現状最新の jetty-distribution-9.3.7.v20160115.tar.gz
をダウンロードしてインストールする:
# ダウンロードした書庫を /opt 以下に展開
curl -L \
http://download.eclipse.org/jetty/9.3.7.v20160115/dist/jetty-distribution-9.3.7.v20160115.tar.gz \
| sudo tar zxvf - -C /opt
# /opt/jetty としてシンボリック化
ln -s /opt/jetty-distribution-9.3.7.v20160115 /opt/jetty
/opt/jetty-distribution-9.3.7.v20160115
(/opt/jetty
) 以下は jetty
ユーザーの所有に変更する:
sudo chown jetty:jetty -R /opt/jetty-distribution-9.3.7.v20160115
前述の Java 環境 jdk-8u74
に対応する Jetty ライブラリ alpn-1.8.0_74.mod
は、上記パッケージには含まれていない。なので別途ダウンロードして、然るべきパスに配置する:
# jetty ユーザーで作業
sudo su - jetty
# /opt/jetty/modules/alpn-impl 以下に alpn-1.8.0_74.mod をダウンロード
cd /opt/jetty/modules/alpn-impl
curl -LO https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-start/src/test/resources/dist-home/modules/alpn-impl/alpn-1.8.0_74.mod
セットアップ
JETTY_BASE
となるディレクトリを作成し、必要なモジュールをインストールする:
# jetty ユーザーで作業
sudo su - jetty
# JETTY_BASE ディレクトリを作成
mkdir -p /var/jetty
# JETTY_BASE ディレクトリにモジュールをインストール
cd /var/jetty
java -jar /opt/jetty/start.jar \
--add-to-start=deploy,http,https,http2,logging
作成された start.ini
に次の設定を追加し、HTTP 通信でアクセスがあった時のリダイレクト先 HTTPS 通信のポート番号を指定する2:
# jetty ユーザーで作業
sudo su - jetty
# start.ini に設定項目を追加
vi /var/jetty/start.ini
229,235d228
< # HTTP 通信でアクセスがあった時のリダイレクト先 HTTPS 通信のポート番号
< # http://www.eclipse.org/jetty/documentation/current/configuring-ssl.html#d0e4938
< jetty.httpConfig.securePort=443
<
GitBucket 用のセットアップ
JETTY_BASE
の webapps
ディレクトリ下に gitbucket.xml
を作成し;
- サーバールートで GitBucket を動作させる設定と
- HTTPS 通信にリダイレクトする設定3
を定義する:
# jetty ユーザーで作業
sudo su - jetty
# gitbucket.xml を作成
touch /var/jetty/webapps/gitbucket.xml
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<!-- ルートパスで GitBucket を動作させる -->
<Set name="contextPath">/</Set>
<Set name="war">/opt/gitbucket/gitbucket.war</Set>
<!-- HTTPS 通信を宣言する -->
<!-- https://wiki.eclipse.org/Jetty/Howto/Configure_SSL#Redirecting_http_requests_to_https -->
<Set name="securityHandler">
<New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
<Call name="addConstraintMapping">
<Arg>
<New class="org.eclipse.jetty.security.ConstraintMapping">
<Set name="pathSpec">/*</Set>
<Set name="constraint">
<New class="org.eclipse.jetty.util.security.Constraint">
<!-- 2 means CONFIDENTIAL. 1 means INTEGRITY -->
<Set name="dataConstraint">2</Set>
</New>
</Set>
</New>
</Arg>
</Call>
</New>
</Set>
</Configure>
-
gitbucket.war
をwebapps
内に設置しなくてもgitbucket.xml
に war ファイルのパスが設定されてれば問題ないみたいだ。
デーモン化
デーモン化するため /etc/default/jetty
を作成し、必要な環境変数を定義する:
sudo touch /etc/default/jetty
JETTY_HOME=/opt/jetty
JETTY_BASE=/var/jetty
JETTY_USER=jetty
GITBUCKET_HOME=/var/gitbucket
デーモン化用スクリプトは Jetty のパッケージに含まれているので、これをコピーして配置する:
sudo cp /opt/jetty/bin/jetty.sh /etc/init.d/jetty
サーバー起動時にも自動起動するよう設定する:
sudo chkconfig --add /etc/init.d/jetty
sudo chkconfig jetty on
自動起動が設定されているのを確認する:
sudo chkconfig --list jetty
jetty 0:off 1:off 2:on 3:on 4:on 5:on 6:off
デーモンを起動する:
sudo service jetty start
ここまでやったが、デーモン起動するとエラーで終了してしまう...
Starting Jetty: StartLog to /var/jetty/logs/start.log
2016-03-15 00:00:00.986:INFO::main: Logging initialized @142ms
2016-03-15 00:00:01.086:INFO::main: Redirecting stderr/stdout to /home/jetty/logs/2016_03_15.stderrout.log
FAILED Tue Mar 15 00:00:04 UTC 2016
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jetty/alpn/ALPN$ServerProvider
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructors(Class.java:1651)
at org.eclipse.jetty.util.TypeUtil.construct(TypeUtil.java:567)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.newObj(XmlConfiguration.java:787)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.itemValue(XmlConfiguration.java:1233)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.value(XmlConfiguration.java:1138)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.access$500(XmlConfiguration.java:274)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration$AttrOrElementNode.getList(XmlConfiguration.java:1366)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration$AttrOrElementNode.getList(XmlConfiguration.java:1341)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.call(XmlConfiguration.java:704)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:417)
at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:358)
at org.eclipse.jetty.xml.XmlConfiguration.configure(XmlConfiguration.java:259)
at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1498)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:1435)
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.alpn.ALPN$ServerProvider
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 29 more
とり急ぎ nohup
でバックグラウンド実行してお茶を濁している:
sudo su - jetty
cd /var/jetty
GITBUCKET_HOME=/var/gitbucket nohup java -jar /opt/jetty/start.jar 2>&1 | logger &
参考
- Jetty 9.3でHTTP/2対応Webサーバを作ってみよう - Qiita
- Jetty上にAD連携したgitbucketを構築する - Qiita
- Jettyのインストール(EC2 ver9.1) - Qiita
TODO
- デーモンで起動するとエラーで失敗する問題の解決
HTTP 通信を HTTPS 通信に自動リダイレクトさせる設定- SSL 証明書の発行と設定
- cloud-init 化