さて、別記事でクラスタの構築をやっていますが、手作業でやるのは面倒ですよね。私は面倒です。
スケールさせられるのが分散処理基盤たるHadoopの魅力。ノード追加もストレスなくやりたいものです。Ansibleでやってしまいましょう。
前提構成
クラスタとして構築する予定の各ノードが、管理対象としてセットアップされているものとします。具体的な作業はAnsibleの解説を参照して下さい。
Hadoopの実行ユーザuserとしてコントローラにログインし、AnsibleのPlaybookを実行していきます。
また、Hadoopインストール用のtarballはコントローラにダウンロードしておきます。
今回はコントローラに置いたtarballを管理対象(クラスタの各ノード)にコピー・解凍することにします。
もちろん、get_url
してもいいのですが、プロキシ環境で設定が面倒だったためです。
1 | wget http://ftp.jaist.ac.jp/pub/apache/hadoop/common/hadoop-2.6.5/hadoop-2.6.5.tar.gz /tmp |
ファイルの準備
コントローラで作業用のディレクトリを作成し、必要なファイル一式を作成しましょう。
ディレクトリ構成の完成像はこんな感じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (current directory) ├─prep.yml (前半Playbook) ├─sendkey.yml (後半Playbook) ├─combine_keys.py (Python スクリプト) ├─tmp (後ほど作成) │ (以下をここで準備) ├─assets │ ├─core-site.xml │ ├─hdfs-site.xml │ ├─yarn-site.xml │ └─ssh_conf ├─group_vars │ └─targets.yml └─inventory └─hosts |
Playbook本体のprep.yml
ファイルとsendkey.yml
ファイルの他、Playbookに読み込ませるために、下記のようなファイルを準備しておきます。
1 2 3 4 | [targets] 192.168.100.10 192.168.100.11 192.168.100.12 |
1 2 3 4 | Host * UserKnownHostsFile /dev/null StrictHostKeyChecking no LogLevel quiet |
1 | hadoop_dir: hadoop-2.6.5 |
core-site.xml
ファイルやhdfs-site.xml
ファイル、yarn-site.xml
ファイルは元の記事と同じ内容で作成してassets
サブディレクトリに格納しておきます。
一応再掲。
1 2 3 4 5 6 | <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://192.168.100.10:9000</value> </property> </configuration> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.data.dir</name> <value>/var/lib/hadoop/data</value> </property> <property> <name>dfs.name.dir</name> <value>/var/lib/hadoop/name</value> </property> <property> <name>dfs.namenode.datanode.registration.ip-hostname-check</name> <value>false</value> </property> </configuration> |
1 2 3 4 5 6 | <configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>192.168.100.10</value> </property> </configuration> |
Playbookの作成と実行:Hadoopのインストール
まずはHadoopをインストールする諸々をprep.yml
ファイルとして作成していきます。
一部にroot権限が必要なものがあったので、基本become: yes
で昇格、不要な場合のみ除外(become: no
)していきます。
あまり賢いPlaybookではありませんが、まあ動けばよし、ということで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | - hosts: targets become: yes tasks: - name: install modules apt: name={{ item }} state=present update_cache=yes with_items: - openjdk-8-jdk - stat: path=/home/user/.ssh/id_rsa register: id_rsa_path - name: generate ssh key become: no shell: ssh-keygen -P "" -t rsa -f /home/user/.ssh/id_rsa when: not id_rsa_path.stat.exists - name: configure ssh become: no copy: src=assets/ssh_conf dest=/home/user/.ssh/config mode=600 - name: download ssh key fetch: src=/home/user/.ssh/id_rsa.pub dest=tmp/ - name: download known ssh key fetch: src=/home/user/.ssh/authorized_keys dest=tmp/ - name: update hosts (/etc/hosts) file lineinfile: > dest=/etc/hosts line={{ item }} with_items: - '192.168.100.10 ha1' - '192.168.100.11 ha2' - '192.168.100.12 ha3' - name: remove itself from hosts file lineinfile: >- dest=/etc/hosts regexp='{{ inventory_hostname }}' line='' - name: expand archive and send extracts unarchive: src=/tmp/{{ hadoop_dir }}.tar.gz dest=/var/local - name: make link to hadoop file: dest=/usr/local/hadoop src=/var/local/{{ hadoop_dir }} state=link - name: append environment variables to hadoop-config.sh blockinfile: path: /usr/local/hadoop/libexec/hadoop-config.sh content: | export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export HADOOP_HOME=/usr/local/hadoop export HADOOP_PREFIX=/usr/local/hadoop export HADOOP_COMMON_HOME=/usr/local/hadoop export HADOOP_HDFS_HOME=/usr/local/hadoop export HADOOP_MAPRED_HOME=/usr/local/hadoop export HADOOP_YARN_HOME=/usr/local/hadoop export HADOOP_CONF_DIR=/usr/local/hadoop/etc/hadoop export YARN_CONF_DIR=$HADOOP_PREFIX/etc/hadoop insertbefore: BOF - name: send hadoop configuration xml files copy: src=assets/{{ item }} dest=/usr/local/hadoop/etc/hadoop/{{ item }} with_items: - hdfs-site.xml - core-site.xml - yarn-site.xml - name: create directories for hdfs file: path={{ item }} state=directory mode=775 with_items: - /var/lib/hadoop/data - /var/lib/hadoop/name - name: change owners file: dest={{ item }} owner=user group=root mode=775 recurse=yes with_items: - /var/lib/hadoop/data - /var/lib/hadoop/name - /var/local/{{ hadoop_dir }} |
下記のように、権限昇格用のオプションをつけてPlaybookを実行します。
また、公開鍵をダウンロード(fetch
モジュール)するためのサブディレクトリ(tmp
)を事前に作成しておきます。
1 2 | mkdir -p tmp ansible-playbook -i inventory/hosts prep.yml --ask-become-pass |
Playbookの作成と実行:SSH公開鍵の転送
公開鍵を対象ノードに登録するには、各ノードの~/.ssh/authorized_keys
ファイルにアクセス元の鍵をすべて列挙しておく必要があります。
前のPlaybookで全ての公開鍵(と登録済の鍵)を個別のファイルとダウンロードしておいたので、後で一括でコピーするために、一旦これらを全て一つのファイルに書き出しておきます。
Ansibleで自動化しても良かったのですが、ちまちまタスクを書くのが面倒だったのでPythonスクリプト(combine_keys.py
)を組んで代替としました。
tmp
サブディレクトリ以下にあるファイルを読み、内容をまとめて同じファイルtmp/keys.pub
に書き出すだけのスクリプトです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import os # file name for combined public keys keyfile_name = "keys.pub" def _search_files(base_dir): for root, dirs, files in os.walk(base_dir): for file_name in files: # skip existing key file if file_name.endswith(keyfile_name): continue file_path = os.path.join(root, file_name) with open(file_path, 'r') as f: yield f.readlines() if __name__ == "__main__": src_dir = "tmp" dest_file = os.path.join(src_dir, keyfile_name) print "combine key files to: {}".format(dest_file) if not os.path.isdir(src_dir): raise Exception("source directory not found") contents = [] for file_content in _search_files(src_dir): contents.extend(file_content) with open(dest_file, 'w') as f: f.writelines(contents) |
sendkey.yml
ファイルとしてPlaybookを作成します。Pythonスクリプトで集めた公開鍵を各ノードにコピーするタスクです。
1 2 3 4 5 6 | - hosts: targets become: no gather_facts: no tasks: - name: send authorized key to targets copy: src=tmp/keys.pub dest=/home/user/.ssh/authorized_keys mode=600 |
上記で作成したスクリプトとPlaybookを順に実行します。
1 2 | python combine_keys.py ansible-playbook -i inventory/hosts sendkey.yml |
残りの作業
あとはHDFSの初期フォーマットと、各ノード機能の立ち上げが残っていますが、これは各マシンにログインして実行しましょう。
Masterノードへログインして行う作業:
1 2 3 4 5 6 | # コントローラからMasterノードへログイン ssh user@192.168.100.10 # Masterノードで実行 /usr/local/hadoop/bin/hdfs namenode -format /usr/local/hadoop/sbin/hadoop-daemon.sh start namenode /usr/local/hadoop/sbin/yarn-daemon.sh start resourcemanager |
Slaveノードへログインして行う作業(該当ノード台数分だけ繰り返す):
1 2 3 4 5 | # コントローラからSlaveノードへログイン ssh user@192.168.100.11 # Slaveノードで実行 /usr/local/hadoop/sbin/hadoop-daemon.sh start datanode /usr/local/hadoop/sbin/yarn-daemon.sh start nodemanager |