Hadoop集羣(第5期)_Hadoop安裝配置


1、集羣部署介紹

1.1 Hadoop簡介

  Hadoop是Apache軟件基金會旗下的一個開源分佈式計算平臺。以Hadoop分佈式文件系統(HDFS,Hadoop Distributed Filesystem)和MapReduce(Google MapReduce的開源實現)爲核心的Hadoop爲用戶提供了系統底層細節透明的分佈式基礎架構。

  對於Hadoop的集羣來講,可以分成兩大類角色:Master和Salve。一個HDFS集羣是由一個NameNode和若干個DataNode組成的。其中NameNode作爲主服務器,管理文件系統的命名空間和客戶端對文件系統的訪問操作;集羣中的DataNode管理存儲的數據。MapReduce框架是由一個單獨運行在主節點上的JobTracker和運行在每個集羣從節點的TaskTracker共同組成的。主節點負責調度構成一個作業的所有任務,這些任務分佈在不同的從節點上。主節點監控它們的執行情況,並且重新執行之前的失敗任務;從節點僅負責由主節點指派的任務。當一個Job被提交時,JobTracker接收到提交作業和配置信息之後,就會將配置信息等分發給從節點,同時調度任務並監控TaskTracker的執行。

  從上面的介紹可以看出,HDFS和MapReduce共同組成了Hadoop分佈式系統體系結構的核心。HDFS在集羣上實現分佈式文件系統MapReduce在集羣上實現了分佈式計算任務處理。HDFS在MapReduce任務處理過程中提供了文件操作和存儲等支持,MapReduce在HDFS的基礎上實現了任務的分發、跟蹤、執行等工作,並收集結果,二者相互作用,完成了Hadoop分佈式集羣的主要任務。

1.2 環境說明

  集羣中包括4個節點:1個Master,3個Salve,節點之間局域網連接,可以相互ping通,具體集羣信息可以查看"Hadoop集羣(第2期)"。節點IP地址分佈如下:

機器名稱

IP地址

Master.Hadoop

192.168.1.2

Salve1.Hadoop

192.168.1.3

Salve2.Hadoop

192.168.1.4

Salve3.Hadoop

192.168.1.5

  四個節點上均是CentOS6.0系統,並且有一個相同的用戶hadoop。Master機器主要配置NameNode和JobTracker的角色,負責總管分佈式數據和分解任務的執行;3個Salve機器配置DataNode和TaskTracker的角色,負責分佈式數據存儲以及任務的執行。其實應該還應該有1個Master機器,用來作爲備用,以防止Master服務器宕機,還有一個備用馬上啓用。後續經驗積累一定階段後補上一臺備用Master機器。

1.3 網絡配置

  Hadoop集羣要按照1.2小節表格所示進行配置,我們在"Hadoop集羣(第1期)"的CentOS6.0安裝過程就按照提前規劃好的主機名進行安裝和配置。如果實驗室後來人在安裝系統時,沒有配置好,不要緊,沒有必要重新安裝,在安裝完系統之後仍然可以根據後來的規劃對機器的主機名進行修改。

  下面的例子我們將以Master機器爲例,即主機名爲"Master.Hadoop",IP爲"192.168.1.2"進行一些主機名配置的相關操作。其他的Slave機器以此爲依據進行修改。

  1)查看當前機器名稱

  用下面命令進行顯示機器名稱,如果跟規劃的不一致,要按照下面進行修改。

hostname

  上圖中,用"hostname"查"Master"機器的名字爲"Master.Hadoop",與我們預先規劃的一致。

  2)修改當前機器名稱

  假定我們發現我們的機器的主機名不是我們想要的,通過對"/etc/sysconfig/network"文件修改其中"HOSTNAME"後面的值,改成我們規劃的名稱。

  這個"/etc/sysconfig/network"文件是定義hostname和是否利用網絡的不接觸網絡設備的對系統全體定義的文件。

  設定形式:設定值=值

  "/etc/sysconfig/network"的設定項目如下:

NETWORKING 是否利用網絡

GATEWAY 默認網關

IPGATEWAYDEV 默認網關的接口名

HOSTNAME 主機名

DOMAIN 域名

  用下面命令進行修改當前機器的主機名(備註:修改系統文件一般用root用戶)

vim /etc/sysconfig/network

  通過上面的命令我們從"/etc/sysconfig/network"中找到"HOSTNAME"進行修改,查看內容如下:

3)修改當前機器IP

假定我們的機器連IP在當時安裝機器時都沒有配置好,那此時我們需要對"ifcfg-eth0"文件進行配置,該文件位於"/etc/sysconfig/network-scripts"文件夾下。

  在這個目錄下面,存放的是網絡接口(網卡)的制御腳本文件(控制文件),ifcfg- eth0是默認的第一個網絡接口,如果機器中有多個網絡接口,那麼名字就將依此類推ifcfg-eth1,ifcfg-eth2,ifcfg- eth3,……。

  這裏面的文件是相當重要的,涉及到網絡能否正常工作。

  設定形式:設定值=值

  設定項目項目如下:

DEVICE 接口名(設備,網卡)

BOOTPROTO IP的配置方法(static:固定IP, dhcpHCP, none:手動)

HWADDR MAC地址

ONBOOT 系統啓動的時候網絡接口是否有效(yes/no)

TYPE 網絡類型(通常是Ethemet)

NETMASK 網絡掩碼

IPADDR IP地址

IPV6INIT IPV6是否有效(yes/no)

GATEWAY 默認網關IP地址

  查看"/etc/sysconfig/network-scripts/ifcfg-eth0"內容,如果IP不復核,就行修改。

  如果上圖中IP與規劃不相符,用下面命令進行修改:

vim /etc/sysconfig/network-scripts/ifcgf-eth0

  修改完之後可以用"ifconfig"進行查看。

  4)配置hosts文件(必須

  "/etc/hosts"這個文件是用來配置主機將用的DNS服務器信息,是記載LAN內接續的各主機的對應[HostName和IP]用的。當用戶在進行網絡連接時,首先查找該文件,尋找對應主機名(或域名)對應的IP地址。

  我們要測試兩臺機器之間知否連通,一般用"ping 機器的IP",如果想用"ping 機器的主機名"發現找不見該名稱的機器,解決的辦法就是修改"/etc/hosts"這個文件,通過把LAN內的各主機的IP地址和HostName的一一對應寫入這個文件的時候,就可以解決問題。

  例如:機器爲"Master.Hadoop:192.168.1.2"對機器爲"Salve1.Hadoop:192.168.1.3"用命令"ping"記性連接測試。測試結果如下:

   從上圖中的值,直接對IP地址進行測試,能夠ping通,但是對主機名進行測試,發現沒有ping通,提示"unknown host——未知主機",這時查看"Master.Hadoop"的"/etc/hosts"文件內容。

  發現裏面沒有"192.168.1.3 Slave1.Hadoop"內容,故而本機器是無法對機器的主機名爲"Slave1.Hadoop" 解析。

  在進行Hadoop集羣配置中,需要在"/etc/hosts"文件中添加集羣中所有機器的IP與主機名,這樣Master與所有的Slave機器之間不僅可以通過IP進行通信,而且還可以通過主機名進行通信。所以在所有的機器上的"/etc/hosts"文件末尾中都要添加如下內容:

192.168.1.2 Master.Hadoop

192.168.1.3 Slave1.Hadoop

192.168.1.4 Slave2.Hadoop

192.168.1.5 Slave3.Hadoop

  用以下命令進行添加:

vim /etc/hosts

  添加結果如下:

  現在我們在進行對機器爲"Slave1.Hadoop"的主機名進行ping通測試,看是否能測試成功。

  從上圖中我們已經能用主機名進行ping通了,說明我們剛纔添加的內容,在局域網內能進行DNS解析了,那麼現在剩下的事兒就是在其餘的Slave機器上進行相同的配置。然後進行測試。(備註:當設置SSH無密碼驗證後,可以"scp"進行復制,然後把原來的"hosts"文件執行覆蓋即可。)

1.4 所需軟件

1)JDK軟件

下載地址:http://www.oracle.com/technetwork/java/javase/index.html

JDK版本:jdk-6u31-linux-i586.bin

2)Hadoop軟件

下載地址:http://hadoop.apache.org/common/releases.html

Hadoop版本:hadoop-1.0.0.tar.gz

1.5 VSFTP上傳

  在"Hadoop集羣(第3期)"講了VSFTP的安裝及配置,如果沒有安裝VSFTP可以按照該文檔進行安裝。如果安裝好了,就可以通過FlashFXP.exe軟件把我們下載的JDK6.0和Hadoop1.0軟件上傳到"Master.Hadoop:192.168.1.2"服務器上。

  剛纔我們用一般用戶(hadoop)通過FlashFXP軟件把所需的兩個軟件上傳了跟目下,我們通過命令查看下一下是否已經上傳了。

從圖中,我們的所需軟件已經準備好了。

2、SSH無密碼驗證配置

  Hadoop運行過程中需要管理遠端Hadoop守護進程,在Hadoop啓動以後,NameNode是通過SSH(Secure Shell)來啓動和停止各個DataNode上的各種守護進程的。這就必須在節點之間執行指令的時候是不需要輸入密碼的形式,故我們需要配置SSH運用無密碼公鑰認證的形式,這樣NameNode使用SSH無密碼登錄並啓動DataName進程,同樣原理,DataNode上也能使用SSH無密碼登錄到NameNode。

2.1 安裝和啓動SSH協議

  在"Hadoop集羣(第1期)"安裝CentOS6.0時,我們選擇了一些基本安裝包,所以我們需要兩個服務:ssh和rsync已經安裝了。可以通過下面命令查看結果顯示如下:

rpm –qa | grep openssh

rpm –qa | grep rsync

  假設沒有安裝ssh和rsync,可以通過下面命令進行安裝。

yum install ssh 安裝SSH協議

yum install rsync (rsync是一個遠程數據同步工具,可通過LAN/WAN快速同步多臺主機間的文件)

service sshd restart 啓動服務

  確保所有的服務器都安裝,上面命令執行完畢,各臺機器之間可以通過密碼驗證相互登。

2.2 配置Master無密碼登錄所有Salve

  1)SSH無密碼原理

  Master(NameNode | JobTracker)作爲客戶端,要實現無密碼公鑰認證,連接到服務器Salve(DataNode | Tasktracker)上時,需要在Master上生成一個密鑰對,包括一個公鑰和一個私鑰,而後將公鑰複製到所有的Slave上。當Master通過SSH連接Salve時,Salve就會生成一個隨機數並用Master的公鑰對隨機數進行加密,併發送給Master。Master收到加密數之後再用私鑰解密,並將解密數回傳給Slave,Slave確認解密數無誤之後就允許Master進行連接了。這就是一個公鑰認證過程,其間不需要用戶手工輸入密碼。重要過程是將客戶端Master複製到Slave上。

  2)Master機器上生成密碼對

   在Master節點上執行以下命令:

ssh-keygen –t rsa –P ''

  這條命是生成其無密碼密鑰對,詢問其保存路徑時直接回車採用默認路徑。生成的密鑰對:id_rsa和id_rsa.pub,默認存儲在"/home/hadoop/.ssh"目錄下。

  查看"/home/hadoop/"下是否有".ssh"文件夾,且".ssh"文件下是否有兩個剛生產的無密碼密鑰對。

  接着在Master節點上做如下配置,把id_rsa.pub追加到授權的key裏面去。

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

  在驗證前,需要做兩件事兒。第一件事兒是修改文件"authorized_keys"權限(權限的設置非常重要,因爲不安全的設置安全設置,會讓你不能使用RSA功能),另一件事兒是用root用戶設置"/etc/ssh/sshd_config"的內容。使其無密碼登錄有效。

  1)修改文件"authorized_keys"

chmod 600 ~/.ssh/authorized_keys

   備註:如果不進行設置,在驗證時,扔提示你輸入密碼,在這裏花費了將近半天時間來查找原因。在網上查到了幾篇不錯的文章,把作爲"Hadoop集羣_第5期副刊_JDK和SSH無密碼配置"來幫助額外學習之用。

2)設置SSH配置

  用root用戶登錄服務器修改SSH配置文件"/etc/ssh/sshd_config"的下列內容。

RSAAuthentication yes # 啓用 RSA 認證

PubkeyAuthentication yes # 啓用公鑰私鑰配對認證方式

AuthorizedKeysFile .ssh/authorized_keys # 公鑰文件路徑(和上面生成的文件同)

  設置完之後記得重啓SSH服務,才能使剛纔設置有效。

service sshd restart

  退出root登錄,使用hadoop普通用戶驗證是否成功。

ssh localhost

  從上圖中得知無密碼登錄本級已經設置完畢,接下來的事兒是把公鑰複製所有Slave機器上。使用下面的命令格式進行復制公鑰:

scp ~/.ssh/id_rsa.pub 遠程用戶名@遠程服務器IP:~/

  例如:

scp ~/.ssh/id_rsa.pub [email protected]:~/

  上面的命令是複製文件"id_rsa.pub"到服務器IP爲"192.168.1.3"的用戶爲"hadoop"的"/home/hadoop/"下面。

  下面就針對IP爲"192.168.1.3"的Slave1.Hadoop的節點進行配置。

  1)把Master.Hadoop上的公鑰複製到Slave1.Hadoop上

  從上圖中我們得知,已經把文件"id_rsa.pub"傳過去了,因爲並沒有建立起無密碼連接,所以在連接時,仍然要提示輸入輸入Slave1.Hadoop服務器用戶hadoop的密碼。爲了確保確實已經把文件傳過去了,用SecureCRT登錄Slave1.Hadoop:192.168.1.3服務器,查看"/home/hadoop/"下是否存在這個文件。

  從上面得知我們已經成功把公鑰複製過去了。

  2)在"/home/hadoop/"下創建".ssh"文件夾

  這一步並不是必須的,如果在Slave1.Hadoop的"/home/hadoop"已經存在就不需要創建了,因爲我們之前並沒有對Slave機器做過無密碼登錄配置,所以該文件是不存在的。用下面命令進行創建。(備註:用hadoop登錄系統,如果不涉及系統文件修改,一般情況下都是用我們之前建立的普通用戶hadoop進行執行命令。)

mkdir ~/.ssh

  然後是修改文件夾".ssh"的用戶權限,把他的權限修改爲"700",用下面命令執行:

chmod 700 ~/.ssh

  備註:如果不進行,即使你按照前面的操作設置了"authorized_keys"權限,並配置了"/etc/ssh/sshd_config",還重啓了sshd服務,在Master能用"ssh localhost"進行無密碼登錄,但是對Slave1.Hadoop進行登錄仍然需要輸入密碼,就是因爲".ssh"文件夾的權限設置不對。這個文件夾".ssh"在配置SSH無密碼登錄時系統自動生成時,權限自動爲"700",如果是自己手動創建,它的組權限和其他權限都有,這樣就會導致RSA無密碼遠程登錄失敗。

  對比上面兩張圖,發現文件夾".ssh"權限已經變了。

  3)追加到授權文件"authorized_keys"

  到目前爲止Master.Hadoop的公鑰也有了,文件夾".ssh"也有了,且權限也修改了。這一步就是把Master.Hadoop的公鑰追加到Slave1.Hadoop的授權文件"authorized_keys"中去。使用下面命令進行追加並修改"authorized_keys"文件權限:

cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

chmod 600 ~/.ssh/authorized_keys

  4)用root用戶修改"/etc/ssh/sshd_config"

  具體步驟參考前面Master.Hadoop的"設置SSH配置",具體分爲兩步:第1是修改配置文件;第2是重啓SSH服務。

  5)用Master.Hadoop使用SSH無密碼登錄Slave1.Hadoop

  當前面的步驟設置完畢,就可以使用下面命令格式進行SSH無密碼登錄了。

ssh 遠程服務器IP

  從上圖我們主要3個地方,第1個就是SSH無密碼登錄命令,第2、3個就是登錄前後"@"後面的機器名變了,由"Master"變爲了"Slave1",這就說明我們已經成功實現了SSH無密碼登錄了。

  最後記得把"/home/hadoop/"目錄下的"id_rsa.pub"文件刪除掉。

rm –r ~/id_rsa.pub

  到此爲止,我們經過前5步已經實現了從"Master.Hadoop"到"Slave1.Hadoop"SSH無密碼登錄,下面就是重複上面的步驟把剩餘的兩臺(Slave2.Hadoop和Slave3.Hadoop)Slave服務器進行配置。這樣,我們就完成了"配置Master無密碼登錄所有的Slave服務器"。

2.3 配置所有Slave無密碼登錄Master

  和Master無密碼登錄所有Slave原理一樣,就是把Slave的公鑰追加到Master的".ssh"文件夾下的"authorized_keys"中,記得是追加(>>)

  爲了說明情況,我們現在就以"Slave1.Hadoop"無密碼登錄"Master.Hadoop"爲例,進行一遍操作,也算是鞏固一下前面所學知識,剩餘的"Slave2.Hadoop"和"Slave3.Hadoop"就按照這個示例進行就可以了。

  首先創建"Slave1.Hadoop"自己的公鑰和私鑰,並把自己的公鑰追加到"authorized_keys"文件中。用到的命令如下:

ssh-keygen –t rsa –P ''

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

  接着是用命令"scp"複製"Slave1.Hadoop"的公鑰"id_rsa.pub"到"Master.Hadoop"的"/home/hadoop/"目錄下,並追加到"Master.Hadoop"的"authorized_keys"中。

  1)在"Slave1.Hadoop"服務器的操作

  用到的命令如下:

scp ~/.ssh/id_rsa.pub [email protected]:~/

  

  2)在"Master.Hadoop"服務器的操作

  用到的命令如下:

cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

  然後刪除掉剛纔複製過來的"id_rsa.pub"文件。

  最後是測試從"Slave1.Hadoop"到"Master.Hadoop"無密碼登錄。

  從上面結果中可以看到已經成功實現了,再試下從"Master.Hadoop"到"Slave1.Hadoop"無密碼登錄。

  至此"Master.Hadoop"與"Slave1.Hadoop"之間可以互相無密碼登錄了,剩下的就是按照上面的步驟把剩餘的"Slave2.Hadoop"和"Slave3.Hadoop"與"Master.Hadoop"之間建立起無密碼登錄。這樣,Master能無密碼驗證登錄每個Slave,每個Slave也能無密碼驗證登錄到Master。

3、Java環境安裝

  所有的機器上都要安裝JDK,現在就先在Master服務器安裝,然後其他服務器按照步驟重複進行即可。安裝JDK以及配置環境變量,需要以"root"的身份進行。

3.1 安裝JDK

  首先用root身份登錄"Master.Hadoop"後在"/usr"下創建"java"文件夾,再把用FTP上傳到"/home/hadoop/"下的"jdk-6u31-linux-i586.bin"複製到"/usr/java"文件夾中。

mkdir /usr/java

cp /home/hadoop/ jdk-6u31-linux-i586.bin /usr/java

  接着進入"/usr/java"目錄通過下面命令使其JDK獲得可執行權限,並安裝JDK。

chmod +x jdk-6u31-linux-i586.bin

./jdk-6u31-linux-i586.bin

  按照上面幾步進行操作,最後點擊"Enter"鍵開始安裝,安裝完會提示你按"Enter"鍵退出,然後查看"/usr/java"下面會發現多了一個名爲"jdk1.6.0_31"文件夾,說明我們的JDK安裝結束,刪除"jdk-6u31-linux-i586.bin"文件,進入下一個"配置環境變量"環節。

3.2 配置環境變量

  編輯"/etc/profile"文件,在後面添加Java的"JAVA_HOME"、"CLASSPATH"以及"PATH"內容。

  1)編輯"/etc/profile"文件

vim /etc/profile

  2)添加Java環境變量

  在"/etc/profile"文件的尾部添加以下內容:

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31/

export JRE_HOME=/usr/java/jdk1.6.0_31/jre

export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib

export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

  或者

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31

export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib

export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin

   以上兩種意思一樣,那麼我們就選擇第2種來進行設置。

  3)使配置生效

  保存並退出,執行下面命令使其配置立即生效。

source /etc/profile

3.3 驗證安裝成功

  配置完畢並生效後,用下面命令判斷是否成功。

java -version

  從上圖中得知,我們以確定JDK已經安裝成功。

3.4 安裝剩餘機器

  這時用普通用戶hadoop通過下面命令格式把"Master.Hadoop"文件夾"/home/hadoop/"的JDK複製到其他Slave的"/home/hadoop/"下面,剩下的事兒就是在其餘的Slave服務器上按照上圖的步驟安裝JDK。

scp /home/hadoop/jdk-6u31-linux-i586.bin 遠程用戶名@遠程服務器IP:~/

  或者

scp ~/jdk-6u31-linux-i586.bin 遠程用戶名@遠程服務器IP:~/

  備註:"~"代表當前用戶的主目錄,當前用戶爲hadoop,所以"~"代表"/home/hadoop"。

  例如:把JDK從"Master.Hadoop"複製到"Slave1.Hadoop"的命令如下。

scp ~/jdk-6u31-linux-i586 [email protected]:~/

  然後查看"Slave1.Hadoop"的"/home/hadoop"查看是否已經複製成功了。

  從上圖中得知,我們已經成功複製了,現在我們就用最高權限用戶root進行安裝了。其他的與這個一樣。

4、Hadoop集羣安裝

  所有的機器上都要安裝hadoop,現在就先在Master服務器安裝,然後其他服務器按照步驟重複進行即可。安裝和配置hadoop需要以"root"的身份進行。

4.1 安裝hadoop

  首先用root用戶登錄"Master.Hadoop"機器,查看我們之前用FTP上傳至"/home/Hadoop"上傳的"hadoop-1.0.0.tar.gz"。

  接着把"hadoop-1.0.0.tar.gz"複製到"/usr"目錄下面。

cp /home/hadoop/hadoop-1.0.0.tar.gz /usr

  下一步進入"/usr"目錄下,用下面命令把"hadoop-1.0.0.tar.gz"進行解壓,並將其命名爲"hadoop",把該文件夾的讀權限分配給普通用戶hadoop,然後刪除"hadoop-1.0.0.tar.gz"安裝包。

cd /usr #進入"/usr"目錄

tar –zxvf hadoop-1.0.0.tar.gz #解壓"hadoop-1.0.0.tar.gz"安裝包

mv hadoop-1.0.0 hadoop #將"hadoop-1.0.0"文件夾重命名"hadoop"

chown –R hadoop:hadoop hadoop #將文件夾"hadoop"讀權限分配給hadoop用戶

rm –rf hadoop-1.0.0.tar.gz #刪除"hadoop-1.0.0.tar.gz"安裝包

  解壓後,並重命名。

  把"/usr/hadoop"讀權限分配給hadoop用戶(非常重要

  刪除"hadoop-1.0.0.tar.gz"安裝包

  最後在"/usr/hadoop"下面創建tmp文件夾,把Hadoop的安裝路徑添加到"/etc/profile"中,修改"/etc/profile"文件(配置java環境變量的文件),將以下語句添加到末尾,並使其有效:

# set hadoop path

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH :$HADOOP_HOME/bin

1)在"/usr/hadoop"創建"tmp"文件夾

mkdir /usr/hadoop/tmp

2)配置"/etc/profile"

vim /etc/profile

  配置後的文件如下:

3)重啓"/etc/profile"

source /etc/profile

4.2 配置hadoop

  1)配置hadoop-env.sh

  該"hadoop-env.sh"文件位於"/usr/hadoop/conf"目錄下。

  在文件的末尾添加下面內容。

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31

  Hadoop配置文件在conf目錄下,之前的版本的配置文件主要是Hadoop-default.xml和Hadoop-site.xml。由於Hadoop發展迅速,代碼量急劇增加,代碼開發分爲了core,hdfs和map/reduce三部分,配置文件也被分成了三個core-site.xml、hdfs-site.xml、mapred-site.xml。core-site.xml和hdfs-site.xml是站在HDFS角度上配置文件;core-site.xml和mapred-site.xml是站在MapReduce角度上配置文件。

  2)配置core-site.xml文件

  修改Hadoop核心配置文件core-site.xml,這裏配置的是HDFS的地址和端口號。

<configuration>

<property>

<name>hadoop.tmp.dir</name>

<value>/usr/hadoop/tmp</value>

備註:請先在 /usr/hadoop 目錄下建立 tmp 文件夾)

<description>A base for other temporary directories.</description>

</property>

<!-- file system properties -->

<property>

<name>fs.default.name</name>

<value>hdfs://192.168.1.2:9000</value>

</property>

</configuration>

  備註:如沒有配置hadoop.tmp.dir參數,此時系統默認的臨時目錄爲:/tmp/hadoo-hadoop。而這個目錄在每次重啓後都會被幹掉,必須重新執行format才行,否則會出錯。

用下面命令進行編輯:

編輯結果顯示如下:

  3)配置hdfs-site.xml文件

  修改Hadoop中HDFS的配置,配置的備份方式默認爲3。

<configuration>

<property>

<name>dfs.replication</name>

<value>1</value>

(備註:replication 是數據副本數量,默認爲3,salve少於3臺就會報錯)

</property>

<configuration>

  用下面命令進行編輯:

  編輯結果顯示如下:

  4)配置mapred-site.xml文件

  修改Hadoop中MapReduce的配置文件,配置的是JobTracker的地址和端口。

<configuration>

<property>

<name>mapred.job.tracker</name>

<value>http://192.168.1.2:9001</value>

</property>

</configuration>

  用下面命令進行編輯:

  編輯結果顯示如下:

  5)配置masters文件

  有兩種方案:

(1)第一種

修改localhost爲Master.Hadoop

(2)第二種

去掉"localhost",加入Master機器的IP:192.168.1.2

  爲保險起見,啓用第二種,因爲萬一忘記配置"/etc/hosts"局域網的DNS失效,這樣就會出現意想不到的錯誤,但是一旦IP配對,網絡暢通,就能通過IP找到相應主機。

  用下面命令進行修改:

  編輯結果顯示如下:

6)配置slaves文件(Master主機特有

有兩種方案:

(1)第一種

去掉"localhost",每行只添加一個主機名,把剩餘的Slave主機名都填上。

例如:添加形式如下

Slave1.Hadoop

Slave2.Hadoop

Slave3.Hadoop

(2)第二種

去掉"localhost",加入集羣中所有Slave機器的IP,也是每行一個。

例如:添加形式如下

192.168.1.3

192.168.1.4

192.168.1.5

  原因和添加"masters"文件一樣,選擇第二種方式。

  用下面命令進行修改:

  編輯結果如下:

  現在在Master機器上的Hadoop配置就結束了,剩下的就是配置Slave機器上的Hadoop。

  一種方式是按照上面的步驟,把Hadoop的安裝包在用普通用戶hadoop通過"scp"複製到其他機器的"/home/hadoop"目錄下,然後根據實際情況進行安裝配置,除了第6步,那是Master特有的。用下面命令格式進行。(備註:此時切換到普通用戶hadoop)

scp ~/hadoop-1.0.0.tar.gz hadoop@服務器IP:~/

例如:從"Master.Hadoop"到"Slave1.Hadoop"複製Hadoop的安裝包。

另一種方式是將 Master上配置好的hadoop所在文件夾"/usr/hadoop"複製到所有的Slave的"/usr"目錄下(實際上Slave機器上的slavers文件是不必要的, 複製了也沒問題)。用下面命令格式進行。(備註:此時用戶可以爲hadoop也可以爲root)

scp -r /usr/hadoop root@服務器IP:/usr/

例如:從"Master.Hadoop"到"Slave1.Hadoop"複製配置Hadoop的文件。

上圖中以root用戶進行復制,當然不管是用戶root還是hadoop,雖然Master機器上的"/usr/hadoop"文件夾用戶hadoop有權限,但是Slave1上的hadoop用戶卻沒有"/usr"權限,所以沒有創建文件夾的權限。所以無論是哪個用戶進行拷貝,右面都是"root@機器IP"格式。因爲我們只是建立起了hadoop用戶的SSH無密碼連接,所以用root進行"scp"時,扔提示讓你輸入"Slave1.Hadoop"服務器用戶root的密碼。

查看"Slave1.Hadoop"服務器的"/usr"目錄下是否已經存在"hadoop"文件夾,確認已經複製成功。查看結果如下:

  從上圖中知道,hadoop文件夾確實已經複製了,但是我們發現hadoop權限是root,所以我們現在要給"Slave1.Hadoop"服務器上的用戶hadoop添加對"/usr/hadoop"讀權限。

  以root用戶登錄"Slave1.Hadoop",執行下面命令。

chown -R hadoop:hadoop(用戶名:用戶組) hadoop(文件夾

  

  接着在"Slave1 .Hadoop"上修改"/etc/profile"文件(配置 java 環境變量的文件),將以下語句添加到末尾,並使其有效(source /etc/profile):

# set hadoop environment

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH :$HADOOP_HOME/bin

  如果不知道怎麼設置,可以查看前面"Master.Hadoop"機器的"/etc/profile"文件的配置,到此爲此在一臺Slave機器上的Hadoop配置就結束了。剩下的事兒就是照葫蘆畫瓢把剩餘的幾臺Slave機器按照《從"Master.Hadoop"到"Slave1.Hadoop"複製Hadoop的安裝包。》這個例子進行部署Hadoop。

4.3 啓動及驗證

  1)格式化HDFS文件系統

  在"Master.Hadoop"上使用普通用戶hadoop進行操作。(備註:只需一次,下次啓動不再需要格式化,只需 start-all.sh)

hadoop namenode -format

  某些書上和網上的某些資料中用下面命令執行。

  我們在看好多文檔包括有些書上,按照他們的hadoop環境變量進行配置後,並立即使其生效,但是執行發現沒有找見"bin/hadoop"這個命令。

  其實我們會發現我們的環境變量配置的是"$HADOOP_HOME/bin",我們已經把bin包含進入了,所以執行時,加上"bin"反而找不到該命令,除非我們的hadoop壞境變量如下設置。

# set hadoop path

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH : $HADOOP_HOME :$HADOOP_HOME/bin

  這樣就能直接使用"bin/hadoop"也可以直接使用"hadoop",現在不管哪種情況,hadoop命令都能找見了。我們也沒有必要重新在設置hadoop環境變量了,只需要記住執行Hadoop命令時不需要在前面加"bin"就可以了。

  從上圖中知道我們已經成功格式話了,但是美中不足就是出現了一個警告,從網上的得知這個警告並不影響hadoop執行,但是也有辦法解決,詳情看後面的"常見問題FAQ"。

  2)啓動hadoop

  在啓動前關閉集羣中所有機器的防火牆,不然會出現datanode開後又自動關閉。

service iptables stop

  使用下面命令啓動。

start-all.sh

  執行結果如下:

  可以通過以下啓動日誌看出,首先啓動namenode 接着啓動datanode1,datanode2,…,然後啓動secondarynamenode。再啓動jobtracker,然後啓動tasktracker1,tasktracker2,…。

  啓動 hadoop成功後,在 Master 中的 tmp 文件夾中生成了 dfs 文件夾,在Slave 中的 tmp 文件夾中均生成了 dfs 文件夾和 mapred 文件夾。

  查看Master中"/usr/hadoop/tmp"文件夾內容

  查看Slave1中"/usr/hadoop/tmp"文件夾內容。

  3)驗證hadoop

  (1)驗證方法一:用"jps"命令

  在Master上用 java自帶的小工具jps查看進程。

  在Slave1上用jps查看進程。

  如果在查看Slave機器中發現"DataNode"和"TaskTracker"沒有起來時,先查看一下日誌的,如果是"namespaceID"不一致問題,採用"常見問題FAQ6.2"進行解決,如果是"No route to host"問題,採用"常見問題FAQ6.3"進行解決。

  (2)驗證方式二:用"hadoop dfsadmin -report"

  用這個命令可以查看Hadoop集羣的狀態。

  Master服務器的狀態:

  Slave服務器的狀態

4.4 網頁查看集羣

  1)訪問"http:192.168.1.2:50030"

  2)訪問"http:192.168.1.2:50070"

5、常見問題FAQ

5.1 關於 Warning: $HADOOP_HOME is deprecated.

  hadoop 1.0.0版本,安裝完之後敲入hadoop命令時,是提示這個警告:

Warning: $HADOOP_HOME is deprecated.

  經查hadoop-1.0.0/bin/hadoop腳本和"hadoop-config.sh"腳本,發現腳本中對HADOOP_HOME的環境變量設置做了判斷,筆者的環境根本不需要設置HADOOP_HOME環境變量。

  解決方案一:編輯"/etc/profile"文件,去掉HADOOP_HOME的變量設定,重新輸入hadoop fs命令,警告消失。

  解決方案二:編輯"/etc/profile"文件,添加一個環境變量,之後警告消失:

export HADOOP_HOME_WARN_SUPPRESS=1

  解決方案三:編輯"hadoop-config.sh"文件,把下面的"if - fi"功能註釋掉。

  我們這裏本着不動Hadoop原配置文件的前提下,採用"方案二",在"/etc/profile"文件添加上面內容,並用命令"source /etc/profile"使之有效。

  1)切換至root用戶

  2)添加內容

  3)重新生效

5.2 解決"no datanode to stop"問題

  當我停止Hadoop時發現如下信息:

  原因:每次namenode format會重新創建一個namenodeId,而tmp/dfs/data下包含了上次format下的id,namenode format清空了namenode下的數據,但是沒有清空datanode下的數據,導致啓動時失敗,所要做的就是每次fotmat前,清空tmp一下的所有目錄。

  第一種解決方案如下:

  1)先刪除"/usr/hadoop/tmp"

rm -rf /usr/hadoop/tmp

  2)創建"/usr/hadoop/tmp"文件夾

mkdir /usr/hadoop/tmp

  3)刪除"/tmp"下以"hadoop"開頭文件

rm -rf /tmp/hadoop*

  4)重新格式化hadoop

hadoop namenode -format

  5)啓動hadoop

start-all.sh

  使用第一種方案,有種不好處就是原來集羣上的重要數據全沒有了。假如說Hadoop集羣已經運行了一段時間。建議採用第二種。

  第二種方案如下:

  1)修改每個Slave的namespaceID使其與Master的namespaceID一致。

  或者

  2)修改Master的namespaceID使其與Slave的namespaceID一致。

  該"namespaceID"位於"/usr/hadoop/tmp/dfs/data/current/VERSION"文件中,前面藍色的可能根據實際情況變化,但後面紅色是不變的。

  例如:查看"Master"下的"VERSION"文件

  本人建議採用第二種,這樣方便快捷,而且還能防止誤刪。

5.3 Slave服務器中datanode啓動後又自動關閉

  查看日誌發下如下錯誤。

  ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: java.io.IOException: Call to ... failed on local exception: java.net.NoRouteToHostException: No route to host

  解決方案是:關閉防火牆

service iptables stop

5.4 從本地往hdfs文件系統上傳文件

  出現如下錯誤:

  INFO hdfs.DFSClient: Exception in createBlockOutputStream java.io.IOException: Bad connect ack with firstBadLink

  INFO hdfs.DFSClient: Abandoning block blk_-1300529705803292651_37023

  WARN hdfs.DFSClient: DataStreamer Exception: java.io.IOException: Unable to create new block.

  解決方案是:

  1)關閉防火牆

service iptables stop

  2)禁用selinux

編輯 "/etc/selinux/config"文件,設置"SELINUX=disabled"

5.5 安全模式導致的錯誤

  出現如下錯誤:

  org.apache.hadoop.dfs.SafeModeException: Cannot delete ..., Name node is in safe mode

  在分佈式文件系統啓動的時候,開始的時候會有安全模式,當分佈式文件系統處於安全模式的情況下,文件系統中的內容不允許修改也不允許刪除,直到安全模式結束。安全模式主要是爲了系統啓動的時候檢查各個DataNode上數據塊的有效性,同時根據策略必要的複製或者刪除部分數據塊。運行期通過命令也可以進入安全模式。在實踐過程中,系統啓動的時候去修改和刪除文件也會有安全模式不允許修改的出錯提示,只需要等待一會兒即可。

  解決方案是:關閉安全模式

hadoop dfsadmin -safemode leave

5.6 解決Exceeded MAX_FAILED_UNIQUE_FETCHES

  出現錯誤如下:

  Shuffle Error: Exceeded MAX_FAILED_UNIQUE_FETCHES; bailing-out

  程序裏面需要打開多個文件,進行分析,系統一般默認數量是1024,(用ulimit -a可以看到)對於正常使用是夠了,但是對於程序來講,就太少了。

  解決方案是:修改2個文件。

  1)"/etc/security/limits.conf"

vim /etc/security/limits.conf

  加上:

soft nofile 102400

hard nofile 409600

  2)"/etc/pam.d/login"

vim /etc/pam.d/login

  添加:

session required /lib/security/pam_limits.so

  針對第一個問題我糾正下答案:

  這是reduce預處理階段shuffle時獲取已完成的map的輸出失敗次數超過上限造成的,上限默認爲5。引起此問題的方式可能會有很多種,比如網絡連接不正常,連接超時,帶寬較差以及端口阻塞等。通常框架內網絡情況較好是不會出現此錯誤的。

5.7 解決"Too many fetch-failures"

  出現這個問題主要是結點間的連通不夠全面。

  解決方案是:

  1)檢查"/etc/hosts"

  要求本機ip 對應 服務器名

  要求要包含所有的服務器ip +服務器名

  2)檢查".ssh/authorized_keys"

  要求包含所有服務器(包括其自身)的public key

5.8 處理速度特別的慢

  出現map,但是reduce,而且反覆出現"reduce=0%"。

  解決方案如下:

  結合解決方案5.7,然後修改"conf/hadoop-env.sh"中的"export HADOOP_HEAPSIZE=4000"

5.9解決hadoop OutOfMemoryError問題

  出現這種異常,明顯是jvm內存不夠得原因。

  解決方案如下:要修改所有的datanode的jvm內存大小。

Java –Xms 1024m -Xmx 4096m

  一般jvm的最大內存使用應該爲總內存大小的一半,我們使用的8G內存,所以設置爲4096m,這一值可能依舊不是最優的值。

5.10 Namenode in safe mode

  解決方案如下:

bin/hadoop dfsadmin -safemode leave

5.11 IO寫操作出現問題

  0-1246359584298, infoPort=50075, ipcPort=50020):Got exception while serving blk_-5911099437886836280_1292 to /172.16.100.165:

  java.net.SocketTimeoutException: 480000 millis timeout while waiting for channel to be ready for write. ch : java.nio.channels.SocketChannel[connected local=/

  172.16.100.165:50010 remote=/172.16.100.165:50930]

  at org.apache.hadoop.net.SocketIOWithTimeout.waitForIO(SocketIOWithTimeout.java:185)

  at org.apache.hadoop.net.SocketOutputStream.waitForWritable(SocketOutputStream.java:159)

  ……

  It seems there are many reasons that it can timeout, the example given in HADOOP-3831 is a slow reading client.

解決方案如下:

  在hadoop-site.xml中設置dfs.datanode.socket.write.timeout=0

5.12 status of 255 error

  錯誤類型:

  java.io.IOException: Task process exit with nonzero status of 255.

  at org.apache.hadoop.mapred.TaskRunner.run(TaskRunner.java:424)

  錯誤原因:

  Set mapred.jobtracker.retirejob.interval and mapred.userlog.retain.hours to higher value. By default, their values are 24 hours. These might be the reason for failure, though I'm not sure restart.

  解決方案如下:單個datanode

  如果一個datanode 出現問題,解決之後需要重新加入cluster而不重啓cluster,方法如下:

bin/hadoop-daemon.sh start datanode

bin/hadoop-daemon.sh start jobtracker

6、用到的Linux命令

6.1 chmod命令詳解

  使用權限:所有使用者

  使用方式:chmod [-cfvR] [--help] [--version] mode file...

  說明:

  Linux/Unix 的檔案存取權限分爲三級 : 檔案擁有者、羣組、其他。利用 chmod 可以藉以控制檔案如何被他人所存取。

  mode :權限設定字串,格式如下 :[ugoa...][[+-=][rwxX]...][,...],其中u 表示該檔案的擁有者,g 表示與該檔案的擁有者屬於同一個羣體(group)者,o 表示其他以外的人,a 表示這三者皆是。

+ 表示增加權限、- 表示取消權限、= 表示唯一設定權限。

r 表示可讀取,w 表示可寫入,x 表示可執行,X 表示只有當該檔案是個子目錄或者該檔案已經被設定過爲可執行。

-c : 若該檔案權限確實已經更改,才顯示其更改動作

-f : 若該檔案權限無法被更改也不要顯示錯誤訊息

-v : 顯示權限變更的詳細資料

-R : 對目前目錄下的所有檔案與子目錄進行相同的權限變更(即以遞迴的方式逐個變更)

--help : 顯示輔助說明

--version : 顯示版本

  範例:

  將檔案 file1.txt 設爲所有人皆可讀取

chmod ugo+r file1.txt

  將檔案 file1.txt 設爲所有人皆可讀取

chmod a+r file1.txt

  將檔案 file1.txt 與 file2.txt 設爲該檔案擁有者,與其所屬同一個羣體者可寫入,但其他以外的人則不可寫入

chmod ug+w,o-w file1.txt file2.txt

  將 ex1.py 設定爲只有該檔案擁有者可以執行

chmod u+x ex1.py

  將目前目錄下的所有檔案與子目錄皆設爲任何人可讀取

chmod -R a+r *

  此外chmod也可以用數字來表示權限如 chmod 777 file

  語法爲:chmod abc file

  其中a,b,c各爲一個數字,分別表示User、Group、及Other的權限。

  r=4,w=2,x=1

  若要rwx屬性則4+2+1=7;

  若要rw-屬性則4+2=6;

  若要r-x屬性則4+1=7。

  範例:

  chmod a=rwx file 和 chmod 777 file 效果相同

  chmod ug=rwx,o=x file 和 chmod 771 file 效果相同

  若用chmod 4755 filename可使此程式具有root的權限

6.2 chown命令詳解

  使用權限:root

  使用方式:chown [-cfhvR] [--help] [--version] user[:group] file...

  說明:

  Linux/Unix 是多人多工作業系統,所有的檔案皆有擁有者。利用 chown 可以將檔案的擁有者加以改變。一般來說,這個指令只有是由系統管理者(root)所使用,一般使用者沒有權限可以改變別人的檔案擁有者,也沒有權限可以自己的檔案擁有者改設爲別人。只有系統管理者(root)纔有這樣的權限。

user : 新的檔案擁有者的使用者

IDgroup : 新的檔案擁有者的使用者羣體(group)

-c : 若該檔案擁有者確實已經更改,才顯示其更改動作

-f : 若該檔案擁有者無法被更改也不要顯示錯誤訊息

-h : 只對於連結(link)進行變更,而非該 link 真正指向的檔案

-v : 顯示擁有者變更的詳細資料

-R : 對目前目錄下的所有檔案與子目錄進行相同的擁有者變更(即以遞迴的方式逐個變更)

--help : 顯示輔助說明

--version : 顯示版本

  範例:

  將檔案 file1.txt 的擁有者設爲 users 羣體的使用者 jessie

chown jessie:users file1.txt

  將目前目錄下的所有檔案與子目錄的擁有者皆設爲 users 羣體的使用者 lamport

chown -R lamport:users *

-rw------- (600) -- 只有屬主有讀寫權限。

-rw-r--r-- (644) -- 只有屬主有讀寫權限;而屬組用戶和其他用戶只有讀權限。

-rwx------ (700) -- 只有屬主有讀、寫、執行權限。

-rwxr-xr-x (755) -- 屬主有讀、寫、執行權限;而屬組用戶和其他用戶只有讀、執行權限。

-rwx--x--x (711) -- 屬主有讀、寫、執行權限;而屬組用戶和其他用戶只有執行權限。

-rw-rw-rw- (666) -- 所有用戶都有文件讀、寫權限。這種做法不可取。

-rwxrwxrwx (777) -- 所有用戶都有讀、寫、執行權限。更不可取的做法。

  以下是對目錄的兩個普通設定:

drwx------ (700) - 只有屬主可在目錄中讀、寫。

drwxr-xr-x (755) - 所有用戶可讀該目錄,但只有屬主才能改變目錄中的內容

  suid的代表數字是4,比如4755的結果是-rwsr-xr-x

  sgid的代表數字是2,比如6755的結果是-rwsr-sr-x

  sticky位代表數字是1,比如7755的結果是-rwsr-sr-t

6.3 scp命令詳解

  scp是 secure copy的縮寫,scp是linux系統下基於ssh登陸進行安全的遠程文件拷貝命令。linux的scp命令可以在linux服務器之間複製文件和目錄。

  scp命令的用處:

  scp在網絡上不同的主機之間複製文件,它使用ssh安全協議傳輸數據,具有和ssh一樣的驗證機制,從而安全的遠程拷貝文件。

  scp命令基本格式:

scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]

[-l limit] [-o ssh_option] [-P port] [-S program]

[[user@]host1:]file1 [...] [[user@]host2:]file2

  scp命令的參數說明:

-1 強制scp命令使用協議ssh1

-2 強制scp命令使用協議ssh2

-4 強制scp命令只使用IPv4尋址

-6 強制scp命令只使用IPv6尋址

-B 使用批處理模式(傳輸過程中不詢問傳輸口令或短語)

-C 允許壓縮。(將-C標誌傳遞給ssh,從而打開壓縮功能)

-p 保留原文件的修改時間,訪問時間和訪問權限。

-q 不顯示傳輸進度條。

-r 遞歸複製整個目錄。

-v 詳細方式顯示輸出。scp和ssh(1)會顯示出整個過程的調試信息。這些信息用於調試連接,驗證和配置問題。

-c cipher 以cipher將數據傳輸進行加密,這個選項將直接傳遞給ssh。

-F ssh_config 指定一個替代的ssh配置文件,此參數直接傳遞給ssh。

-i identity_file 從指定文件中讀取傳輸時使用的密鑰文件,此參數直接傳遞給ssh。

-l limit 限定用戶所能使用的帶寬,以Kbit/s爲單位。

-o ssh_option 如果習慣於使用ssh_config(5)中的參數傳遞方式,

-P port 注意是大寫的P, port是指定數據傳輸用到的端口號

-S program 指定加密傳輸時所使用的程序。此程序必須能夠理解ssh(1)的選項。

  scp命令的實際應用

  1)從本地服務器複製到遠程服務器

  (1) 複製文件:

  命令格式:

scp local_file remote_username@remote_ip:remote_folder

  或者

scp local_file remote_username@remote_ip:remote_file

  或者

scp local_file remote_ip:remote_folder

  或者

scp local_file remote_ip:remote_file

  第1,2個指定了用戶名,命令執行後需要輸入用戶密碼,第1個僅指定了遠程的目錄,文件名字不變,第2個指定了文件名

  第3,4個沒有指定用戶名,命令執行後需要輸入用戶名和密碼,第3個僅指定了遠程的目錄,文件名字不變,第4個指定了文件名

  實例:

scp /home/linux/soft/scp.zip [email protected]:/home/linux/others/soft

scp /home/linux/soft/scp.zip [email protected]:/home/linux/others/soft/scp2.zip

scp /home/linux/soft/scp.zip www.mydomain.com:/home/linux/others/soft

scp /home/linux/soft/scp.zip www.mydomain.com:/home/linux/others/soft/scp2.zip

  (2) 複製目錄:

  命令格式:

scp -r local_folder remote_username@remote_ip:remote_folder

  或者

scp -r local_folder remote_ip:remote_folder

  第1個指定了用戶名,命令執行後需要輸入用戶密碼;

  第2個沒有指定用戶名,命令執行後需要輸入用戶名和密碼;

  例子:

scp -r /home/linux/soft/ [email protected]:/home/linux/others/

scp -r /home/linux/soft/ www.mydomain.com:/home/linux/others/

  上面 命令 將 本地 soft 目錄 複製 到 遠程 others 目錄下,即複製後遠程服務器上會有/home/linux/others/soft/ 目錄。

  2)從遠程服務器複製到本地服務器

  從遠程複製到本地的scp命令與上面的命令雷同,只要將從本地複製到遠程的命令後面2個參數互換順序就行了。

  例如:

scp [email protected]:/home/linux/soft/scp.zip /home/linux/others/scp.zip

scp www.mydomain.com:/home/linux/soft/ -r /home/linux/others/

  linux系統下scp命令中很多參數都和ssh1有關,還需要看到更原汁原味的參數信息,可以運行man scp 看到更細緻的英文說明。

發佈了26 篇原創文章 · 獲贊 10 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章