SMF功能概覽
Solaris 10中的SMF提供了強(qiáng)大的服務(wù)管理功能。以下是一部分重要的功能:
1. SMF向系統(tǒng)管理員提供統(tǒng)一的服務(wù)管理平臺(tái),利用svcs(1)命令就可以查看SMF所轄的各種服務(wù),利用svccfg(1M)和svcadm(1M)命令可以配置各種服務(wù)和管理各種服務(wù)。對(duì)于傳統(tǒng)UNIX,系統(tǒng)管理員必須記住各種服務(wù)不同的啟動(dòng)/停止方法、配置修改方法、服務(wù)狀態(tài)及日志查詢方法而言, SMF統(tǒng)一管理平臺(tái)極大地降低了系統(tǒng)管理的難度,也降低了系統(tǒng)管理出錯(cuò)的機(jī)率。
2. SMF提供各服務(wù)間的依賴關(guān)系設(shè)定,可以自動(dòng)按依賴關(guān)系順序啟動(dòng)各服務(wù)。這對(duì)于傳統(tǒng)UNIX以rc腳本文件名排列先后決定啟動(dòng)/停止順序而言,SMF提供了無可比擬的完善的管理能力。
3. 并行啟動(dòng)不相互依賴的服務(wù),從而使系統(tǒng)啟動(dòng)更快。由于各服務(wù)的依賴關(guān)系在SMF中有明確的定義,所以不相干的服務(wù)完全可以并行啟動(dòng)而不必?fù)?dān)心沖突。
4. 自動(dòng)偵測(cè)所轄服務(wù)的運(yùn)行狀態(tài),在必要時(shí)可以重啟服務(wù)或停止服務(wù)。作為預(yù)測(cè)性自愈技術(shù)(Predictive Self-Healing)的組成部分,SMF可以對(duì)所轄服務(wù)進(jìn)行狀態(tài)監(jiān)控。根據(jù)服務(wù)的需要,SMF可以在服務(wù)進(jìn)程不存在時(shí),自動(dòng)重啟服務(wù),或者在服務(wù)所依賴關(guān)系發(fā)生問題時(shí),重啟服務(wù)。也可以在服務(wù)連續(xù)發(fā)生問題時(shí),將服務(wù)置為維護(hù)(maintenance)狀態(tài)。
當(dāng)然,SMF的管理機(jī)制并不排斥傳統(tǒng)rc腳本運(yùn)行服務(wù)的機(jī)制,以最大程度兼容傳統(tǒng)方式的運(yùn)作。有關(guān)SMF更多的介紹,請(qǐng)參看Solaris Service Management Facility - Quickstart Guide。
一個(gè)簡(jiǎn)單的服務(wù)程序
<表1是一個(gè)簡(jiǎn)單的程序myapp.c,它運(yùn)行后將成為后臺(tái)守護(hù)進(jìn)程存在于系統(tǒng)中,并每間隔5秒鐘向日志文件/tmp/myapp.log插入一行記錄以報(bào)告自己的存在。雖然它實(shí)際上不向外提供任何服務(wù),但該程序模擬了一般服務(wù)程序的設(shè)計(jì)結(jié)構(gòu)和運(yùn)行模式。即,程序運(yùn)行后以守護(hù)進(jìn)程形式存在于系統(tǒng),程序頭部有模擬服務(wù)配置read_config()和初始化app_init()邏輯,中部使用sleep(5)模擬等待服務(wù)請(qǐng)求邏輯,通過向日志插入記錄模擬服務(wù)請(qǐng)求處理邏輯,然后返回至循環(huán)開始處sleep(5)繼續(xù)等待下一個(gè)服務(wù)請(qǐng)求等。只要在此結(jié)構(gòu)上修改和擴(kuò)充相應(yīng)的邏輯就可以將此程序修改為一個(gè)真正的服務(wù)程序。本文要點(diǎn)是說明如何部署應(yīng)用為SMF服務(wù),所以僅采用此程序作為例子。
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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
/***************************************/ /* myapp.c */ /***************************************/ #include <unistd.h> #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <time.h> /* global exit status code */ #define RUN_OK 0 #define CONFIG_ERROR 1 #define FATAL_ERROR 2 /* function declaration */ int read_config(void); int app_init(void); void daemonize(void); int main(int argc, char **argv) { FILE *fp; time_t t; /* Read the app configuration here if applicable. */ /* If error occurred, return non-zero. */ if (read_config() != RUN_OK) { printf("%s: read configuration failuren", argv[0]); exit(CONFIG_ERROR); } /* Initialize application. Any error, return non-zero. */ if (app_init() != RUN_OK) { printf("%s: initialization failuren", argv[0]); exit(FATAL_ERROR); } daemonize(); /* Make the application a daemon. */ /* Service logic is placed here. */ while (1) { sleep(5); /* Sleep for 5 seconds. In a real application, it could be */ /* waiting for service requests, e.g. waiting on message */ /* queue, etc. */ /* service logic */ if ((fp = fopen("/tmp/myapp.log","a")) != NULL) { t = time(0); fprintf(fp, "myapp is running at %sn",asctime(localtime(&t))); fclose(fp); } else { exit(FATAL_ERROR); } } } /* make the application a daemon */ void daemonize(void) { int pid; int i; if (pid = fork()) exit(RUN_OK); /* parent exits */ else if (pid < 0) exit(FATAL_ERROR); /* fork() failed */ /* first child process */ setsid(); if (pid = fork()) exit(RUN_OK); /* first child exits */ else if(pid < 0) exit(FATAL_ERROR); /* fork() failed */ /* second child */ for (i = 0; i < NOFILE; ++i) /* close all files */ close(i); chdir("/"); /* change directory to root(/) directory */ umask(0); /* set proper file mask */ } /* read configuration */ int read_config(void) { return RUN_OK; /* OK */ } /* initialize application */ int app_init(void) { return RUN_OK; /* OK */ } |
利用Sun公司最新推出的C/C++/Fortran開發(fā)及編譯環(huán)境Sun Studio 11,使用以下命令將myapp.c編譯成可執(zhí)行程序myapp。
$ /opt/SUNWspro/bin/cc -o ./myapp ./myapp.c |
或者直接使用Solaris 10自帶的gcc編譯器將myapp.c編譯成可執(zhí)行程序myapp。
$ /usr/sfw/bin/gcc -o ./myapp ./myapp.c |
編譯成功后在當(dāng)前目錄下會(huì)生成myapp可執(zhí)行程序。本例假設(shè)當(dāng)前目錄為/export/home/smfdemo。直接在命令行輸入. /myapp就可以啟動(dòng)myapp為后臺(tái)守護(hù)進(jìn)程,同時(shí)可以在/tmp目錄下找到myapp.log文件。使用“/usr/bin/tail -f /tmp/myapp.log”命令可以看到進(jìn)程myapp不斷在日志文件中報(bào)告自己的存在。為了使這個(gè)進(jìn)程在服務(wù)器啟動(dòng)時(shí)自動(dòng)運(yùn)行,傳統(tǒng)做法一般需要寫三個(gè)shell腳本。其中一個(gè)shell腳本置于/etc/init.d目錄下用以實(shí)際啟動(dòng)和停止myapp服務(wù)(例如<表2所示的/etc/init.d/myapp.sh)。另外兩個(gè)shell腳本的名字分別以S和K開頭(例如,S79myapp和K79myapp),置于合適的/etc/rc?.d目錄下(例如,/etc/rc2.d目錄下),分別以不同參數(shù)(start和stop)調(diào)用 /etc/init.d/myapp.sh。操作系統(tǒng)在boot時(shí)會(huì)自動(dòng)運(yùn)行S開頭的腳本以啟動(dòng)myapp服務(wù),而在shutdown時(shí)自動(dòng)運(yùn)行K開頭的腳本以關(guān)閉myapp服務(wù)。
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 |
#!/sbin/sh ############################################################################### # /etc/init.d/myapp.sh # ############################################################################### RUN_OK=0 CONFIG_ERROR=1 FATAL_ERROR=2 case "$1" in 'start') /export/home/smfdemo/myapp if [ $? -eq $CONFIG_ERROR ]; then exit $CONFIG_ERROR fi if [ $? -eq $FATAL_ERROR ]; then exit $FATAL_ERROR fi ;; 'stop') /usr/bin/pkill myapp ;; *) echo "Usage: $0 { start | stop }" ;; esac exit $RUN_OK |
SMF可部署的服務(wù)
本節(jié)講述如何將上述例子改為SMF可部署的服務(wù)。根據(jù)SMF的要求,開發(fā)一個(gè)SMF可部署的服務(wù)需要至少以下幾個(gè)步驟。
創(chuàng)建manifest文件
SMF manifest文件是一個(gè)XML文件,它用以定義SMF服務(wù)各屬性。比如,定義服務(wù)名稱、服務(wù)依賴關(guān)系、服務(wù)啟動(dòng)方法、服務(wù)停止方法、服務(wù)所需參數(shù)等。創(chuàng)建manifest文件最簡(jiǎn)單的方法是從/var/svc/manifest目錄下挑選已存在的相同類型的服務(wù)XML文件,將它拷貝到開發(fā)目錄,比如/export/home/smfdemo目錄下,以拷貝件為基礎(chǔ)修改而成。本文是個(gè)簡(jiǎn)單的服務(wù),所以參考了 /var/svc/manifest/system/utmp.xml文件(因?yàn)樗埠芎?jiǎn)單),在其基礎(chǔ)上修改成表3所示的/export/home/smfdemo/myapp.xml。#p#副標(biāo)題#e#
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 |
<?xml version="1.0"?> <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> <service_bundle type='manifest' name='mypackage:myapp'> <service name='application/myapp' type='service' version='1'> <create_default_instance enabled='true' /> <single_instance/> <dependency name='milestone' grouping='require_all' restart_on='none' type='service'> <service_fmri value='svc:/milestone/multi-user' /> </dependency> <exec_method type='method' name='start' exec='/export/home/smfdemo/myapp.sh start' timeout_seconds='60' /> <exec_method type='method' name='stop' exec=':kill' timeout_seconds='60' /> </service> </service_bundle> |
myapp.xml必須符合/usr/share/lib/xml/dtd/service_bundle.dtd.1規(guī)范,其意理解如下:
1. <service_bundle>標(biāo)簽用以告知SMF如何處理myapp.xml文件。本例中myapp.xml是一個(gè)manifest文件用以定義SMF服務(wù),所以type賦為“manifest”。同時(shí)需要給service_bundle一個(gè)名字,一般命名規(guī)范是以服務(wù)所在安裝包名為前綴,所以本例將name賦為“mypackage:myapp”。其實(shí)name只要不與系統(tǒng)中已有的相重就可以了,當(dāng)然對(duì)于企業(yè)級(jí)應(yīng)用服務(wù)應(yīng)該有一個(gè)合適的名字。
2. <service>標(biāo)簽主要定義SMF服務(wù)的名稱,由于myapp只是一個(gè)簡(jiǎn)單應(yīng)用,所以name賦為“application/myapp”。如果myapp是網(wǎng)絡(luò)服務(wù),則根據(jù)命名規(guī)范名字應(yīng)以“network/”開頭加myapp,即 “network/myapp”,請(qǐng)參考/var/svc/manifest/下的目錄結(jié)構(gòu)以此類推。type當(dāng)然應(yīng)賦為“service”。至于version,根據(jù)情況設(shè)定,缺省取1。
3. 根據(jù)需要SMF服務(wù)可以為同一個(gè)服務(wù)啟動(dòng)多個(gè)實(shí)例(instance)。比如,在系統(tǒng)中同一種數(shù)據(jù)庫(kù)平臺(tái)可以啟動(dòng)多個(gè)服務(wù)實(shí)例,分別服務(wù)于不同的應(yīng)用;或者同一種WEB服務(wù)平臺(tái)啟動(dòng)多個(gè)服務(wù)實(shí)例,在不同的端口提供不同WEB應(yīng)用服務(wù)等。在SMF框架中只需定義一個(gè)SMF service及屬性,在同一個(gè)service下定義不同的instance和特定屬性即可。service下已定義的屬性適用于所有instance,但任何一個(gè)instance也可以根據(jù)需要特定某個(gè)或某幾個(gè)屬性。比如增加屬性或覆蓋service同名屬性定義。由于本例非常簡(jiǎn)單,只需一個(gè)服務(wù)一個(gè)實(shí)例就行了,所以采用標(biāo)簽<single_instance/>,所有屬性全部采用service中的屬性即可。
4. 由于希望myapp服務(wù)在系統(tǒng)boot時(shí)自動(dòng)啟動(dòng),所以將<create_default_instance>標(biāo)簽中enable置為“true”。
5. <dependency>是manifest文件中最難定義的部分,它定義了此服務(wù)所依賴的其他資源,包括服務(wù)、文件系統(tǒng)等。一個(gè)SMF服務(wù)根據(jù)需要可以定義多個(gè)<dependency>,每個(gè)<dependency>具有自己的標(biāo)識(shí)名name、grouping、 restart_on、type,以及所依賴的各資源的service_fmri。其中name只是個(gè)標(biāo)識(shí),不相重有意義即可。grouping取值定義了所列其他服務(wù)與本資源的依存關(guān)系,取值“require_all”是指當(dāng)所列其他資源全部啟動(dòng)和可用后才能滿足本服務(wù)啟動(dòng)的要求。restart_on 規(guī)定了當(dāng)所依賴的其他資源發(fā)生何種情況時(shí)需要重啟本服務(wù),取值“none”是指只要本服務(wù)處于運(yùn)行狀態(tài)就行了,不必考慮所依賴的其他資源的狀態(tài)是否改變。 type指向依賴資源的類型,比如“service”指服務(wù),“path”指文件系統(tǒng)等。service_fmri指其他服務(wù)的FMRI(Fault Management Resource Idengifier)。本例僅需在/tmp目錄下生成日志文件,而“milestone/multi-user”所指的運(yùn)行狀態(tài)完全可以滿足要求,所以 service_fmri設(shè)為“svc:/milestone/multi-user”。
有關(guān)milestone的概念請(qǐng)參看Solaris Service Management Facility - Quickstart Guide。
6. manifest文件必須定義啟動(dòng)和停止服務(wù)的方法,<exec_method>標(biāo)簽即用于此目的。原先利用/etc/init.d/myapp.sh加合適的參數(shù)即可啟動(dòng)和停止myapp,現(xiàn)仍可利用。不過myapp.sh需作小小改動(dòng),改動(dòng)方法請(qǐng)參看下節(jié)啟停方法客戶化。對(duì)于啟動(dòng)和停止,分別需要定義兩個(gè)method,它們的type當(dāng)然都為“method”,其中一個(gè)name設(shè)為為“start”,exec表示執(zhí)行什么動(dòng)作以完成這個(gè)start方法,其動(dòng)作設(shè)為“/export/home/smfdemo/myapp.sh start”。另一個(gè)name設(shè)為“stop”,由于原腳本是使用pkill命令殺掉myapp進(jìn)程,所以這里可以直接將“:kill”賦給exec,表示SMF可直接殺掉myapp服務(wù)相關(guān)的所有進(jìn)程。timeout_seconds定義了完成啟動(dòng)和停止服務(wù)操作所需的最長(zhǎng)時(shí)間,如果在這個(gè)時(shí)間內(nèi)未能完成相應(yīng)操作,SMF會(huì)認(rèn)為服務(wù)存在問題,因?yàn)闀?huì)將服務(wù)置為maintenance狀態(tài),由人工進(jìn)行排錯(cuò)。本例中,timeout_seconds設(shè)為60秒足矣。
事實(shí)上還有許多標(biāo)簽項(xiàng)目可以設(shè)定,但對(duì)于本例不是必要的,所以可省略不設(shè)。有關(guān)manifest文件編寫更詳細(xì)信息,請(qǐng)參看Solaris Service Management Facility - Service Developer Introduction。
啟停方法客戶化SMF框架中svcs(1)命令非常有用,它不但可以列出系統(tǒng)中所有的服務(wù)資源及狀態(tài),還可以提供那些未正常啟動(dòng)的服務(wù)的出錯(cuò)原因、影響范圍和可能的恢復(fù)方法等。比如,它可以報(bào)告說某個(gè)服務(wù)因?yàn)榕渲貌徽_而未正常啟動(dòng),或者某服務(wù)遇到致使錯(cuò)誤請(qǐng)參SMF的某個(gè)日志文件等。SMF之所以能夠提供這些信息是由于啟動(dòng)和停止方法提供了相關(guān)的信息。SMF要求所有啟動(dòng)和停止方法必須返回一組特定的值,具體值可以參看Solaris 10操作系統(tǒng)/lib/svc/share/smf_include.sh文件尾部。
本例中,表2所示的/etc/init.d/myapp.sh可能返回3種值,$CONFIG_ERROR、$FATAL_ERROR和$RUN_OK。現(xiàn)目標(biāo)是要替換原返回值為相應(yīng)的SMF返回值,如果沒有相應(yīng)的SMF返回值,則替換為最合適的SMF返回值,使服務(wù)非正常退出時(shí),SMF能夠報(bào)告可令人接受的錯(cuò)誤原因。本例修改方法如下:
1. 首先,將表2所示的/etc/init.d/myapp.sh文件拷貝到開發(fā)目錄下,比如/export/home/smfdemo目錄下。后面步驟中所有修改都改在拷貝內(nèi)。2. 通過增加“. /lib/svc/share/smf_include.sh”到myapp.sh頭部,將SMF所需的各返回變量和過程包含到myapp.sh腳本。
3. 替換“exit $CONFIG_ERROR”為“exit $SMF_EXIT_ERR_CONFIG”,因?yàn)?SMF_EXIT_ERR_CONFIG與原退出碼$CONFIG_ERROR退出原因最相近。
4. 替換“exit $FATAL_ERROR”為“exit $SMF_EXIT_ERR_FATAL”,因?yàn)?SMF_EXIT_ERR_FATAL與原退出碼$FATAL_ERROR退出原因最相近。
5. 替換“exit $RUN_OK”為“exit $SMF_EXIT_OK”,因?yàn)?SMF_EXIT_OK與原退出碼$RUN_OK退出原因最相近。6. 刪除stop case及其操作。因?yàn)閟top方法已在myapp.xml中另行處理,不再需要myapp.sh了。
7. 修改default case中的echo,以反映正確的usage。
修改后的myapp.sh如表4所示。至此,所有前期準(zhǔn)備工作都已完成,下面就可以進(jìn)行部署了。
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 |
#!/sbin/sh ############################################################################### # /export/home/smfdemo/myapp.sh # ############################################################################### . /lib/svc/share/smf_include.sh RUN_OK=0 CONFIG_ERROR=1 FATAL_ERROR=2 case "$1" in 'start') /export/home/hunter/smf/myapp_smf if [ $? -eq $CONFIG_ERROR ]; then exit $SMF_EXIT_ERR_CONFIG fi if [ $? -eq $FATAL_ERROR ]; then exit $SMF_EXIT_ERR_FATAL fi ;; *) echo "Usage: $0 start" ;; esac exit $SMF_EXIT_OK |
部署我的應(yīng)用為SMF服務(wù)
管理和修改SMF服務(wù)分別需要solaris.smf.manage和solaris.smf.modify權(quán)限,具體請(qǐng)參看 smf_security(5)。缺省只有root有此權(quán)限,可使用root部署SMF服務(wù)。如果使用普通用戶賬號(hào),則需要root將solaris.smf.manage和 solaris.smf.modify權(quán)限賦予相關(guān)用戶。方法是在/etc/user_attr文件中加入授權(quán)記錄。比如為用戶hunter加入SMF管理和修改權(quán)限,則/etc/user_attr顯示如下,其中粗斜體部分為hunter所需的權(quán)限。
# cat /etc/user_attr |
假設(shè)本例中開發(fā)目錄和所有文件都位于/export/home/smfdemo目錄下,則將本例部署為SMF服務(wù)的步驟如下:
1. 使用svccfg(1M)命令檢查myapp.xml文件是否符合XML規(guī)范。如果沒問題則不會(huì)有任何輸出,否則根據(jù)出錯(cuò)提示修改myapp.xml。
# /usr/sbin/svccfg validate /export/home/smfdemo/myapp.xml |
2. 使用svcs(1)命令看是否已存在名為myapp的服務(wù)。如有則必須修改在myapp.xml中定義的服務(wù)名,否則繼續(xù)。
# /usr/bin/svcs application/myapp |
3. 使用svccfg(1M)命令加載myapp.xml所定義的服務(wù)并自動(dòng)啟動(dòng)服務(wù)。
# /usr/sbin/svccfg import /export/home/smfdemo/myapp.xml |
4. 使用svcs(1)命令查看myapp服務(wù)狀態(tài)。如狀態(tài)為online,則說明部署已成功且已運(yùn)行,否則參看出錯(cuò)原因以及SMF日志以確定問題所在,然后重復(fù)上文中相關(guān)的步驟后再試。
# /usr/bin/svcs -xv application/myapp |
至此,我的應(yīng)用myapp已經(jīng)成功部署為SMF。
其他操作
myapp成為SMF服務(wù)后可以使用以下命令進(jìn)行管理。
1. 要禁用myapp服務(wù),請(qǐng)使用/usr/sbin/svcadm disable application/myapp。
2. 要再次啟用myapp服務(wù),請(qǐng)使用/usr/sbin/svcadm enable application/myapp。
3. 要重啟myapp服務(wù),請(qǐng)使用/usr/sbin/svcadm restart application/myapp。
4. 當(dāng)myapp服務(wù)出現(xiàn)配置錯(cuò)誤或其他原因致使myapp的狀態(tài)為maintenance時(shí),在解決錯(cuò)誤原因后,可使用/usr/sbin/svcadm clear application/myapp清除maintenance狀態(tài)。
5. 當(dāng)需要對(duì)myapp進(jìn)行維護(hù)時(shí),可將其狀態(tài)改為maintenance狀態(tài),方法是/usr/sbin/svcadm mark application/myapp。
6. 可使用svccfg(1M)命令對(duì)myapp進(jìn)行配置管理。具體方法請(qǐng)參看svccfg(1M)使用說明。
總結(jié)
Solaris 10操作系統(tǒng)是Sun公司最新的下一代操作系統(tǒng),包含了600多項(xiàng)革新技術(shù),SMF技術(shù)就是其中之一。通過使用SMF技術(shù),系統(tǒng)中所有的服務(wù)可以在一個(gè)統(tǒng)一而強(qiáng)大的平臺(tái)中進(jìn)行配置和管理。同時(shí),它也是預(yù)測(cè)性自愈技術(shù)的組成部分,可以自我偵測(cè)各服務(wù)的運(yùn)行狀態(tài),盡可能地減少服務(wù)下線的機(jī)率。另外,利用SMF 技術(shù)系統(tǒng)管理員可以降低系統(tǒng)維護(hù)難度,減少人為出錯(cuò)機(jī)會(huì)。讓我們把自已的應(yīng)用盡早地部署到SMF框架中去吧。
參考資料
1. Predictive Self-Healing at BigAdmin System Administration Portal
2. SMF System Administration Guide
3. Solaris 10操作系統(tǒng)/usr/share/lib/xml/dtd/service_bundle.dtd文件
4. Solaris 10操作系統(tǒng)上,以下man頁(yè)面:
- netadm(1M)
- inetconv(1M)
- inetd(1M)
- kernel(1M)
- smf(5)
- smf_bootstrap(5)
- smf_method(5)
- svc.startd(1M)
- svcadm(1M)
- svccfg(1M)
- svcprop(1)
- svcs(1)