最後更新: 2015-08-04
目錄
- 介紹
- Installation
- 運作
- Use SSH Keys For Authentication
- Config File 的載入次序
- Testing Example
- Manage Your Inventory
- sudo
- Configuration File
- Modules
- Debug
- Speedup Ansible
- Playbooks
- Doc
介紹
ansible 是一個 automation engine, Infrastructure as Code 功具.
* no agents ( 因為它是用 ssh 的)
* no additional custom security infrastructure ( ssh key login )
* automation jobs (Playbooks - YAML)
Homepage: https://www.ansible.com/
Program: Python
說明
* Each playbook is composed of one or more ‘plays’ in a list.
* The goal of a play is to map a group of hosts to some well defined "roles"
* Each play contains a list of tasks.
Installation
Debian11:
apt-get install sshpass ansible
ansible --version
ansible 2.10.8
運作
Ansible 會有這樣的 File Upload 到 Client:
/home/ansible/.ansible/tmp/ansible-xxxx/command
#!/usr/bin/python ..........
被 upload 的 script 叫 "Ansible Modules"
upload 完後, Ansible 就會用 ssh 執行它
Use SSH Keys For Authentication
# Set up SSH agent to avoid retyping passwords
ssh-agent bash
ssh-add ~/.ssh/id_rsa
Manage Your Inventory
/etc/ansible/hosts
# 沒有 Group 的 host 192.168.1.50 server1 ansible_host=192.168.1.51 # 將 3 個 host 組成 1 個 Group [webservers] www1.example.com www2.example.com:2222 www3.example.com ansible_ssh_host=192.168.1.50 ansible_ssh_port=5555 # 用 [1:3] / [a:c] 去定義 3 架機 [ftpservers] ftp[1:3].example.com [databases] db-[a:c].example.com [apis] api1.example.com ansible_connection=ssh ansible_ssh_user=pro_user api2.example.com ansible_connection=ssh ansible_ssh_user=pro_user
設定
- ansible_host
- ansible_connection
- ansible_ssh_user
Other Host Variables:
- ansible_ssh_private_key_file=/home/example/.ssh/aws.pem
- ansible_ssh_pass # --ask-pass (-k)
- ansible_sudo_pass # --ask-sudo-pass
Config File 的載入次序
* FIRST Win
- ANSIBLE_CONFIG env variable / $ANSIBLE_CONFIG
- ./ansible.cfg
- ~/.ansible.cfg
- /etc/ansible/ansible.cfg
功具: ansible-config
View ansible configuration
# Displays the current config file
ansible-config view
[defaults] host_key_checking = False
# list all current configs
# reading lib/constants.py and shows env and config file setting names
ansible-config list
Testing Example
Ping all your nodes
ansible all -m ping
lamp | success >> { "changed": false, "ping": "pong" } mailscanner | FAILED => Authentication failure.
Testing
# Now run a live command on all of your nodes:
ansible all -a "/bin/echo hello"
加速 ansible
1) 停用 Host Key Checking:
# reasonably slow
~/.ansible.cfg
[defaults]
host_key_checking = False
Ad-Hoc Commands
-a MODULE_ARGS
-u REMOTE_USER
-k, --ask-pass # must install the sshpass program (apt-get install sshpass)
-K, --ask-sudo-pass # If using sudo features and when sudo requires a password
-f FORKS
-s, --sudo # to access sudo mode
-T TIMEOUT
-t TREE, --tree=TREE
--list-hosts # outputs a list of matching hosts; does not execute anything else
-i INVENTORY, # (default=/etc/ansible/hosts)
-l SUBSET, --limit=SUBSET # further limit selected hosts to an additional pattern
Example:
Running from another user account
ansible atlanta -f 10 -a "/usr/bin/foo" -u username
Simultaneous processes
ansible atlanta -f 10 -a "/usr/bin/foo"
* 10 simultaneous processes to use (Default: 5)
shell module
ansible raleigh -m shell -a 'echo $TERM'
* If we want to execute a module using a shell
yum module
# 在 foo.example.com 上以 yum 安裝 httpd
* state=present|latest|absent
absent => remove
present => install
latest => upgrade
ansible foo.example.com -m yum -a "name=httpd state=present"
File Transfer:(file module)
ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"
ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"
File Permission (file module)
# Grant user Joe read access to a file
name=/etc/foo.conf entity=joe etype=user permissions="r" state=present
lineinfile
\s # it matches any whitespace character, this is equivalent to the set [ \t\n\r\f\v]
# backup=yes: main.cf.2015-07-17@03:32~
# state=present: 當不中 "regexp"時, 亦會加這行. 這行係加在檔尾的
# state=absent: remove 中 regexp 的那行
# create: the file will be created if it does not already exist
i.e.
*** Disable selinux
- lineinfile: dest=/etc/selinux/config regexp=^SELINUX=enforcing line=SELINUX=disabled
- lineinfile: dest=/etc/sudoers state=absent regexp="^%wheel"
- lineinfile: dest=/etc/hosts regexp='^127\.0\.0\.1' line='127.0.0.1 localhost'
# Add a line to a file if it does not exist, without passing regexp
- lineinfile: dest=/tmp/testfile line="192.168.1.99 foo.lab.net foo"
- lineinfile: dest=/etc/postfix/main.cf regexp='myhostname' replace='myhostname={{ droplet }}' backup=yes
replace
# Default: backup=no
# regexp: Uses Python regular expressions (https://docs.python.org/2/library/re.html)
# replace: backreferences(\1 , \2) that will get expanded with the regexp capture groups if the regexp matches
- replace: dest=/etc/hosts regexp='(\s+)old\.host\.name(\s+.*)?$' replace='\1new.host.name\2' backup=yes
Replace String
# backup=no
replace: dest=/etc/hosts regexp='(\s+)old\.host\.name(\s+.*)?$' replace='\1new.host.name\2' backup=yes
Managing Services:
started/stopped
* started/stopped are idempotent actions that will not run commands unless necessary
# restart service
ansible webservers -m service -a "name=httpd state=restarted"
* restarted will always bounce the service.
# enable service
ansible webservers -m service -a "name=httpd enabled=yes"
Gathering Facts:
ansible all -m setup
Users and Groups:
* uid
* comment
* append=no # only add groups
* home
* createhome=yes
* group="" # Optionally sets the user's primary group
* groups="" # Puts the user in this comma-delimited list of groups
* state=present # taking action if the state is different from what is stated.
* system=no
* shell=""
# Remove the user 'tester'
- user: name=tester state=absent remove=yes
ansible all -m user -a "name=foo password=<crypted password here>"
建立 crypted password:
# generate crypted passwords for the user module
# -m, --method=TYPE
# mkpasswd -m help
mkpasswd --method=sha-512
Selinux
當 guest 有用 selinux 時, 那一定要安裝 "libselinux-python" package
# 在 guest
yum install libselinux-python
# ansible
# install libselinux-python - name: install libselinux-python yum: name=libselinux-python state=present - lineinfile: dest=/etc/selinux/config regexp=^SELINUX=enforcing line=SELINUX=disabled - command: setenforce 0 args: removes: /selinux/enforce
sudo
ansible all -m ping -u bruce --sudo
在用 --sudo (-s) 時, 會有以下 default value
- sudo_user=root
- sudo_exe=sudo
- sudo_flags=-H
# The default is "-H" which preserves the environment of the original user.
Debug
ansible -vvv sshgw -m ping -s -K
<192.168.123.13> ESTABLISH CONNECTION FOR USER: ansible <192.168.123.13> EXEC ['sshpass', '-d6', 'ssh', '-tt', '-q', '-o', .... "/bin/sh -c 'mkdir -p $HOME/.ansible/tmp/ansible-1389164997.16-134005641234727 ...'"] <192.168.123.13> REMOTE_MODULE ping <192.168.123.13> PUT /tmp/tmpY4V4M2 TO /home/ansible/.ansible/tmp/ansible-1388998234.57-103349027143503/ping <192.168.123.13> PUT /tmp/tmpTKCHln TO /home/ansible/.ansible/tmp/ansible-1388998234.57-103349027143503/arguments <192.168.123.13> EXEC ['sshpass', '-d6', 'ssh', '-tt', '-q', '-o', ... -u root /bin/sh -c ... /usr/bin/python ...
那 user 要可以 sudo /bin/sh 及 master 要安 sshpass
# 當沒有 NOPASSWD: 時, 那要用 -K Host_Alias OFFICE=sshgw ansible OFFICE=(root) NOPASSWD: /bin/sh
Configuration File
~/.ansible.cfg
#ask_pass=True #ask_sudo_pass=True forks=5 hostfile = /etc/ansible/hosts host_key_checking=True log_path=/var/log/ansible.log action_plugins = /usr/share/ansible_plugins/action_plugins # default SSH timeout to use on connection attempts timeout = 10 accelerate_port = 5099
Modules
http://www.ansibleworks.com/docs/modules.html
* All modules technically return JSON format data
add_host
加 Client:
# add host to group 'just_created' with variable foo=42
- add_host: name={{ ip_from_ec2 }} groups=just_created foo=42
# add a host with a non-standard port local to your machines
- add_host: name={{ new_ip }}:{{ new_port }}
# add a host alias that we reach through a tunnel
- add_host: hostname={{ new_ip }}
ansible_ssh_host={{ inventory_hostname }}
ansible_ssh_port={{ new_port }}
firewalld
firewalld: port=8081/tcp permanent=true state=disabled
Run cmd
command:
* variables like $HOME and operations like "<", ">", "|", and "&" will not work in command.
usage:
ansible lamp -m command -a 'iptables -nL'
args:
# Run the command if the specified file does not exist.(creates)
- command: /usr/bin/make_database.sh arg1 arg2 creates=/path/to/database
# You can also use the 'args' form to provide the options.
# This command will change the working directory to somedir/ and
# will only run when /path/to/database doesn't exist.
- command: /usr/bin/make_database.sh arg1 arg2
args:
chdir: somedir/
creates: /path/to/database
* creates # a filename or glob pattern, when it already exists, this step will not be run.
* chdir # cd into this directory before running the command
* removes # when it does not exist, this step will not be run
Error Handling In Playbooks
- name: this will not be counted as a failure
command: /bin/false
ignore_errors: yes
shell module:
command through a shell (/bin/sh)
i.e.
<1>
- shell: somescript.sh >> somelog.txt
<2>
- shell: somescript.sh >> somelog.txt args: chdir: somedir/ creates: somelog.txt
args:
removes: filename # 當 filename 不在時, 不會 run
creates: filename # 當 filename 存在時, 不會 run
chdir: /selinux # cd into this directory
executable: /bin/bash # 轉用另一個 shell
Debug
Step1: ping
ansible sshgw -m ping
sshgw | success >> { "changed": false, "ping": "pong" }
Step2: -vvv
-vvv
ansible -vvv sshgw -m ping -s
Step3: debug module
Print statements during execution
- debug: msg="System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"
TASK: [debug msg="Install useful package"] ************************************ ok: [x.y.z] => { "msg": "Install useful package" }
Speedup Ansible
在 Ansible > 1.5 就可以不用 "accelerated mode"了, 因為有 SSH pipelining (2-6x faster) 了
SSH pipelining 原理
* reduces the number of SSH operations required to execute a module
* when using "sudo:", you must first disable "requiretty" in /etc/sudoers
SSH pipelining Setting
# Default: False pipelining=True
P.S.
在 Enterprise Linux 6 上的 OpenSSH 是用不到的.
所以 Ansible 會用 pure-python SSH client (paramiko) 去 connect guest
Accelerated Mode 原理
ControlPersist (10x faster)
launching a temporary daemon(port 5099) over SSH
* uses SSH for initial secure key exchange( AES key)
* daemon will accept connections for 30 minutes)
Guest 要 requirement
* python 2.5
* python-keyczar (centos6 default 沒有這個 package)
Accelerated Mode Setting
# controller
# port to use for accelerated mode accelerate_port = 5099 # A keepalive packet is sent back to the controller every 15 seconds accelerate_timeout = 30
*.yml
- hosts: all accelerate: true timeout: 300 minutes: 30 # accelerate_port: 5099 ..........
sudo 的限制
You must remove requiretty from your sudoers options.
the NOPASSWD option is required for sudo’ed commands.
Playbooks
/usr/bin/ansible-playbook
modules: can be executed directly on remote hosts or through Playbooks
Each module supports taking arguments. Nearly all modules take key=value arguments
Version
ansible-playbook --version
ansible-playbook 1.5.4
Ops
-i INVENTORY
--list-hosts
--list-tasks
--private-key=PRIVATE_KEY_FILE
-s, --sudo run operations with sudo (nopasswd)
-K, --ask-sudo-pass ask for sudo password
--step one-step-at-a-time: confirm each task before running
Run Script(yml):
ansible-playbook --syntax-check server.yml
ansible-playbook server.yml
當 yml 沒有指定 hosts 時
--- - hosts: x.y.z
ansible-playbook -i hosts server.yml
YAML Syntax
# indicates the start of a document
---
# Members of a list, must same indentation level
- Apple
- Orange
- Strawberry
# A dictionary, key: value
name: Example Developer job: Developer skill: Elite
# 相等
{name: Example Developer, job: Developer, skill: Elite}
Example1: 建立一個 smtp server
file: hosts
[smtpserver] gw1.local gw2.local
file: server.yml
--- # separated by colons - hosts: w.x.y.z # Variables, call by {{ droplet }} vars: droplet: w.x.y.z # running things as another user remote_user: root sudo: yes tasks: # create user - name: create user user: name=smtpuser password="xxxx" # iptables - name: config iptables copy: src=/root/config_file/iptables dest=/etc/sysconfig/iptables - name: config iptables service: name=iptables state=restarted # dovecot - name: install dovecot yum: name=dovecot state=present # If the action line is getting too long for comfort you can # break it on a space and indent any continuation lines - name: copy config file copy: src=/root/config_file/10-master.conf dest=/etc/dovecot/conf.d/10-master.conf - name: restart dovecot service: name=dovecot state=restarted - name: auto start dovecot service: name=dovecot enabled=yes # postfix - copy: src=/root/config_file/main.cf dest=/etc/postfix - lineinfile: dest=/etc/postfix/main.cf regexp='^myhostname' line='myhostname = {{ droplet }}' backup=yes - copy: src=/root/config_file/header_checks.cf dest=/etc/postfix - service: name=postfix state=restarted - service: name=postfix enabled=yes
Doc
- ansible-doc command
- https://docs.ansible.com/