Raspberry pi 2 Clusteringで遊ぶ

発売当時くらいに買ったRaspberry pi 2を一通り遊んだ後、放置して使っていなかったので、またちょっと遊んでみる。

ゴール

Raspberry piが手元に7台あるので、Load Balancer、 Reverse Proxy、Application Server、DB Serverで冗長構成を作成してみる。

Raspberry pi 2はメインメモリが2GB、CPUも32bitと今の基準からすると非力なので、なるべく軽い構成にする。Kubernetesとか使いたかったが、スペック的に無理だった。

Load Balancer: Nginx
Reverse Proxy: Nginx ※無くても良い
Application Server: Apache Tomcat 10 (Java 21)
DB Server: MariaDB

Ubuntu 22のインストール

Raspberry Pi Imagerを使ってUbuntuをSDカードにインストールする。

OSでOther general-purpose OS > Ubuntu > Ubuntu Server 22.04.4 LTS (32bit)を選択する。

初期設定

適当に選んだRaspberry Piで起動し、共通の設定を行ってから、SDカードのイメージを取り、他のSDカードにコピーしていく。

sudo apt update
sudo apt upgrade
sudo apt install net-tools


ローカルで使うだけなので、SSH接続でパスワード認証出来るようにしてしまう。

sudo vi /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
下記の通り修正

PasswordAuthentication yes


静的なIPを割り当てる。
イメージを他の機器にコピーするときはIPを変更する。

sudo vi /etc/netplan/50-cloud-init.yaml
下記の通り修正

network:
    ethernets:
        eth0:
            dhcp4: false
            dhcp6: false
            addresses: [ローカルIP/24] #設定したIPは機器ごとに後で変更すること
            routes:
                - to: default
                  via: ゲートウェイIP #無いならroutesの設定いらない
            nameservers:
                addresses: [8.8.8.8, 8.8.4.4]
    version: 2


ホスト名を変更可能にする。

sudo netplan try
sudo vi /etc/cloud/cloud.cfg
下記の通り修正

preserve_hostname: true


ホスト名を設定する。
イメージを他の機器にコピーするときはホスト名を変更する。

sudo hostnamectl set-hostname rp1 #設定したホスト名は機器ごとに後で変更すること

Load Balancer

初期設定したイメージから作業を始める。

nginxをインストールする。

sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo vi /etc/nginx/nginx.conf
下記の通り修正

worker_connections 16;


Web Serverとしては使用しないのでdefaultの設定は読み込まないようにし、Load Balancer用の設定ファイルを作成する。

sudo rm /etc/nginx/sites-enabled/default
sudo vi /etc/nginx/conf.d/loadbalancer.conf
下記の通り新規作成

upstream backend {
    #least_conn;
    #ip_hash; #設定しても良いが、ラウンドロビンされていることを直ぐに確認したいので今回はなし
    server Reverse Proxy Server No.1のIP:80;
    server Reverse Proxy Server No.2のIP:80;
}

server {
    listen 80 default_server;
    server_name _;

    location / {
        proxy_pass http://backend;
    }
}


設定ファイルに誤りがないことを確認し、nginxを再起動する。

sudo nginx -t
sudo systemctl restart nginx

Reverse Proxy

初期設定したイメージから作業を始める。
Reverse Proxy Serverは2台作成する。

nginxをインストールする。

sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo vi /etc/nginx/nginx.conf
下記の通り修正

worker_connections 16;


Web Serverとしては使用しないのでdefaultの設定は読み込まれないようにし、Reverse Proxy用の設定ファイルを作成する。

sudo rm /etc/nginx/sites-enabled/default
sudo vi /etc/nginx/conf.d/proxy.conf
下記の通り新規作成

server {
    listen 80 default_server;
    server_name _;

    location / {
        proxy_pass http://Application Server1のIP:8080;
    }
}


もう1台のReverse Proxy Serverも同様に設定する。

Application Serverも2台構成にする予定のため、
proxy_pass http://Application Server1のIP:8080;のIPはそれぞれ別のIPになる。

設定ファイルに誤りがないことを確認し、nginxを再起動する。

sudo nginx -t
sudo systemctl restart nginx

Application Server

Java 21のインストール
Tomcat 10のインストール

Tomcat 10はインストールパッケージがなかったため、Apache Tomcatのページからダウンロードして解凍する。実行ユーザー、シンボリックリンクも作っておく。

sudo apt install openjdk-21-jdk

cd /opt
sudo tar xvfz /mnt/nfs/apache-tomcat-10.1.20.tar.gz
sudo ln -s /opt/apache-tomcat-10.1.20 tomcat
sudo useradd -m -U -d /opt/tomcat -s /bin/false tomcat
sudo chown -R tomcat: /opt/apache-tomcat-10.1.20


起動用のスクリプトを作成する。

sudo vi /usr/lib/systemd/system/tomcat.service
下記の通り新規作成

#
# Systemd unit file for Apache Tomcat
#
[Unit]
Description=Apache Tomcat 10 Web Application Server
After=network.target
[Service]
# Configuration
Environment="JAVA_OPTS=-Djava.awt.headless=true"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_OPTS=-Xmx1024m -server"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
# Lifecycle
Type=forking
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
Restart=always
PIDFile=/opt/tomcat/temp/tomcat.pid
# Logging
SyslogIdentifier=tomcat
# Security
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target


自動起動は設定しない。

sudo systemctl daemon-reload
sudo systemctl start tomcat


もう1台のApplication Serverも同様に設定する。

起動確認が終わったら一度Tomcatを停止して、レプリケーションの設定をserver.xmlに追記する。

今回は簡単に確認するため、SimpleTcpClusterを使用している。別の機会にSpring Sessionを使用した方法を試してみたい。

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
         channelSendOptions="6">
  <Manager className="org.apache.catalina.ha.session.BackupManager"
           expireSessionsOnShutdown="false"
           notifyListenersOnReplication="true"
           mapSendOptions="6"/>
  <Channel className="org.apache.catalina.tribes.group.GroupChannel">
    <Membership className="org.apache.catalina.tribes.membership.McastService"
                address="228.0.0.4"
                port="45564"
                frequency="500"
                dropTime="3000"/>
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
              address="auto"
              port="5000"
              selectorTimeout="100"
              maxThreads="6"/>

    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
      <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
    </Sender>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
  </Channel>

  <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>

  <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>


もう1台のApplication Serverも同様に設定する。

DB Server

まずは3台に対してMariaDBをインストールして初期設定を実行する。

sudo apt install mariadb-server
sudo systemctl stop mariadb
sudo mkdir /mnt/ssd/mariadb


データの保存先を変更し、リモートアクセス可能にする。

sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
下記の通り修正

datadir                 = /mnt/ssd/mariadb
#bind-address            = 127.0.0.1


MariaDBが外部フォルダにアクセス出来るようにApplication Armorのセキュリティ設定を変更する。

sudo vi /etc/apparmor.d/tunables/alias
下記の通り修正

alias /var/lib/mysql/ -> /mnt/ssd/mariadb/,


Application Armorを再起動し、MariaDBのデータフォルダをコピーする。

sudo systemctl restart apparmor
sudo rsync -avuz /var/lib/mysql/ /mnt/ssd/mariadb


MariaDBを起動し、初期設定する。

sudo systemctl start mariadb
sudo mysql_secure_installation
Enter current password for root (enter for none):
Switch to unix_socket authentication [Y/n] n
Change the root password? [Y/n] 
New password:
Re-enter new password:
Remove anonymous users? [Y/n] y
Disallow root login remotely? [Y/n] n
Remove test database and access to it? [Y/n] y
Reload privilege tables now? [Y/n] y

mysql -u root -p
grant all privileges on *.* to root@"%" identified by 'password' with grant option;
exit;

sudo systemctl restart mariadb


接続確認を行ったら、一度停止する。

MariaDBをインストールすると自動的にGalera Clusterがインストールされているはず。

自動起動を無効にし、バイナリロギングを有効にする。

sudo systemctl disable mariadb
sudo mkdir -p /mnt/ssd/log/mariadb
sudo chown -R mysql: /mnt/ssd/log/mariadb
sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
下記の通り修正

server-id               = 1 # サーバー毎に変更
log_bin                 = /mnt/ssd/log/mariadb/mariadb-bin.log


Galera Clusterの設定を行う。

sudo vi /etc/mysql/mariadb.conf.d/60-galera.cnf
下記の通り修正

[galera]
# Mandatory settings
wsrep_on                 = ON
wsrep_cluster_name       = "MariaDB Galera Cluster"
wsrep_cluster_address    = gcomm://サーバーIP1,サーバーIP2,サーバーIP3
binlog_format            = row
default_storage_engine   = InnoDB
innodb_autoinc_lock_mode = 2
wsrep_provider           = /usr/lib/galera/libgalera_smm.so

# Allow server to accept connections on all interfaces.
bind-address = 0.0.0.0


最初の1台は下記コマンドで実行する。以降は通常の起動コマンドで起動する。

sudo galera_new_cluster
※再起動時の注意

データが保存されているフォルダにgrastate.datというファイルがあるので、safe_to_bootstrap: 1となっているサーバーからgalera_new_clusterで起動する。

sudo less /mnt/ssd/mariadb/grastate.dat

# GALERA saved state
version: 2.1
uuid:    xxx
seqno:   -1
safe_to_bootstrap: 1


それ以外だと、エラー(WSREP: It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates. To force cluster bootstrap with this node, edit the grastate.dat)となって起動できない。

動作確認

Javaアプリを作りクラスタリングされているか確認する。

Raspberry Pi 4のBiosは表示されるがOSが起動しない

Raspberry Pi 4のBiosは表示されるがOSが起動しない問題が発生した。

SSDの破損、モニタとの相性等確認したが問題なしで、確認を進めた結果、原因は電源供給のUSBアダプタにあった。

最近下記のUSBアダプタを購入したので、それをRaspberry Piの電源に使用したところ、今回の現象が発生した。Biosは表示されるがOSが起動する段階になるとRaspberry Piの電源が落ちてしまう。

下記の少し古いモデルに変更すると問題なくRaspberry OSが起動した。

USBケーブル等全て同じにして、USBアダプタのみの変更で問題が解決したので、おそらくUSBアダプタとの相性に問題があったのだと思う。

Raspberry Pi 4 Model Bを設定する

やりたいこと

Raspberry Pi OS動かして、デスクトップ画面を表示する。

Raspberry Pi 4を購入する

今は品薄で、なかなか手が入らない。仕方がないので必要なものが揃っている下記を購入した。

Raspberry Pi OSをインストールする

下記からRaspberry Pi Imagerをダウンロードしてインストールする。
https://www.raspberrypi.com/software/

SDカードを挿入し、Raspberry Pi Imagerを起動して、OS選択し下記の通り実行する。

OSをインストールしたSDカードをRaspberry Piに挿入し電源を入れる。

※4/21追記
コンパクトなキーボードとマウスが欲しかったので下記を購入した。USBレシーバーも付属しているので、互換性を気にしなくて良く、すぐ使用できる点が気に入った。

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

前回、HDFS高可用性(HA)機能を設定した。今回はResourceManagerをHA化する。

準備

SDカードの寿命を縮めるため、あまり推奨は出来ないが、本格的にメモリ不足のため、念のためスワップ領域を作成しておく。

sudo mkdir /var/swap
sudo dd if=/dev/zero of=/var/swap/swap0 bs=1M count=2048
sudo chmod 600 /var/swap/swap0
sudo mkswap /var/swap/swap0
sudo swapon /var/swap/swap0
#再起動後も自動的にスワップ領域が適用されるようにする
sudo vi /etc/fstab
#下記の行を追加する
/var/swap/swap0 none swap defaults 0 0

2021/02/25 追記:
SDカードへ書き込みを少なくするため、スワップの頻度が少なくなるように調整する。

#スワップの閾値を確認
cat /proc/sys/vm/swappiness
> 60

sudo vi /etc/sysctl.conf
#下記を追記する(物理メモリが1%以下になったらスワップを開始)
vm.swappiness = 1

#設定を反映
sudo sysctl -p
cat /proc/sys/vm/swappiness
> 1

サーバー構成を検討

NameNodeは奇数で構成する必要があったが、ResourceManagerはそのような制約はないため、下記の2台構成とする。

master1・・・Zookeeper,NameNode(Standby), ResourceManager(Standby)
master2・・・Zookeeper,NameNode(Standby), ResourceManager(Active)
master3・・・Zookeeper,NameNode(Active)
slave1・・・DataNode, NodeManager
slave2・・・DataNode, NodeManager
slave3・・・DataNode, NodeManager
slave4・・・DataNode, NodeManager

ResourceManager高可用性(HA)機能の設定

master1~2のetc/hadoop/yarn-site.xmlを編集する。(メモリ関連の記述は省略)

   <property>
    <name>yarn.resourcemanager.ha.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>yarn.resourcemanager.cluster-id</name>
    <value>rpcluster_r</value>
  </property>
  <property>
    <name>yarn.resourcemanager.ha.rm-ids</name>
    <value>rm1,rm2</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname.rm1</name>
    <value>master1</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname.rm2</name>
    <value>master2</value>
  </property>
  <property>
    <name>yarn.resourcemanager.webapp.address.rm1</name>
    <value>master1:8088</value>
  </property>
  <property>
    <name>yarn.resourcemanager.webapp.address.rm2</name>
    <value>master2:8088</value>
  </property>
  <property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
  </property>
  <property>
    <name>yarn.resourcemanager.recovery.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>yarn.resourcemanager.store.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
  </property>
  <property>
    <name>hadoop.zk.address</name>
    <value>master1:2181,master2:2181,master3:2181</value>
  </property>

slave1~4のetc/hadoop/yarn-site.xmlを編集する。(メモリ関連の記述は省略)

   <property>
    <name>yarn.resourcemanager.ha.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>yarn.resourcemanager.cluster-id</name>
    <value>rpcluster_r</value>
  </property>
  <property>
    <name>yarn.resourcemanager.ha.rm-ids</name>
    <value>rm1,rm2</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname.rm1</name>
    <value>master1</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname.rm2</name>
    <value>master2</value>
  </property>
  <property>
    <name>yarn.resourcemanager.webapp.address.rm1</name>
    <value>master1:8088</value>
  </property>
  <property>
    <name>yarn.resourcemanager.webapp.address.rm2</name>
    <value>master2:8088</value>
  </property>
  <property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
  </property>
  <property>
    <name>yarn.resourcemanager.recovery.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>yarn.resourcemanager.store.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
  </property>
  <property>
    <name>hadoop.zk.address</name>
    <value>master1:2181,master2:2181,master3:2181</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>

ResourceManagerの起動

master1で下記コマンドを実行し、ResourceManagerを起動する。

$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

最終的に下記の結果が表示された。スワップを設定したからか今回は前回に比べて50秒ほど早く処理が終わった。

Job Finished in 256.114 seconds
Estimated value of Pi is 3.14120000000000000000

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

前回の構成ではNameNodeが単一障害点になっていたため、HDFS高可用性(HA)機能を試してみる。

サーバー構成を検討

スタンバイのNameNodeがSecondary NameNodeの代わりになるため、Secondary NameNodeは不要になる。

master1・・・Zookeeper,NameNode(Active), ResourceManager
master2・・・Zookeeper,NameNode(Standby)
master3・・・Zookeeper,NameNode(Standby)
slave1・・・DataNode, NodeManager
slave2・・・DataNode, NodeManager
slave3・・・DataNode, NodeManager
slave4・・・DataNode, NodeManager

Zookeeperをインストール

master1~3にインストールする。

wget https://downloads.apache.org/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
tar xvfz apache-zookeeper-3.6.2-bin.tar.gz
ln -nfs /home/xxx/apache-zookeeper-3.6.2-bin zookeeper-latest

master1~3のconf/zoo.cfgを編集する。

tickTime=2000
initLimit=5
syncLimit=2
dataDir=/home/xxx/zookeeper-latest/data
clientPort=2181
server.1=master1:2888:3888
server.2=master2:2888:3888
server.3=master3:2888:3888

master1~3の~/zookeeper-latest/data/myidファイルを作成し、server.xで指定した数字と同じ数字を記入する。
その後、master1~3のZookeeperを順次起動する。

~/zookeeper-latest/bin/zkServer.sh start

HDFS高可用性(HA)機能の設定

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

  <property>
    <name>dfs.nameservices</name>
    <value>rpcluster</value>
  </property>
  <property>
    <name>dfs.ha.namenodes.rpcluster</name>
    <value>nn1,nn2,nn3</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn1</name>
    <value>master1:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn2</name>
    <value>master2:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn3</name>
    <value>master3:8020</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn1</name>
    <value>master1:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn2</name>
    <value>master2:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn3</name>
    <value>master3:9870</value>
  </property>
  <property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>qjournal://master1:8485;master2:8485;master3:8485/rpcluster</value>
  </property>
  <property>
    <name>dfs.client.failover.proxy.provider.rpcluster</name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
  </property>
  <property>
    <name>dfs.ha.fencing.methods</name>
    <value>sshfence</value>
  </property>
  <property>
    <name>dfs.ha.fencing.ssh.private-key-files</name>
    <value>/home/ubuntu/.ssh/id_rsa</value>
  </property>
  <property>
    <name>dfs.ha.fencing.methods</name>
    <value>shell(/bin/true)</value>
  </property>
  <property>
    <name>dfs.ha.automatic-failover.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>ha.zookeeper.quorum</name>
    <value>master1:2181,master2:2181,master3:2181</value>
  </property>
  <property>
    <name>dfs.journalnode.edits.dir</name>
    <value>/home/ubuntu/hadoop-latest/dfs/journalnode</value>
  </property>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>/home/ubuntu/hadoop-latest/dfs/namenode</value>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>3</value>
  </property>

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

   <property>
    <name>dfs.nameservices</name>
    <value>rpcluster</value>
  </property>
  <property>
    <name>dfs.ha.namenodes.rpcluster</name>
    <value>nn1,nn2,nn3</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn1</name>
    <value>master1:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn2</name>
    <value>master2:8020</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-address.rpcluster.nn3</name>
    <value>master3:8020</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn1</name>
    <value>master1:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn2</name>
    <value>master2:9870</value>
  </property>
  <property>
    <name>dfs.namenode.http-address.rpcluster.nn3</name>
    <value>master3:9870</value>
  </property>
  <property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>qjournal://master1:8485;master2:8485;master3:8485/rpcluster</value>
  </property>
  <property>
    <name>dfs.client.failover.proxy.provider.rpcluster</name>
    <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
  </property>
  <property>
    <name>dfs.ha.fencing.methods</name>
    <value>sshfence</value>
  </property>
  <property>
    <name>dfs.ha.fencing.ssh.private-key-files</name>
    <value>/home/ubuntu/.ssh/id_rsa</value>
  </property>
  <property>
    <name>dfs.ha.fencing.methods</name>
    <value>shell(/bin/true)</value>
  </property>
  <property>
    <name>dfs.ha.automatic-failover.enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>ha.zookeeper.quorum</name>
    <value>master1:2181,master2:2181,master3:2181</value>
  </property>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>/mnt/usbhdd/hadoop/data</value>
  </property>

master1~3、slave1~4のetc/hadoop/core-site.xmlを編集する。

  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://rpcluster</value>
  </property>
  <property>
    <name>ha.zookeeper.quorum</name>
    <value>master1:2181,master2:2181,master3:2181</value>
  </property>

HDFSのフォーマット

master1~3のJournalNodeのみ起動する。

$HADOOP_HOME/bin/hdfs --daemon start journalnode

master1でNameNodeをフォーマットする。

$HADOOP_HOME/bin/hdfs namenode -format
$HADOOP_HOME/bin/hdfs namenode -initializeSharedEdits -force
$HADOOP_HOME/bin/hdfs zkfc -formatZK -force

master1のNameNodeのみ起動する。

$HADOOP_HOME/bin/hdfs --daemon start namenode

master2、3でスタンバイ用のNameNodeを作成する。

$HADOOP_HOME/bin/hdfs namenode -bootstrapStandby -force

HDFSの起動

全てのNameNode、JournalNodeを停止する。

$HADOOP_HOME/bin/hdfs --daemon stop namenode
$HADOOP_HOME/bin/hdfs --daemon stop journalnode

master1で下記コマンドを実行し、HDFSを再起動する。

$HADOOP_HOME/sbin/start-dfs.sh

次回、ResourceManagerをHA化する。

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モードでの動作を確認できたので今回はここまで。

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