maintenanceサーバの構築

環境

環境 Ver.
OS 4.9.32-15.41.amzn1.x86_64
Apache 2.4

▼以下は既に適宜設定/構築されているものとする。

  • EC2起動/基本設定
  • 静的メンテナンスファイルとCSS/JSは “/var/www/html” 配下に配置。
  • “_Health_Check.html”を配置。
  • サーバーの基本設定Apacheの基本設定等が設定済み。
  • ELB HealthCheckも適宜設定済み。
$ cd /var/www/html
$ tree
.
├── _Health_Check.html
├── maintenance.html
└── resources

コンテンツの権限/所有者の変更

$ chown -R apache:apache /var/www/html
$ cd /var/www/
$ find html -type f -exec chmod 0644 {} \;
$ find resources -type d -exec chmod 0755 {} \;

503リダイレクト設定

/etc/httpd/conf.d/maintenance.conf

ErrorDocument 503 /maintenance.html
Alias /maintenance.html /var/www/html/maintenance.html
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} !^/resources/.*$
  RewriteCond %{REQUEST_URI} !^/maintenance.html$
  RewriteCond %{REQUEST_URI} !^/_Health_Check.html$
  RewriteRule ^.*$ /maintenance.html [R=503,L]
</IfModule>

サーバーへ反映する。

$ service httpd graceful

Apache基本設定

更新途中。

環境

  • Apache2.2, 2.4
  • AWS ELB

モジュールの確認

$ /usr/sbin/httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c

DSO(Dynamic Shared Object)

実行時にモジュールを組み込める。
“httpd.conf” ファイルで “LoadModule” ディレクティブを使い、モジュールを指定する。

KeepAlive

ELBヘルスチェックの設定

ルート「/」へのチェックは、毎回トップへアクセスし、動的処理が入るため、負荷が高い。
ライトにする為、”_health_check.html”等適当にファイルを用意し、ヘルスチェック先を変更する。

アクセスログに正常にスタンプされることを確認。

"GET /_health_check.html HTTP/1.1" 200

ELBのタイムアウト時間/ApacheのKeepAliveを調整

ELBのデフォルトタイムアウト時間は、”60秒”である。
ELBは、コネクション生成コスト削減の為、常設コネクション(セッション維持)を確立を生成し、60秒毎(デフォルト)に再生成している。
Apache2.4系では、”mod_reqtimeout”がデフォルトで有効になっており、デフォルト値は”header=20-40,MinRate=500 body=20,MinRate=500″である。
TCPコネクション接続後、”20-40秒”のうちにHTTPヘッダーの応答がないコネクションは、破棄を行う。
デフォルト値ではbodyは20秒、headerは20秒以内に受信できないときはタイムアウトとみなし、データ送信中は500Bytes受信するごとに1秒、タイムアウトまでの時間を延長し、最大30秒まで延長することが可能。

ELBで常設コネクション(セッション維持)を行っているのに、Apache側でタイムアウトすると、408エラーが起こってしまう。セッションが切れてしまう為、切断されたセッションを繋ぎ直す余計な処理も走ることになる。

ELBのタイムアウト設定を変更し、Apache側のタイムアウトは変更しない場合

“KeepAliveTimeout”を設定している環境であれば、”KeepAliveTimeout”>”ELBアイドルタイムアウト”とし、そうでなければ、”ELBアイドルタイムアウト”を”5秒-50秒”に設定するのがよい(Apache2.2、2.4系では、”KeepAliveTimeout”のデフォルト値が”5秒”であり、5秒後に接続を遮断しコネクション再接続のコスト削減とサーバ側リソースとの調整を行っている為、ELBの”アイドルタイムアウト”を”5秒”まで短くしても問題ない。)。

ELBのタイムアウト設定を変更せず、Apache側のタイムアウトを変更する場合

“KeepAliveTimeout”を設定している環境であれば、”KeepAliveTimeout”の値 > “ELBアイドルタイムアウト”の値 とする。
AWSのドキュメントでは、ApacheのTimeout値は120秒を推奨値としている。
例えば、ELBのアイドルタイムアウト値を”60秒”のままにする場合、以下のように設定する。

  • Apacheタイムアウト時間: 120sec
  • keepalive: 90sec

▼設定例
/path/to/httpd.conf

<VirtualHost *:80>
  ServerName   www.example.com
  DocumentRoot /var/www/html/
  # Enable TCP keepalive
  Timeout 120
  KeepAlive On
  KeepAliveTimeout 90
  MaxKeepAliveRequests 100
  LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" combined
</VirtualHost>

ELB配下のApacheアクセスログにClientIPを表示する(Apache2.2系)

ELB配下のApacheアクセスログは、デフォルトだとClientIPではなくELBのIPが表示されてしまう。

10.20.0.80 - - [05/Sep/2017:16:45:08 +0900] "GET /_health_check.html HTTP/1.1" 200 227 "-" "ELB-HealthChecker/2.0"
10.20.1.168 - - [05/Sep/2017:16:45:08 +0900] "GET /_health_check.html HTTP/1.1" 200 227 "-" "ELB-HealthChecker/2.0"
$ yum --enablerepo=epel install mod_extract_forwarded

/etc/httpd/conf.d/mod_extract_forwarded.conf

LoadModule extract_forwarded_module modules/mod_extract_forwarded.so
MEForder refuse,accept
MEFrefuse all
MEFaccept all

Apacheの再起動を行う。

$ service httpd graceful

ELB配下のApacheアクセスログにClientIPを表示する(Apache2.4系)

“mod_remoteip”モジュールが読み込まれていることを確認する。
/etc/httpd/conf.modules.d/00-base.conf

LoadModule remoteip_module modules/mod_remoteip.so
$ apachectl -M | grep remoteip
 remoteip_module (shared)

/path/to/httpd-vhosts.conf

<VirtualHost *:80>
    ServerName xxx.net
    # IP check through ELB
    RemoteIPHeader X-Forwarded-For
    RemoteIPTrustedProxy 10.20.0.80/32
    RemoteIPTrustedProxy 10.20.1.168/32
    DocumentRoot /var/www/vhosts/xxx/htdocs/yyy
    <Directory "/var/www/vhosts/xxx/htdocs/yyy">
        Require all granted
    </Directory>
</VirtualHost>

LogFormatを変更する。

<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    #
    #LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined # 追加
    #LogFormat "%h %l %u %t \"%r\" %>s %b" common
    LogFormat "%a %l %u %t \"%r\" %>s %b" common # 追加

access.logへ、ClientIPがスタンプされるようになる。

61.xxx.x.xxx - - [05/Sep/2017:17:16:01 +0900] "GET / HTTP/1.1" 200 65387 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"

Apacheの再起動を行う。

$ service httpd graceful

アクセス制限

全てのアクセスを許可

2.4系

<Directory "/admin">
  Require all granted
</Directory>

2.2系

<Directory "/admin">
  Order allow,deny
  Allow from all
</Directory>

全てのアクセスを拒否

2.4系

<Directory "/admin">
  Require all denied
</Directory>

2.2系

<Directory "/admin">
  Order allow,deny
  Deny from all
</Directory>

特定のIPのみ許可

2.4系

<Directory "/admin">
  Require ip xxx.xxx.xxx.xxx
</Directory>

2.2系

<Directory "/admin">
  Order deny,allow
  Deny from all
  Allow from xxx.xxx.xxx.xxx
</Directory>

IPによるアクセス等、設定したServerName以外でのアクセスには “403 Forbidden” を返す。

セキュリティの向上

“Trace method” を無効にする。
XSSの脆弱性がある場合に、APサーバー側でTrace methodが有効になっていると、”クロスサイトトレーシング”という脆弱性が発生する可能性があり、暗号化されていないBasic認証のID/パスワード/Cookieが漏洩する可能性がある。

/etc/httpd/conf/httpd.conf

TraceEnable off

反映を確認する。
“405 Method Not Allowed” と出力されていることを確認する。

$ curl -I https://{YOUR_DOMAIN}
HTTP/1.1 405 Method Not Allowed
Allow: GET
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: text/plain
Date: Mon, 25 Sep 2017 04:36:35 GMT
Expires: 0
Pragma: no-cache
Server: Apache-Coyote/1.1
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Connection: keep-alive

welcomeページを非表示に。

$ cd /etc/httpd/conf.d/
$ mv welcome.conf welcome.conf.org

Apache Bench

Webサーバー公開前に、サーバーがどの程度のアクセス負荷に耐えられるかを事前に簡易確認しておく。
対象URLは1つのみ指定可能である。
例えば、ApacheBenchで同時10リクエスト/トータル400リクエスト/各クライアントは40リクエストを送信し、レスポンスを確認する、といった検証が可能。

環境

環境 Ver.
ApacheBench 2.3

オプション

オプション 説明
-n リクエスト数
-c 同時リクエスト数
-t レスポンスの待時間(秒)
-p サーバへ送信するファイル名
-w 結果をHTML出力
-x tableタグへ属性追加}(BORDERなど)
-y trタグへ属性追加
-z td, thタグへ属性追加
-C {Cookie名}={値}
-A {Basic認証ユーザー名}:{パスワード}
-P {プロキシ認証ユーザー名}:{パスワード}
-X {プロキシサーバ名}:{ポート番号}
-k KeepAliveを有効に

結果

項目 説明
Time taken for tests 経過時間
Complete requests リクエスト成功数
Failed requests リクエスト失敗数
Non-2xx responses レスポンスステータスコードが200以外の総数
Total transferred 送受信Byte数
HTML transferred HTML送受信Byte数
Requests per second 1秒当たりの平均処理リクエスト
Time per request 1リクエスト当たりの平均処理時間
Transfer rate 1秒当たりの受信Byte数
Connection Times 最小値(min)/平均値と標準偏差(mean[+/-sd])/中央値(median)/最大値(max)
Percentage of the requests served within a certain time 時間内に処理されたリクエストの割合。{リクエスト進捗}% {処理時間(秒)}
$ ab -n 400 -c 10 -w -t 10 https://xxx.com/xxx.html > ./test-ab.html
$ open test-ab.html

▼実行結果(ブラウザ)

Jenkinsサーバの構築

環境

環境 Ver.
Jenkins 2.58
Java 1.8

サーバー基本設定

Timezoneの変更

# 現在のTimezoneの確認 ※現在はUTC
$ strings /etc/localtimeTZif2TZif2UTC0

# オリジナルをバックアップ
$ cp /etc/localtime /etc/localtime.org

# Timezoneファイルの変更1
$ ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# Timezoneファイルの変更2
$ cp /etc/sysconfig/clock /etc/sysconfig/clock.org
$ vi /etc/sysconfig/clock
---
# ZONEの変更
ZONE="Asia/Tokyo"// falseにするとハードウェアのクロックもローカルタイムで設定されてしまう為、変更しない
UTC=true
---
# Timezoneが変更されていることを確認
strings /etc/localtime
TZif2
TZif2
JST-9

Swapの追加

$ dd if=/dev/zero of=/swapfile bs=1M count=1000
$ mkswap /swapfile
$ swapon /swapfile
swapon: /swapfile: 安全でない権限 0644 を持ちます。 0600 がお勧めです。
$ chmod 0600 /swapfile

Hostnameの設定

/etc/sysconfig/network

HOSTNAME={YOUR_HOSTNAME}

/etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4 {YOUR_HOSTNAME}

文字コードの変更

/etc/sysconfig/i18n

LANG="ja_JP.UTF-8"

各種ツールのインストール

Gitのインストール

$ yum install git

Javaのインストール

# Javaのバージョン確認
$ java -version
java version "1.7.0_131"OpenJDK Runtime Environment (amzn-2.6.9.0.71.amzn1-x86_64 u131-b00)
OpenJDK 64-Bit Server VM (build 24.131-b00, mixed mode)

# Java1.8のインストール ※2017年5月現在、Java1.8でなければJenkins2.58が動作しない。
$ yum install java-1.8.0-openjdk-devel
Installed:
java-1.8.0-openjdk-devel.x86_64 1:1.8.0.121-0.b13.29.amzn1

# 既存のJavaからインストールしたJavaに向き先を変更する
$ alternatives --config java
$ java -versionopenjdk version "1.8.0_121"

EB CLIのインストール (デプロイ例として)

$ pip install --upgrade --user awsebcli
$ vi .bash_profile
---
//追加
export PATH=""~/.local/bin"":$PATH
---
$ source .bash_profile$ eb --version
EB CLI 3.10.1 (Python 2.7.1)

Jenkinsの設定

Jenkinsダウンロード

$ wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo

# パッケージ署名チェック用のキーインポート
$ rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key

# Jenkinsをインストール
$ yum install jenkins
Installing:jenkins noarch 2.58-1.1 jenkins 67 M
Transaction Summary

# ReverseProxy用にApacheのインストール
$ yum install httpd24

Jenkinsのポート変更

※デフォルトは8080

/etc/sysconfig/jenkins

JENKINS_PORT="8081"
$JENKINS_USER="root"

権限変更

Jenkins作業ディレクトリをroot権限へ変更する。

$ chown -R root:root /var/lib/jenkins
$ chown -R root:root /var/cache/jenkins
$ chown -R root:root /var/log/jenkins

# Jenkinsの再起動
$ service jenkins restart
$ ps -ef | grep jenkins

以降、Jenkins jobを全てroot権限で実行することとなる。

Digest認証の設定

# 認証可能なユーザの作成
$ htdigest -c /etc/httpd/conf/.htdigest Jenkins jenkins
Adding password for jenkins in realm Jenkins.
New password:
Re-type new password:

# Digest認証の反映を確認
$ less /etc/httpd/conf/.htdigest

ReverseProxyの設定

/etc/httpd/conf.d/jenkins.conf

# Canonicalise URLデコードを抑制
ProxyPass / http://localhost:8081/ nocanon
ProxyPassReverse / http://localhost:8081/
ProxyRequests Off
AllowEncodedSlashes NoDecode

<Proxy http://localhost:8081/*>
Order deny,allow
Allow from all
AuthType Digest
AuthName Jenkins
AuthUserFile /etc/httpd/conf/.htdigest
Require valid-user
</Proxy>
---

jenkins へ sudo の実行許可を付与

$ sudo visudo -f /etc/sudoers.d/cloud-init
$ sudo visudo
---
jenkins ALL=(ALL) NOPASSWD: ALL
---

設定の反映

# Apacheの再起動
$ service httpd restart

# Jenkinsの再起動
$ service jenkins restart

Jenkinsの起動

$ service jenkins start
# chkconfig コマンドを使用して、システムがブートするたびに Jenkinsが起動するように設定
$ chkconfig jenkins on

# 起動設定を確認
$ chkconfig --list jenkins

# ステータスを確認
$ service jenkins status

ブラウザから http://{public DNS} へアクセスし、Jenkinsへアクセスできることを確認。

Webアプリケーションの配置

$ mkdir {project_name}
$ git init
$ git add -A
$ git config user.name "xxx"
$ git config user.email "xxx@xxx"
$ git commit -m 'first commit'
$ git remote add origin https://github.com/xxx/xxx.git
$ git remote -v
origin https://github.com/xxx/xxx.git (fetch)
origin https://github.com/xxx/xxx.git (push)
$ git push -u origin develop
# もしくはclone$ git clone https://github.com/xxx/yyy

shell実行時、Gitのユーザー名/パスワードの入力を省略させるため以下を一旦、設定している。

~/.netrc

machine github.com
login {user_name}
password xxx

shellの設定

デプロイスクリプト 等。

Jenkinsファイルパス

  • 初期ログインパスワード記載ファイル: /var/lib/jenkins/secrets/initialAdminPassword
  • Jenkinsの環境設定ファイル: /etc/sysconfig/jenkins
  • Jenkins作業ディレクトリ: /var/lib/jenkins/*
  • Jenkinsのジョブ定義ファイル: /var/lib/jenkins/config.xml
  • Job格納ディレクトリ: /var/lib/jenkins/jobs
  • ビルド格納ディレクトリ: /var/lib/jenkins/jobs/{job}/builds/
  • ワークスペースディレクトリ: /var/lib/jenkins/workspace/
  • 起動スクリプト: /etc/init.d/jenkins
  • ログ: /var/log/jenkins/jenkins.log

Jenkins コマンド

# Jenkinsの起動
$ service jenkins start

# ステータスを確認
$ service jenkins status

# 起動確認
$ ps -elf | grep [j]enkins

Jenkins 再起動

“/safeRestart” へアクセスする。

Jenkinsのshell

Jenkinsの「シェルの実行」は、ビルド時に以下のオプション付きで実行される。

/bin/sh -xe
  • -x: スクリプト実行結果を表示。
  • -e: 実行後、exit codeが0以外の場合(エラー発生時)、終了させる。

Jenkinsのディスク容量不足

主にビルド履歴やプラグインがメモリを食う。

$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         489M   56K  488M    1% /dev
tmpfs            497M     0  497M    0% /dev/shm
/dev/xvda1       7.8G  6.4G  1.4G   83% /

$ sudo du -sh /var/lib/jenkins/* | sort -h -r | head -20

ジョブ設定で、「古いビルドの破棄」オプションは30日以上、100ビルドまで保存等、適宜設定/保存する。

トラブルシューティング

ログ

“/log/all”の画面へアクセスする。

ログには何も出ていない場合は、以下のようにプロセスを停止し、フォアグラウンドでJenkinsを実行する。

$ service stop jenkins
$ java -jar /usr/lib/jenkins/jenkins.war

ジョブが表示されない。

Jenkinsの管理 > 設定の再読み込み を実行する。

“リバースプロキシの設定がおかしいようです。”と表示される。

  • URLエンコードの設定ミス
  • Jenkinsの設定ミス
    • Jenkinsの設定 > システムの管理 > Jenkins URL

プラグインのアンインストールが出来ない

Jenkins自体を再起動しないと、アンインストールが行われない。

参考サイト

関連記事

ReverseProxy配下でDNSアクセス制御をかけると、Apacheが403となる

他ドメインのDNSアクセス制御

他ドメインのDNSをアクセス制御する場合、下記を設定する。
VirtualHostを設定している場合は、上記設定をしないと他ドメインのネームサーバーに同じドメインが指定された場合、他ドメインが同じサーバを向き、ドメイン以外の全てが同じ表示となってしまう。
Googleのペナルティを受ける可能性もある。

/etc/httpd/conf/httpd.conf

<VirtualHost *:80>
     ServerName dummy
     <Location />
        Order deny,allow
        Deny from all
     </Location>
</VirtualHost>

アクセス制御によるエラー

/var/log/httpd/error_log

client denied by server configuration: /var/www/dvh/xxx, referer: http://yyy.com
  • Aサーバ: http://example.com
  • Bサーバ: http://example.com/xxx

上記のようにプロキシサーバを経由し、ディレクトリでサーバ間を振り分けている場合、/xxx側ではApacheがエラーとなり403 forbidden となる。
この場合は、/xxx側のサーバでは、DNSアクセス制御は不要である。