本文檔基于 CFEngine Core 3.9.1
Cfengine 是歷史最悠久的配置管理軟件. 雖然受到了來自眾多后起之秀(puppet, saltstack, Chef...)的挑戰(zhàn), 但是 CFE 還是成功的活了下來. 相對于其他配置管理軟件, CFE 最明顯的優(yōu)勢有:
-
依賴少, 部署方便.
當前版本只需要安裝一個包, 執(zhí)行一個命令即可完成部署.
-
運行開銷小, 效率高
CFE 使用 c/c++ 編寫, 運行效率毋庸置疑.
-
架構(gòu)簡單
最核心的組件只有 cf-agent 一個, 其他的組件其實都是可選的.
cf-serverd 通常只是一個提供策略文件下載的文件服務(wù)器. 策略文件的解析是在 agent 端進行的. 所以, 相對于其他配置管理軟件, CFE 的 server 端能夠管理更多的 agent 端.
cf-execd 也不過是個定時器.
所以, 如果你用 rsync 同步策略文件, 用 crond 定期執(zhí)行 cf-agent, 完全可以不啟動 cf-agent 和 cf-serverd.
DSL不依賴于特定編程語言.
-
安全
外部只能觸發(fā) cf-agent 執(zhí)行, 以及類的定義. 無法利用 cf-agent 執(zhí)行策略文件之外的命令.
Install
參考官方文檔
軟件安裝完成后, 需要執(zhí)行/var/cfengine/bin/cf-agent --bootstrap <IP address of policy server>
來進行初始化.
COMPONENT
cf-execd
周期性觸發(fā) cf-agent 執(zhí)行. 默認間隔 5 分鐘.
cf-runagent
cf-runagent 可以遠程批量觸發(fā) cf-agent 運行(通過 cf-serverd). 在這個過程中, 可以通過 --define-class, -D value
選項來定義 cf-agent 執(zhí)行時候的 class; 可以通過 --select-class, -s value
來篩選需要被觸發(fā)的 cf-agent. 實際使用的時候, 這些機制大概有兩方面的作用:
- 測試. 通過指定 class 可以在目標機器上應(yīng)用任意的策略.
- 可以遠程批量觸發(fā)命令執(zhí)行, 從而部分替代批量執(zhí)行工具, 例如: ansible. 但是通過這種方式觸發(fā)的命令需要在策略文件中預(yù)先定義, 而且無法傳遞參數(shù), 所以靈活性上稍有欠缺.
Modules
用法有兩種
-
Set variables and classes based on command output
- lines which begin with a ^ are protocol extensions
- ^context=xyz sets the module context to xyz instead of the default
- ^meta=a,b,c sets the class and variable tags for any following definitions to a, b, and c
- lines which begin with a + are treated as classes to be defined (like -D)
- lines which begin with a - are treated as classes to be undefined (like -N)
- lines which begin with = are scalar variables to be defined
- lines which begin with = and include [] are array variables to be defined
- lines which begin with @ are lists.
- lines which begin with % are data containers. The value needs to be valid JSON and will be decoded.
- lines which begin with a ^ are protocol extensions
Promis
Command
Files
edit_line
雖然 edit_line 可以很靈活的控制文件內(nèi)容, 尤其是以"行"為單位的/簡單的配置文件. 但是對于復(fù)雜的, 結(jié)構(gòu)化的配置文件就顯得力不從心.
另一個問題是, 基于 edit_line 來控制文件內(nèi)容, 很難控制配置文件的完整內(nèi)容, 配置難以收斂. 所以強烈不推薦使用 edit_line.
edit template
- template_method => "cfengine", native-CFEngine template format, default. v3.3.0
- template_method => "mustache". v3.6.0
edit_template 是多年來一直希望添加的功能. 但是即便是在不支持模板的 cfengine2 時代, 也不是實現(xiàn)不了, 只是稍微麻煩一點而已.
edit_xml
有了模板其實就不需要這個東西了, 和 edit_line 問題一樣, 無法控制配置的完整內(nèi)容, 配置難以收斂.
Q&A
如何確定 policy_server.
/var/cfengine/policy_server.dat 中保存著 policy_server 的地址.
default:cfe_autorun_inventory_cmdb.sys#policy_hub 192.168.80.136 source=agent
default:def.policy_servers {"192.168.80.136"} source=promise
default:def.sys#policy_hub 192.168.80.136 source=agent
default:sys.policy_hub 192.168.80.136 source=bootstrap
這些變量的值都是通過policy_server.dat的內(nèi)容決定的.
默認情況下policy file 是如何更新的?
controls/cf_serverd.cf
!windows::
# last single quote in cfruncommand is left open, so that
# arguments (like -K and --remote-bundles) are properly appended.
cfruncommand => "$(def.cf_runagent_shell) -c \'
$(sys.cf_agent) -I -D cfruncommand -f $(sys.update_policy_path) ;
$(sys.cf_agent) -I -D cfruncommand";
controls/cf_execd.cf
!windows::
exec_command => "$(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated -f \"$(sys.update_policy_path)\" ; $(sys.cf_agent) -Dfrom_cfexecd,cf_execd_initiated";
從 cf_serverd.cf 和 cf_execd.cf 中我們可以看到, 不管是通過 cf_serverd 還是 cf_execd 執(zhí)行 cf_agent 都會先執(zhí)行sys.update_policy_path中的策略來更新策略文件. 那么sys.update_policy_path是誰呢? 我們可以通過下面的命令查看.
$ cf-promises --show-vars |grep update.cf
default:sys.update_policy_path /var/cfengine/inputs/update.cf source=agent
/var/cfengine/inputs/update.cf 引入了大量更新相關(guān)的策略文件
body common control
{
bundlesequence => {
"update_def",
"u_cfengine_enterprise",
@(u_cfengine_enterprise.def),
"cfe_internal_dc_workflow",
"cfe_internal_bins",
"cfe_internal_update_policy",
"cfe_internal_update_bins",
"cfe_internal_update_processes",
};
version => "update.cf $(update_def.current_version)";
inputs => {
@(cfengine_update_controls.update_def_inputs),
"cfe_internal/update/update_bins.cf",
"cfe_internal/update/cfe_internal_dc_workflow.cf",
"cfe_internal/update/cfe_internal_local_git_remote.cf",
"cfe_internal/update/cfe_internal_update_from_repository.cf",
"cfe_internal/update/update_policy.cf",
"cfe_internal/update/update_processes.cf"
};
}
主要的更新邏輯包含在 cfe_internal/update/update_policy.cf 中.
!am_policy_hub:: # policy hub should not alter inputs/ uneccessary
"$(inputs_dir)/cf_promises_validated"
comment => "Check whether a validation stamp is available for a new policy update to reduce the distributed load",
handle => "cfe_internal_update_policy_check_valid_update",
copy_from => u_rcp("$(master_location)/cf_promises_validated", @(update_def.policy_servers)),
action => u_immediate,
classes => u_if_repaired("validated_updates_ready");
這一段通過檢查policy_servers 上的 cf_promises_validated 是否更新, 來確定policy_servers 上的策略文件是否有變化.
am_policy_hub|validated_updates_ready:: # policy hub should always put masterfiles in inputs in order to check new policy
"$(inputs_dir)"
comment => "Copy policy updates from master source on policy server if a new validation was acquired",
handle => "cfe_internal_update_policy_files_inputs_dir",
copy_from => u_rcp("$(master_location)", @(update_def.policy_servers)),
depth_search => u_recurse("inf"),
file_select => u_input_files,
action => u_immediate;
如果policy_server 上的策略文件有變化, 則更新本地的策略文件. 這里我們看到客戶端 和 policy_server 本身的更新邏輯是相同的.
"validated_updates_ready"
expression => "cfengine_internal_disable_cf_promises_validated",
comment => "If cf_promises_validated is disabled, then updates are
always considered validated.";
cfengine_internal_disable_cf_promises_validated 如果被設(shè)置, 那么無論服務(wù)器端的 cf_promises_validated 文件是否有變化, validated_updates_ready 這個類都會被設(shè)置, 也就是說: 會觸發(fā)客戶端進行更新.
"cfengine_internal_disable_cf_promises_validated"
expression => "!any",
comment => "When cf_promises_validated is disabled remote agents will
always scan all of masterfiles for changes. Disabling this
is not recomended as it will increase the load on the policy
server and increases the possibility for remote agents to
recieve broken policy.";
但是在 controls/update_def.cf 中我們可以看到 cfengine_internal_disable_cf_promises_validated 默認情況下是不被設(shè)置的, 也就是說默認情況下需要通過 cf_promises_validated 文件的狀態(tài)來判斷是否更新.
body copy_from u_rcp(from,server)
{
source => "$(from)";
compare => "digest";
trustkey => "false";
!am_policy_hub::
servers => { "$(server)" };
cfengine_internal_encrypt_transfers::
encrypt => "true";
cfengine_internal_purge_policies::
purge => "true";
cfengine_internal_preserve_permissions::
preserve => "true";
}
在 copy_from u_rcp 的定義中我們看到, 客戶端更新時 servers 屬性(policy_server) 被設(shè)置為傳入的參數(shù), 但是沒有指定 policy_server 更新時的 servers. 實際上, copy_from 的默認 servers 是 localhost, policy_server 更新的時候是從本機同步文件, 所以無需指定.