Przedstawię Wam dzisiaj jak może wyglądać ekspresowa instalacja Hadoop na potrzeby rozpoznania/testów/zabawy. Wykorzystam do tego oprogramowanie do automatyzacji procesów administracyjnych Chef.
Instalacja Ubuntu
W pierwszym kroku musimy przygotować sobie instalację Ubuntu Linux 14.04.1 w wersji Server 64bit. Wykonamy możliwie najprostszy wariant, oszczędzając dzięki temu czas oraz przestrzeń dyskową, skupiając się jedynie na istotnym dla nas oprogramowaniu.
Swój przykład opieram na lokalnej maszynie wirtualnej utworzonej pod Oracle VirtualBox. Parametry maszyny:
- 2x CPU
- 2GB RAM
- 20GB HDD
Proces instalacji udokumentowałem na YouTube. Pod filmem wrzucam kolejne kroki wykonane w ramach instalacji. Cały proces zajmuje poniżej 8 minut.
Wybrane kolejno parametry:
- Język instalacji – English (domyślna wartość)
- Install Ubuntu Server (domyślna wartość)
- Język systemu – English (domyślna wartość)
- Lokalizacja – other
- Lokalizacja – Europe
- Lokalizacja – Poland
- Locale – en_US.UTF-8 (domyślna wartosć)
- Detect Keyboard Layout – No (domyślny wybór)
- Keyboard layout – Polish (domyślny wybór)
- Keyboard layout – Polish (domyślny wybór)
- Hostname – hadoop
- Full Name – Maciej Stopa
- User Name – administrator
- Password – xxx
- Re-enter Password – xxx
- Encrypt Home Directory – No (domyślny wybór)
- Timezone Europe/Warsaw – Yes (domyślny wybór)
- Guided – use entire disk and set up LVM (domyślny wybór)
- SCSI – 21,5GB (domyślna wartość)
- Write changes and configure LVM – Yes
- Amount of volume group to use – 21.2GB (domyślna wartość)
- Write changes to disk – Yes
- Http Proxy – u mnie brak – pusto
- How do you want to manage upgrades – No automatic (domyślny wybór)
- Choose software – OpenSSH Server
- Install GRUB to master boot record – Yes (domyślny wybór)
- Finish the installation – Continue (domyślny wybrór)
Założenia środowiska
- nazwa hosta to hadoop
- nazwa użytkownika z uprawnieniami administratora to administrator
- wykorzystana dystrybucja Ubuntu Server 14.04.1 LTS
Instalacja Chef
Logujemy się na użytkownika administrator i rozpoczynamy od utworzenia struktury katalogów w których nasz kucharz (Chef) będzie trzymać receptury oraz role do instalacji. Poniższe komendy nie wymagają uprawnień super użytkownika.
mkdir -p /home/administrator/chef-solo mkdir -p /home/administrator/chef-repo/.chef mkdir -p /home/administrator/chef-repo/cookbooks mkdir -p /home/administrator/chef-repo/roles
Następnie potrzebujemy zainstalować naszego Kucharza czyli oprogramowanie Chef które zrobi za nas 99% trudnej pracy przy konfiguracji.
Startujemy od zaktualizowania listy dostępnych w Ubuntu pakietów (komenda apt-get update), opierając się na najnowszych dla danej dystrubucji. Potrzebujemy do tego uprawnień superużytkownika (komenda sudo -s).
sudo -s apt-get update
Ze spraw kosmetycznych specyficznych dla Ubuntu, musimy wyłączyć czyszczenie katalogu /tmp po restarcie – ponieważ nasz Hadoop sporo rzeczy będzie tam przechowywał. Dla uproszczenia środowiska nie modyfikujemy tego zachowania, a jedynie wyłączamy czyszczenie /tmp. Edytujemy plik /etc/default/rcS i dopisujemy poniższą linię:
TMPTIME=-1
Instalację Chef’a wykona za nas automatyczny skrypt, kolejny raz upraszczając cały proces. Wykonujemy poniższą komendę już z uprawnieniami administratora przyznanymi przez sudo
curl -L https://www.opscode.com/chef/install.sh | bash
W tym momencie mamy zainstalowane oprogramowanie do automatyzowania naszej instalacji.
Konfiguracja Chef’a
Tworzymy katalog z konfiguracją Chef’a oraz edytujemy plik /etc/chef/solo.rb przy pomocy ulubionego edytora (u mnie vi)
mkdir /etc/chef vi /etc/chef/solo.rb
Zawartość pliku /etc/chef/solo.rb
file_cache_path "/home/administrator/chef-solo" repo = '/home/administrator/chef-repo' cookbook_path repo + '/cookbooks' data_bag_path repo + '/data_bags' role_path repo + '/roles' log_location STDOUT
Potrzebujemy jeszcze tylko skonfigurować automagiczny menedżer receptur Knife, który pobierze dla nas to co będzie dalej wymagane, czyli receptury dla apt, yum, java oraz hadoop. Przechodzimy do katalogu repozytorium Chef’a i edytujemy plik /home/administrator/chef-repo/.chef/knife.rb
cd /home/administrator/chef-repo vi /home/administrator/chef-repo/.chef/knife.rb
Zawartość pliku /home/administrator/chef-repo/.chef/knife.rb
cookbook_path [ '/root/chef-repo/cookbooks' ]
Przygotowanie receptur
Powoli pozwalamy magii działać, czyli pobieramy nasze receptury dla środowiska narzędziem Knife, pozostając w katalogu /home/administrator/chef-repo
knife cookbook site download apt knife cookbook site download yum knife cookbook site download java knife cookbook site download hadoop knife cookbook site download selinux knife cookbook site download sysctl knife cookbook site download ohai knife cookbook site download ulimit
Wchodzimy do katalogu z recepturami (cookbooks) aby je rozpakować
cd /home/administrator/chef-repo/cookbooks find .. -name "*.gz" -exec tar zxvf "{}" \;
Pozostaje nam przygotować role dla Chef’a czyli wybrać wersje oprogramowania które mają zostać zainstalowane – dostawcę Java (Oracle, OpenJDK) oraz dostawcę stacka Hadoop. Edytujemy kolejno pliki /home/administrator/chef-repo/roles/java.rb oraz /home/administrator/chef-repo/roles/hadoop.rb
Zawartość pliku /home/administrator/chef-repo/roles/java.rb
name "java" description "Install Oracle Java" default_attributes( "java" => { "install_flavor" => "oracle", "jdk_version" => "7", "set_etc_environment" => true, "oracle" => { "accept_oracle_download_terms" => true } } ) run_list( "recipe[ java]" )
Zawartość pliku /home/administrator/chef-repo/roles/hadoop.rb
name "hadoop" description "set Hadoop attributes" default_attributes( "hadoop" => { "distribution" => "bigtop", "core_site" => { "fs.defaultFS" => "hdfs://hadoop" }, "yarn_site" => { "yarn.resourcemanager.hostname" => "hadoop" } } ) run_list( "recipe[hadoop]" )
Gdy konfiguracja Chef’a jest gotowa, wskazujemy które role oraz receptury mają zostać wykonane w celu przygotowania naszego środowiska Hadoop. W tym celu przygotowujemy ostatni plik, niejako naszą recepturę instalacji komponentów. Edytujemy plik vi /home/administrator/chef-repo/hadoop.json.
Zawartość pliku vi /home/administrator/chef-repo/hadoop.json
{ "run_list": [ "role[ java]", "recipe[ java]", "role[hadoop]", "recipe[hadoop::hadoop_hdfs_namenode]", "recipe[hadoop::hadoop_yarn_nodemanager]", "recipe[hadoop::hadoop_yarn_resourcemanager]", "recipe[hadoop::hadoop_hdfs_datanode]", "recipe[hadoop::hbase]", "recipe[hadoop::hive]", "recipe[hadoop::hive_metastore]", "recipe[hadoop::oozie]", "recipe[hadoop::pig]", "recipe[hadoop::zookeeper]" ] }
Siła szefa kuchni
Pozostając w katalogu /home/administrator/chef-repo i posiadając nadal uprawnienia administratora, pozwalamy na przygotowanie naszego środowiska. Chef zajmie się wszystkim od pobrania pakietów, przez ich konfigurację, integrację ze sobą, utworzenie i uruchomienie niezbędnych serwisów. MAGIC!
chef-solo -c /etc/chef/solo.rb -j hadoop.json
Należy teraz zainicjalizować naszego Hadoop NameNode
su -l hdfs hadoop namenode -format exit
Uruchamiamy NameNode oraz DataNode
service hadoop-hdfs-namenode start service hadoop-hdfs-datanode start
Formatujemy HDFS i przygotowujemy katalogi dla usług
/usr/lib/hadoop/libexec/init-hdfs.sh
Uruchamiamy Hive Metastore
service hive-metastore start
Jeżeli nie pomyliliśmy się i wszystkie komendy i wklejki zostały wykonane poprawnie, po kilku minutach otrzymujemy w pełni skonfigurowane środowisko z dostępnymi Hadoop, Hive, Pig, YARN, Oozie i Zookeeper.
Testy
Na potrzeby testów wykorzystam przykład z HDP polegający na pobraniu archiwów meczy baseball’owych, wrzuceniu ich na HDFS, import do Hive i wykorzystanie MapReduce do wykonania kilku Select’ów.
Z uprawnieniami superużytkownika doinstalowujemy unzip i tworzymy katalog dla danych /home/administrator/data następnie przechodząc do niego.
apt-get install unzip mkdir /home/administrator/data cd /home/administrator/data
Pobieramy plik z danymi i rozpakowujemy go
wget http://seanlahman.com/files/database/lahman591-csv.zip unzip lahman591-csv.zip
Umieszczamy kopię pliku na HDFS, tak aby węzły (w naszym przypadku jeden) miały dostęp do tych danych, jednocześnie udostępniając je dla usług np Hive.
hdfs dfs -copyFromLocal Batting.csv /user/root/
Uruchamiamy Hive, który pozwoli na potraktowanie danych CSV prostymi zapytaniami SQL wykorzystując mechanizmy Hadoop i MapReduce. Jeżeli wszystko jest dobrze skonfigurowane, powinniśmy zostać przywitani promptem Hive czyli hive>.
root@hadoop:~/data# hive Logging initialized using configuration in jar:file:/usr/lib/hive/lib/hive-common-0.13.0.jar!/hive-log4j.properties hive>
Kolejne komendy wpisujemy w prompcie Hive. Tworzymy tabelę tymczasową temp_batting do której przypisujemy nasz plik z HDFS /user/root/Batting.csv. Na jego podstawie utworzymy docelową tabelę z danymi batting które zostaną wyodrębnione przy pomocy Regexp’ów. Nie przejmuj się komunikatami Warning – mówią one o tym że działamy na pojedynczym węźle i pewne zmienne dotyczące podziału zadań pomiędzy większą ilość węzłów poprostu nie są skonfigurowane, w naszych przykładach nie jest to żadnym problemem.
CREATE TABLE temp_batting (col_value STRING); LOAD DATA INPATH '/user/root/Batting.csv' OVERWRITE INTO TABLE temp_batting; CREATE TABLE batting (player_id STRING, year INT, runs INT); INSERT OVERWRITE TABLE batting SELECT regexp_extract(col_value, '^(?:([^,]*)\,?){1}', 1) player_id, regexp_extract(col_value, '^(?:([^,]*)\,?){2}', 1) year, regexp_extract(col_value, '^(?:([^,]*)\,?){9}', 1) run FROM temp_batting;
Efekt powyższych komend powinien wyglądać mniej więcej tak:
hive> CREATE TABLE temp_batting (col_value STRING); OK Time taken: 0.215 seconds hive> LOAD DATA INPATH '/user/root/Batting.csv' OVERWRITE INTO TABLE temp_batting; Loading data to table default.temp_batting Table default.temp_batting stats: [numFiles=1, numRows=0, totalSize=6398990, rawDataSize=0] OK Time taken: 1.017 seconds hive> INSERT OVERWRITE TABLE batting > SELECT > regexp_extract(col_value, '^(?:([^,]*)\,?){1}', 1) player_id, > regexp_extract(col_value, '^(?:([^,]*)\,?){2}', 1) year, > regexp_extract(col_value, '^(?:([^,]*)\,?){9}', 1) run > FROM temp_batting; Total jobs = 3 Launching Job 1 out of 3 Number of reduce tasks is set to 0 since theres no reduce operator Execution log at: /tmp/root/root_20150219105353_aa0aabfe-3589-48d7-a047-6b0987fe20c3.log Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 0; number of reducers: 0 2015-02-19 10:53:04,269 null map = 0%, reduce = 0% 2015-02-19 10:53:05,287 null map = 100%, reduce = 0% Ended Job = job_local592960733_0001 Execution completed successfully MapredLocal task succeeded Stage-4 is selected by condition resolver. Stage-3 is filtered out by condition resolver. Stage-5 is filtered out by condition resolver. Moving data to: hdfs://hadoop/tmp/hive-root/hive_2015-02-19_10-53-00_203_7795567368711574116-1/-ext-10000 Loading data to table default.batting rmr: DEPRECATED: Please use 'rm -r' instead. Deleted hdfs://hadoop/user/hive/warehouse/batting Table default.batting stats: [numFiles=1, numRows=95196, totalSize=1653324, rawDataSize=1558128] OK Time taken: 5.89 seconds
Tak przygotowane tabele zawierają już dane które mogą nas zainteresować. Możemy teraz wyszukiwać zapytaniami SQL szczegółowe informacje.
hive> SELECT year, max(runs) FROM batting GROUP BY year; Total jobs = 1 Launching Job 1 out of 1 Number of reduce tasks not specified. Estimated from input data size: 1 Execution log at: /tmp/root/root_20150219105454_01e4848f-b71c-4e12-88c3-ed4e9360ace6.log Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 0; number of reducers: 0 2015-02-19 10:54:09,113 null map = 100%, reduce = 100% Ended Job = job_local2006606267_0001 Execution completed successfully MapredLocal task succeeded OK NULL NULL 1871 66 1872 94 ... 2010 115 2011 136 Time taken: 4.689 seconds, Fetched: 142 row(s)
hive> SELECT a.year, a.player_id, a.runs from batting a > JOIN (SELECT year, max(runs) runs FROM batting GROUP BY year ) b > ON (a.year = b.year AND a.runs = b.runs) ; Total jobs = 2 Launching Job 1 out of 2 Number of reduce tasks not specified. Estimated from input data size: 1 Execution log at: /tmp/root/root_20150219105555_a44780bc-4cbc-4d07-b65e-775f58325789.log Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 0; number of reducers: 0 2015-02-19 10:55:41,842 null map = 100%, reduce = 100% Ended Job = job_local1016279659_0001 Execution completed successfully MapredLocal task succeeded Execution log at: /tmp/root/root_20150219105555_a44780bc-4cbc-4d07-b65e-775f58325789.log 2015-02-19 10:55:44 Starting to launch local task to process map join; maximum memory = 932184064 2015-02-19 10:55:45 Dump the side-table into file: file:/tmp/root/hive_2015-02-19_10-55-37_776_592556185375942408-1/-local-10004/HashTable-Stage-4/MapJoin-mapfile00--.hashtable 2015-02-19 10:55:45 Uploaded 1 File to: file:/tmp/root/hive_2015-02-19_10-55-37_776_592556185375942408-1/-local-10004/HashTable-Stage-4/MapJoin-mapfile00--.hashtable (2033677 bytes) 2015-02-19 10:55:45 End of local task; Time Taken: 0.93 sec. Execution completed successfully MapredLocal task succeeded Launching Job 2 out of 2 Number of reduce tasks is set to 0 since there's no reduce operator Execution log at: /tmp/root/root_20150219105555_a44780bc-4cbc-4d07-b65e-775f58325789.log Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 0; number of reducers: 0 2015-02-19 10:55:50,002 null map = 100%, reduce = 0% Ended Job = job_local1075503958_0001 Execution completed successfully MapredLocal task succeeded OK 1871 barnero01 66 1872 eggleda01 94 ... 2010 pujolal01 115 2011 grandcu01 136 Time taken: 12.746 seconds, Fetched: 151 row(s)
Proste prawda? Pomyśl teraz jak wiele różnych danych mógłbyś w ten sposób przetwarzać. Jest to tylko bardzo mały wycinek tego co możemy uzyskać dzięki zestawom aplikacji Hadoop. Zachęcam do wrzucenia swoich zestawów danych i eksperymentowania.