Linux
CentOS
pacemaker
drbd
RADIUS

シングル構成のRADIUSサーバを、HA環境に移行した時のメモ

はじめに

Wi-Fi の認証(802.1x)基盤として LinuxベースのRADIUSサーバをシングル構成で運用していますが、先日サーバが突然フリーズし、全社のWi-Fi端末が長時間使えなくなるという、なんとも切ない障害が発生しました。
原因はAWSサポート様曰く「ご連絡いただきました時間帯に、当該 EC2 インスタンスが稼働していた仮想サーバーホストにて、問題が生じていたことを確認しました。」とのことでした。要はインスタンスが乗っかっていたハードウェア側の障害でしょうね。

というわけで、社内ではWi-Fiを利用する端末も劇的に増えてきており、障害発生の度に「針のむしろ」という事態もできれば避けたいため、思い切ってRADIUSサーバをHA構成で再構築し移行させました。

本投稿は、その際書き留めておいたメモをまとめたものです。

現行サーバソフトウェア構成

Software Package & Version
OS Amazon Linux AMI release 2014.03
RADIUS freeradius-2.1.12, freeradius-utils-2.1.12
OpenSSL openssl-1.0.1k, openssl-perl-1.0.1k

新サーバソフトウェア構成

Software Package & Version
OS CentOS Linux release 7.4.1708 (Core)
DRBD kmod-drbd90-9.0.9-1, drbd90-utils-9.1.0-1, drbdmanage-0.99.14-1
Pacemaker pacemaker-1.1.16-12, pacemaker-libs-1.1.16-12, pacemaker-cluster-libs-1.1.16-12, pacemaker-cli-1.1.16-12, pcs-0.9.158-6
Corosync corosync-2.4.0-9, corosynclib-2.4.0-9
NFS nfs-utils-1.3.0-0.48
RADIUS freeradius-3.0.13-8, freeradius-utils-3.0.13-8

新サーバトポロジ

ポイント

  • Subnetを跨いでのVIPの付替えは、VPC側ルートテーブルの経路情報の変更も必要。
    ※後述のPacemakerリソースエージェント(RA)で実装
  • VIP(今回はPrivate IP)は、VPCに登録されているSubnetに含まれないプライベートIPアドレスを付与すること。
    但し、VPC側においてロンゲストマッチルールで解決できるなら、この限りではない。 Topology.png

前提条件

  • 各インスタンスは、下表のHostname、Private IPが設定済み
  • 各インスタンスでは、/etc/hostsに下表VIP以外の4ホストを追加済み
  • インスタンスnfs01nfs02では、インスタンス作成時に20GBのEBS(/dev/sdb)が追加でアタッチ済み volume.png
Hostname Private IP
rad01 10.0.1.11/24
rad02 10.0.2.11/24
nfs01 10.0.1.12/24
nfs02 10.0.2.12/24
(VIP) 192.168.254.254/32

NFSサーバ(nfs01, nfs02)構築

DRBD9

本体インストール

(nfs01, nfs02)$ wget http://ftp.kddilabs.jp/Linux/RPMS/elrepo/elrepo/el7/x86_64/RPMS/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
(nfs01, nfs02)$ sudo yum localinstall elrepo-release-7.0-3.el7.elrepo.noarch.rpm
(nfs01, nfs02)$ sudo yum update elrepo-release
(nfs01, nfs02)$ sudo sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/elrepo.repo
(nfs01, nfs02)$ sudo yum install --enablerepo=elrepo kmod-drbd90*

インストール確認

(nfs01, nfs02)$ sudo modprobe drbd
(nfs01, nfs02)$ lsmod | grep drbd
drbd                  524709  0
libcrc32c              12644  2 xfs,drbd

DRBD管理ツール(drbdmanage)インストール

(nfs01, nfs02)$ sudo yum install rpmdevtools git libxslt pygobject2 help2man
(nfs01, nfs02)$ git clone https://github.com/LINBIT/drbdmanage.git
(nfs01, nfs02)$ cd drbdmanage
(nfs01, nfs02)$ sed -ie 's/drbd-utils/drbd90-utils/g' setup.cfg
(nfs01, nfs02)$ make rpm
(nfs01, nfs02)$ ls dist
drbdmanage-0.99.14-1.noarch.rpm  drbdmanage-0.99.14.tar.gz
drbdmanage-0.99.14-1.src.rpm
(nfs01, nfs02)$ sudo yum localinstall dist/drbdmanage-0.99.14-1.noarch.rpm

ボリューム準備

LVM2インストール

(nfs01, nfs02)$ sudo yum install lvm2

ディスク、パーティション情報の確認

(nfs01, nfs02)$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  10G  0 disk
 -xvda1 202:1    0  10G  0 part /
xvdb    202:16   0  20G  0 disk

パーティション作成

(nfs01, nfs02)$ sudo fdisk /dev/xvdb
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table
Building a new DOS disklabel with disk identifier 0x3eafc292.

コマンド (m でヘルプ): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
パーティション番号 (1-4, default 1): 1
最初 sector (2048-41943039, 初期値 2048):
初期値 2048 を使います
Last sector, +sectors or +size{K,M,G} (2048-41943039, 初期値 41943039):
初期値 41943039 を使います
Partition 1 of type Linux and of size 20 GiB is set

コマンド (m でヘルプ): w
パーティションテーブルは変更されました!

ioctl() を呼び出してパーティションテーブルを再読込みします。
ディスクを同期しています。

パーティション作成後のディスク情報確認

(nfs01, nfs02)$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  10G  0 disk
 -xvda1 202:1    0  10G  0 part /
xvdb    202:16   0  20G  0 disk
 -xvdb1 202:17   0  20G  0 part

パーティションを物理ボリューム、ボリュームグループdrbdpool`に登録

(nfs01, nfs02)$ sudo pvcreate /dev/xvdb1
(nfs01, nfs02)$ sudo vgcreate drbdpool /dev/xvdb1

物理ボリュームの確認

(nfs01, nfs02)$ sudo pvscan
PV /dev/xvdb1   VG drbdpool        lvm2 [<20.00 GiB / <20.00 GiB free]
Total: 1 [<20.00 GiB] / in use: 1 [<20.00 GiB] / in no VG: 0 [0   ]

DRBD設定

初期化

(nfs01)$ sudo drbdmanage init 10.0.1.12

You are going to initialize a new drbdmanage cluster.
CAUTION! Note that:
  * Any previous drbdmanage cluster information may be removed
  * Any remaining resources managed by a previous drbdmanage installation
    that still exist on this system will no longer be managed by drbdmanage

Confirm:

  yes/no: yes
Empty drbdmanage control volume initialized on '/dev/drbd0'.
Empty drbdmanage control volume initialized on '/dev/drbd1'.
Waiting for server: .
Operation completed successfully

ノード確認

(nfs01)$ sudo drbdmanage list-nodes
+------------------------------------------------------------------------------+
| Name  | Pool Size | Pool Free |                                      | State |
|------------------------------------------------------------------------------|
| nfs01 |     20476 |     20468 |                                      |    ok |
+------------------------------------------------------------------------------+

セカンダリノード追加

(nfs01)$ sudo drbdmanage new-node nfs02 10.0.2.12
Operation completed successfully
Operation completed successfully
Host key verification failed.
Give leader time to contact the new node
Operation completed successfully
Operation completed successfully

Join command for node nfs02:
drbdmanage join -p 6999 10.0.2.12 1 nfs01 10.0.1.12 0 7ayQLI6J6J8ojrEXFt/F

ノードの確認

(nfs01)$ sudo drbdmanage list-nodes
+--------------------------------------------------------------------------------------------------+
| Name  | Pool Size | Pool Free |                                                            State |
|--------------------------------------------------------------------------------------------------|
| nfs01 |     20476 |     20468 |                                       online/quorum vote ignored |
| nfs02 |   unknown |   unknown | offline/quorum vote ignored, pending actions: adjust connections |
+--------------------------------------------------------------------------------------------------+

セカンダリノードからjoin

前述のプライマリノード側でセカンダリノードを追加した際、最下行に表示されたコマンド(drbdmanage join ~)で、DRBDクラスタに接続する。

(nfs02)$ sudo drbdmanage join -p 6999 10.0.2.12 1 nfs01 10.0.1.12 0 7ayQLI6J6J8ojrEXFt/F

ノード確認

(nfs01)$ sudo drbdmanage list-nodes
+------------------------------------------------------------------------------+
| Name  | Pool Size | Pool Free |                                      | State |
|------------------------------------------------------------------------------|
| nfs01 |     20476 |     20468 |                                      |    ok |
| nfs02 |     20476 |     20468 |                                      |    ok |
+------------------------------------------------------------------------------+

リソースr0登録

(nfs01)$ sudo drbdmanage add-resource r0
(nfs01)$ sudo drbdmanage list-volumes --show Port
+------------------------------------------------------------------------------+
| Name | Vol ID | Size | Minor | Port |                                | State |
|------------------------------------------------------------------------------|
| r0   |      * |    * |     * | 7000 |                                |     * |
+------------------------------------------------------------------------------+

ボリューム登録&全ノードにデプロイ

(nfs01)$ sudo drbdmanage new-volume r0 20GB --deploy 2
(nfs01)$ sudo drbdmanage list-volumes --show Port
+------------------------------------------------------------------------------+
| Name | Vol ID |      Size | Minor | Port |                           | State |
|------------------------------------------------------------------------------|
| r0   |      0 | 18.63 GiB |   100 | 7000 |                           |    ok |
+------------------------------------------------------------------------------+

ノード確認

両ノードでPool Freeのサイズが減っているか確認

(nfs01)$ sudo drbdmanage list-nodes
+------------------------------------------------------------------------------+
| Name  | Pool Size | Pool Free |                                      | State |
|------------------------------------------------------------------------------|
| nfs01 |     20476 |      1376 |                                      |    ok |
| nfs02 |     20476 |      1376 |                                      |    ok |
+------------------------------------------------------------------------------+

ノード間の同期確認

ボリュームのデプロイ直後からノード間の同期が開始され、debdadm statusコマンドで最下行のreplication:SyncSourceに、同期の進捗度がパーセント表示される。

(nfs01)$ sudo drbdadm status
.drbdctrl role:Primary
  volume:0 disk:UpToDate
  volume:1 disk:UpToDate
  nfs02 role:Secondary
    volume:0 peer-disk:UpToDate
    volume:1 peer-disk:UpToDate

r0 role:Secondary
  disk:UpToDate
  nfs02 role:Secondary
    replication:SyncSource peer-disk:Inconsistent done:37.11

ファイルシステム作成

(nfs01)$ sudo mkfs -t xfs /dev/drbd100

マウント

マウントした側のノードがプライマリリソースとなり、プライマリリソースのノードがマウント中は、他のノードからはマウントできない。

(nfs01)$ sudo mount /dev/drbd100 /opt

書込テスト

nfs01ノード側
(nfs01)$ sudo dd if=/dev/zero of=/opt/testfile bs=1M count=100
100+0 レコード入力
100+0 レコード出力
104857600 バイト (105 MB) コピーされました、 0.0587609 秒、 1.8 GB/秒
(nfs01)$ ls -l /opt/testfile
-rw-r--r-- 1 root root 104857600  1月 31 11:58 /opt/testfile
(nfs01)$ sudo umount /opt
nfs02ノード側

nfs01ノードで作成されたファイルが、nfs02側に同期されているか確認。

(nfs02)$ sudo mount /dev/drbd100 /opt
(nfs02)$ ls -l /opt/testfile
-rw-r--r-- 1 root root 104857600  1月 31 11:58 /opt/testfile
(nfs02)$ sudo umount /opt

DRBD Managerの自動起動設定

ノードブート時にdrbdmanagedが起動するよう設定

(nfs01, nfs02)$ sudo systemctl enable drbdmanaged
(参考)drbdmanagedを手動起動する方法

各ノード上で、次の何れかの方法で起動できる。

$ sudo drbdmanage startup
or
$ suro drbdmanage ping
or
$ systemctl start drbdmanaged

Pacemaker, Corosync

インストール、起動

(nfs01, nfs02)$ sudo yum install pacemaker corosync pcs
(nfs01, nfs02)$ sudo systemctl enable pcsd
(nfs01, nfs02)$ sudo systemctl start pcsd

pcs管理ユーザーhaclusterのパスワード設定

(nfs01, nfs02)$ sudo passwd hacluster
ユーザー hacluster のパスワードを変更。
新しいパスワード:
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。

ノードの認証

(nfs01)$ sudo pcs cluster auth nfs01 nfs02 -u hacluster
Password:
nfs02: Authorized
nfs01: Authorized

クラスタ作成

(nfs01)$ sudo pcs cluster setup --name nfs-cluster nfs01 nfs02
Destroying cluster on nodes: nfs01, nfs02...
nfs01: Stopping Cluster (pacemaker)...
nfs02: Stopping Cluster (pacemaker)...
nfs01: Successfully destroyed cluster
nfs02: Successfully destroyed cluster

Sending 'pacemaker_remote authkey' to 'nfs01', 'nfs02'
nfs01: successful distribution of the file 'pacemaker_remote authkey'
nfs02: successful distribution of the file 'pacemaker_remote authkey'
Sending cluster config files to the nodes...
nfs01: Succeeded
nfs02: Succeeded

Synchronizing pcsd certificates on nodes nfs01, nfs02...
nfs02: Success
nfs01: Success
Restarting pcsd on the nodes in order to reload the certificates...
nfs01: Success
nfs02: Success

クラスタの起動

(nfs01)$ sudo pcs cluster start --all
nfs01: Starting Cluster...
nfs02: Starting Cluster...

クラスタ自動起動の有効化

(nfs01)$ sudo pcs cluster enable --all
nfs01: Cluster Enabled
nfs02: Cluster Enabled

クラスタ情報確認

(nfs01)$ sudo pcs status
Cluster name: nfs-cluster
WARNING: no stonith devices and stonith-enabled is not false
Stack: corosync
Current DC: nfs02 (version 1.1.16-12.el7_4.5-94ff4df) - partition with quorum
Last updated: Mon Jan 22 11:57:03 2018
Last change: Mon Jan 22 11:55:54 2018 by hacluster via crmd on nfs02

2 nodes configured
0 resources configured

Online: [ nfs01 nfs02 ]

No resources


Daemon Status:
  corosync: active/enabled
  pacemaker: active/enabled
  pcsd: active/enabled

Pacemakerのプロパティ値変更

STONITH(fencing)を無効化、Quorumを無効化(今回は2ノード構成の為)。

(nfs01)$ sudo pcs property set stonith-enabled=false
(nfs01)$ sudo pcs property set no-quorum-policy=ignore

AWS CLIのセットアップ

VPCルートテーブルのルーティング情報を操作する為、AWS CLIをインストールする必要がある。

インストール

(nfs01, nfs02)$ sudo yum install epel-release
(nfs01, nfs02)$ sudo yum install python-pip
(nfs01, nfs02)$ sudo pip install awscli

AWS認証情報設定

(nfs01, nfs02)$ sudo aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

リソース設定

VPCルートテーブル側の事前準備

VPCルートテーブルをAPI(awscli)から操作するには、ルートテーブルを識別する為に名前を設定する必要がある。
今回はprivate,、publicと設定。
route_tables.png

NFSサーバインストール

(nfs01, nfs02)$ sudo yum install nfs-utils

IPv6 Listen Port 設定削除

カーネルパラメータでIPv6を無効化している場合は、IPv6 Listen Port を無効化しておかないと、rpcbindを起動できなくなる。

(nfs01, nfs02)$ sudo vi /usr/lib/systemd/system/rpcbind.socket

---
ListenStream=[::]:111
   ↓↓↓コメントアウト
#ListenStream=[::]:111
---

(参考) http://www.sebegginer.com/article/449338938.html

mountd, statd, lockdのポート固定

これらdaemonは、デフォルトではポートが動的に割当てられる為、固定化しなければセキュリティグループで制御できない。

(nfs01, nfs02)$ sudo sh -c "cat <<EOF >>/etc/sysconfig/nfs
MOUNTD_PORT=2050
STATD_PORT=2051
EOF"

(nfs01, nfs02)$ sudo sh -c "cat <<EOF >>/etc/modprobe.d/lockd.conf
options lockd nlm_tcpport=2052
options lockd nlm_udpport=2052
EOF"

リソースエージェント(RA)確認

次のRAがシステムにインストールされているか確認。

  • ocf:linbit
    • drbd
  • ocf:heartbeat
    • Filesystem
    • IPaddr2
    • nfsserver
    • exportfs
(nfs01)$ sudo pcs resource list ocf:linbit
(nfs01)$ sudo pcs resource list ocf:heartbeat

VPCルートテーブル操作用のRA作成

こちらで公開されているRAや、RA開発者ガイドを参考にして、独自RAを新規作成

(nfs01, nfs02)$ sudo vi /usr/lib/ocf/resource.d/heartbeat/route-change

---
#!/bin/sh

#
#   Resource Agent for managing RT resources.
#

######
# Initialization:

: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs

meta_data() {
    cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="route-change" version="0.9">
  <version>1.0</version>
  <longdesc lang="en">
This is a Resource Agent for AWS EC2 Route Tables
  </longdesc>
  <shortdesc lang="en">Manage AWS EC2 Route Tables</shortdesc>
  <parameters>
    <parameter name="route_table" unique="0" required="1">
      <longdesc lang="en">
      Route table name
      </longdesc>
      <shortdesc lang="en">rt</shortdesc>
      <content type="string"/>
    </parameter>
   <parameter name="ip" unique="1" required="1">
      <longdesc lang="en">
      The IPv4
      example IPv4 "192.168.254.254".
      </longdesc>
    <shortdesc lang="en">IPv4</shortdesc>
    <content type="string" default="" />
   </parameter>
  </parameters>
  <actions>
    <action name="start"        timeout="20" />
    <action name="stop"         timeout="20" />
    <action name="monitor"      timeout="20" interval="10" depth="0" />
    <action name="meta-data"    timeout="5" />
    <action name="validate-all"   timeout="20" />
  </actions>
</resource-agent>
END
}

##接続環境情報の指定
export AWS_CONFIG_FILE="/root/.aws/config"
export AWS_CREDENTIAL_FILE="/root/.aws/credentials"
export AWS_DEFAULT_REGION=ap-northeast-1
##Proxy設定
#/etc/profile.d/proxy.shの内容:
#---
#PROXY_HOST=<Hostname or IP address>
#PROXY_PORT=<Port number>
#export http_proxy=http://${PROXY_HOST}:${PROXY_PORT}/
#export HTTP_PROXY=http://${PROXY_HOST}:${PROXY_PORT}/
#export https_proxy=http://${PROXY_HOST}:${PROXY_PORT}/
#export HTTPS_PROXY=http://${PROXY_HOST}:${PROXY_PORT}/
#export ftp_proxy=http://${PROXY_HOST}:${PROXY_PORT}/
#export NO_PROXY=169.254.169.254
#---
if [ -f /etc/profile.d/proxy.sh ] ; then
        source /etc/profile.d/proxy.sh
fi

###function Logger_Cmd()
#/var/log/messages に指定した内容を追記する
#$1 に追記する内容を記載する
#example:Logger_Cmd testlog
Logger_Cmd() {
        logger -t `basename $0` "$1"
        sync;sync;sync;
}

#####処理内容の記載##########
RT_USAGE() {
 echo "Usage: $0 {start|stop|monitor}"
}

###route tableの確認
#function AWS_ROUTE_CHECK() {
AWS_ROUTE_CHECK() {
aws ec2 describe-route-tables  |grep -q -w ${INSTANCE_ID}
case "$?" in
    0)
        rc=${OCF_SUCSESS}
        ;;
    *)
        rc=${OCF_NOT_RUNNING}
        ;;
esac

return ${rc}
}

###route tableの追加・書換
AWS_ROUTE_CHANGE() {
AWS_ROUTE_CHECK
if [ ${rc} == 0 ];then
    return ${OCF_SUCSESS}
else
    aws ec2 create-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK} --instance-id ${INSTANCE_ID}
    if [ $? == 0 ];then
        Logger_Cmd "create route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK} --instance-id ${INSTANCE_ID}"
        return ${OCF_SUCSESS}
    else
        aws ec2 replace-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK} --instance-id ${INSTANCE_ID}
        Logger_Cmd "replace route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK} --instance-id ${INSTANCE_ID}"
        return ${OCF_SUCSESS}
    fi
fi
}

###route tableの削除
AWS_ROUTE_DELETE() {
AWS_ROUTE_CHECK
case "${rc}" in
    "${OCF_NOT_RUNNING}")
        Logger_Cmd "No route table or Delete route table"
        return ${OCF_SUCSESS}
        ;;
    "${OCF_SUCSESS}")
        aws ec2 delete-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK}
        if [ $? == 0 ];then
            Logger_Cmd "delete route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block ${CIDER_BLOCK}"
            return ${OCF_SUCSESS}
        else
            Logger_Cmd "delete route-table fail"
            return ${OCF_ERR_GENERIC}
    fi
        ;;
esac
}

###
#function RT_VALIDATE() {
RT_VALIDATE() {
    return ${OCF_SUCSESS}
}

####
case $__OCF_ACTION in
  start|stop|monitor)
     #####AWS環境設定#############
     ##利用するroute tableの指定
     RTNAME="${OCF_RESKEY_route_table}"
     ROUTE_TABLE_ID=`aws ec2 describe-tags --filters "Name=resource-type,Values=route-table" "Name=value,Values=${RTNAME}" --query "Tags[*].[ResourceId]" --output text`
     ##ec2 instance idの取得
     INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
     #############################

     ###CIDER-BLOCKの指定
     CIDER_BLOCK="${OCF_RESKEY_ip}/32"
     ;;
esac

case $__OCF_ACTION in
    meta-data)
        meta_data
        exit $OCF_SUCCESS
        ;;
    start)
        AWS_ROUTE_CHANGE
        ;;
    stop)
        AWS_ROUTE_DELETE
        ;;
    monitor)
        AWS_ROUTE_CHECK
        ;;
    validate-all)
        RT_VALIDATE
        ;;
    usage|help)
        RT_USAGE
        exit $OCF_SUCCESS
        ;;
    *)
        RT_USAGE
        exit ${OCF_ERR_UNIMPLEMENTED}
        ;;
esac
---

RAファイルに実行権限付与

(nfs01, nfs02)$ sudo chmod +x /usr/lib/ocf/resource.d/heartbeat/route-change

CIBのメタデータをファイルにエクスポート

(nfs01)$ sudo pcs cluster cib ~/nfs.cib

リソースオプションのデフォルト値設定

こちらの情報を参考に、resource-stickinessmigration-thresholdfailure-timeoutのデフォルト値を設定

(nfs01)$ sudo pcs -f ~/nfs.cib resource defaults resource-stickiness=INFINITY
(nfs01)$ sudo pcs -f ~/nfs.cib resource defaults migration-threshold=3
(nfs01)$ sudo pcs -f ~/nfs.cib resource defaults failure-timeout=600s

DRBDリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create drbd ocf:linbit:drbd \
  drbd_resource=r0 drbdconf=/etc/drbd.conf \
    op start timeout=240s on-fail=restart \
    op stop  timeout=100s on-fail=block \
    op monitor interval=20s timeout=20s start-delay=1m on-fail=restart

マルチステートメントリソースの設定

※DRBD以外のリソースは片系のみ起動するが、DRBDはデータを同期するため全てのノードで起動する必要がある

(nfs01)$ sudo pcs -f nfs.cib resource master ms-drbd drbd \
  master-max=1 master-node-max=1 clone-max=2 clone-node-max=1 notify=true

ファイルシステムのリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create fs ocf:heartbeat:Filesystem \
  device=/dev/drbd100 directory=/drbd/nfs fstype=xfs \
    op start timeout=60s on-fail=restart \
    op stop  timeout=60s on-fail=block \
    op monitor interval=10s timeout=60s on-fail=restart

VIPをNIC(lo:0)に付与するリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create virtual_ip ocf:heartbeat:IPaddr2 \
  ip=192.168.254.254 cidr_netmask=32 nic=lo:0 \
    op start timeout=20s on-fail=restart \
    op stop  timeout=20s on-fail=block \
    op monitor interval=30s timeout=20s on-fail=restart

VPCルートテーブル'private'のルートを操作するリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create route-change-1 ocf:heartbeat:route-change \
  ip=192.168.254.254 route_table=private \
    op start timeout=20s on-fail=restart \
    op stop  timeout=20s on-fail=block \
    op monitor interval=20s timeout=20s on-fail=restart

VPCルートテーブル'public'のルートを操作するリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create route-change-2 ocf:heartbeat:route-change \
  ip=192.168.254.254 route_table=public \
    op start timeout=20s on-fail=restart \
    op stop  timeout=20s on-fail=block \
    op monitor interval=20s timeout=20s on-fail=restart

NFSサーバのリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create nfsserver ocf:heartbeat:nfsserver \
  nfs_shared_infodir=/drbd/nfs/var-lib-nfs \
    op start timeout=20s on-fail=restart \
    op stop  timeout=20s on-fail=block \
    op monitor interval=10s timeout=20s on-fail=restart start-delay=20s

NFS仮想ディレクトリをエクスポートするリソース追加

(nfs01)$ sudo pcs -f nfs.cib resource create exportfs ocf:heartbeat:exportfs \
  clientspec="10.0.0.0/16" options="rw,no_root_squash" directory="/drbd/nfs/nfsroot" fsid="root"

DRBD以外のリソースをグループ化

(nfs01)$ sudo pcs -f nfs.cib resource group add group-nfs fs virtual_ip route-change-1 route-change-2 nfsserver exportfs

リソースに対する制約を追加

上記で作成したリソースグループは、DRBDのマスターと同じノードで起動する

(nfs01)$ sudo pcs -f nfs.cib constraint colocation add master ms-drbd with group-nfs INFINITY

DRBDのマスターが起動した後、上記リソースグループを起動する

(nfs01)$ sudo pcs -f nfs.cib constraint order promote ms-drbd then start group-nfs

CIBへインポート

(nfs01)$ sudo pcs cluster cib-push nfs.cib
CIB updated

リソースの起動確認

(nfs01)$ sudo pcs status
Cluster name: nfs-cluster
Stack: corosync
Current DC: nfs01 (version 1.1.16-12.el7_4.5-94ff4df) - partition with quorum
Last updated: Wed Jan 31 10:09:03 2018
Last change: Mon Jan 29 12:15:38 2018 by root via cibadmin on nfs01

2 nodes configured
8 resources configured

Online: [ nfs01 nfs02 ]

Full list of resources:

 Master/Slave Set: ms-drbd [drbd]
     Masters: [ nfs01 ]
     Slaves: [ nfs02 ]
 Resource Group: group-nfs
     fs (ocf::heartbeat:Filesystem):    Started nfs01
     virtual_ip (ocf::heartbeat:IPaddr2):       Started nfs01
     route-change-1     (ocf::heartbeat:route-change):  Started nfs01
     route-change-2     (ocf::heartbeat:route-change):  Started nfs01
     nfsserver  (ocf::heartbeat:nfsserver):     Started nfs01
     exportfs   (ocf::heartbeat:exportfs):      Started nfs01

Daemon Status:
  corosync: active/enabled
  pacemaker: active/enabled
  pcsd: active/enabled

VIP確認

(nfs01)$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 192.168.254.254/32 scope global lo:0
       valid_lft forever preferred_lft forever

(nfs02)$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever

ルートテーブル確認

各ルートテーブル(privatepublic)にVIP(192.168.254.254)宛のルートが設定されており、ターゲットがプライマリノードのインスタンスに向いていればOK
route.png

マウント確認

(nfs01)$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/xvda1        10G  5.2G  4.9G   52% /
devtmpfs         1.9G     0  1.9G    0% /dev
tmpfs            1.9G   39M  1.9G    3% /dev/shm
tmpfs            1.9G   25M  1.9G    2% /run
tmpfs            1.9G     0  1.9G    0% /sys/fs/cgroup
/dev/drbd100      19G  160M   19G    1% /drbd/nfs
tmpfs            379M     0  379M    0% /run/user/1000

(nfs02)$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/xvda1        10G  5.3G  4.8G   53% /
devtmpfs         1.9G     0  1.9G    0% /dev
tmpfs            1.9G   54M  1.8G    3% /dev/shm
tmpfs            1.9G   17M  1.9G    1% /run
tmpfs            1.9G     0  1.9G    0% /sys/fs/cgroup
tmpfs            379M     0  379M    0% /run/user/1000

テスト

詳細は割愛。
プライマリノードのインスタンスを停止する等、VCPルートテーブルも含めてフェイルオーバすることを確認する。
また、今回の設定ではフェイルバックは行わない為、その挙動も確認する。

RADIUSサーバ(rad01, rad02)構築

NFSクライアント設定

インストール

(rad01, rad02)$ sudo yum install nfs-utils

自動マウント設定追加

(rad01, rad02)$ sudo vi /etc/fstab

---
192.168.254.254:/drbd/nfs/nfsroot       /opt    nfs     rsize=8192,wsize=8192,nosuid,hard,intr  0 0
---

マウント

(rad01, rad02)$ sudo mount -a

rpcbindの設定変更

カーネルパラメータでIPv6を無効にしている場合は、rpcbind.socketを変更する必要がある。

(rad01, rad02)$ sudo vi /usr/lib/systemd/system/rpcbind.socket

---
ListenStream=[::]:111
↓↓↓ コメントアウト
#ListenStream=[::]:111

BindIPv6Only=ipv6-only
↓↓↓ 変更
BindIPv6Only=both
---

(参考) https://teratail.com/questions/64662

rpcbind自動起動設定、リブート

(rad01, rad02)$ sudo systemctl daemon-reload
(rad01, rad02)$ sudo systemctl enable rpcbind.socket
(rad01, rad02)$ sudo reboot

NFS自動マウント確認

(rad01, rad02)$ df -h
192.168.254.254:/drbd/nfs/nfsroot    19G   33M   19G    1% /opt

RADIUSサーバ構築

証明書、秘密鍵、証明書失効リストのパス

種類 ファイルパス
CA証明書 /opt/raddb/certs/cacert.pem
CA秘密鍵 /opt/raddb/certs/private/cakey.pem
サーバ証明書 /opt/raddb/certs/radius_cert.pem
サーバ秘密鍵 /opt/raddb/certs/radius_key.pem
証明書失効リスト(CRL) /opt/raddb/certs/crl/crl.pem

インストール

(rad01, rad02)$ sudo yum install freeradius freeradius-utils openssl openssl-perl

ディレクトリの移動&シンボリックリンク

RADIUSの設定や証明書類は両ノードで共有する必要があるので、/etc/raddbディレクトリを仮想ディレクトリ/opt内に移動し、元の場所(/etc)にシンボリックリンクを貼る。

(rad01, rad02)$ sudo mv /etc/raddb ~/
(rad01)$ sudo cp -p -r ~/raddb /opt/
(rad01)$ sudo chown -R root.radiusd /opt/raddb
(rad01, rad02)$ sudo ln -s /opt/raddb /etc/raddb
(rad01, rad02)$ sudo chown -h root.radiusd /etc/raddb
(rad01, rad02)$ sudo ls -l /etc/
lrwxrwxrwx   1 root radiusd    10  1月 24 17:52 raddb -> /opt/raddb
(rad01)$ sudo chown -R root.radiusd /opt/raddb

EAP-TLSの設定

(rad01)$ sudo vi /opt/raddb/mods-available/eap

---
eap {
        default_eap_type = tls
        timer_expire     = 60
        ignore_unknown_eap_types = no
        cisco_accounting_username_bug = no
        max_sessions = ${max_requests}
        md5 {
        }
        leap {
        }
        gtc {
                auth_type = PAP
        }
        tls-config tls-common {
                private_key_password = "<MY PASSWORD>"
                private_key_file = ${certdir}/radius_key.pem
                certificate_file = ${certdir}/radius_cert.pem
                ca_file = ${cadir}/cacert.pem
                #dh_file = ${certdir}/dh
                random_file = ${certdir}/random
                check_crl = yes
                ca_path = ${cadir}/crl
                cipher_list = "DEFAULT"
                cipher_server_preference = no
                ecdh_curve = "prime256v1"
                cache {
                        enable = no
                        lifetime = 24 # hours
                        name = "EAP-TLS"
                        persist_dir = "${logdir}/tlscache"
                }
                verify {
                }
                ocsp {
                        enable = no
                        override_cert_url = yes
                        url = "http://127.0.0.1/ocsp/"
                }
        }
        tls {
                tls = tls-common
        }
        ttls {
                tls = tls-common
                default_eap_type = md5
                copy_request_to_tunnel = no
                use_tunneled_reply = no
                virtual_server = "inner-tunnel"
        }
        peap {
                tls = tls-common
                default_eap_type = mschapv2
                copy_request_to_tunnel = no
                use_tunneled_reply = no
                virtual_server = "inner-tunnel"
        }
        mschapv2 {
        }
}
---

RADIUSクライアント情報の設定

(rad01)$ sudo vi /opt/raddb/clients.conf

---
client localhost {
        ipaddr = 127.0.0.1
        secret          = <secret password>
        require_message_authenticator = no
        nastype     = other    # localhost isn't usually a NAS...
        #limit {
        #        max_connections = 16
        #        lifetime = 0
        #        idle_timeout = 30
        #}
}
client private-network-1 {
        ipaddr      = 172.16.1.0
        netmask     = 24
        secret      = <secret password>
}
client private-network-2 {
        ipaddr      = 172.16.2.0
        netmask     = 24
        secret      = <secret password>
}
---

RADIUSサーバの設定

(rad01)$ sudo vi /opt/raddb/radiusd.conf

---
prefix = /usr
exec_prefix = /usr
sysconfdir = /etc
localstatedir = /var
sbindir = /usr/sbin
logdir = ${localstatedir}/log/radius
raddbdir = ${sysconfdir}/raddb
radacctdir = ${logdir}/radacct
name = radiusd
confdir = ${raddbdir}
modconfdir = ${confdir}/mods-config
certdir = ${confdir}/certs
cadir   = ${confdir}/certs
run_dir = ${localstatedir}/run/${name}
#db_dir = ${localstatedir}/lib/radiusd
db_dir = ${raddbdir}
libdir = /usr/lib64/freeradius
pidfile = ${run_dir}/${name}.pid
correct_escapes = true
max_request_time = 30
cleanup_delay = 5
max_requests = 16384
hostname_lookups = no
log {
        destination = files
        colourise = yes
        file = ${logdir}/radius.log
        syslog_facility = daemon
        stripped_names = no
        auth = no
        auth_badpass = no
        auth_goodpass = no
        msg_denied = "You are already logged in - access denied"
}
checkrad = ${sbindir}/checkrad
security {
        user = radiusd
        group = radiusd
        allow_core_dumps = no
        max_attributes = 200
        reject_delay = 1
        status_server = yes
}
proxy_requests  = yes
$INCLUDE proxy.conf
$INCLUDE clients.conf
thread pool {
        start_servers = 5
        max_servers = 32
        min_spare_servers = 3
        max_spare_servers = 10
        max_requests_per_server = 0
        auto_limit_acct = no
}
modules {
        $INCLUDE mods-enabled/
}
instantiate {
}
policy {
        $INCLUDE policy.d/
}
$INCLUDE sites-enabled/
---

現行RADIUSサーバから証明書関連のファイルをコピー

  • 現行サーバから前述の証明書、秘密鍵、証明書失効リストを新サーバ側ファイルパスにコピー
  • 現行サーバからクライアント証明書(.{pem,p12})を/opt/raddb/certs直下にコピー

RADIUS起動設定ファイル(radiusd.service)の変更

RADIUSサービス起動時、/optをマウントするまで起動遅延するよう変更

(rad01, rad02)$ sudo vi /usr/lib/systemd/system/radiusd.service

---
[Unit]
・・・
RequiresMountsFor=/opt
---

RADIUSサーバの起動

(rad01, rad02)$ sudo systemctl daemon-reload
(rad01, rad02)$ sudo systemctl enable radiusd
(rad01, rad02)$ sudo systemctl start radiusd

テスト

詳細は割愛
各インスタンスの片系を交互に停止し、短時間(概ね1分前後)のダウンタイムでWi-Fi通信が再開できることを確認する。

Tips

スプリットブレイン時の動作設定 (2018.02.16加筆)

DRBDでは、クラスタノードでスプリットブレインが発生した際、メールによる通知や、スプリットブレインからの自動復旧方法を設定することができる。

DRBDの設定はどこに記録される?

drbdmanageを利用して設定した内容は、/var/lib/drbd.dディレクトリ内に、次のファイル名で保存される。

  • drbdmanage_global_common.conf -> リソース共通の設定ファイル
  • drbdmanage_<リソース名>.res (ex. drbdmanage_r0.res) -> リソース別設定ファイル

尚、上記設定ファイルをエディタ等で直接編集することはできない。(仮に直接設定を書き加えても、ノード再起動で設定が巻き戻る)
また1台のノード上でdrbdmanageコマンドを用いて設定すれば、全てのノードに設定が反映される。

設定内容についての詳細はこちらを参照

スプリットブレインの通知設定

メール送信の環境設定

CentOS7は、デフォルトでmailコマンドがインストールされていない為、mailxをインストール

(nfs01, nfs02)$ sudo yum -y install mailx

SMTPサーバの設定を.mailrcに保存

(nfs01, nfs02)$ sudo sh -c 'cat <<EOF >/root/.mailrc
set smtp=smtp://mail.example.com:587
set smtp-auth-user=USER
set smtp-auth-password=PASSWORD
set from=from@example.com
EOF'

メール送信テスト

(nfs01, nfs02)$ sudo sh -c 'cat <<EOF | mail -t
To: to@example.com
Subject: This is test mail.

Mail test message. 
EOF'

通知ハンドラの設定

(nfs01)$ sudo drbdmanage handlers --resource r0 --split-brain "\"/usr/lib/drbd/notify-split-brain.sh to@example.com\""

(参考) 通知ハンドラの設定を削除する場合

(nfs01)$ sudo drbdmanage handlers --resource r0 --unset-split-brain

スプリットブレインからの自動復旧ポリシー設定

(nfs01)$ sudo drbdmanage net-options \
                  --resource r0 \
                  --after-sb-0pri discard-zero-changes \
                  --after-sb-1pri discard-secondary \
                  --after-sb-2pri disconnect

(参考) 自動復旧ポリシーの設定を削除する場合

(nfs01)$ sudo drbdmanage net-options \
                  --resource r0 \
                  --unset-after-sb-0pri \
                  --unset-after-sb-1pri \
                  --unset-after-sb-2pri

さいごに

  • 今回、RADIUS HAを実装するのに4インスタンスを使いました。このほうが将来EFSが東京リージョンでサービスインした際、バックエンドのNFSノードを容易に移行できると見込んでいる為です。そこまでする必要がない環境であれば、コスト面を考えて2インスタンスで同様のHA環境を構築することもできます。
  • 当初、NFSインスタンスのタイプをt2-smallに設定して稼働させましたが、VPCにトラフィックが多く集中するような状況下では、ハートビートがタイムアウトする事象が発生しました。お金に余裕があるなら、ネットワーク帯域をある程度確保できるインスタンスタイプ上で稼働させるほうが良さそうです。
  • 実運用環境では、NFSノードでインターコネクト用のNICを追加、更にはRADIUSノードでNFSクライアント専用のNICも追加する構成が最善だと思います。
  • Amazon様、東京リージョンでもEFSの提供をお願いします。お願いします。
  • 前提条件
  • NFSサーバ(nfs01, nfs02)構築
  • RADIUSサーバ(rad01, rad02)構築
  • Tips
  • さいごに