Raspberry Pi 2でクラスタ構築 その2(Hadoop編)

前回、Hadoop環境を構築しStandaloneモードで動作確認出来たため、今度は完全分散モードで動作を確認する。

準備

各サーバーからパスワード無しで接続出来るようにする。この作業を行っておくとマスターの起動とスレーブの起動を1コマンドで実行出来るようになる。

#鍵を生成
ssh-keygen -t rsa
#各サーバーに公開鍵をコピー
ssh-copy-id -i /home/foo/.ssh/id_rsa.pub foo@192.168.0.xxx
#パスワード無しでログイン出来ることを確認
ssh 192.168.0.xxx

データ保存用のUSBハードディスクを起動時にマウントするようにする。

#USBハードディスクのパスを確認
sudo fdisk -l
#UUIDを確認
sudo blkid /dev/sda1 #パスは環境毎に異なる可能性があるため注意
sudo vi /etc/fstab
#下記の1行を追記
UUID=xxx-xxx-xxx-xxx-xxx /mnt/usbhdd ext4 defaults 0 0 #UUIDは機器毎に異なるため注意
#再起動して自動的にマウントされることを確認
sudo shutdown -r now

ホスト名を/etc/hostsに記載する。Hadoopの設定でIPアドレスを使用するとエラーが発生するため、ここでホスト名を設定しておく。

sudo vi /etc/hosts

192.168.0.xx1 master1
192.168.0.xx2 master2
192.168.0.xx3 master3
192.168.0.xx4 slave1
192.168.0.xx5 slave2
192.168.0.xx6 slave3
192.168.0.xx7 slave4

クラスターの構成を検討

手元にには7台のRaspberry Pi 2があり、そのうち4台にはUSBハードディスクを接続している。

Hadoopの構築に必要な機能は以下の通りであることから、

  • NameNode
  • DataNode
  • Secondary NameNode
  • ResourceManager
  • NodeManager
  • WebAppProxy
  • Map Reduce Job History Server

各サーバーに下記の様に割り振ることにした。Zookeeperによる自動フェイルオーバーについては今回は設定しない。

master1・・・NameNode, ResourceManager
master2・・・Secondary NameNode
master3・・・今回は使用しない
slave1・・・DataNode, NodeManager
slave2・・・DataNode, NodeManager
slave3・・・DataNode, NodeManager
slave4・・・DataNode, NodeManager
※slave1~4にはUSBハードディスクが接続されている。

Hadoopデーモンの環境設定

~/.bashrcに下記を追記する。

export JAVA_HOME="$HOME/jdk-11-latest"
export HADOOP_HOME="$HOME/hadoop-latest"
if [ -d "$HADOOP_HOME" ] ; then
    export HADOOP_CONF_DIR="$HADOOP_HOME/etc/hadoop"
fi

全サーバーのetc/hadoop/hadoop-env.shを編集する。

export JAVA_HOME=/home/xxx/jdk-11-latest
export HADOOP_HOME=/home/xxx/hadoop-latest
export HADOOP_HEAPSIZE_MAX=200m

HDFSの設定

全サーバーのetc/hadoop/core-site.xmlを編集する。

<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://master1:9000/</value>
  </property>
</configuration>

master1のetc/hadoop/hdfs-site.xmlを編集する。

<configuration>
  <property>
    <name>dfs.replication</name>
    <value>3</value>
  </property>
  <property>
    <name>dfs.namenode.secondary.http-address</name>
    <value>master2:50090</value>
  </property>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>/home/xxx/hadoop-latest/name</value>
  </property>
</configuration>

master1のetc/hadoop/workersを編集する。

slave1
slave2
slave3
slave4

slave1~4のetc/hadoop/hdfs-site.xmlを編集する。

<configuration>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>/mnt/usbhdd/hadoop/data</value>
  </property>
</configuration>

master1で下記を実行する。

$HADOOP_HOME/bin/hdfs namenode -format <cluster_name>
$HADOOP_HOME/sbin/start-dfs.sh

正常動作を確認出来たら、下記を実行し停止する。

$HADOOP_HOME/sbin/stop-dfs.sh

この時下記のWarningが出たため、

OpenJDK Server VM warning: You have loaded library /home/xxx/hadoop-3.2.2/lib/native/libhadoop.so.1.0.0 which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.

下記の対応を行った。

sudo apt-get install prelink
sudo execstack -c /home/xxx/hadoop-3.2.2/lib/native/libhadoop.so.1.0.0
sudo execstack -c /home/xxx/hadoop-3.2.2/lib/native/libhdfs.so.0.0.0
sudo execstack -c /home/xxx/hadoop-3.2.2/lib/native/libnativetask.so.1.0.0

YARNの設定

注意:メモリの設定は参考にしないでください。物理メモリが1GBしかないRaspberry Pi 2で無理やり動くように設定したものなので、正しい設定方法ではないです。

master1、slave1~4のetc/hadoop/yarn-site.xmlを編集する。(yarn.scheduler.~はmaster1のみ追記、yarn.nodemanager.~はslave1~4のみ追記)

<configuration>
  <property>
      <name>yarn.resourcemanager.hostname</name>
      <value>master1</value>
  </property>
  <property>
      <name>yarn.scheduler.minimum-allocation-mb</name>
      <value>200</value>
  </property>
  <property>
      <name>yarn.scheduler.maximum-allocation-mb</name>
      <value>800</value>
  </property>
  <property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>200</value>
  </property>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
  <property>
    <name>yarn.nodemanager.env-whitelist</name>
    <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
  </property>
</configuration>

master1のetc/hadoop/mapred-site.xmlを編集する。

<configuration>
  <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
  <property>
    <name>yarn.app.mapreduce.am.resource.mb</name>
    <value>200</value>
  </property>
  <property>
    <name>mapreduce.map.memory.mb</name>
    <value>200</value>
  </property>
  <property>
    <name>mapreduce.map.java.opts</name>
    <value>-Xmx200M</value>
  </property>
  <property>
    <name>mapreduce.reduce.memory.mb</name>
    <value>200</value>
  </property>
  <property>
    <name>mapreduce.reduce.java.opts</name>
    <value>-Xmx200M</value>
  </property>
  <property>
    <name>yarn.app.mapreduce.am.env</name>
    <value>HADOOP_MAPRED_HOME=/home/xxx/hadoop-latest</value>
  </property>
  <property>
    <name>mapreduce.map.env</name>
    <value>HADOOP_MAPRED_HOME=/home/xxx/hadoop-latest</value>
  </property>
  <property>
    <name>mapreduce.reduce.env</name>
    <value>HADOOP_MAPRED_HOME=/home/xxx/hadoop-latest</value>
  </property>
</configuration>

master1で下記を実行する。

$HADOOP_HOME/sbin/start-dfs.sh
$HADOOP_HOME/sbin/start-yarn.sh

問題なく両方の起動が確認できたので、実際に処理を実行してみる。

$HADOOP_HOME/bin/hadoop jar /home/xxx/hadoop-latest/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.2.jar pi 10 10000

下記の様に処理が進んで行き、

2021-02-18 08:12:21,597 INFO mapreduce.Job:  map 0% reduce 0%
2021-02-18 08:13:06,983 INFO mapreduce.Job:  map 19% reduce 0%
2021-02-18 08:13:38,044 INFO mapreduce.Job:  map 31% reduce 0%
2021-02-18 08:13:39,081 INFO mapreduce.Job:  map 38% reduce 0%
2021-02-18 08:14:09,121 INFO mapreduce.Job:  map 44% reduce 0%
2021-02-18 08:14:10,159 INFO mapreduce.Job:  map 56% reduce 0%
2021-02-18 08:14:41,193 INFO mapreduce.Job:  map 69% reduce 0%
2021-02-18 08:14:48,435 INFO mapreduce.Job:  map 69% reduce 23%
2021-02-18 08:15:12,225 INFO mapreduce.Job:  map 81% reduce 23%
2021-02-18 08:15:13,260 INFO mapreduce.Job:  map 81% reduce 27%
2021-02-18 08:15:42,218 INFO mapreduce.Job:  map 88% reduce 27%
2021-02-18 08:15:43,256 INFO mapreduce.Job:  map 94% reduce 27%
2021-02-18 08:15:44,290 INFO mapreduce.Job:  map 94% reduce 31%
2021-02-18 08:16:14,264 INFO mapreduce.Job:  map 100% reduce 31%
2021-02-18 08:16:15,298 INFO mapreduce.Job:  map 100% reduce 67%
2021-02-18 08:16:17,369 INFO mapreduce.Job:  map 100% reduce 100%

最終的に下記の結果が表示された。やはりかなり遅い。

Job Finished in 320.709 seconds
Estimated value of Pi is 3.14157500000000000000

次回、NameNodeをHA化する。

Raspberry Pi 2でクラスタ構築

動機

以前、Raspberry Pi 2でTomcatのクラスターサーバーを構築したときの機材が放置してあったので、そのままにしておくのはもったいないと思い、もう一度何かしらのクラスターを構築してみようと思った。

使用しなくなっていた原因

  • Raspberry Pi 2はかなり非力で、重いJavaアプリケーションを動作させることが不適であったこと
  • セッションはサーバー毎で別になるため、負荷を分散する恩恵をあまり受けられなかったこと(セッションを共有することも出来るが、メモリの消費が増えるため断念)
  • データ保存用のSDカードが壊れた

何を構築するか

今回のクラスターの構築は実務で使うより、勉強の意味合いが大きい。まず、最近はどのようなものが流行っているのか調査する。また、Raspberry Pi 2は非力なため1台1台別々に動作させるのではなく、今回は複数サーバーを束ねて1台のサーバーとして動作させるようにしたい。

  • Kubernetes
     機能説明は省略、次回以降調べて記載する。
     Googleの検索でヒットするのは、殆どこのクラスター構築に関する記事。実際にRaspberry Piで構築している人も多いため、構築方法は見つけやすいと思う。ただし、古いRaspberry Pi 2でも動作させることが出来るかはやってみないと分からない。
  • Hadoop
     大量データを分散処理するためのプラットフォーム。Javaで動作するため構築に関しては問題ないはず。分散処理のため使用用途は限られるかも知れない。
  • その他
     アプリケーションの機能限定でクラスター化するもの。以前構築したTomcatのクラスター、MySQLや、Apache HTTPサーバーなどに用意されているクラスター化機能を使用する。

取り敢えず今回はHadoopを構築した後に、Kubernetesの構築も試してみる。

Raspberry Pi 2の動作確認

しばらく動かしていなかったため、まずは正常に起動するか確認する。

下記からインストーラーをダウンロードし、SDカードにインストールする。
https://ubuntu.com/download/raspberry-pi

アップデートや固定IPの設定をしたら、ここでSDカードのイメージバックアップを取得しておく。他のRaspberry Piにはそのイメージをコピーして構築の手間を省く。

# 固定IP化
sudo vi /etc/netplan/50-cloud-init.yaml
# 下記を追記
addresses: [192.168.0.xx/24]
gateway4: 192.168.0.xx
nameservers:
  addresses: [192.168.0.xx]
  search: []
dhcp4: false
# 変更を適用
sudo netplan apply
# 最新化
sudo apt update
sudo apt upgrade
# Firmwareを最新化(通常運用する場合は実施しないこと)
sudo apt install python3-pip
sudo pip3 install setuptools
sudo pip3 install vcgencmd

sudo curl -L --output /usr/bin/rpi-update https://raw.githubusercontent.com/Hexxeh/rpi-update/master/rpi-update && sudo chmod +x /usr/bin/rpi-update

sudo rpi-update

Hadoop構築

# 必要なソフトウェアをインストール
sudo apt install ssh
sudo apt install pdsh

下記からJDKをダウンロードする。
https://adoptopenjdk.net/releases.html

Raspberry Pi 2はArm 32bitプロセッサのため、上記をダウンロードした。

# 解凍し、リンクを作成
tar xvfz OpenJDK11U-jdk_arm_linux_hotspot_11.0.10_9.tar.gz
ln -nfs /home/xxx/jdk-11.0.10+9 jdk-11-latest
# バージョン表示確認
java -version
openjdk version "11.0.10" 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9)
OpenJDK Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)

下記からHadoopをダウンロードする。
https://hadoop.apache.org/releases.html

# 解凍
tar xvfz hadoop-3.2.2.tar.gz
# JAVA_HOMEを設定
export JAVA_HOME=/home/xxx/jdk-11-latest
# Standaloneモードで動作確認
cd hadoop-3.2.2
mkdir input
cp etc/hadoop/*.xml input
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.2.jar grep input output 'dfs[a-z.]+'
cat output/*

1       dfsadmin

Standaloneモードでの動作を確認できたので今回はここまで。

次回、完全分散モードで動作を確認する。

GoogleのTsunamiを使ってみる

1.概要

Tsunamiは、重大度の高い脆弱性を検出するための拡張可能なプラグインシステムを備えた汎用ネットワークセキュリティスキャナー。
https://opensource.googleblog.com/2020/06/tsunami-extensible-network-scanning.html

2.実行環境の準備

yum install nmap ncrack
※JDKを別途用意すること

3.ビルド&実行

mkdir tsunami
cd tsunami
bash -c "$(curl -sfL https://raw.githubusercontent.com/google/tsunami-security-scanner/master/quick_start.sh)"

ビルドに成功すると下記のような実行コマンドが表示される。後はコピペして実行するだけで/tmp/tsunami-output.jsonにレポートが出力される。

cd /home/user/tsunami && \
java -cp "tsunami-main-0.0.2-SNAPSHOT-cli.jar:/home/user/tsunami/plugins/*" \
-Dtsunami-config.location=/home/user/tsunami/tsunami.yaml \
com.google.tsunami.main.cli.TsunamiCli \
--ip-v4-target=127.0.0.1 \
--scan-results-local-output-format=JSON \
--scan-results-local-output-filename=/tmp/tsunami-output.json

無料のSSL証明書をインストールする

※2022/04/25追記 無料のSSL証明書をインストールする(Ubuntu 22.04版)を公開。

No-IPドメインでLet’s Encryptの証明書を使うことが出来る。

1.事前確認
・ドメイン名を取得していること(今回はNo-IPを使用する)
・80、443ポートが外部に開放されていること

2.Certbotをインストールする

yum install epel-release
yum install certbot python-certbot-apache

3.証明書取得する

certbot certonly --agree-tos --webroot -w /var/www/html/ -d hoge.no-ip.org

取得に成功した場合、証明書は以下に保存される。

ll /etc/letsencrypt/live/hoge.no-ip.org/

4.Apacheに証明書を設定し再起動する

バージョン2.4.7以前の場合

SSLCertificateFile /etc/letsencrypt/live/hoge.no-ip.org/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/hoge.no-ip.org/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/hoge.no-ip.org/chain.pem

バージョン2.4.8以降の場合

SSLCertificateFile /etc/letsencrypt/live/hoge.no-ip.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/hoge.no-ip.org/privkey.pem
systemctl restart httpd

5.証明書を自動的に更新する

証明書は90日で失効してしまうため、cron.dに以下のスクリプトを置いておく。
有効期限の30日前になると自動的に更新されるので、とりあえず毎日か、毎週実行する様にしておけば良い。

#!/bin/sh

/bin/certbot renew --webroot-path /var/www/html/ --post-hook "systemctl reload httpd"

Apache Tomcat 9.0.31にアップデートしたらエラー発生

Apache Tomcat 9.0.31にアップデートしたらAJP経由でアクセス出来なくなった。
server.xmlを見てみるとAJPの設定がコメントアウトされていたので、下記を追記した。

    <Connector protocol="AJP/1.3"
               port="8009"
               redirectPort="8443"/>

しかし、今度は下記のようなエラーが出るようになった。

Caused by: java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
    at org.apache.coyote.ajp.AbstractAjpProtocol.start(AbstractAjpProtocol.java:264)
    at org.apache.catalina.connector.Connector.startInternal(Connector.java:1035)

secretRequiredがデフォルトで”true”になっているため、secretを設定しろということらしい。
面倒なのでアドレスでアクセス制限をかけて、secretRequiredは”false”にした。

<Connector protocol="AJP/1.3"
           address="localhost"
           port="8009"
           redirectPort="8443"
           secretRequired="false"/>