ansible怎么批量初始化服務(wù)器?針對這個問題,今天小編總結(jié)這篇有關(guān)ansible批量初始化服務(wù)器的文章,有需要的小伙伴們可以參考借鑒,希望對大家有所幫助。
我們提供的服務(wù)有:網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、涵江ssl等。為上千余家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的涵江網(wǎng)站制作公司
[root@nginx ansible]# tail -3 /etc/ansible/hosts #要初始的主機(jī)如下
[node]
192.168.20.4
192.168.20.5
playbook文件內(nèi)容如下:
[root@nginx ansible]# cat ssh.yaml
---
- name: configure ssh connection
hosts: node
gather_facts: false
connection: local
tasks:
- name: configure ssh connection
shell: |
ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hosts
sshpass -p '123.com' ssh-copy-id root@{{inventory_hostname}}
...
注:
配置主機(jī)名可以使用shell模塊,但是對于不太專業(yè),ansible提供了一個專用于配置主機(jī)名的模塊:hostname模塊。
當(dāng)然,要使用ansible去設(shè)置多個主機(jī)名,要求目標(biāo)主機(jī)和目標(biāo)名稱已經(jīng)關(guān)聯(lián)好,否則多個主機(jī)和多個主機(jī)名之間無法對應(yīng)去設(shè)置。
例如:分別設(shè)置node組中的兩個節(jié)點(diǎn)主機(jī)名為node01和node02,playbook內(nèi)容如下:
[root@ansible ansible]# cat test.yaml
---
- name: set hostname
hosts: node
gather_facts: false
vars:
hostnames:
- host: 192.168.20.4
name: node01
- host: 192.168.20.5
name: node02
tasks:
- name: set hostname
hostname:
name: "{{item.name}}"
when: item.host == inventory_hostname
loop: "{{hostnames}}"
在上面的hostname模塊中,需要詳細(xì)介紹vars指令以及when、loop指令。
vars指令可用于設(shè)置變量,可以設(shè)置一個或多個變量。下面幾種方式都是合理的:
# 設(shè)置單個變量
vars:
var1: value1
vars:
- var1: value1
# 設(shè)置多個變量
vars:
var1: value1
var2: value2
vars:
- var1: value1
- var2: value2
vars可以設(shè)置在play級別,也可以設(shè)置在task級別,設(shè)置在play級別,該play范圍內(nèi)的task可以訪問這些變量,其他play范圍內(nèi)則無法訪問;設(shè)置在task級別,只有該task能訪問這些變量,其他task和其他play則無法訪問。
例如:
[root@ansible ansible]# cat test.yaml
---
- name: play1
hosts: localhost
gather_facts: false
vars:
- var1: "value1"
tasks:
- name: access var1
debug:
msg: "var1's value: {{var1}}"
- name: play2
hosts: localhost
gather_facts: false
tasks:
- name: cat's access vars from play1
debug:
var: var1
- name: set and access var2 in this task
debug:
var: var2
vars:
var2: "value2"
- name: cat't accesss var2
debug:
var: var2
執(zhí)行結(jié)果如下:
[root@ansible ansible]# ansible-playbook test.yaml
PLAY [play1] **************************************************************************
TASK [access var1] ********************************************************************
ok: [localhost] => {
"msg": "var1's value: value1"
}
PLAY [play2] **************************************************************************
TASK [cat's access vars from play1] ***************************************************
ok: [localhost] => {
"var1": "VARIABLE IS NOT DEFINED!"
}
TASK [set and access var2 in this task] ***********************************************
ok: [localhost] => {
"var2": "value2"
}
TASK [cat't accesss var2] *************************************************************
ok: [localhost] => {
"var2": "VARIABLE IS NOT DEFINED!"
}
PLAY RECAP ****************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
回到我們更改主機(jī)名的配置vars指令中:
vars:
hostnames:
- host: 192.168.20.4
name: node01
- host: 192.168.20.5
name: node02
上面只設(shè)置了一個變量hostnames,但這個變量的值是一個數(shù)組結(jié)構(gòu),數(shù)組的兩個元素又都是對象(字典/hash)結(jié)構(gòu)。
所以想要訪問主機(jī)名node01和它的IP地址192.168.20.4,可以:
tasks:
- debug:
var: hostnames[0].name
- debug:
var: hostnames[0].host
在ansible中,提供的唯一一個通用的條件判斷是when指令,當(dāng)when指令的值為true時,則執(zhí)行該任務(wù),否則不執(zhí)行該任務(wù)。
例如:
[root@ansible ansible]# cat test.yaml
---
- name: play1
hosts: localhost
gather_facts: false
vars:
- myname: "Ray"
tasks:
- name: task will skip
debug:
msg: "myname is : {{myname}}"
when: myname == "lv"
- name: task will execute
debug:
msg: "myname is : {{myname}}"
when: myname == "Ray"
在上面的myname值設(shè)置為Ray,第一個任務(wù)因?yàn)閣hen的判斷條件是myname==“l(fā)v”,所以判斷結(jié)果為false,該任務(wù)不執(zhí)行,同理,第二個任務(wù)因?yàn)閣hen的值為true,所以執(zhí)行了。
該playbook的執(zhí)行結(jié)果:
PLAY [play1] **************************************************************************
TASK [task will skip] *****************************************************************
skipping: [localhost]
TASK [task will execute] **************************************************************
ok: [localhost] => {
"msg": "myname is : Ray"
}
PLAY RECAP ****************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
[root@ansible ansible]# cat add_dns.yaml
---
- name: play1
hosts: node
gather_facts: true
tasks:
- name: add DNS
lineinfile:
path: "/etc/hosts"
line: "{{item}} {{hostvars[item].ansible_hostname}}"
when: item != inventory_hostname
loop: "{{ play_hosts }}"
執(zhí)行結(jié)果如下:
TASK [Gathering Facts] ****************************************************************
ok: [192.168.20.4]
ok: [192.168.20.5]
TASK [add DNS] ************************************************************************
skipping: [192.168.20.4] => (item=192.168.20.4)
changed: [192.168.20.4] => (item=192.168.20.5)
changed: [192.168.20.5] => (item=192.168.20.4)
skipping: [192.168.20.5] => (item=192.168.20.5)
需求如下:
playbook如下:
[root@ansible ansible]# cat config_yum.yaml
- name: config yum repo add install software
hosts: node
gather_facts: false
tasks:
- name: backup origin yum repos
shell:
cmd: "mkdir bak; mv *.repo bak"
chdir: /etc/yum.repos.d
creates: /etc/yum.repos.d/bak
- name: add os repo and epel repo
yum_repository:
name: "{{item.name}}"
description: "{{item.name}} repo"
baseurl: "{{item.baseurl}}"
file: "{{item.name}}"
enabled: 1
gpgcheck: 0
reposdir: /etc/yum.repos.d
loop:
- name: os
baseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch"
- name: epel
baseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch"
- name: install pkgs
yum:
name: lrzsz,vim,dos2unix,wget,curl
state: present
在上面的yaml文件中,第一個任務(wù)是將所有系統(tǒng)默認(rèn)的repo文件備份到bak目錄中,chdir參數(shù)表示在執(zhí)行shell模塊的命令前先切換到/etc/yum.repos.d目錄下,creates參數(shù)表示bak目錄存在時則不執(zhí)行shell模塊。
第二個任務(wù)是使用yum_repository模塊配置yum源,該模塊可添加或移除yum源。
相關(guān)參數(shù)如下:
- name:指定repo的名稱,對應(yīng)于repo文件中的[name];
- description:repo的描述信息,對應(yīng)repo文件中的name:xxx;
- baseurl:指定該repo的路徑;
- file:指定repo的文件名,不需要加.repo后綴,會自動加上;
- reposdir:repo文件所在的目錄,默認(rèn)為/etc/yum.repos.d目錄;
- enabled:是否啟用該repo,對應(yīng)于repo文件中的enabled;
- gpgcheck:該repo是否啟用gpgcheck,對應(yīng)于repo文件中的gpgcheck;
- state:present表示保證該repo存在,absent表示移除該repo。
在上面的配置中使用了一個loop循環(huán)來添加兩個repo:os和epel。
第三個任務(wù)是使用yum模塊安裝一些rpm包,yum模塊可以更新、安裝、移除、下載包。
yum常用參數(shù)說明:
- name:指定要操作的包名
- 可以帶版本號;
- 可以是單個包名,也可以是包名列表,或者逗號分隔多個包名;
- 可以是url;
- 可以是本地rpm包
- state:
- present和installed:保證包已安裝,它們是等價的別名;
- latest:保證包已安裝了最新版本,如果不是則更新;
- absent和removed:移除包,它們是等價的別名;
- download_only:僅下載不安裝包(ansible 2.7才支持)
- download_dir:下載包存放在哪個目錄下(ansible 2.8才支持)
yum模塊是RHEL系列的包管理器,如果是ubuntu則無法使用,可以使用另一個更為通用的包管理器模塊:package,它可以自動探測目標(biāo)節(jié)點(diǎn)的包管理器類型并使用它們?nèi)ス芾碥浖?。大多?shù)時候使用package來代替yum或代替apt-install等不會有什么問題,但是有些包名在不同的操作系統(tǒng)上是不一樣的,這是需要注意的。
保證時間同步可以避免很多玄學(xué)性的問題,特別是對集群中的節(jié)點(diǎn)。
通常會使用ntpd時間服務(wù)器來保證時間的同步,這里使用aliyun提供的時間服務(wù)器來保證時間同步,并將同步后的時間同步到硬件。
playbook文件如下:
---
- name: sync time
hosts: node
gather_facts: false
tasks:
- name: install and sync time
block:
- name: install ntpdate
yum:
name: ntpdate
state: present
- name: ntpdate to sync time
shell: |
ntpdate ntp1.aliyun.com
hwclock -w
上面使用了一個block指令來組織了兩個有關(guān)聯(lián)性的任務(wù),將他們作為了一個整體。block更多的用于多個關(guān)聯(lián)性任務(wù)之間的異常處理。
關(guān)閉selinux的playbook如下:
[root@ansible roles]# cat disable_selinux.yaml
---
- name: disable selinux
hosts: node
gather_facts: false
tasks:
- name: disable on the fly
shell: setenforce 0
ignore_errors: true #由于上條命令執(zhí)行后的返回狀態(tài)碼不一定為0,所以為了防止非0報(bào)錯并停止palsybook接下來的任務(wù),所以使用ignore_errors忽略錯誤
- name: disable forever in config
lineinfile:
path: /etc/selinux/config
line: "SELINUX=disabled" #修改配置文件中的值,以便永久關(guān)閉
regexp: '^SELINUX=' #要修改的內(nèi)容
注:ignore_errors也經(jīng)常結(jié)合block使用,因?yàn)樵赽lock級別上設(shè)置異常處理,可以處理block內(nèi)部的所有錯誤。
playbook文件如下:
- name: Set Firewall
hosts: node
gather_facts: false
tasks:
- name: set iptables rule
shell: |
# 備份已有規(guī)則
iptables-save > /tmp/iptables.bak$(date +"%F-%T")
# 給它三板斧
iptables -X
iptables -F
iptables -Z
# 放行l(wèi)o網(wǎng)卡和允許ping
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
# 放行關(guān)聯(lián)和已建立連接的包,放行22、443、80端口
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
# 配置filter表的三鏈默認(rèn)規(guī)則,INPUT鏈丟棄所有包
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
有時候?yàn)榱朔?wù)器的安全,可能會去修改目標(biāo)節(jié)點(diǎn)上sshd服務(wù)的默認(rèn)配置,比如禁止root用戶登錄、禁止密碼認(rèn)證登錄而只允許使用ssh密碼認(rèn)證等。
在修改服務(wù)的配置文件時,一般有幾種方法:
相對來說,第三種方案是最統(tǒng)一、最易維護(hù)的方案。
此外,對于服務(wù)進(jìn)程來說,修改了配置文件往往意味著要重啟服務(wù),使其加載新的配置文件,對于sshd也一樣如此,但是sshd要比其他服務(wù)特殊一些,因?yàn)閍nsible默認(rèn)基于ssh連接,重啟sshd服務(wù)會使ansible連接斷開,好在ansible默認(rèn)會重試建立連接,無非是多等待幾秒。但重建連接有可能會失敗,比如修改了配置文件不允許重試、修改了sshd的監(jiān)聽端口等,這可能會使得ansible因連接失敗而無法再繼續(xù)執(zhí)行后續(xù)任務(wù)。
所以,在修改sshd配置文件時,有如下建議:
- 將此任務(wù)作為初始化服務(wù)器的最后一個任務(wù),即使連接失敗也無所謂;
- 在playbook中加入連接失敗的異常處理;
- 如果目標(biāo)節(jié)點(diǎn)修改了sshd端口號,建議通過ansible自動或者我們手動去修改inventory文件中的ssh連接端口號。
這里為了簡單,我準(zhǔn)備使用lineinfile模塊去修改配置文件,要修改的內(nèi)容只有兩項(xiàng):
playbook內(nèi)容如下:
[root@ansible roles]# cat sshd_config.yaml
---
- name: modify sshd_config
hosts: node
gather_facts: false
tasks:
# 1.備份/etc/ssh/sshd_config文件
- name: backup sshd config
shell:
/usr/bin/cp -f {{path}} {{path}}.bak
vars:
- path: /etc/ssh/sshd_config
# 2.設(shè)置PermitRootLogin no
- name: disable root login
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PermitRootLogin no"
insertafter: "^#PermitRootLogin"
regexp: "^PermitRootLogin"
notify: "restart sshd"
# 3.設(shè)置PasswordAuthentication no
- name: disable password auth
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PasswordAuthentication no"
regexp: "^PasswordAuthentication yes"
notify: "restart sshd"
handlers:
- name: "restart sshd"
service:
name: sshd
state: restarted
關(guān)于notify和handlers的作用如下:
ansible會監(jiān)控playbook執(zhí)行后的changed的狀態(tài),如果changed=1,則表示關(guān)注的狀態(tài)發(fā)生了改變,即本次任務(wù)的執(zhí)行不具備冪等性,如果changed=0,則表示本次任務(wù)要么沒執(zhí)行,要么執(zhí)行了也沒有影響,即本次任務(wù)具備冪等性。
ansible提供了notify指令和handlers功能,如果在某個task中定義notify指令,當(dāng)ansible在監(jiān)控到該任務(wù)changed=1時,會觸發(fā)該notify指令所定義的handler,然后去執(zhí)行handler。所謂handler,其實(shí)就是task,無論是在寫法上還是作用上它和task都沒有什么區(qū)別,唯一的區(qū)別在于handler是被觸發(fā)而被動執(zhí)行的,不像普通task一樣會按流程正常執(zhí)行。
唯一需要注意的是,notify和handler中任務(wù)的名稱必須一致。比如: notify: "restart sshd",那么handlers中必須得有一個任務(wù)設(shè)置了 name: "restart sshd"。
此外,在上面的playbook中,兩個lineinfile任務(wù)都設(shè)置了相同的notify,但ansible不會多次去重啟sshd,而是在最后重啟一次。實(shí)際上,ansible在執(zhí)行完某個任務(wù)之后,并不會立即去執(zhí)行對應(yīng)的handler,而是在當(dāng)前play中所有普通任務(wù)都執(zhí)行完成后再去執(zhí)行handler,這樣的好處是可以多次觸發(fā)notify,但最后只執(zhí)行一次對應(yīng)的handler,從而避免多次重啟。
這里將前面所有的playbook集合到單個playbook文件中去,這樣就可以一次性執(zhí)行所有任務(wù)。
整合后的playbook如下:
---
- name: Configure ssh Connection
hosts: node
gather_facts: false
connection: local
tasks:
- name: configure ssh connection
shell: |
ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hosts
sshpass -p'123.com' ssh-copy-id root@{{inventory_hostname}}
- name: Set Hostname
hosts: node
gather_facts: false
vars:
hostnames:
- host: 192.168.20.4
name: node01
- host: 192.168.20.5
name: node02
tasks:
- name: set hostname
hostname:
name: "{{item.name}}"
when: item.host == inventory_hostname
loop: "{{hostnames}}"
- name: Add DNS For Each
hosts: node
gather_facts: true
tasks:
- name: add DNS
lineinfile:
path: "/etc/hosts"
line: "{{item}} {{hostvars[item].ansible_hostname}}"
when: item != inventory_hostname
loop: "{{ play_hosts }}"
- name: Config Yum Repo And Install Software
hosts: node
gather_facts: false
tasks:
- name: backup origin yum repos
shell:
cmd: "mkdir bak; mv *.repo bak"
chdir: /etc/yum.repos.d
creates: /etc/yum.repos.d/bak
- name: add os repo and epel repo
yum_repository:
name: "{{item.name}}"
description: "{{item.name}} repo"
baseurl: "{{item.baseurl}}"
file: "{{item.name}}"
enabled: 1
gpgcheck: 0
reposdir: /etc/yum.repos.d
loop:
- name: os
baseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch"
- name: epel
baseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch"
- name: install pkgs
yum:
name: lrzsz,vim,dos2unix,wget,curl
state: present
- name: Sync Time
hosts: node
gather_facts: false
tasks:
- name: install and sync time
block:
- name: install ntpdate
yum:
name: ntpdate
state: present
- name: ntpdate to sync time
shell: |
ntpdate ntp1.aliyun.com
hwclock -w
- name: Disable Selinux
hosts: node
gather_facts: false
tasks:
- block:
- name: disable on the fly
shell: setenforce 0
- name: disable forever in config
lineinfile:
path: /etc/selinux/config
line: "SELINUX=disabled"
regexp: '^SELINUX='
ignore_errors: true
- name: Set Firewall
hosts: node
gather_facts: false
tasks:
- name: set iptables rule
shell: |
# 備份已有規(guī)則
iptables-save > /tmp/iptables.bak$(date +"%F-%T")
# 給它三板斧
iptables -X
iptables -F
iptables -Z
# 放行l(wèi)o網(wǎng)卡和允許ping
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
# 放行關(guān)聯(lián)和已建立連接的包,放行22、443、80端口
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
# 配置filter表的三鏈默認(rèn)規(guī)則,INPUT鏈丟棄所有包
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
- name: Modify sshd_config
hosts: node
gather_facts: false
tasks:
- name: backup sshd config
shell:
/usr/bin/cp -f {{path}} {{path}}.bak
vars:
- path: /etc/ssh/sshd_config
- name: disable root login
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PermitRootLogin no"
insertafter: "^#PermitRootLogin"
regexp: "^PermitRootLogin"
notify: "restart sshd"
- name: disable password auth
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PasswordAuthentication no"
regexp: "^PasswordAuthentication yes"
notify: "restart sshd"
handlers:
- name: "restart sshd"
service:
name: sshd
state: restarted
看完這篇文章,你們學(xué)會ansible批量初始化服務(wù)器的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀。