天天看點

[翻譯]用 Puppet 搭建易管理的伺服器基礎架構(4)

我通過伯樂線上翻譯了一個Puppet簡明教程,一共分為四部分,這是第四部分。

原文位址:http://blog.jobbole.com/89214/

本文由 伯樂線上 - Wing 翻譯,黃利民 校稿。未經許可,禁止轉載!

英文出處:Manuel Kiessling。歡迎加入翻譯組。

  • 《用 Puppet 搭建易管理的伺服器基礎架構(1)》
  • 《用 Puppet 搭建易管理的伺服器基礎架構(2)》
  • 《用 Puppet 搭建易管理的伺服器基礎架構(3)》

在 《用 Puppet 搭建易管理的伺服器基礎架構(3)》中,我們建立了第一個可重用的、真實的Puppet子產品:apache2。在第四部分以及之後的章節中,我們會停止使用示例,轉而開始建構一個具有完整特性基礎架構,包括使用者和權限管理、Nagios監控、檔案伺服器、負載均衡、web伺服器、資料庫、VPN以及開發系統等等。

通過Puppet管理使用者群組賬号

在我工作的公司,我會在使用Puppet來管理我以及和我一起在Linux機器上工作的同僚的Unix使用者賬号。由于隻有幾個人需要使用shell或者ssh的方式來通路系統,是以這種方式可以很好得工作。萬一你需要支援大量的使用者,每個使用者都在機器上有他/她自己的賬号,那麼一個類似LDAP的解決方案可能更加合适。

但如果你像我一樣,隻有少數的軟體開發人員和系統管理者需要通路系統,并且一個集中式的目錄服務會過于複雜,那麼通過Puppet來管理這些使用者賬号就非常簡單可行。

對于通過Puppet進行管理的每一台機器上的清單來說,使用者和使用者組管理所使用的清單是一個很典型的示例。這種方式下每個使用者都可以預期他/她會在每一台機器上都有自己可用的賬号(當然在每個系統裡面可能會有不同的權限)。你沒有必要在多個使用者之間分享root或者其他任何Unix賬号——sudo會去為每個賬号配置設定合适的權限。

使用集中式的配置管理會在這裡顯示它的威力:我們無論何時向基礎設施中添加一個新機器,每個使用者都可以預期他/她在新機器中的賬号已經建立,包括一些個人配置,諸如Bash設定等等。同樣,SSH密鑰的管理也會從一件麻煩事變得小菜一碟。

在你的基礎設施中,為使用者ID和使用者組ID定義一個簡單但嚴格的規則集,是很有意義的。

在我的示例中,規則集看起來就像這樣:

  • 内部系統管理者:UID/GID在1000到1999之間
  • 外部系統管理者:UID/GID在2000到2999之間
  • SFTP賬号:UID/GID在3000到3999之間

在我們的Ubuntu系統中,Ubuntu在安裝過程中會建立使用者ubuntu,它已經使用了UID/GID 1000,但如我們在後面所見,我們總可以通過Puppet來管理它。

如我之前所說,我假設你有一個基礎設施,隻有很少的使用者會需要Unix賬号來通路你的機器。在這種情況下,為每個使用者建立一個Puppet子產品是有意義的(和将所有使用者清單放到一個子產品相比)。通過這種方式,你可以建立簡單或者複雜的賬号子產品,這些子產品可以很好的共存,而不會最終變成一個混亂的子產品。并且,你還可以在必要的時候在節點上“混合”不同的賬号。例如Bob和Mary都需要通路你的web伺服器,但隻有Mary需要通路資料庫,那麼我們可以通過簡單的将節點映射到子產品的方式,來實作這一特性。如果你有一個包含10個使用者的使用者組,它需要通路所有的機器,我們不需要将每個賬号映射到每個節點上,而隻需要将這10個使用者子產品放到一個類中即可,我們會在後面對此進行詳細說明。

第一個使用者子產品

那麼,這在實際中看起來是什麼樣子呢?讓我們針對已經存在的ubuntu賬号,建立第一個使用者子產品。為了讓子產品容易被識别,我們應該在所有的使用者子產品的名字添加“user-”字首——是以,我們需要為子產品建立如下所示的目錄結構:

1

2

3

On the puppetserver VM

~

# sudo mkdir -p /etc/puppet/modules/user-ubuntu/manifests

使用者賬号和它所在的使用者組可以通過兩個專門的塊進行維護:user和group。我們可以像這樣使用它們:

/etc/puppet/modules/user-ubuntu/manifests/init.pp on puppetserver

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class user-ubuntu {

user { "ubuntu":

comment    => "ubuntu,,,",

home       => "/home/ubuntu",

shell      => "/bin/bash",

uid        => 1000,

gid        => 1000,

managehome => "true",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]

}

group { "ubuntu":

gid => 1000,

}

}

如你所見,這基本上反應了使用者的建立過程,就好像它已經在系統中存在一樣。請注意密碼哈希碼是放在單引号裡面,而不是像其它參數那樣放在雙引号裡面——這是因為對于Puppet來說,雙引号中的$符号有特殊的含義。

像以前一樣,為了應用這個新的清單(确切得說,是我們建立的新子產品),我們需要在節點中聲明這個新子產品:

/etc/puppet/manifests/site.pp on puppetserver

node "puppetclient" {

include user-ubuntu

include apache2

}

我們從輸出可以看出,這并沒有改變任何事情(好吧,在你看來,這可能不是真的——密碼可能已經生成了不同的哈希碼,而且它會更新——盡管如此,最終你還是會有一個名為ubuntu的使用者,它的密碼也是ubuntu)。

使用Puppet管理SSH密鑰

對于一個非常初級的使用者管理來說,user塊可以很好的工作,但我們肯定想要更多。Puppet可以用來管理SSH密鑰,進而真正消除大量的“管理痛點”。讓我在puppetserver系統上為root使用者建立一個新的私有/公有SSH密鑰對,然後将公有密鑰添加到puppetclient系統的ubuntu使用者的authorized_keys 檔案中。

為此,我們首先生成一個新的密鑰對,并将公有密鑰輸出到控制台上:

On the puppetserver VM

18

19

20

21

22

23

24

25

~# sudo ssh-keygen

Generating public/private rsa key pair.

Enter file in which to save the key (/root/.ssh/id_rsa):

Created directory '/root/.ssh'.

Enter passphrase (empty for no passphrase):

Enter same passphrase again:

Your identification has been saved in /root/.ssh/id_rsa.

Your public key has been saved in /root/.ssh/id_rsa.pub.

The key fingerprint is:

e8:5e:ae:f4:be:1c:a1:79:da:a4:94:50:20:dd:7e:b3 root@puppetserver

The key's randomart image is:

+--[ RSA 2048]----+

|  ....           |

|   ....          |

|     ..          |

|     ...o        |

|    . ..So       |

|     o +E.       |

|      B =        |

|     + @ .       |

|      =oB.       |

+-----------------+

~# sudo cat /root/.ssh/id_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT root@puppetserver

(當然,在生成你自己系統的密鑰時,最終你會得到一個不同的密鑰對)。

Puppet還有一些代碼用來管理使用者的已經授權的SSH密鑰,我們按照如下方式使用,來對新生成的密鑰進行授權:

26

class user-ubuntu {

user { "ubuntu":

comment    => "ubuntu,,,",

home       => "/home/ubuntu",

shell      => "/bin/bash",

uid        => 1000,

gid        => 1000,

managehome => "true",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]

}

group { "ubuntu":

gid => 1000,

}

ssh_authorized_key { "default-ssh-key-for-ubuntu":

user   => "ubuntu",

ensure => present,

type   => "ssh-rsa",

key    => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT",

name   => "root@puppetserver",

}

}

正如我們期望的那樣,這會在puppetclient系統上為ubuntu使用者生成如下的authorized_keys檔案:

/home/ubuntu/.ssh/authorized_keys on puppetclient

# HEADER: This file was autogenerated at Tue Apr 22 21:58:56 +0200 2014

# HEADER: by puppet.  While it can still be managed manually, it

# HEADER: is definitely not recommended.

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT root@puppetserver

使用宏來避免重複

現在,雖然所有的一切都非常好,但如果檢視最終的清單檔案,我們會發現大量的重複資訊:

class user-ubuntu {

user { "ubuntu":

comment    => "ubuntu,,,",

home       => "/home/ubuntu",

shell      => "/bin/bash",

uid        => 1000,

gid        => 1000,

managehome => "true",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"]

}

group { "ubuntu":

gid => 1000,

}

ssh_authorized_key { "default-ssh-key-for-ubuntu":

user   => "ubuntu",

ensure => present,

type   => "ssh-rsa",

key    => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT",

name   => "root@puppetserver",

}

}

在建立使用者時,真正需要的資訊包括使用者名、UID、密碼哈希碼、使用者組以及SSH密鑰。但是在我們的清單中,我們需要提供使用者名6次、UID 3次。現在假設一下你要通過這種方式管理20個使用者。幸運的是,我們有更好的解決方案。

Puppet允許我們定義自己的參數化宏(被稱為defines),它可以讓我們通過提供不同值的方式來多次重用清單檔案,而無需一遍又一遍的重複寫出整個清單檔案。

我們首先來建立一個新子產品來存儲我們的宏:

/etc/puppet/modules/macro-useradd/manifests/init.pp on puppetserver

27

28

define macro-useradd ( $name, $uid, $password, $groups, $sshkeytype, $sshkey ) {

$username = $title

user { "$username":

comment    => "$name",

home       => "/home/$username",

shell      => "/bin/bash",

uid        => $uid,

gid        => $uid,

managehome => "true",

password   => "$password",

groups     => $groups,

}

group { "$username":

gid => $uid,

}

ssh_authorized_key { "default-ssh-key-for-$username":

user   => "$username",

ensure => present,

type   => "$sshkeytype",

key    => "$sshkey",

name   => "$username",

}

}

上述清單代碼中,除了“$username=$title”這一行外,其他内容可能都是自解釋的。在Puppet中的每一個塊總會有一個标題——它是一個跟在大括号後面的一個字元串,并在結束後會有一個冒号:

file { "/this/is/a/title":

...

}

group { "and-so-is-this".

...

}

package { "yet-another-title":

...

}

我們用标題來唯一辨別一個塊語句的。對于一個給定的節點,你不可以有多個指定類型的塊帶有相同的标題——也就是說,你不可以為/foo/bar檔案聲明兩個file塊。并且,正如你在這些示例中所看到的的那樣,當塊被執行時,标題經常被用作執行時所需要的一個值——例如,在file塊中,标題被設定成塊應該處理的檔案的路徑。

我們在自己的宏中也進行同樣的處理:對于傳入define的标題,我們期望它是一個Unix使用者名,并且在我們的宏的塊語句中,也将其作為對應的使用者名進行使用。

請注意在我們的宏中,在處理參數時是如何使用引号的:對于數字類型的值,例如uid和gid,以及數組類型的值,例如groups,不需要引号,但對于字元串類型的值,例如comment和key,是需要引号的。我們在上面提過密碼哈希碼由于包含了$字元,是以需要将其放在單引号中,來防止對參數開頭的$進行解析。我們稍後會看到,當調用宏的時候,情況依然如此。在宏中,我們需要再次使用雙引号——這會導緻$password參數會被解析,但參數中的内容(包含$字元的密碼哈希碼)就不會再被解析了。

既然我們已經定義了宏,接下來我們需要重寫user-ubuntu清單,并在清單内使用宏:

class user-ubuntu {

macro-useradd { "ubuntu":

name       => "ubuntu",

uid        => "1000",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => ["adm", "cdrom", "sudo", "dip", "plugdev", "lpadmin", "sambashare"],

sshkeytype => "ssh-rsa",

sshkey     => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"

}

}

我們需要再次運作sudo puppet agent –verbose –no-daemonize –onetime,但在目标機器上不會有任何變化,這是因為我們僅僅重構了清單,并沒有改變它的邏輯。

增加更多使用者

我們的重構生成了一個非常簡潔的user-ubuntu清單,如果使用宏,那麼添加大量使用者就會變得非常直接。在下面的示例中,我們會添加兩個使用者:Mary和Bob。在這裡我會直接重用ubuntu使用者的密碼哈希碼和SSH密鑰:

/etc/puppet/modules/user-mary/manifests/init.pp on puppetserver

class user-mary {

macro-useradd { "mary":

name       => "Mary",

uid        => "1001",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => ["sudo"],

sshkeytype => "ssh-rsa",

sshkey     => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"

}

}

/etc/puppet/modules/user-bob/manifests/init.pp on puppetserver

class user-bob {

macro-useradd { "bob":

name       => "Bob",

uid        => "1002",

password   => '$6$WzFG7Ga3$.BbRW/DFGkx5EIakXIt1udCGxVDPs2uFZg.o8EFzH8BX7cutimTCfTUWDdyHoFjDVTFnBkUWVPGntQTRSo1zp0',

groups     => [],

sshkeytype => "ssh-rsa",

sshkey     => "AAAAB3NzaC1yc2EAAAADAQABAAABAQCvphOrxjMvgtBVTjMzPolL4JGarEigbPuH3cE3iNIcSBPgHyBjDwtin6ls6aMzm0ZbHMdinj1qxSbolkTQ1danZpOAe0G9NB9/ZnYCNd/kUeMAX91B0Pitx6NKoaz0x7H7V1Javd11RN3ylBw6dtOh35Lqmjx22RXNK8sMpLW8tKYOQuY01F5Eiv08U/AKO83w2ZNxYbNuuhHWeN7wHTb176uhuhGGnob0ArvaxCJgJ96bvDYLSph6V067q0chTuutLGSDA4AbC1Bb/d3wcAIqEM1s6VMT8oU0rUHkPH/1AqaKhWDrEcbSp94gAqTMWQxVz+XWBvu1Dc+CsujsqigT"

}

}

管理使用者權限

Puppet不僅能處理使用者賬戶,而且還能處理賬号的超級使用者權限,讓我們使用上面建立的兩個賬号來示範這一點。目标是能夠集中定義哪個使用者在哪台機器上有哪些權限。

正如你在使用者清單中所見,Mary是sudo使用者組中的成員,但Bob并不是。這已經表明Mary有能力以超級使用者的權限在機器上執行任何一個指令。然而,我們并不像給Bob超級使用者權限——我們隻是想将他的超級使用者權限限制在puppetclient虛拟機系統上,在其它機器上,隻需要為其設定普通使用者權限。單獨的組關系并不能解決這個問題——為此,我們需要一個子產品來處理所有機器上的sudoers檔案。

首先,我們建立這個子產品所需的目錄結構:

~# sudo mkdir -p /etc/puppet/modules/sudo/manifests

~# sudo mkdir -p /etc/puppet/modules/sudo/files/etc

然後,我們為子產品建立清單檔案:

/etc/puppet/modules/sudo/manifests/init.pp on puppetserver

class sudo {

package { "sudo":

ensure => present,

}

file { "/etc/sudoers":

owner   => "root",

group   => "root",

mode    => 0440,

source  => "puppet://$puppetserver/modules/sudo/etc/sudoers",

require => Package["sudo"],

}

}

我們需要将清單所管理的檔案/etc/sudoers添加到子產品的file檔案夾下:

/etc/puppet/modules/sudo/files/etc/sudoers on puppetserver

Defaults        env_reset

Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification

root ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges

%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command

%sudo ALL=(ALL:ALL) ALL

# Bob has superuser privileges on puppetclient only

bob puppetclient=(ALL:ALL) ALL

在這裡,我們的想法是在我們的基礎設施的所有機器上隻保留一份sudoers檔案。這樣我們可以通過集中的方式進行權限管理,即使每台機器都使用它本地的/etc/sudoers副本來檢查使用者權限。

在我們部署這些改動之前,我們需要将新的清單檔案映射到節點上。我們會把所有的user子產品和sudo子產品放到一個新的類中,這樣一旦我們向Puppet基礎設施中添加更多的節點時,可以避免一些重複工作:

class users {

include user-ubuntu

include user-mary

include user-bob

include sudo

}

node "puppetclient" {

include users

include apache2

}

現在,我們終于可以應用這些改變:

On the puppetclient VM

~# sudo puppet agent --verbose --no-daemonize --onetime

info: Caching catalog for puppetclient

info: Applying configuration version '1399957763'

info: FileBucket adding {md5}1b00ee0a97a1bcf9961e476140e2c5c1

info: /Stage[main]/Sudo/File[/etc/sudoers]: Filebucketed /etc/sudoers to puppet with sum 1b00ee0a97a1bcf9961e476140e2c5c1

notice: /Stage[main]/Sudo/File[/etc/sudoers]/content: content changed '{md5}1b00ee0a97a1bcf9961e476140e2c5c1' to '{md5}c5de61ca64ad4ef2e85e37d728d1be9f'

notice: /Stage[main]/User-mary/Macro-useradd[mary]/Group[mary]/ensure: created

notice: /Stage[main]/User-mary/Macro-useradd[mary]/User[mary]/ensure: created

notice: /Stage[main]/User-mary/Macro-useradd[mary]/Ssh_authorized_key[default-ssh-key-for-mary]/ensure: created

notice: /Stage[main]/User-bob/Macro-useradd[bob]/Group[bob]/ensure: created

notice: /Stage[main]/User-bob/Macro-useradd[bob]/User[bob]/ensure: created

notice: /Stage[main]/User-bob/Macro-useradd[bob]/Ssh_authorized_key[default-ssh-key-for-bob]/ensure: created

notice: Finished catalog run in 0.47 seconds

總結

在這一部分中,我們示範了如何在不使用集中式目錄服務的前提下,通過Puppet來部署一個集中式的使用者和權限管了解決方案。我們還學習了如果使用宏來簡化那些複雜的、需要被使用多次的Puppet子產品。

在接下來的第五部分中,我們會去檢視Puppet如何內建Nagios,并示範一個通過Puppet建構的可管理的伺服器基礎設施如何在不增加額外管理負擔的情況下,對伺服器進行監控。

(伯樂線上注:本系列的第四篇,原作者完成于2014年3月,雖然說了會有第五部分,不過目前還沒有在他部落格看到有。)

    作者:李潘

    出處:http://wing011203.cnblogs.com/

    本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀