SMF功能概覽
Solaris 10中的SMF提供了強大的服務管理功能。以下是一部分重要的功能:
1. SMF向系統管理員提供統一的服務管理平臺,利用svcs(1)命令就可以查看SMF所轄的各種服務,利用svccfg(1M)和svcadm(1M)命令可以配置各種服務和管理各種服務。對于傳統UNIX,系統管理員必須記住各種服務不同的啟動/停止方法、配置修改方法、服務狀態及日志查詢方法而言, SMF統一管理平臺極大地降低了系統管理的難度,也降低了系統管理出錯的機率。
2. SMF提供各服務間的依賴關系設定,可以自動按依賴關系順序啟動各服務。這對于傳統UNIX以rc腳本文件名排列先后決定啟動/停止順序而言,SMF提供了無可比擬的完善的管理能力。
3. 并行啟動不相互依賴的服務,從而使系統啟動更快。由于各服務的依賴關系在SMF中有明確的定義,所以不相干的服務完全可以并行啟動而不必擔心沖突。
4. 自動偵測所轄服務的運行狀態,在必要時可以重啟服務或停止服務。作為預測性自愈技術(Predictive Self-Healing)的組成部分,SMF可以對所轄服務進行狀態監控。根據服務的需要,SMF可以在服務進程不存在時,自動重啟服務,或者在服務所依賴關系發生問題時,重啟服務。也可以在服務連續發生問題時,將服務置為維護(maintenance)狀態。
當然,SMF的管理機制并不排斥傳統rc腳本運行服務的機制,以最大程度兼容傳統方式的運作。有關SMF更多的介紹,請參看Solaris Service Management Facility - Quickstart Guide。
一個簡單的服務程序
<表1是一個簡單的程序myapp.c,它運行后將成為后臺守護進程存在于系統中,并每間隔5秒鐘向日志文件/tmp/myapp.log插入一行記錄以報告自己的存在。雖然它實際上不向外提供任何服務,但該程序模擬了一般服務程序的設計結構和運行模式。即,程序運行后以守護進程形式存在于系統,程序頭部有模擬服務配置read_config()和初始化app_init()邏輯,中部使用sleep(5)模擬等待服務請求邏輯,通過向日志插入記錄模擬服務請求處理邏輯,然后返回至循環開始處sleep(5)繼續等待下一個服務請求等。只要在此結構上修改和擴充相應的邏輯就可以將此程序修改為一個真正的服務程序。本文要點是說明如何部署應用為SMF服務,所以僅采用此程序作為例子。
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開發及編譯環境Sun Studio 11,使用以下命令將myapp.c編譯成可執行程序myapp。
$ /opt/SUNWspro/bin/cc -o ./myapp ./myapp.c |
或者直接使用Solaris 10自帶的gcc編譯器將myapp.c編譯成可執行程序myapp。
$ /usr/sfw/bin/gcc -o ./myapp ./myapp.c |
編譯成功后在當前目錄下會生成myapp可執行程序。本例假設當前目錄為/export/home/smfdemo。直接在命令行輸入. /myapp就可以啟動myapp為后臺守護進程,同時可以在/tmp目錄下找到myapp.log文件。使用“/usr/bin/tail -f /tmp/myapp.log”命令可以看到進程myapp不斷在日志文件中報告自己的存在。為了使這個進程在服務器啟動時自動運行,傳統做法一般需要寫三個shell腳本。其中一個shell腳本置于/etc/init.d目錄下用以實際啟動和停止myapp服務(例如<表2所示的/etc/init.d/myapp.sh)。另外兩個shell腳本的名字分別以S和K開頭(例如,S79myapp和K79myapp),置于合適的/etc/rc?.d目錄下(例如,/etc/rc2.d目錄下),分別以不同參數(start和stop)調用 /etc/init.d/myapp.sh。操作系統在boot時會自動運行S開頭的腳本以啟動myapp服務,而在shutdown時自動運行K開頭的腳本以關閉myapp服務。
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可部署的服務
本節講述如何將上述例子改為SMF可部署的服務。根據SMF的要求,開發一個SMF可部署的服務需要至少以下幾個步驟。
創建manifest文件
SMF manifest文件是一個XML文件,它用以定義SMF服務各屬性。比如,定義服務名稱、服務依賴關系、服務啟動方法、服務停止方法、服務所需參數等。創建manifest文件最簡單的方法是從/var/svc/manifest目錄下挑選已存在的相同類型的服務XML文件,將它拷貝到開發目錄,比如/export/home/smfdemo目錄下,以拷貝件為基礎修改而成。本文是個簡單的服務,所以參考了 /var/svc/manifest/system/utmp.xml文件(因為它也很簡單),在其基礎上修改成表3所示的/export/home/smfdemo/myapp.xml。
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規范,其意理解如下:
1. <service_bundle>標簽用以告知SMF如何處理myapp.xml文件。本例中myapp.xml是一個manifest文件用以定義SMF服務,所以type賦為“manifest”。同時需要給service_bundle一個名字,一般命名規范是以服務所在安裝包名為前綴,所以本例將name賦為“mypackage:myapp”。其實name只要不與系統中已有的相重就可以了,當然對于企業級應用服務應該有一個合適的名字。
2. <service>標簽主要定義SMF服務的名稱,由于myapp只是一個簡單應用,所以name賦為“application/myapp”。如果myapp是網絡服務,則根據命名規范名字應以“network/”開頭加myapp,即 “network/myapp”,請參考/var/svc/manifest/下的目錄結構以此類推。type當然應賦為“service”。至于version,根據情況設定,缺省取1。
3. 根據需要SMF服務可以為同一個服務啟動多個實例(instance)。比如,在系統中同一種數據庫平臺可以啟動多個服務實例,分別服務于不同的應用;或者同一種WEB服務平臺啟動多個服務實例,在不同的端口提供不同WEB應用服務等。在SMF框架中只需定義一個SMF service及屬性,在同一個service下定義不同的instance和特定屬性即可。service下已定義的屬性適用于所有instance,但任何一個instance也可以根據需要特定某個或某幾個屬性。比如增加屬性或覆蓋service同名屬性定義。由于本例非常簡單,只需一個服務一個實例就行了,所以采用標簽<single_instance/>,所有屬性全部采用service中的屬性即可。
4. 由于希望myapp服務在系統boot時自動啟動,所以將<create_default_instance>標簽中enable置為“true”。
5. <dependency>是manifest文件中最難定義的部分,它定義了此服務所依賴的其他資源,包括服務、文件系統等。一個SMF服務根據需要可以定義多個<dependency>,每個<dependency>具有自己的標識名name、grouping、 restart_on、type,以及所依賴的各資源的service_fmri。其中name只是個標識,不相重有意義即可。grouping取值定義了所列其他服務與本資源的依存關系,取值“require_all”是指當所列其他資源全部啟動和可用后才能滿足本服務啟動的要求。restart_on 規定了當所依賴的其他資源發生何種情況時需要重啟本服務,取值“none”是指只要本服務處于運行狀態就行了,不必考慮所依賴的其他資源的狀態是否改變。 type指向依賴資源的類型,比如“service”指服務,“path”指文件系統等。service_fmri指其他服務的FMRI(Fault Management Resource Idengifier)。本例僅需在/tmp目錄下生成日志文件,而“milestone/multi-user”所指的運行狀態完全可以滿足要求,所以 service_fmri設為“svc:/milestone/multi-user”。
有關milestone的概念請參看Solaris Service Management Facility - Quickstart Guide。
6. manifest文件必須定義啟動和停止服務的方法,<exec_method>標簽即用于此目的。原先利用/etc/init.d/myapp.sh加合適的參數即可啟動和停止myapp,現仍可利用。不過myapp.sh需作小小改動,改動方法請參看下節啟停方法客戶化。對于啟動和停止,分別需要定義兩個method,它們的type當然都為“method”,其中一個name設為為“start”,exec表示執行什么動作以完成這個start方法,其動作設為“/export/home/smfdemo/myapp.sh start”。另一個name設為“stop”,由于原腳本是使用pkill命令殺掉myapp進程,所以這里可以直接將“:kill”賦給exec,表示SMF可直接殺掉myapp服務相關的所有進程。timeout_seconds定義了完成啟動和停止服務操作所需的最長時間,如果在這個時間內未能完成相應操作,SMF會認為服務存在問題,因為會將服務置為maintenance狀態,由人工進行排錯。本例中,timeout_seconds設為60秒足矣。
事實上還有許多標簽項目可以設定,但對于本例不是必要的,所以可省略不設。有關manifest文件編寫更詳細信息,請參看Solaris Service Management Facility - Service Developer Introduction。
啟停方法客戶化SMF框架中svcs(1)命令非常有用,它不但可以列出系統中所有的服務資源及狀態,還可以提供那些未正常啟動的服務的出錯原因、影響范圍和可能的恢復方法等。比如,它可以報告說某個服務因為配置不正確而未正常啟動,或者某服務遇到致使錯誤請參SMF的某個日志文件等。SMF之所以能夠提供這些信息是由于啟動和停止方法提供了相關的信息。SMF要求所有啟動和停止方法必須返回一組特定的值,具體值可以參看Solaris 10操作系統/lib/svc/share/smf_include.sh文件尾部。
本例中,表2所示的/etc/init.d/myapp.sh可能返回3種值,$CONFIG_ERROR、$FATAL_ERROR和$RUN_OK,F目標是要替換原返回值為相應的SMF返回值,如果沒有相應的SMF返回值,則替換為最合適的SMF返回值,使服務非正常退出時,SMF能夠報告可令人接受的錯誤原因。本例修改方法如下:
1. 首先,將表2所示的/etc/init.d/myapp.sh文件拷貝到開發目錄下,比如/export/home/smfdemo目錄下。后面步驟中所有修改都改在拷貝內。2. 通過增加“. /lib/svc/share/smf_include.sh”到myapp.sh頭部,將SMF所需的各返回變量和過程包含到myapp.sh腳本。
3. 替換“exit $CONFIG_ERROR”為“exit $SMF_EXIT_ERR_CONFIG”,因為$SMF_EXIT_ERR_CONFIG與原退出碼$CONFIG_ERROR退出原因最相近。
4. 替換“exit $FATAL_ERROR”為“exit $SMF_EXIT_ERR_FATAL”,因為$SMF_EXIT_ERR_FATAL與原退出碼$FATAL_ERROR退出原因最相近。
5. 替換“exit $RUN_OK”為“exit $SMF_EXIT_OK”,因為$SMF_EXIT_OK與原退出碼$RUN_OK退出原因最相近。6. 刪除stop case及其操作。因為stop方法已在myapp.xml中另行處理,不再需要myapp.sh了。
7. 修改default case中的echo,以反映正確的usage。
修改后的myapp.sh如表4所示。至此,所有前期準備工作都已完成,下面就可以進行部署了。
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 |
部署我的應用為SMF服務
管理和修改SMF服務分別需要solaris.smf.manage和solaris.smf.modify權限,具體請參看 smf_security(5)。缺省只有root有此權限,可使用root部署SMF服務。如果使用普通用戶賬號,則需要root將solaris.smf.manage和 solaris.smf.modify權限賦予相關用戶。方法是在/etc/user_attr文件中加入授權記錄。比如為用戶hunter加入SMF管理和修改權限,則/etc/user_attr顯示如下,其中粗斜體部分為hunter所需的權限。
# cat /etc/user_attr |
假設本例中開發目錄和所有文件都位于/export/home/smfdemo目錄下,則將本例部署為SMF服務的步驟如下:
1. 使用svccfg(1M)命令檢查myapp.xml文件是否符合XML規范。如果沒問題則不會有任何輸出,否則根據出錯提示修改myapp.xml。
# /usr/sbin/svccfg validate /export/home/smfdemo/myapp.xml |
2. 使用svcs(1)命令看是否已存在名為myapp的服務。如有則必須修改在myapp.xml中定義的服務名,否則繼續。
# /usr/bin/svcs application/myapp |
3. 使用svccfg(1M)命令加載myapp.xml所定義的服務并自動啟動服務。
# /usr/sbin/svccfg import /export/home/smfdemo/myapp.xml |
4. 使用svcs(1)命令查看myapp服務狀態。如狀態為online,則說明部署已成功且已運行,否則參看出錯原因以及SMF日志以確定問題所在,然后重復上文中相關的步驟后再試。
# /usr/bin/svcs -xv application/myapp |
至此,我的應用myapp已經成功部署為SMF。
其他操作
myapp成為SMF服務后可以使用以下命令進行管理。
1. 要禁用myapp服務,請使用/usr/sbin/svcadm disable application/myapp。
2. 要再次啟用myapp服務,請使用/usr/sbin/svcadm enable application/myapp。
3. 要重啟myapp服務,請使用/usr/sbin/svcadm restart application/myapp。
4. 當myapp服務出現配置錯誤或其他原因致使myapp的狀態為maintenance時,在解決錯誤原因后,可使用/usr/sbin/svcadm clear application/myapp清除maintenance狀態。
5. 當需要對myapp進行維護時,可將其狀態改為maintenance狀態,方法是/usr/sbin/svcadm mark application/myapp。
6. 可使用svccfg(1M)命令對myapp進行配置管理。具體方法請參看svccfg(1M)使用說明。
總結
Solaris 10操作系統是Sun公司最新的下一代操作系統,包含了600多項革新技術,SMF技術就是其中之一。通過使用SMF技術,系統中所有的服務可以在一個統一而強大的平臺中進行配置和管理。同時,它也是預測性自愈技術的組成部分,可以自我偵測各服務的運行狀態,盡可能地減少服務下線的機率。另外,利用SMF 技術系統管理員可以降低系統維護難度,減少人為出錯機會。讓我們把自已的應用盡早地部署到SMF框架中去吧。
參考資料
1. Predictive Self-Healing at BigAdmin System Administration Portal
2. SMF System Administration Guide
3. Solaris 10操作系統/usr/share/lib/xml/dtd/service_bundle.dtd文件
4. Solaris 10操作系統上,以下man頁面:
- 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)