NaviPlus Engineers' Blog

NaviPlusのサーバ構築

どもです。インフラ池田(@mikeda)です。
今日は『NaviPlusではどういうふうにサーバ構築作業をしているのか』について話します。

※編集長注:以下は記事執筆時点のものです。将来的に弊社がこの手順を使用することを保証するものではありません。

まずNaviPlusでは今のところクラウドは利用していないので、
サーバ構築は基本的に以下のどっちかになります。

OSはCentOSの6系です。
作業は以下の技術を使って自動化しています。

それではさっそく、ザックリとした実際の構築手順から。

構築手順

物理サーバとVMに分けて説明します。
事前にローカルDNSの登録と、chefのnode定義の作成だけやっておきます。

物理サーバ

まずは物理サーバの構築手順です。
以下の作業は並列実行可能、 またiLO/iDRAC等を使ってリモートでコンソールが取得できればオフィスからでも実行可能です。

PXE Boot + kickstartでOSインストール

サーバをPXEブートさせます。
HPサーバ、DELLサーバであれば起動中にF12を押せばOKです。

そうするとモニターにこんな画面が表示されるので、

========================
Select kickstart setting
========================
 1. CentOS-6
 2. CentOS-6  KVM HOST
 9. CentOS-6  Installer

boot:

数字を入力してほおっておくとOSのインストールと初期設定が完了します。
1が通常サーバ、
2がKVMホスト(パーティションの切り方を変えてます)、
9は手動インストール(kickstartなしでインストーラが起動)です。

ネットワーク設定

初回起動時はネットワーク設定がDHCPになってるので、設定スクリプトを使って設定します。

# sh /tmp/nw_setting.sh test01 192.168.1.1
# reboot

bonding設定もこのスクリプトでやっちゃってます。
※VMのホストサーバの場合は、さらにブリッジ設定とLVM設定を実施します。

chef-solo実行

管理ユーザでログインし、chef-soloを実行します。

$ sudo chef-solo -c ~/chef/solo.rb -j ~/chef/nodes/`hostname`.json

で完了です。

VM

続いてVM(KVMゲスト)の構築手順です

virt-install + kickstartでOSインストール

VM構築スクリプトを使ってVMを作成します。

# ./vm_create.sh test01 192.168.1.101 1/2

これだけでOSのインストールと初期設定、NW設定が完了します。

chef-solo実行

ここからは物理サーバと同じです。
管理ユーザでログインし、chef-soloを実行、

$ sudo chef-solo -c ~/chef/solo.rb -j ~/chef/nodes/`hostname`.json

で完了。

以上で手順の説明は終わりです。

サーバ構築環境について

以降はサーバ構築環境の細かい設定などを説明していきます。

基本的に管理サーバが1台あって、そこに一通りの機能を突っ込んでいます。

PXE Boot + kickstart

PXE boot、kickstartの細かい説明は割愛します。

PXE bootの設定はこんな感じです。

$ cat /var/lib/tftpboot/boot.msg
========================
Select kickstart setting
========================
 1. CentOS-6
 2. CentOS-6  KVM HOST
 9. CentOS-6  Installer


$ cat /var/lib/tftpboot/pxelinux.cfg/default
prompt 1
display boot.msg

# CentOS-6
label 1
kernel /centos6-x86_64/vmlinuz
append ksdevice=eth0 ks=http://192.168.1.100/ks/centos6.cfg initrd=/centos6-x86_64/initrd.img noipv6 biosdevname=0

# CentOS-6 KVM HOST
label 2
kernel /centos6-x86_64/vmlinuz
append ksdevice=eth0 ks=http://192.168.1.100/ks/centos6_kvmhost.cfg initrd=/centos6-x86_64/initrd.img noipv6 biosdevname=0

# CentOS-6 installer
label 9
kernel /centos6-x86_64/vmlinuz
append initrd=/centos6-x86_64/initrd.img noipv6 biosdevname=0

kickstartファイルはこんな感じ

install
text
url --url=http://192.168.1.100/mrepo/centos6-x86_64/disc1/
lang ja_JP.UTF-8
keyboard jp106
network --onboot yes --device eth0 --bootproto dhcp --noipv6
rootpw  --iscrypted XXXXXXXXXXXXXXXXXXXXXX
firewall --service=ssh
authconfig --enableshadow --passalgo=sha512
selinux --enforcing
timezone Asia/Tokyo
reboot

zerombr
bootloader --location=mbr --driveorder=sda --append=" biosdevname=0"
clearpart --all --initlabel --drives=sda
part /boot --fstype ext4 --size=300 --ondisk=sda
part swap --size=2048
part / --fstype ext4 --size=1 --grow


%packages
@base
@core
@debugging
@development
@japanese-support
@performance
@server-policy
sgpio
systemtap-client

%pre
#/bin/sh
/usr/bin/wget http://192.168.1.100/setup/os_setup.sh -O /tmp/os_setup.sh

%post
%include /tmp/os_setup.sh

DVDからインストールしたサーバの/root/anaconda-ks.cfgをちょっといじっただけですね。

※DVDから手動インストールしても同じ状態にできるように、kickstartでは特殊なことはやらないようにしてます

OSの初期設定スクリプトではこんなことをやっています。

要するに『管理ユーザでログインしてchef-soloが実行できる状態』にします。

chef-solo

特に言うことがないw
githubで管理、各サーバに展開してます。

パッケージ管理

ミドルウェア系のパッケージ管理について話しておきます。

RPMはローカルレポジトリからyum installします。
ローカルレポジトリにはCentOS標準レポジトリのミラー、EPELのミラー、オリジナルのyumレポジトリ(外部から入手したもの、自分で作ったものを追加)があります。
外部から直接アクセスのあるReverseProxy用のApacheやAPP用のRubyなど、セキュリティや機能の面でバージョンを細かく更新したいものはソースインストールしています。
RPMの自作は管理が面倒なのであまりやっていません。

サーバ構築に必要なRPMやソースコードは全て管理サーバ上に配置されていて、外部からのファイル取得はありません(gemやpearのようなものは除いて)。
外部yumレポジトリなどを使っていると以下の問題が発生するためです。

ローカルyumレポジトリの構築にはmrepoを使っています。
構築手順はとっても簡単です。
ここからmrepoの0.8.8をダウンロード&インストールして、コンフィグをこんな感じに作成

# cat /etc/mrepo.conf
[main]
srcdir = /var/mrepo
wwwdir = /var/www/mrepo
confdir = /etc/mrepo.conf.d
arch = x86_64 

createrepo-options = --checksum sha --pretty --database --update

mailto = root@localhost
smtp-server = localhost

# cat /etc/mrepo.conf.d/centos6.conf 
[centos6]
name = CentOS $release ($arch)
release = 6
arch = x86_64
metadata = repomd repoview

### ISO images
iso = CentOS-6.4-x86_64-bin-DVD?.iso

### Additional repositories
updates    = rsync://ftp.riken.jp/centos/$release/updates/$arch/Packages/
fasttrack  = rsync://ftp.riken.jp/centos/$release/fasttrack/$arch/Packages/
centosplus = rsync://ftp.riken.jp/centos/$release/centosplus/$arch/Packages/
extras     = rsync://ftp.riken.jp/centos/$release/extras/$arch/Packages/
contrib    = rsync://ftp.riken.jp/centos/$release/contrib/$arch/Packages/

### EPEL repositories
epel = rsync://ftp.jaist.ac.jp/pub/Linux/Fedora/epel/$release/$arch/

### Original repositories
naviplus      = file:///naviplus/rpm/centos/$release/$arch/naviplus/

/var/mrepo/iso/にCentOS6のISOファイルを置いて

# ls /var/mrepo/iso/
CentOS-6.4-x86_64-bin-DVD1.iso  CentOS-6.4-x86_64-bin-DVD2.iso

そしてレポジトリの更新を実行

# mrepo -ugvv

これだけで(あとはapacheのgracefulくらい)ここまでやってくれます。

EPELレポジトリだけを再同期したければこんなコマンドを実行します

# mrepo -ugvv -r epel centos6-x86_64

オリジナルのyumレポジトリにパッケージを追加したい場合は、rpmをコンフィグで指定したディレクトリ以下に配置、更新コマンドを実行するだけでOKです。

# ls /naviplus/rpm/centos/6/x86_64/naviplus/td/
td-agent-1.1.18-0.x86_64.rpm
td-libyaml-0.1.4-1.x86_64.rpm

# mrepo -ugvv -r naviplus centos6-x86_64

作成したレポジトリは、yum設定をこんな感じにすれば使えます。

[base-local]
name=CentOS-$releasever - Base
baseurl=http://192.168.1.100/mrepo/centos$releasever-$basearch/RPMS.os/
gpgcheck=0

#released updates 
[updates-local]
name=CentOS-$releasever - Updates
baseurl=http://192.168.1.100/mrepo/centos$releasever-$basearch/RPMS.updates/
gpgcheck=0

...

[epel-local]
name=Extra Packages for Enterprise Linux $releasever - $basearch
baseurl=http://192.168.1.100/mrepo/centos$releasever-$basearch/RPMS.epel/
gpgcheck=0
enabled=1

[naviplus]
name=CentOS-$releasever - Naviplus
baseurl=http://192.168.1.100/mrepo/centos$releasever-$basearch/RPMS.naviplus/
gpgcheck=0
enabled=1

mrepoは便利なツールですが、情報が少ないしメンテされてなさそうなので、あんまオススメできないかもw

VMのインストールスクリプト

これもちょっとだけ話をしておきます。

ちなみにKVMのホストは、物理運用サーバを流用したそこそこスペックの1Uサーバがけっこう多いです。
その場合のサーバスペックはこのくらいです。

冗長化前提のサービス用VMはVM移動などを考慮して細かいリソース調整はせず、
各リソースを1/2、1/4、1/8した固定サイズのVMを使うようにしています。
※開発サーバや管理系のサーバは、それ用のホストに必要なだけリソースを確保してVMを作成してます。

最初の手順にあった1/2っていうのは、1/2サイズってことでした。

# ./vm_create.sh test01 192.168.1.101 1/2

このスクリプトの中身はこんな感じです。

#!/bin/bash

HOSTNAME=$1
IP=$2
VM_SIZE=$3

LOCATION="http://192.168.1.100/mrepo/centos6-x86_64/disc1/"
LV=/dev/vm_vg/lv_${HOSTNAME}

case "$VM_SIZE" in
  "1/8")
    VCPUS=3
    RAM=3584
    DISK=32
    ;;
  "1/4")
    VCPUS=6
    RAM=7168
    DISK=64
    ;;
  "1/2")
    VCPUS=12
    RAM=14336
    DISK=128
    ;;
  *)
    echo "undefined size"
    exit
    ;;
esac

if [ -b $LV ];then
  echo 'LV is already exists'
  exit
fi

lvcreate -L ${DISK}G -n lv_${HOSTNAME} vm_vg

virt-install \
  --name $HOSTNAME \
  --ram $RAM \
  --vcpus=$VCPUS \
  --disk path=${LV} \
  --os-variant=rhel6 \
  --hvm \
  --nographics \
  --accelerate \
  -w bridge:br0 \
  --location="${LOCATION}" \
  --extra-args="ks=http://192.168.1.100/ks/ks_kvm.php?cfg=centos6.cfg__${HOSTNAME}__${IP} console=tty0 console=ttyS0,115200n8"

VMのイメージはファイルじゃなくてLVMボリュームを使っています。
ファイルのほうが取り回しは楽なのですが、KVM徹底入門の平さん に「安定性とパフォーマンスの面でLVMのほうがオススメ」と言われたのでそうしてます。
(自分じゃ細かい検証してません!サーセンw)

kickstartファイルは動的に生成しています。

$ curl -g 'http://192.168.1.100/ks/ks_kvm.php?cfg=centos6.cfg__test01__192.168.1.101'
install
text
url --url=http://192.168.1.100/mrepo/centos6-x86_64/disc1/
lang ja_JP.UTF-8
keyboard jp106
network --device eth0 --bootproto static --onboot yes --noipv6 --ip 192.168.1.101 --netmask 255.255.255.0 --gateway 192.168.1.1 --nameserver 192.168.1.2 --hostname test01
...

といっても今のところ元kickstartファイルの、networkやデバイスを書き換えてるだけです。

<?php
list($cfg, $hostname, $ip) = explode("__", $_GET["cfg"]);

switch (preg_replace('/\.\d+$/', '', $ip)) {
  case "192.168.0":
    $netmask    = '255.255.255.0';
    $gateway    = '192.168.0.1';
    $nameserver = '192.168.0.2';
    break;
  case "192.168.1":
    $netmask    = '255.255.255.0';
    $gateway    = '192.168.1.1';
    $nameserver = '192.168.1.2';
    break;
}

$ks_config = file_get_contents("$cfg");
$ks_config = preg_replace(
  "/^network.*/m",
  "network --device eth0 --bootproto static --onboot yes --noipv6 "
    . "--ip $ip --netmask $netmask --gateway $gateway "
    . "--nameserver $nameserver "
    . "--hostname $hostname",
  $ks_config
);
$ks_config =  preg_replace( "/=sda/", "=vda", $ks_config);
$ks_config =  preg_replace(
  "/^bootloader.*/m",
  'bootloader --location=mbr --driveorder=vda --append="console=ttyS0,115200n8"',
  $ks_config
);
echo $ks_config;
?>

ちょっとこのスクリプト適当すぎだな・・・

まとめ

というわけで、NaviPlusのサーバ構築手順と使っている技術についての話でした!

PXE Bootやkickstart、ローカルyumレポジトリはcobblerを使ってかっこ良くテンプレート管理する手もあります。
ただサーバベンダやOS構成の種類が限られる場合、cobblerはちょっと大げさ過ぎる気がして使ってません。

構築時間はどのくらいですかね。
OSインストール中、chef-solo実行中は他の作業してるので、特にVMだと実際の作業時間はほとんどないです。
ここはもう、クラウドだろうがオンプレミスだろうがこのぐらいは当たり前になってきてる気はします。
もし1桁台の構築台数で『サーバ構築は丸1日以上かかります』とかって状態であれば、何か非効率的なことをしていると思います。

サーバ構築の速度と精度は高いに越したことはないです(もちろんどこまでやるかは状況によりけりです)。
工数削減はもちろんそうですが、加えて『作業』のコストとリスクを下げることは『改善』のコストとリスクを下げることになるからです。
『こうすればもっとよくなるのに』を簡単に実現できるように、環境を整備しましょう!

なんかいろいろ端折ってるので、わからないこと/気になることあったらTwitterで聞いて下さいw