How to run a simple Apache Spark application? (Ubuntu 12.04)

17 June 2015

因緣際會下,得稍微摸一下Apache Spark,
說實在,在粗淺地摸完Spark以後,我還是想不太出他的應用面。
Anyway,這篇主要分為3個section,

  • 如何安裝Spark
  • 如何使用
  • 如何寫個Spark application


1. Install JDK

首先要先講安裝,
安裝spark之前,要先安裝java+scala,
apt-add-repository ppa:webupd8team/java
apt-get update
apt-get install oracle-java7-installer

2. Install scala

接著要安裝scala,這裡安裝的版本是2.11.6。
wget http://www.scala-lang.org/files/archive/scala-2.11.6.tgz
mkdir /usr/local/src/scala
tar xvf scala-2.11.6.tgz -C /usr/local/src/scala/

安裝完以後,要把scala的路徑加入到PATH環境變數中
echo "export SCALA_HOME=/usr/local/src/scala/scala-2.11.6" >> .bashrc
echo "export PATH=\$SCALA_HOME/bin:\$PATH" >> .bashrc
. .bashrc

3. Install Spark

最後就安裝spark,安裝會需要一段時間。
wget http://www.apache.org/dist/spark/spark-1.4.0/spark-1.4.0.tgz
tar xvf spark-1.4.0.tgz
cd spark-1.4.0
sbt/sbt assembly

4. Spark Interactive Shell

安裝完以後,可以進入spark的interactive shell模式做個簡單的測試,
進入interactive mode,
./bin/spark-shell

大概會花個10秒做init,進入以後,
會看到scala的prompt字樣,這裏就可以對spark進行操作了。
下面有二個例子,都是對README.md去做操作,
分別為找出含"apache"的句子,
以及找出有幾個"apache"的字。
scala> val textFile = sc.textFile("README.md")
scala> textFile.count()
scala> val results = textFile.filter(line => line.contains("apache")) // search apache
scala> results.count()
scala> results.collect() // find out those lines contain apache

5. Start a Spark cluster

好了,剛剛都是自爽模式,
接著我們要啟動一個spark cluster,
這個cluster可以讓很多個worker(slave)加入,
讓他們去執行工作。

啟動cluster的指令如下:
./sbin/start-master.sh

啟動完以後,你可以用netstat -tnlp去看一下,
你會發現有3個java bind住的connection,
預設分別為7077、8080、6066
7077就是這個cluster的port,將來worker要加入就得指定這個port,
8080是這個spark的web management UI。
所以你就可以打開browser,然後去看看web UI.
這時候你的worker數量應該是0。
如果想要加入一個worker,得透過下面的指令,
./sbin/start-slave.sh spark://ubuntu:7077 # change ubuntu to your hostname

此時再去web UI看,應該就看到有一個worker存在。
上面是透過二個指令去分別啟動master and slave,
其實你也可以透過下面一個指令就去啟動master, slave,
./sbin/start-all.sh

6. Connecting an Application to the Cluster

有了一個spark cluster以後,
我們就可以把application掛上去,
我們可以透過前面使用的spark-shell,把這個shell run在cluster上。
只要加入--master這參數即可。
./bin/spark-shell --master spark://ubuntu:7077 # change ubuntu to your hostname

進去以後,你一樣可以執行上面的example玩玩看。
同時你也可以去web UI上看看,會在"Running Applications"裡面看到這個shell。

7. Writing an Application

上一步也算是自爽模式,
我想應該不可能透過shell mode去做你想做的事情,
應該還是要寫個application,
所以這一步驟就是要寫一個很簡單的classs,
然後build它,再把它掛到cluster上執行。

開始之前,先來設定一下spark的環境變數,
echo "export SK_HOME=/root/spark-1.4.0" >> .bashrc
. .bashrc

然後改這隻script,($SK_HOME/build/sbt-launch-lib.bash)
因為我在build的時候,有發生路徑的問題。
vim $SK_HOME/build/sbt-launch-lib.bash

打開這檔案以後,找到這幾行,
SBT_VERSION=`awk -F "=" '/sbt\.version/ {print $2}' ./project/build.properties`
...
JAR=build/sbt-launch-${SBT_VERSION}.jar
換成下面這幾行,其實也只是變成絕對路徑。
SBT_VERSION=`awk -F "=" '/sbt\.version/ {print $2}' $SK_HOME/project/build.properties`
...
JAR=$SK_HOME/build/sbt-launch-${SBT_VERSION}.jar

完成以後,先來create幾個資料夾,
mkdir -p ./spark-app/src/main/scala/

接著就可以寫一個簡單的class,
這class上面差不多,也是用來算count,
記得該class一定要放置在src/main/scala底下。
cd ./spark-app
vim src/main/scala/SimpleApp.scala

class內容如下,
/* SimpleApp.scala */
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf

object SimpleApp {
def main(args: Array[String]) {
val logFile = "/root/spark-1.4.0/README.md"
val conf = new SparkConf().setAppName("Simple Application")
val sc = new SparkContext(conf)
val logData = sc.textFile(logFile, 2).cache()
val numApache = logData.filter(line => line.contains("apache")).count()
println("Line with apache: %s ".format(numApache))
}
}

而在開始build之前,要寫一下這個app的dependency,
放置在sparka-app底下就好。
vim simple.sbt

內容如下,
name := "Simple Project"

version := "1.0"

scalaVersion := "2.11.6"

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.4.0"

接著就可以開始build,
$SPARK_HOME/build/sbt package


build完以後,
會發現spark-app底下多了二個folder(project, target),
而我們要的jar檔會放在target底下,
我們就可以把這jar交付給spark執行。
$SPARK_HOME/bin/spark-submit --class "SimpleApp" --master spark://ubuntu:7077 target/scala-2.11/simple-project_2.11-1.0.jar

應該會看到下面的字樣,
Line with apache: 9 15/06/17 02:55:34 INFO TaskSetManager: Finished task 1.0 in stage 2.0 (TID 5) in 32 ms on 172.16.131.140 (2/2)

最後在web UI裡面的"Completed Applications"會看到我們剛剛執行的那個item。
基本上這樣就完成了簡單的spark application。




read more »


How to create a customized Ubuntu ISO?

13 April 2015

這篇要講怎麼客製化你自己想要的ubuntu ISO,
會講解一些preseed的參數,
preseed file是debian系列的automation config,而redhat系列就是用kickstart,
debian系列就是根據preseed file去進行安裝.
因此ubuntu也是用preseed,當然ubuntu也可以用kickstart,只是“似乎”支援沒有那麼好!?
題外話,雖然說preseed file是debian的標準格式,
但有一點要記得,debian自己本身的preseed file,並不是每一個command,ubuntu都能接受!


1. Download ubuntu ISO

首先,你要有一個ubuntu的ISO,
這裏我選用ubuntu 12.04的ISO,
wget http://releases.ubuntu.com/12.04.5/ubuntu-12.04.5-server-amd64.iso


2. Mount the ISO

接著要把ISO內的東西,先mount起來,接著再copy出來,
mkdir -p /mnt/tmp /mnt/iso
mount -o loop ./ubuntu-12.04.5-server-amd64.iso /mnt/tmp
rsyn -av /mnt/tmp /mnt/iso
umount /mnt/tmp

3. Add boot menu option

因為要客製化你自己的ISO,
所以我們要在boot menu中,新增一個屬於我們自己的menu,
首先先開啟這個isolinux/txt.cfg檔案
vim /mnt/iso/isolinux/txt.cfg

打開以後應該會看到下面的內容,

我們就在label install之前新增一筆boot menu,
內容如下,
label YourCustomizedISO
menu label ^Customized ISO
kernel /install/vmlinuz
append file=/cdrom/preseed/customized.seed initrd=/install/initrd.gz quiet --


4. Add pressed file

接著就要新增一個客製化的preseed file,
vim /mnt/iso/preseed/customized.seed

內容如下,這份preseed讓你可以不用輸入任何按鍵就進行自動安裝!
我覺得還蠻淺顯易懂的,每個section都有標註是在做什麼,
比較值得注意的是『Advance command』這個section,
這section裡面放的是,『安裝完ubuntu以後,要做些什麼事情。』
但是如果你想要在『安裝之前』或者『partition之前或者之後』做些事情,都是可以的。
只不過這個example,我們是在『安裝完ubuntu以後』。

『preseed/late_command』就是『安裝完ubuntu以後』的意思。
不過這邊可能大家會覺得很奇怪,為什麼在late_command中,
一開始要cp至/target目錄?/target目錄又是什麼?
大家在安裝ubuntu的時候,可以change console一下,這時候cd至/target底下,
你會發現其實裡面就是你真正ubuntu安裝的位置!
那/target外面的東西是誰的呢(bin, lib, etc,....)?
其實是busybox的,所以你一定得把你想要的東西搬到/target裡面,
這樣在你安裝完以後,該東西才會存在。
接著還有另一段奇怪的command:in-target,
『in-target』,簡單的說就是chroot /target而已,
你也可以用chroot /target去取代『in-target』。

####### Localization #######
d-i debian-installer/locale string en_US
d-i debian-installer/language string en
d-i debian-installer/country string US


####### Keyboard selection #######
d-i console-setup/ask_detect boolean false
d-i console-setup/layoutcode string us
d-i console-setup/modelcode string pc105
d-i kbd-chooser/method string us
d-i keyboard-configuration/layout string USA
d-i keyboard-configuration/variant string USA

####### Network configuration #######
#d-i netcfg/choose_interface select auto
d-i netcfg/choose_interface select eth0
d-i netcfg/get_hostname string ubuntu
d-i netcfg/get_domain string
# configure the network manually
#d-i netcfg/disable_autoconfig boolean true
# configure the network automatically
d-i netcfg/get_nameservers string 8.8.8.8
d-i netcfg/get_ipaddress string 172.16.131.138
d-i netcfg/get_netmask string 255.255.255.0
d-i netcfg/get_gateway string 172.16.131.2
d-i netcfg/confirm_static boolean true

####### Apt setup #######
d-i apt-setup/security-updates boolean false
d-i apt-setup/restricted boolean false
d-i apt-setup/universe boolean false
d-i apt-setup/backports boolean false
d-i apt-setup/security boolean false
d-i mirror/http/proxy string

####### Base system installation #######
d-i debian-installer/splash boolean false
d-i base-installer/install-recommends boolean true
d-i base-installer/kernel/altmeta string lts-saucy

####### Clock and time zone setup #######
d-i clock-setup/utc boolean true
d-i clock-setup/utc-auto boolean true
d-i time/zone string UTC

####### Boot loader installation #######
d-i finish-install/reboot_in_progress note
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true

####### Partitioning using LVM #######
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true

####### User #######
d-i passwd/root-login boolean true
d-i passwd/root-password password 1234
d-i passwd/root-password-again password 1234
d-i user-setup/allow-password-weak boolean true
# create user or not
d-i passwd/make-user boolean false
#d-i user-setup/encrypt-home boolean false
#d-i passwd/user-fullname string ken
#d-i passwd/username string ken
#d-i passwd/user-password password 123456
#d-i passwd/user-password-again password 123456

####### Package selection #######
d-i pkgsel/include string openssh-server vim
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select none

tasksel tasksel/first multiselect standard, ubuntu-server

####### Advance command #######
d-i preseed/late_command string \
cp /cdrom/preseed/setup-something.bash /target/tmp/setup-something.bash && in-target chmod +x /tmp/setup-something.bash && in-target /bin/bash /tmp/setup-something.bash




5. Make an ISO

完成上面的步驟後,
剩下最後一步就完成了,
把剛剛copy出來的內容,在包成一個ISO就好了。
mkisofs -r -R -J -T -v -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -z -iso-level 4 -c isolinux/isolinux.cat -o custom.iso /mnt/iso

大概等個20秒以後,就會看到一個custom.iso,就代表你成功了!









read more »


MogileFS: Distributed filesystem

24 November 2014


MogileFS是分散式檔案系統(DFS)的一種,
在MogileFS中,主要分成2個角色,
     1. tracker: 記錄有哪些storage
     2. storage node: 單純放檔案的角色
其實還有第三個角色,是tracker的DB,
這db角色是用來存放storage在哪裡、有幾個storage等資訊,
以及每個檔案類型要replicate幾份等等之類的資訊,
不過我會把tracker's DB和tracker本身歸類在一起,
因為它們兩個一定得在同檯機器上,而且缺一不可,
但tracker和storage不一定要在同檯機器上面!

如果說為什麼要用mogilefs的話,大概有以下2個原因吧,
     1. 自動作replicate
     2. application level,不需要特別的kernel module(setup簡單)

1. Install mogilefs

安裝mogilefs方式大概有三種,用cpan, apt, 拿source自己build,
個人偏好用apt-get安裝,
這裡選擇在ubuntu 14.04上安裝,
apt-get install python-software-properties
add-apt-repository ppa:saz/mogilefs
apt-get update
apt-get install mogilefsd mogstored mogilefs-utils

這樣就完成安裝,接下來要setup mogilefs,

2. Setup mogilefs tracker

前面有說到有tracker就一定要有db,
所以請先安裝好mysql,安裝好以後就執行下面的command,
記得把password換成你想要使用的密碼!
mysql> CREATE DATABASE mogilefs;
mysql> CREATE USER 'mogilefs'@'%' IDENTIFIED BY 'password';
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,ALTER ON mogilefs.* TO 'mogilefs'@'%';
mysql> flush privileges;

接著要編輯/etc/mogilefs/mogilefsd.conf這個檔案,
這個檔案,其實只有在tracker的角色上才需要去編輯,
意思是說如果你有三檯機器,那麼只有其中一檯才需要去設定這個檔案!
只要改下面兩個設定就好,
db_pass = password
listen = 10.0.0.172:7001
db_pass就是上一步驟你所輸入的密碼,
listen就是這個tracker要listen在哪個ip以及哪一個port上面!

接著使用下面的command進行db setup,
一樣記得把password換成跟上面一樣的!
mogdbsetup --dbhost=10.0.0.172:7001 --dbname=mogilefs --dbuser=mogilefs --dbpassword=password

接著編輯.mogilefs.conf,這檔案會記錄著你的tracker在哪裡,
然後mogilefs會去問這tracker所有storage的資訊.
echo "trackers = 10.0.0.172:7001" > ~/.mogilefs.conf


3. Setup mogilefs storage node

tracker的目的在於記錄著有哪些storage可以用,
所以在完成setup tracker以後,接著就要來setup storage,
首先,先增加一個store host,
mogadm --trackers=10.0.0.172:7001 host add store1 --ip=10.0.0.172 --status=alive
--trackers:指定加在哪個tracker上
store1:是你的host name,
--ip:就是那一顆storage在哪裡

那麼有了host以後,
就要有device,一個host裡面可以有很多個device,
可以把device想像成local disk、NFS...etc.
所以接著要告訴host有哪些device,
mogadm --trackers=10.0.0.172:7001 device add store1 1 --status=alive
比較特別參數是“1”,
1的意思是device 1,

再來還要增加一個domain,等等後面會說明這domain的用途
mogadm domain add ken-storage

完成增加一個device了,
接著mogilefs store會去找那個device,
所以接著要去create folder,之後檔案都會放在那folder裡面,
mkdir -p /var/mogdata/dev1
chown mogstored /var/mogdata/dev1

這樣storage也設定完成了,
那麼該怎麼知道有設定成功?
可以透過mogadm check指令去看,
如果是成功的話,應該會看到下面的畫面,
root@ip-10-0-0-172:~# mogadm check
Checking trackers...
10.0.0.172:7001 ... OK

Checking hosts...
[ 1] store1 ... OK

Checking devices...
host device size(G) used(G) free(G) use% ob state I/O%
---- ------------ ---------- ---------- ---------- ------ ---------- -----
[ 1] dev1 7.324 1.480 5.844 20.21% writeable N/A
---- ------------ ---------- ---------- ---------- ------
total: 7.324 1.480 5.844 20.21%



4. Test using command line

測試之前,請先建立一個tmp file(hello.txt)
上傳檔案,
mogupload  --file="./hello.txt" --domain=ken-storage --key="first-file"
下載檔案
mogfetch --key="first-file" --file="./output-file" --domain=ken-storage
刪除檔案
mogdelete --key="first-file"  --domain=ken-storage


5. Advance: Replication

完成上面的基本操作以後,
並沒有發揮mogilefs replicate的功能,
那要做到replication也很簡單,
記得上面我們有add domain嗎?
domain其實有點難解釋,可以把它想像成像是AWS S3的bucket,
而這bucket下有許多policy,
所以我們現在就要替這這bucket(domain)加replicate的policy,
mogadm class add ken-storage txt --mindevcount=2
這指令的意思是,當檔案的class是txt時,請replicate 2份!

完成replication設定以後,
因為前面的設定都只有一個device,
只有一個deivce情況下,mogilefsd是不會進行replication的,
所以我們得再增加一個device,
建議你不要在同一檯機器上多增加一個device,畢竟沒有太大意義,
你在同一檯機器上做replication....?
如果這檯機器掛了,檔案就通通不見了,
所以會建議你在第二檯機器上增加一個device,
第二檯機器上就單純是storage node的角色了,
所以只需要做下面設定,記得把ip換成第二檯的ip!(只要改10.0.0.199的部分)
echo "trackers = 10.0.0.172:7001" > ~/.mogilefs.conf
mogadm --trackers=10.0.0.172:7001 host add store2 --ip=10.0.0.199 --status=alive
mogadm --trackers=10.0.0.172:7001 device add store2 2 --status=alive
mkdir -p /var/mogdata/dev2
chown mogstored /var/mogdata/dev2

6. Test Replication

其實跟第四步驟是一樣的,只是多一個參數--class,
這class就和上一步驟所填寫的一樣即可.這樣hello.txt就會被複製到另外一檯機器上去!
mogupload  --file="./hello.txt" --domain=ken-storage --key="first-file" --class=txt

如果replication失敗,那麼在db裡面的table file_to_replication將會有那筆資料,
你也可以透過/var/log/syslog看是什麼error message,
我自己使用的mogilefs版本,就有遇到一個syscal module的版本問題
透過連結的中的方法去解決的!


7. Advance: Integrate it with application

當然有了mogilefs以後,不可能都透過command line去操作呀,
如果我想要整合在我的application裡怎麼辦?
其實也很簡單,看你用什麼language,至少我用的language(java, python)都有相關的lib可以使用,
這裡使用pyhton來作為說明,
pymogile來作為操作mogile的lib,
安裝pymogile,
git clone https://github.com/AloneRoad/pymogile.git
cd pymogile/
python setup.py install

下面是一個簡單的example用pymogile操作mogilefs,
from pymogile import Client, MogileFSError

datastore = Client(domain='ken-storage', trackers=['10.0.0.172:7001'])
key_id='replicate'

def upload_file():
fp = datastore.new_file(key_id)
fp.write('test')
fp.close()

def get_path():
print datastore.get_paths(key_id)

def get_data():
print datastore.get_file_data(key_id)

upload_file()
get_path()
get_data()









read more »