Ansible 學習筆記

weixin_33866037發表於2018-07-27

一、安裝

Ansible 是預設來通過 ssh 協議來管理機器的。安裝好 ansible 之後不需要啟動或者允許一個後臺程式,只要安裝好(哪怕是在我們的筆記本上,只要安裝了 python 2.6 或2.7),就可以通過這臺機器管理一組遠端的機器。在被管理的遠端機器上,也不需要安裝執行任何軟體(安裝了 python 2.5 以上),因此升級 ansible 版本很容易。

mac 上使用 pip 來安裝:

sudo pip install ansible

二、Ansible 如何通過 ssh 與遠端伺服器連線

Ansible 1.3 及之後的版本預設會在本地的 OpenSSH 可用時會嘗試與其進行遠端通訊。會棄用 ControlPersist(新的效能特性),Kerberos,和在~/.ssh/config 中配置選項如 Jump Host setup。在Ansible 1.2 及之前的版本,預設將會使用 paramiko. 本地OpenSSH必須通過-c ssh 或者 在配置檔案中設定。

對於遠端裝置,Ansible會預設假定你使用 SSH Key(推薦)但是密碼也一樣可以.通過在需要的地方新增 –ask-pass選項 來啟用密碼驗證.如果使用了sudo 特性,當sudo需要密碼時,也同樣適當的提供了–ask-sudo-pass選項.

注意:任何管理系統受益於被管理的機器在主控機附近執行.如果在雲中執行,可以考慮在使用雲中的一臺機器來執行Ansible.

三、使用

1. 基本用法

編輯(或建立) /etc/ansible/hosts 並在其中加入一個或多個遠端系統。public SSH key必須在這些系統的authorized_keys中,或者 -i 指定 inventory 檔案

   $ ansible -i /etc/ansible/host all [options]

其中 -i 用來指定 inventory 檔案,預設就是使用 /etc/ansible/hosts,其中 all 是針對 hosts 定義的所有主機執行,這裡也可以指定 hosts 中定義的組名或模式。

[options]

-m:指定模組(預設是command模組)

-a:指定模組的引數

-u:指定執行遠端主機的使用者(預設是root)ansible.cfg中可配置

-k:指定遠端主機的密碼

-s:以sudo方式執行

-U:sudo到那個使用者(預設是root)

-f:指定多少個程式併發處理(預設是5)

--private-key=/path:指定私鑰路徑

-T:ssh連線超時時間(預設(10s)

-t:日誌輸出到該目錄

-v:顯示詳細資訊

2. Inventory

Inventory 檔案用來定義你要管理的主機,其預設位置在 /etc/ansible/hosts,如果不儲存在預設位置,也可通過 -i 選項指定。被管理的機器可以通過其 IP 或域名指定。每個中括號裡代表一個分組,其下的機器列表都歸屬於這個分組,直到出現下一個中括號為止。通常我們按組來執行任務,同一組受控伺服器應用相同的配置。一臺伺服器也可以歸屬到多個組,以完成多個功能角色的配置。低耦合、模組化,非常靈活!如下定義一個分組 [gocd-agent] 和 [elk] 分組,並給定一組主機。

[gocd-agent]
vm-gocd-agent-batman ansible_ssh_host=shell-gocd-agent-1
vm-gocd-agent-flashman ansible_ssh_host=shell-gocd-agent-2
vm-gocd-agent-spiderman ansible_ssh_host=shell-gocd-agent-3

[elk]
vm-elk ansible_ssh_host=shell-elk

使用 ansible 來測試被管理的節點的網路連通情況, -u 是使用者,-k 是使用密碼登入

ansible -i /etc/ansible/hosts test -u root -m ping -k

或者

$ ansible test -u root -m ping -k

或者使用--private-key 測試連通 inventory

ansible -m ping -i inventory_infra.ini elk --private-key ./keys/decoded_keys/inf_key -u dcsp

輸出的正確的資訊如下:


13236273-ab1c40e2a816102e.png
image.png

注意:全域性使用者的設定見配置檔案 /etc/ansible/ansible.cfg,修改[defaults]段落裡的 remote_user 的值即可。修改 remote_port 可以來定義 ansible 使用非預設的22/SSH 埠來連線和管理。如果沒有給被管理節點或者被管理組進行特定設定時,ansible將預設使用全域性設定。

1)格式:

# 指定IP地址並且支援通配;
[web01] ###[]中是組名,用於對系統進行分類,便於對不同系統進行個別管理。一個系統可以屬於不同的組,例如一臺伺服器可以同時屬於 webserver 和 dbserver組.這時屬於兩個組的變數都可以為這臺主機所用
192.168.10.1
192.168.10.[1-9]
 
# 指定IP加埠;
[web02]
192.168.20.1:5252
 
# 指定域名,必須可以解析;
[web03]
www.example.com
 
# 組巢狀,當執行組[weball]時就會執行它的所有子組但是子組可以獨立執行;
[weball:children]
[web01]
[web02]
[web03]

常用引數如下:

ansible_connection=ssh                #指定連線型別,可以使local、ssh、paramiko;
ansible_ssh_user=root                 #用於指定遠端主機的賬號;
ansible_ssh_pass=password             #指定連線到主機的密碼連-k都省了;
ansible_sudo_pass       #sudo 密碼(這種方式並不安全,我們強烈建議使用 --ask-sudo-pass);
ansible_sudo_exe (new in version 1.8)  #sudo 命令路徑(適用於1.8及以上版本)
ansible_ssh_host         #將要連線的遠端主機名.與你想要設定的主機的別名不同的話,可通過此變數設定;
ansible_ssh_port=23                   #用於指定遠端主機SSH埠;
ansible_ssh_privare_key_file=/PATH    #用於指定key檔案;
aost_key_checking=false               #當第一次連線遠端主機,跳過yes/no環節;
ansible_shell_type                    #指定目標系統的shell(預設為sh);
ansible_python_interpreter=/          #指定Python直譯器路徑(預設/USR/BIN/PYTHON);
ansible_shell_type    #目標系統的shell型別.預設情況下,命令的執行使用 'sh' 語法,可設定為 'csh' 或 'fish'.

使用方式:

[web01]###對於每一個 host,可以選擇連線型別和連線使用者名稱:
localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_ssh_user=mpdehaan
192.168.10.1 ansible_ssh_user=root 
192.168.10.[1] ansible_ssh_pass=password

2)主機變數和組變數
Inventory除了上述的基本功能外,還可以在主機後面定義變數以便於在playbook中使用,例如:

[web01]
192.168.10.1 http_port=80 maxRequest=100
192.168.10.2 http_port=8080 maxRequest=200

不光對主機可以定義變數,對組也可以定義變數。組變數是指賦予給組內所有主機,都可以在playbook中使用,格式如下:

[web01:vars]##這就是針對web01組設定的組變數,:vars是固定格式,組內的所有變數都可以給組內的主機使用。
http_port01=80
http_port02=8080

3)分檔案定義 group_vars 和 host_vars
在 inventory 主檔案中儲存所有的變數並不是最佳的方式.還可以儲存在獨立的檔案中,這些獨立檔案與 inventory 檔案保持關聯. 不同於 inventory 檔案(INI 格式),這些獨立檔案的格式為 YAML. group_vars/ 和 host_vars/目錄可放在 inventory 目錄下,或是 playbook 目錄下. 如果兩個目錄下都存在,那麼 playbook 目錄下的配置會覆蓋 inventory 目錄的配置
4)關於動態 Inventory,之後遇到要使用時 查資料

3. 常用模組

根據Ansible官方的分類,將模組分為核心模組和額外模組
1)ping 模組
測試主機是否是通的,上面介紹過
2)setup 模組
主要是用於獲取主機資訊。在playbooks裡經常會用到的一個引數。gather_facts就與該模組相關,setup模組下經常使用的一個引數是filter引數,具體使用示例如下:

# 檢視主機記憶體資訊
$ ansible test -m setup -a 'filter=ansible_*_mb'
 
# 檢視地介面為eth0-2的網路卡資訊
$ ansible test -m setup -a 'filter=ansible_eth[0-2]'
 
# 將所有主機的資訊輸入到/tmp/facts目錄下,每臺主機的資訊輸入到主機名檔案中(/etc/ansible/hosts裡的主機名)
$ ansible test -m setup --tree /tmp/facts

操作示例:


13236273-746663f88670ddb3.png
image.png

3)file 模組
file模組主要用於遠端主機上的檔案操作,具體使用示例如下:

# 建立一個軟連線
$ ansible test -m file -a "src=/etc/fstab dest=/tmp/fstab state=link"
 
# 刪除一個檔案
$ ansible test -m file -a "path=/tmp/fstab state=absent"
 
# 建立一個檔案
$ ansible test -m file -a "path=/tmp/test state=touch"

常用引數:

force        #需要在兩種情況下強制建立軟連結,一種是原始檔不存在但之後會建立的情況下;另一種是目標軟連結已存在,需要先取消之前的軟鏈,然後建立新的軟鏈,有兩個選項:yes|no;
group        #定義檔案/目錄的屬組;
mode         #定義檔案/目錄的許可權;
owner        #定義檔案/目錄的屬主;
path         #必選項,定義檔案/目錄的路徑, required;
recurse      #遞迴的設定檔案的屬性,只對目錄有效;
src          #要被連結的原始檔的路徑,只應用於state=link的情況;
dest         #被連結到的路徑,只應用於state=link的情況;
state:
  directory  #如果目錄不存在,建立目錄;
  file       #即使檔案不存在,也不會被建立;
  link       #建立軟連結;
  hard       #建立硬連結;
  touch      #如果檔案不存在,則會建立一個新的檔案,如果檔案或目錄已存在,則更新其最後修改時間;
  absent     #刪除目錄、檔案或者取消連結檔案;
13236273-bf61532e4754cd0f.png
image.png

4)copy 模板
複製檔案到遠端主機,如下示例:

$ ansible test -m copy -a "src=/srv/myfiles/foo.conf dest=/etc/foo.conf owner=foo group=foo mode=0644"
$ ansible test -m copy -a "src=/mine/ntp.conf dest=/etc/ntp.conf owner=root group=root mode=644 backup=yes"
$ ansible test -m copy -a "src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s'"

常用引數:

backup          #在覆蓋之前將原檔案備份,備份檔案包含時間資訊。有兩個選項:yes|no;
content         #用於替代"src",可以直接設定指定檔案的值;
directory_mode  #遞迴的設定目錄的許可權,預設為系統預設許可權;
force           #如果目標主機包含該檔案,但內容不同,如果設定為yes,則強制覆蓋,如果為no,則只有當目標主機的目標位置不存在該檔案時,才複製。預設為yes;
others          #所有的file模組裡的選項都可以在這裡使用;
dest            #必選項,要將原始檔複製到的遠端主機的絕對路徑,如果原始檔是一個目錄,那麼該路徑也必須是個目錄, required;
src             #要複製到遠端主機的檔案在本地的地址,可以是絕對路徑,也可以是相對路徑。如果路徑是一個目錄,它將遞迴複製。在這種情況下,
                #如果路徑使用"/"來結尾,則只複製目錄裡的內容,如果沒有使用"/"來結尾,則包含目錄在內的整個內容全部複製,類似於rsync;
validate        #複製檔案前進行驗證,檔案的路徑的驗證是通過"%s";

5)service 模組
用於管理主機服務,能夠同時管理CentOS6和CentOS7,不區分CentOS6的service和CentOS7的systemctl,如下例項:

$ ansible test -m service -a "name=httpd state=started enabled=yes"
$ ansible test -m service -a "name=foo pattern=/usr/bin/foo state=started"
$ ansible test -m service -a "name=network state=restarted args=eth0"

常用引數:

name        #必選項,服務名稱;
state       #對當前服務執行啟動,停止、重啟、重新載入等操作(started, stopped, restarted, reloaded);
enabled     #是否開機啟動yes|no;
pattern     #定義一個模式,如果通過status指令來檢視服務的狀態時,沒有響應,就會通過ps指令在程式中根據該模式進行查詢,如果匹配到,則認為該服務依然在執行;
runlevel    #執行級別;
arguments   #給命令列提供一些選項;
sleep       #如果執行了restarted,在則stop和start之間沉睡幾秒鐘;

6)cron 模組
用於管理計劃任務,如下:

$ ansible test -m cron -a 'name="a job for reboot" special_time=reboot job="/some/job.sh"'
$ ansible test -m cron -a 'name="yum autoupdate" minute=*/1 hour=* day=* month=* weekday=* user="root" job="date>>/tmp/1.txt"'
$ ansible test -m cron -a 'name="yum autoupdate" minute=1 hour=*/1 day=* month=* weekday=* user="root" job="date>>/tmp/1.txt"'
$ ansible test -m cron -a 'backup="True" name="test" minute="0" hour="5,2" job="ls -alh > /dev/null"'
$ ansilbe test -m cron -a 'cron_file=ansible_yum-autoupdate state=absent'

常用引數:

name          #該任務的描述;
backup        #對遠端主機上的原任務計劃內容修改之前做備份;
cron_file     #如果指定該選項,則用該檔案替換遠端主機上的cron.d目錄下的使用者的任務計劃;
day           #日(1-31,,/2,……);
hour          #小時(0-23,,/2,……);
minute        #分鐘(0-59,,/2,……);
month         #月(1-12,,/2,……);
weekday       #周(0-7,*,……);
job           #要執行的任務,依賴於state=present;
special_time  #指定什麼時候執行,引數:reboot(重啟時),yearly(每年),annually,monthly,weekly,daily,hourly;
state         #確認該任務計劃是建立還是刪除;
user          #以哪個使用者的身份執行;

7)yum 模組
使用yum包管理器來管理軟體包,如下:

# 安裝最新版本的apache;
$ ansible test -m yum -a 'name=httpd state=latest'
 
# 移除apache;
$ ansible test -m yum -a 'name=httpd state=absent'
 
# 升級所有的軟體包;
$ ansible test -m yum -a 'name=* state=latest '
 
# 安裝整個Development tools相關的軟體包;
$ ansible test -m yum -a 'name="@Development tools" state=present'
 
# 從本地倉庫安裝nginx;
$ ansible test -m yum -a 'name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present'
 
# 從一個遠端yum倉庫安裝nginx;
$ ansible test -m yum -a 'name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present'

常用引數:

name               #要進行操作的軟體包的名字,也可以傳遞一個url或者一個本地的rpm包的路徑;
config_file        #yum的配置檔案;
disable_gpg_check  #關閉gpg_check;
disablerepo        #不啟用某個源;
enablerepo         #啟用某個源;
state              #用於描述安裝包最終狀態,<em>present/latest</em>用於安裝包,<em>absent</em>用於解除安裝安裝包;

8)user 模組
使用者管理模組,使用示例:

$ ansible test -m user -a 'name=johnd comment="John Doe" uid=1040 group=admin'

常用引數

home        #指定使用者的家目錄,需要與createhome配合使用;
groups      #指定使用者的屬組;
uid         #指定用的uid;
password    #指定使用者的密碼;
name        #指定使用者名稱;
createhome  #是否建立家目錄yes|no;
system      #是否為系統使用者;
remove      #當state=absent時,remove=yes則表示連同家目錄一起刪除,等價於userdel -r;
state       #是建立還是刪除;
shell       #指定使用者的shell環境;

注:指定password引數時,不能使用明文密碼,因為後面這一串密碼會被直接傳送到被管理主機的/etc/shadow檔案中,所以需要先將密碼字串進行加密處理。然後將得到的字串放到password中即可。
生成一個密碼:

$ echo "123456" | openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32) -stdin
$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0

用上面生成的密碼建立使用者

$ ansible all -m user -a 'name=foo password="$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0"'

不同的發行版預設使用的加密方式可能會有區別,具體可以檢視/etc/login.defs檔案確認,centos 6.5版本使用的是SHA512加密演算法。
9)group 模組
組管理模組,使用示例:

$ ansible all -m group -a 'name=somegroup state=present'

常用引數:

gid     #指定gid;
name    #指定組名稱;
state   #操作狀態,present,absent;
system  #是否為系統組;

10)filesystem 模組
在塊裝置上建立檔案系統,示例如下:

$ ansible test -m filesystem -a 'fstype=ext2 dev=/dev/sdb1 force=yes'
$ ansible test -m filesystem -a 'fstype=ext4 dev=/dev/sdb1 opts="-cc"'

常用引數

dev     #目標塊裝置;
force   #在一個已有檔案系統的裝置上強制建立;
fstype  #檔案系統的型別;
opts    #傳遞給mkfs命令的選項;

11)mount 模組
配置掛載點

$ ansible test -m mount 'name=/mnt/dvd src=/dev/sr0 fstype=iso9660 opts=ro state=present'
$ ansible test -m mount 'name=/srv/disk src='LABEL=SOME_LABEL' state=present'
$ ansible test -m mount 'name=/home src='UUID=b3e48f45-f933-4c8e-a700-22a159ec9077' opts=noatime state=present'
$ ansible test -a 'dd if=/dev/zero of=/disk.img bs=4k count=1024'
$ ansible test -a 'losetup /dev/loop0 /disk.img'
$ ansible test -m filesystem -a 'fstype=ext4 force=yes opts=-F dev=/dev/loop0'
$ ansible test -m mount 'name=/mnt src=/dev/loop0 fstype=ext4 state=mounted opts=rw'

常用引數:

fstype    #必選項,掛載檔案的型別;
name      #必選項,掛載點;
opts      #傳遞給mount命令的引數;
src       #必選項,要掛載的檔案;
state     #必選項;
present   #只處理fstab中的配置;
absent    #刪除掛載點;
mounted   #自動建立掛載點並掛載之;
umounted  #解除安裝;

12) get_url 模組
主要用於從 http、ftp、https 伺服器上下載檔案,類似wget

$ ansible test -m filesystem -a 'url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440'

常用引數:

sha256sum                   #下載完成後進行sha256 check;
timeout                     #下載超時時間,預設10s;
url                         #下載的URL;
url_password、url_username  #主要用於需要使用者名稱密碼進行驗證的情況;
use_proxy                   #使用代理,代理需事先在環境變更中定義;

13)unarchive 模組
用於解壓檔案的模組

$ ansible test -m unarchive -a 'src=foo.tgz dest=/var/lib/foo'
$ ansible test -m unarchive -a 'src=/tmp/foo.zip dest=/usr/local/bin copy=no'
$ ansible test -m unarchive -a 'src=https://example.com/example.zip dest=/usr/local/bin copy=no'

常用引數:

copy        #在解壓檔案之前,是否先將檔案複製到遠端主機,預設為yes。若為no,則要求目標主機上壓縮包必須存在;
creates     #指定一個檔名,當該檔案存在時,則解壓指令不執行;
dest        #遠端主機上的一個路徑,即檔案解壓的路徑;
group       #解壓後的目錄或檔案的屬組;
list_files  #如果為yes,則會列出壓縮包裡的檔案,預設為no,2.0版本新增的選項;
mode        #解決後檔案的許可權;
src         #如果copy為yes,則需要指定壓縮檔案的源路徑;
owner       #解壓後檔案或目錄的屬主;

14)script 模組
在指定節點上執行 shell、python 指令碼(該指令碼必須是ansible控制節點上面的)

$ ansible test -m script -a '/root/src.sh'

15)shell 模組
在指定節點上執行shell/python指令碼(注意,該指令碼是在遠端節點)

$ ansible test -m shell -a '/bin/bash /root/dest.sh'

16)command 模組
用於執行遠端系統命令,此模組為 ansible 預設執行的模組,也是常用模組之一。不支援 shell 變數,也不支援管道等。

$ ansible test -m command -a 'uname -n'

17)raw 模組
類似 command 模組,區別在於 raw 模組支援管道傳遞

$ ansible test -m raw -a "tail -n2 /etc/passwd | head -n1"

4. Ad-Hoc Commands

Ansible提供兩種方式去完成任務,一是 ad-hoc 命令,一是寫 Ansible playbook.前者可以解決一些簡單的任務, 後者解決較複雜的任務。(ad-hoc 其實是一個概念性的名字,是相對於寫 Ansible playbook 來說的。類似於在命令列敲入shell命令和 寫shell scripts兩者之間的關係)
例如:現在執行如下命令,這個命令中,atlanta是一個組,這個組裡面有很多伺服器,”/sbin/reboot”命令會在atlanta組下 的所有機器上執行.這裡ssh-agent會fork出10個子程式(bash),以並行的方式執行reboot命令.如前所說“每次重啟10個” 即是以這種方式實現:

$ ansible atlanta -a "/sbin/reboot" -f 10

確認某個服務在所有的webservers上都已經啟動:

$ ansible webservers -m service -a "name=httpd state=started"

或是在所有的webservers上重啟某個服務:

$ ansible webservers -m service -a "name=httpd state=restarted"

確認某個服務已經停止:

$ ansible webservers -m service -a "name=httpd state=stopped"

需要長時間執行的命令可以放到後臺去,在命令開始執行後我們也可以檢查執行的狀態.如果執行命令後,不想獲取返回的資訊, 可執行如下命令:

$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"

如果你確定要在命令執行後檢查執行的狀態,可以使用 async_status 模組.前面執行後臺命令後會返回一個 job id, 將這個 id 傳給 async_status 模組:

$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"

獲取狀態的命令如下:

$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

其中 -B 1800 表示最多執行30分鐘, -P 60 表示每隔60秒獲取一次狀態資訊.
Polling 獲取狀態資訊的操作會在後臺工作任務啟動之後開始.若你希望所有的工作任務快速啟動, --forks 這個選項的值 要設定得足夠大,這是前面講過的併發程式的個數.在執行指定的時間(由-B選項所指定)後,遠端節點上的任務程式便會被終止.

5. 許可權提升 become

Ansible可以使用現有的許可權提升系統來允許使用者執行另一個任務。
Ansible允許你成為另一個使用者,與登入到本機的使用者或遠端使用者不同。這是使用現有的特權升級工具(privilege escalation tools)完成的,可能已經使用或已經配置了這些工具,如sudo,su,pfexec,doas,pbrun,dzdo,ksu等。
說明:
(1)在1.9 Ansible之前,大多數情況下都允許使用sudo和有限的su來允許登入/遠端使用者成為不同的使用者並執行任務,用第二個使用者的許可權建立資源。從1.9開始become代替舊的sudo / su,同時仍然向後相容。這個新系統也使得新增諸如pbrun(Powerbroker),pfexec,dzdo(Centrify)等其他特權升級工具變得更加容易。
(2)變數和指令是獨立的,即設定become_user並不是設定become。

Directives

(1)become
set to ‘true’/’yes’ to activate privilege escalation.
使用“true”或“yes”來表示啟用這個特權,如:become=true
表示開啟了become開關。
(2)become_user
set to user with desired privileges — the user you ‘become’, NOT the user you login as. Does NOT imply become: yes, to allow it to be set at host level.
become_user=root 設定為root賬戶,相當於我們以普通賬戶登入到遠端主機時,再使用su - root切換為root賬戶。
(3)become_method
(at play or task level) overrides the default method set in ansible.cfg, set to sudo/su/pbrun/pfexec/doas/dzdo/ksu
become_method=su 表示用什麼方式將普通賬戶切換到root或所需的其他賬戶,這裡可以用su或sudo。
(4)become_flags
(at play or task level) permit to use specific flags for the tasks or role. One common use is to change user to nobody when the shell is set to no login. Added in Ansible 2.2.
表示允許為任務或角色使用特定的標誌。一個常見的用法是在shell設定為不登入時將使用者更改為nobody。ansible2.2版本中增加。

例如,在以非root使用者身份連線時管理系統服務(需要root許可權)(become_user 的預設值為root),此時以 root 使用者執行

- name: Ensure the httpd service is running
  service:
    name: httpd
    state: started
  become: yes

下面的將以 apache 使用者

- name: Run a command as the apache user
  command: somecommand
  become: yes
  become_user: apache

此任務不會對become_user執行任何操作,因為未設定為yes並且預設為false / no

- do: something
  become_user: someone

6. playbook

參考:
http://ansible-tran.readthedocs.io/en/latest/docs/intro_configuration.html#ssh-args
http://www.ywnds.com/?p=6045

相關文章