課程指數(shù)
難度指數(shù): 4星(滿星5星)
技術(shù)指數(shù): 5星(滿星5星)
理論指數(shù): 3星(滿星5星)
面向人群: 自動(dòng)化運(yùn)維&初中級(jí)運(yùn)維
分享目錄
1.1 添加額外的源 1
1.2 部署一個(gè)Node.js app 4
1.3 運(yùn)行一個(gè)Node.js app 6
1.4 Node.js app服務(wù)器總結(jié)。 7
1.5 問(wèn)題: 7
下面我們將要在我們的CentOS6.x服務(wù)器上配置Nodejs,啟動(dòng)一個(gè)簡(jiǎn)單的nodejs實(shí)例,這個(gè)服務(wù)器有很簡(jiǎn)單的架構(gòu)。
開(kāi)始了,首先創(chuàng)建一個(gè)playbook文件,我們盡量讓它保持簡(jiǎn)單。
---
- hosts: all
tasks:
定義一些運(yùn)行這個(gè)playbook的主機(jī),然后下面列出一系列的tasks。
1.1 添加額外的源
在準(zhǔn)備應(yīng)用一個(gè)服務(wù)器的時(shí)候,為了確保指定些軟件包可以用或者在最新的版本,管理員經(jīng)常首先添加額外的源。
下面的腳本,我們想要添加EPEL和Remi源,以便于我們可以得到類似node.js的軟件包。如果使用shell腳本處理的話,如下所示。
# 導(dǎo)入 Remi GPG 密鑰 – 請(qǐng)參閱: http://rpms.famillecollet.com/RPM-GPG-KEY-remi
wget http://rpms.famillecollet.com/RPM-GPG-KEY-remi \
-O /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
# 安裝 Remi repo, Remi repo里面包含了很多的PHP擴(kuò)展
rpm -Uvh --quiet \
http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
# 安裝EPEL源
yum install epel-release
# 安裝 Node.js (npm + 和它的依賴關(guān)系).
yum --enablerepo=epel install npm
這個(gè)shell腳本用于導(dǎo)入EPEL和Remi的GPG keys,然后添加這源,最后安裝Nodejs。這對(duì)于簡(jiǎn)單的部署是沒(méi)有問(wèn)題的,但是運(yùn)行這么多命令是比較笨的方法,如果你的連接不小心斷開(kāi)了,那么你的腳本也會(huì)停止的。而如果這個(gè)時(shí)候,你的腳本剛準(zhǔn)備完成呢?
提示:如果你想跳過(guò)指定的步驟,你可以跳過(guò)添加GPG keys的步驟,只需要在運(yùn)行命令的時(shí)候加上—nogpgcheck.或者在Ansible中,yum模塊中設(shè)置disable_gpg_check參數(shù)為yes,但是最好還是添加GPG keys。使用GPG,你可以知道包的作者是誰(shuí),包有沒(méi)有修改稿,除非你知道你正在做什么,否則最好不要禁止GPG檢查。
Ansible讓事情變的更有健壯性,下面使用Ansible的案例顯得更加詳細(xì),它和上面的shell腳本有同樣的功能,但是更容易理解,更加結(jié)構(gòu)化。 下面使用了Ansible的變量和其它的一些有用的特性。接著上面的playbook,我們繼續(xù)往下寫(xiě)。
tasks:
- name: Import Remi GPG key
rpm_key: "key={{ item }} state=present"
with_items:
- "http://rpms.famillecollet.com/RPM-GPG-KEY-remi"
- name: Install Remi repo.
command: "rpm -Uvh --force {{ item.href }} creates={{ item.creates }}"
with_items:
- href: "http://rpms.famillecollet.com/enterprise/remi-release-6.rpm"
creates: "/etc/yum.repos.d/remi.repo"
- name: Install epel repo
yum: name=epel-release state=present
- name: Stop the firewall
service: name=iptables state=stopped
- name: Install NodeJS and npm
yum: name=npm state=present enablerepo=epel
我們看一下具體步驟。
- rpm_key 是一個(gè)的Ansible模塊用于從你的RPM數(shù)據(jù)庫(kù)中添加或移除GPG key。我們正在從Remi的源中導(dǎo)入一個(gè)key。
- 因?yàn)锳nsible沒(méi)有rpm命令,因此我們使用command模塊來(lái)使用rpm命令,這樣我們可以做其它的兩件事情。
a) 使用creatse參數(shù)告訴Ansible什么時(shí)候不運(yùn)行這個(gè)命令,這個(gè)例子里,我們告訴Ansible,這個(gè)命令成功執(zhí)行后,將會(huì)創(chuàng)建那些文件。當(dāng)這個(gè)文件存在的時(shí)候,這個(gè)命令將不會(huì)運(yùn)行。
b) 使用with_items定義一個(gè)URL和用于creates檢查的文件。 - yum負(fù)責(zé)安裝EPEL源。
- 因?yàn)檫@個(gè)服務(wù)器我們將用來(lái)做測(cè)試用,所以我們使用service模塊禁止系統(tǒng)防火墻,防止它干涉我們測(cè)試。
- yum安裝Node.js(同時(shí)安裝npm,Node’s package manager),我們使用enablerepo來(lái)指定在EPEL源中搜索它,當(dāng)然也可以使用disablerepo來(lái)指定不使用那個(gè)源(repository)。
- 因?yàn)镹PM現(xiàn)在被安裝了,我們使用Ansible 的npm模塊安裝Node.JS工具forever,forever來(lái)運(yùn)行我們的app,設(shè)置global為yes,告訴NPM安裝模塊在/usr/lib/node_modules位置,然后所有的用戶都可以使用它了。
我們已經(jīng)有一個(gè)Node.js app 服務(wù)器了,讓我們部署一個(gè)簡(jiǎn)單的Node.js app,在80端口響應(yīng)HTTP請(qǐng)求
1.2 部署一個(gè)Node.js app
這一步是在我們的服務(wù)器上部署簡(jiǎn)單的Node.js app。首先,通過(guò)創(chuàng)建一個(gè)新的文件夾,我們創(chuàng)建一個(gè)簡(jiǎn)單的Node.js app,這個(gè)文件夾和你上面的ymal文件處于相同的路徑下面。然后創(chuàng)建新的文件,app.js,在這個(gè)文件夾里面,編輯下面的文件
//app.js
// 加載express 模塊.
var express = require('express'),
app = express.createServer();
// 響應(yīng)”/”請(qǐng)求為 'Hello World'.
app.get('/', function(req, res){
res.send('Hello World! Yunzhonge');
});
// 像一個(gè)真實(shí)服務(wù)器那樣監(jiān)聽(tīng)在80端口
app.listen(80);
console.log('Express server started successfully.')
不要擔(dān)心node.js的語(yǔ)法的和我們的案例。我們需要一個(gè)快速的部署案例,這個(gè)案例可以用Python,Perl,Java,PHP或者其他編程語(yǔ)言來(lái)寫(xiě),但是因?yàn)镹ode是非常簡(jiǎn)單的語(yǔ)言,運(yùn)行一個(gè)簡(jiǎn)單的輕量級(jí)的環(huán)境,它是一個(gè)非常不錯(cuò)的語(yǔ)言來(lái)測(cè)試你的服務(wù)器。
因?yàn)檫@個(gè)小app依賴于Express(一個(gè)簡(jiǎn)單的Node的HTTP框架),我們同樣需要通過(guò)一個(gè)package.json文件告訴NPM關(guān)于它的依賴關(guān)系,這個(gè)文件與app.js處于相同的路徑下面。
{
"name": "examplenodeapp",
"description": "Example Express Node.js app.",
"author": "yunzhonghe",
"dependencies": {
"express": "3.x.x"
},
"engine": "node >= 0.10.6"
}
然后添加下面內(nèi)容到你的playbook里面,拷貝整個(gè)app到這個(gè)服務(wù)器,然后讓npm下載依賴的東西,(這里為express.)
- name: Ensure Node.js app folder exists.
file: "path={{ node_apps_location }} state=directory"
- name: Copy example Node.js app to server.
copy: "src=app dest={{ node_apps_location }}"
- name: Install app dependencies defined in package.json.
npm: "path={{ node_apps_location }}/app"
- 首先我們使用file模塊確保我們安裝的app目錄存在。{{ node_apps_location }}變量可以在vars部分定義,vars部分位于playbook的頂部。當(dāng)然也可以在inevntory中定義,也可以在運(yùn)行ansible-playbook的時(shí)候定義。
- 我們使用Ansible的copy模塊拷貝這整個(gè)app文件夾到測(cè)試服務(wù)器,copy模塊可以聰明的區(qū)分單一的文件和包含文件的目錄,然后在目錄中遞歸,就像rsync或scp。Ansible的copy模塊在單個(gè)文件或少量文件時(shí)候非常好用,但是如果你拷貝大量的文件,嵌套幾層的目錄,copy模塊就不能勝任。這種情形下,如果你想拷貝整個(gè)目錄,你最好考慮使用synchronize模塊,如果你想拷貝一個(gè)歸檔,然后展開(kāi)它,最好使用unarchive模塊。
- 第三步,我們使用npm模塊,這次除了app的路徑之外沒(méi)有額外的參數(shù)。這告訴NPM來(lái)解析package.json文件,然后確保所有的依賴關(guān)系都存在。
已經(jīng)都完成了,最后一步是啟動(dòng)這個(gè)app
1.3 運(yùn)行一個(gè)Node.js app
我們現(xiàn)在使用forever來(lái)啟動(dòng)這個(gè)app。
- name: Check list of running Node.js apps.
command: forever list
register: forever_list
changed_when: false
- name: Start example Node.js app.
command: "forever start {{ node_apps_location }}/app/app.js"
when: "forever_list.stdout.find('{{ node_apps_location }}/app/app.js') == -1"
在這個(gè)play中,我們做了兩件新的事情。
- register 創(chuàng)建了一個(gè)新的變量,forever_list,以便于下次task的時(shí)候使用用于判斷是否允運(yùn)行下一個(gè)命令。register用于保存命令的輸出包括標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出,然后賦給變量名。
- changed_when告訴Ansible什么時(shí)候這個(gè)play會(huì)導(dǎo)致改變。在這里,forever list命令永遠(yuǎn)都不會(huì)導(dǎo)致服務(wù)器的改變,因?yàn)槲覀冎付薴alse。
第二個(gè)play實(shí)際上使用forever啟動(dòng)了這個(gè)app。我們可以啟動(dòng)這個(gè)app通過(guò)調(diào)用node {{ node_apps_location }}/app/app.js,不過(guò)這種方式更難控制。
Forever跟蹤它管理的Node app,然后我們使用Forever的list選項(xiàng),打印一系列的運(yùn)行app。我們第一次運(yùn)行這個(gè)playbook時(shí)候,這list明顯是空的,但是判斷為空之后就會(huì)運(yùn)行,如果app正在運(yùn)行,我們不會(huì)啟動(dòng)另外一個(gè)實(shí)例了,為了避免這種情形,我們使用when語(yǔ)句,指定,當(dāng)app的路徑不在forever list的輸出信息的時(shí)候,我們啟動(dòng)這個(gè)app。
1.4 Node.js app服務(wù)器總結(jié)。
在這時(shí)候上,你已經(jīng)完成了playbook,然后安裝一個(gè)簡(jiǎn)單的Node.js app,在80端口響應(yīng)HTTP請(qǐng)求。
為了運(yùn)行這個(gè)playbook在一個(gè)服務(wù)器上,使用下面的命令,傳遞node_apps_location變量通過(guò)命令
ansible-playbook --extra-vars="node_apps_location=/usr/local/opt/node"
當(dāng)服務(wù)器完成配置和部署服務(wù)器的時(shí)候,在瀏覽器中指定測(cè)試服務(wù)器的主機(jī)名查看效果
簡(jiǎn)單,但是有效,我們已經(jīng)在少于50行的YMAL文件中配置了一個(gè)Nodejs應(yīng)用服務(wù)器
到此結(jié)束,非常感謝朋友們的關(guān)注。
1.5 問(wèn)題:
問(wèn)題1:當(dāng)我在給100臺(tái)服務(wù)器進(jìn)行nodejs app部署的時(shí)候,到20臺(tái)中斷了,我再重新執(zhí)行,他是一個(gè)怎么個(gè)過(guò)程,前面已經(jīng)安裝的軟件包,進(jìn)行的配置的會(huì)重新的再進(jìn)行執(zhí)行一遍還是跳過(guò)呢?麻煩大牛解答
答:Ansible自身有冪等特性使其能有效保證所有操作的安全可靠性,針對(duì)執(zhí)行失敗的情況會(huì)自動(dòng)在家目錄下生成 對(duì)應(yīng)的錯(cuò)誤服務(wù)器列表 通過(guò) --limit 再次有針對(duì)性的完成剩余工作
問(wèn)題2: 請(qǐng)問(wèn)大俠的nodejs 是通過(guò)工具部署的? npm 好復(fù)雜啊
哈,npm的配置是一次性的,初始配置確認(rèn)比較耗時(shí)且麻煩,yum 或 npm 各有優(yōu)劣,視業(yè)務(wù)而定吧
問(wèn)題3: --extra-vars= 請(qǐng)問(wèn)企業(yè)中應(yīng)用的多嗎?
答:多,最少在我們的工作中一直有應(yīng)用,前幾期的分享大家應(yīng)該也有看到,這個(gè)參數(shù)應(yīng)用的很多。但官網(wǎng)介紹的卻一筆代過(guò)。還是建議大家多用
問(wèn)題4:當(dāng)我通過(guò)ansible執(zhí)行任務(wù)的過(guò)程中,會(huì)出現(xiàn)任務(wù)被長(zhǎng)時(shí)間卡主的原因,這一般需要重哪些地方排查呢
答:
很多朋友遇到這個(gè)問(wèn)題,一直有問(wèn),根據(jù)個(gè)人的經(jīng)驗(yàn)建議如下幾個(gè)方面排查:
- 確實(shí)當(dāng)前命令執(zhí)行時(shí)間不長(zhǎng),
- pong 檢測(cè)服務(wù)器存活
- 有些命令需連接外網(wǎng)下載更新耗時(shí)較長(zhǎng)的可檢查網(wǎng)絡(luò)寬帶情況
- -vvv 是非常好的排查方式
- 如若卻無(wú)解,/etc/ansible/ansible.cfg 中縮短操作執(zhí)行時(shí)長(zhǎng),等待最終結(jié)束嘗試