Automation
ansible
inventory
bash
# initial: for initalize the system
inventories/initial.host
# test hosts
inventories/test.host
# production hosts
inventories/prod.host
ansible(ad-hoc modules)
bash
# command usage
# optional arguments
--vault-password-file VAULT_PASSWORD_FILES
-B --backgroud SECONDS
-C --check
-D --diff
-P --poll POLL_INTERVAL
-a --args MODULE_ARGS
-e --extra-vars EXTRA_VARS
-f --forks FORKS
-i --inventory INVENTORY
-m --module-name MODULE_NAME
-t --tree TREE
# Privilege Escalation Options
--become-method BECOME_METHOD
--become-user BECOME_USER
-K --ask-become-pass
-b --become
# Connection Options
--private-key PRIVATE_KEY_FILE
--ssh-extra-args SSH_EXTRA_ARGS
-T --timeout TIMEOUT
-k --ask-pass
-u --user REMOTE_USER
-vvvv --verbose
# check all hosts
ansible all --list-hosts [-i inventories/test.host]
ansible all -m ping
ansible '*' -m ping
# pattern hosts
ansible '192.*' -m ping
ansible 'db*' -m ping
ansible 'web:&db' -m ping
ansible 'web:db' -m ping
ansible 'web:!db' -m ping
ansible "~(web|db).*\.example\.com" –m ping
# Common modules
# shell or script
ansible test -m command -a 'echo test'
ansible test -m shell -a 'chdir=/opt/ ls'
ansible test -m script -a /tmp/t.sh
# file or git transfer
ansible test -m copy -a "src=/tmp.t.sh dest=/tmp/t.sh mode=600 backup=yes"
ansible test -m file -a "dest=/etc/ansible/facts.d/ state=directory"
ansible test -m git -a "repo=git://foo.example.org/repo.git dest=/srv/myapp version=HEAD"
ansible test -m lineinfile
ansible test -m replace
# managing packages
ansible test -m apt -a "name=acme state=present"
ansible test -m yum -a "name=acme state=absent"
ansible test -m package -a "name=ntpdate state=absent"
# users and groups
ansible test -m user -a "name=nobody group=nobody state=present"
# managing services
ansible test -m service -a "name=httpd state=restarted"
ansible web -m systemd -a "name=httpd state=started"
# managing firewall
ansible test -m iptables -a "chain=INPUT destination_port=22 protocol=tcp jump=ACCEPT"
ansible test -m firewalld -a "service=https permanent=yes state=enabled"
# time limited background operations
ansible test -B 300 -P 2 -a "sleep 30"
ansible test -m async_status -a "jid=488359678239.2844"
vars fact template
vars
bash
# how to define
# 1. extra vars
-e "init_hosts=10.0.10.12,10.0.10.13" --check
-e '{"foo":"bar","numbers":["one","two"]}'
-e @variables.yaml
# 2. inventory vars
host1 http_port=80
[webservers:vars]
ntp_server=ntp.example.com
# 3. playbook: vars, include_vars, vars_files, vars_prompt, registered vars
- hosts: webservers
vars:
http_port: 80
tasks:
include_vars: myvars.yml
vars_files:
- /vars/external_vars.yml
vars_prompt:
- name: root_password
prompt: 'Please input the root password:'
private: yes
---
tasks:
- shell: uptime
register: result
- name: show uptime
debug: var=result
# 4. role: roles/x/defaults/main.yml, roles/x/vars/main.yml
http_port: 80
# 5. fact
- host:
tasks:
- command: whoami
register: result
- set_fact: w={{result.stdout}}
- debug: var=w
# priority
https://ansible.leops.cn/basic/Variables/#_3
# variable range
https://ansible.leops.cn/basic/Variables/#_4
# how to use
# 1. jinja2: template, vars
- hosts: test
template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg
vars:
app_path: "{{ base_path }}/22"
# 2. fact
- name: example
hosts: test
tasks:
- name: debug test
# debug: msg={{ ansible_facts }}
debug: msg={{ ansible_facts["default_ipv4"]["address"] }}
# 3. Built in variables
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html
fact
bash
# ansible setup info
ansible test -m setup
ansible test -m setup -a 'filter=ansible_eth*'
# custom fact
cat > playbooks/fact_test.yml << "EOF"
- name: facts test
hosts: test
tasks:
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/opt/ansible/facts.d
- name: install custom test fact
copy:
content: |
[forbid]
foor=bar
dest: /opt/ansible/facts.d/forbidden.fact
- name: re-read facts after adding custom fact
setup: filter=ansible_local
- name: ansible_local vars
debug: msg={{ansible_local}}
# debug: var=ansible_local
EOF
ansible-playbook playbooks/fact_test.yml
# third cache: ansible.cfg
fact_caching_connection = localhost:6379:0:admin
fact_caching_connection = localhost:11211
# flush cache
ansible-playbook --flush-cache playbooks/example.yml
template
bash
# jinja2 template
ansible test -m debug -a "msg={{ now(utc='True',fmt='%H-%m-%d %T') }}"
# use jinja2
vars:
motd_value: "{{ lookup('file', '/etc/motd') }}"
tasks:
- debug:
msg: "motd value is {{ motd_value }}"
tasks:
- shell: cat /some/path/to/file.json
register: result
- set_fact:
myvar: "{{ result.stdout | from_json }}"
ansible-console
bash
ansible-console test
root@all (1)[f:5]$ ping
10.0.0.1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible-doc
bash
# List available plugins
ansible-doc --list
# show infomation
ansible-doc file
ansible-doc -t become -l
ansible-doc -j ping
ansible-doc -s ping
ansible-galaxy
bash
# collections
ansible-galaxy collection
# roles
# manual init template role and define tasks
ansible-galaxy role search nginx
ansible-galaxy role install nginx
ansible-galaxy role info nginx
ansible-galaxy role init /opt/ansible/roles/nginx
# online role
ansible-galaxy list
ansible-galaxy install geerlingguy.redis
ansible-galaxy remove geerlingguy.redis
ansible-lint
bash
# install
pip install ansible-lint
# use
ansible-lint playbooks/example.yml
ansible-playbook
bash
# execute example playbook
ansible-playbook playbooks/example.yml
# execute playbook use extra vars hosts
ansible-playbook -i inventories/initial.hosts playbooks/initial.yml -e "init_hosts=10.0.10.12,10.0.10.13" --check
# optional arguments
--ask-vault-pass
--flush-cache
--list-hosts
--list-tags
--list-tasks
--start-at-task START_AT_TASK
--step
--syntax-check
-C --check
-D --diff
-i --inventory INVENTORY
# Privilege Escalation Options
--become-method BECOME_METHOD
--become-user BECOME_USER
-K --ask-become-pass
-b --become
# Connection Options
--private-key PRIVATE_KEY_FILE
--ssh-extra-args SSH_EXTRA_ARGS
-T --timeout TIMEOUT
-k --ask-pass
-u --user REMOTE_USER
-vvvv --verbose
# import
# roles/example/tasks/main.yml
- name: added in 2.4, previously you used 'include'
import_tasks: redhat.yml
when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
when: ansible_facts['os_family']|lower == 'debian'
# roles/example/tasks/redhat.yml
- yum:
name: "httpd"
state: present
# roles/example/tasks/debian.yml
- apt:
name: "apache2"
state: present
# include
- hosts: test
tasks:
- include_tasks: "{{inventory_hostname}}.yml"
- include_tasks: other.yml param={{item}}
loop: "{{ items | flatten(levels=1) }}"
- include_role:
name: example
- name: include vars
include_vars: "{{ lookup('first_found', possible_files) }}"
vars:
possible_files:
- "{{ ansible_distribution }}.yaml"
- "{{ ansible_os_family }}.yaml"
- default.yaml
# become
# asynchronous
# debuger
# step task
ansible-playbook playbooks/example.yml --start-at-task="install packages"
ansible-playbook playbooks/example.yml --step
ansible-vault
bash
# playbook operation
ansible-vault create playbooks/new.yml
ansible-vault edit playbooks/new.yml
ansible-vault view playbooks/example.yml
ansible-vault encrypt playbooks/example.yml
ansible-vault decrypt playbooks/example.yml
# option1: --ask-vault-pass or --vault-id
# --ask-vault-pass
ansible-vault encrypt_string pwd123 --name root_password
ansible-playbook playbooks/example.yml --start-at-task "Vault task" --ask-vault-pass
# --vault-id (recommend)
ansible-vault encrypt_string pwd123 --name root_password --vault-id prompt
#ansible-vault encrypt_string pwd123 --name root_password --vault-id example@prompt
ansible-playbook playbooks/example.yml --vault-id prompt
# option2: --vault-password-file or --vault-id
# --vault-password-file
ansible-vault encrypt playbooks/example.yml
echo "your_vault_pwd" > pwd.vault
ansible-playbook playbooks/example.yml --vault-password-file pwd.vault
# --vault-id (recommend)
ansible-vault encrypt playbooks/example.yml --vault-id pwd.vault
#ansible-vault encrypt playbooks/example.yml --vault-id [email protected]
echo "your_vault_pwd" > pwd.vault
ansible-playbook playbooks/example.yml --vault-id pwd.vault
saltstack
minion keys
bash
# select all keys
salt-key -L
# accept key
salt-key -a db1
salt-key -A
# delete key
salt-key -d web1
salt-key -d 'web*'
salt-key -D
# verify
salt '*' test.version
salt '*' test.ping
match minion
bash
# regular
salt '*' test.ping
salt 'web0[3-7]' test.ping
# regex pcre
salt -E 'web*|db*' test.ping
# list
salt -L 'node1,node2' test.ping
# grains
salt -G 'os:Ubuntu' test.version
# grains pcre
salt -P 'os:Arch.*' test.ping
# custom groups
cat /etc/salt/master.d/nodegroups.conf
nodegroups:
FRONTEND: L@frontend1,frontend2,frontend3
BACKEND: L@backend1,backend2,backend3
salt -N FRONTEND test.ping
# compound
salt -C 'G@roles:apps or I@myname:logic' test.ping
# pillar
salt -I 'myname:logic' test.ping
# CIDR
salt -S '192.168.1.0/24' test.ping
module
bash
# doc
salt 'node1' sys.doc
salt 'node1' sys.doc saltutil
salt 'node1' sys.doc pkg[.install]
# pkg
salt 'node1' pkg.install wget
# cmd
salt 'node1' cmd.run "ls /opt"
# cp
salt 'node1' cp.get_file salt://tmp/files/1.conf /tmp/1.conf
salt 'node1' cp.get_file salt://{{grains.os}}/vimrc /etc/vimrc template=jinja
salt 'node1' cp.get_dir salt://tmp/dir /tmp/dir
# custom module
mkdir /srv/salt/base/_modules
tee > /srv/salt/base/_modules/mydisk.py << "EOF"
def df():
return __salt__['cmd.run']('df -h')
EOF
salt '*' saltutil.sync_modules
salt 'node1' mydisk.df
state structure
bash
# state sls files
tee > /srv/salt/base/package/tree.sls << "EOF"
install_tree_now:
pkg.installed:
- pkgs:
- tree
EOF
tee > /srv/salt/base/package/nginx.sls << "EOF"
install_tree_now:
pkg.installed:
- pkgs:
- nginx
EOF
tee > /srv/salt/base/tmp/init.sls << "EOF"
apache:
pkg.installed:
- pkgs:
- httpd
file.managed:
- name: /etc/httpd/conf/httpd.conf
- source: salt://tmp/files/httpd.conf
service.running:
- name: httpd
- reload: true
- enable: truej
- watch:
- file: apache
EOF
# show state sls
salt 'node1' state.show_highstate [saltenv=dev]
salt 'node1' state.show_sls template [saltenv=dev]
salt 'node1' cp.list_states [saltenv=dev]
# execute top high state sls
tee > /srv/salt/base/top.sls << "EOF"
base:
'node1':
- package.tree
- package.nginx
'node2':
- tmp
'frontend'
- match: nodegroup
- nginx
'os:Ubuntu':
- match: grain
- apache
dev:
'webserver*dev*':
- webserver
'db*dev*':
- db
EOF
salt 'node1' state.highstate [--batch 10%|10] [test=True]
# execute regular state sls
salt '*' state.sls tmp[.init] [saltenv=dev] [test=True]
salt '*' state.sls package.nginx [saltenv=dev] [test=True]
grains
bash
salt '*' saltutil.refresh_grains [saltenv=base|dev|prod]
salt '*' saltutil.sync_grains
salt '*' grains.ls
salt '*' grains.items
salt '*' grains.item username
# default cache dir
/var/cache/salt/master/minions/node1/data.p
# listening grains
salt '*' grains.ls
- os
- username
...
salt '*' grains.items
os:
Ubuntu
osrelease:
20.04
...
# target with grains
salt -G 'os:Ubuntu' test.version
salt -G 'host:node1' grains.item os
salt -G 'ip_interfaces:ens160:172.22.3.*' test.ping
# defining custom grains:
# in master
# option1 (save to minion /etc/salt/grains)
salt minion01 grains.setval roles "['web','app1','dev']"
# option2 (save to minion /var/cache/salt/minion/extmods/grains)
mkdir /srv/salt/base/_grains
tee > /srv/salt/base/_grains/mem.py << "EOF"
def my_grains():
grains = {}
grains['my_bool'] = True
grains['my_str'] = 'str_test'
return grains
EOF
salt minion01 saltutil.sync_grains
# in minion
# option1
tee > /etc/salt/minion.d/grains.conf << "EOF"
grains:
roles: app1
project: frontend
EOF
systemctl restart salt-minion
# option2
tee > /etc/salt/grains << "EOF"
roles: app1
project: frontend
EOF
salt minion01 saltutil.sync_grains
# test
salt minion01 grains.item roles project
salt minion01 grains.item my_bool my_str
salt -G 'roles:app1' test.ping
# use state sls with grains
{{ salt['grains.get']('os') }}
{{ salt['grains.get']('os', ‘Debian’) }}
pillar
bash
salt '*' saltutil.refresh_pillar [pillarenv=base|dev|prod]
salt '*' pillar.ls
salt '*' pillar.items
salt '*' pillar.item mysql
# pillar_roots
tee > /srv/salt/pillar/mypillar.sls << "EOF"
{% if grains['fqdn'] == 'node1' %}
myname: xxx
{% elif grains['fqdn'] == 'node2' %}
myname: yyy
{% endif %}
port: 80
EOF
tee > /srv/salt/pillar/top.sls << "EOF"
base:
'*':
- mypillar
dev:
'os:Debian':
- match: grain
- vim
test:
'* and not G@os: Debian':
- match: compound
- emacs
EOF
salt '*' pillar.items
salt '*' saltutil.refresh_pillar
salt '*' pillar.item myname port
# use pillar by sls file
tee > /srv/salt/base/tmp/init.sls << "EOF"
apache:
pkg.installed:
- pkgs:
- {{ pillar['myname'] }}
service.running:
- name: httpd
- reload: true
- enable: true
- watch:
- file: /etc/httpd/conf/httpd.conf
/etc/httpd/conf/httpd.conf:
file.managed:
- source: salt://apache/httpd.conf
EOF
salt '*' state.sls tmp.init