Advanced
Ansible 是一个易读非常好的自动化运维开发语言以及技术体系,它基于 Python,提供数千个管理基础设施的模块,用户无需深厚的技术背景,也可以完成服务器部署、网络配置、云资源管理等相关的工作。它自动化能力,可大大降低技术复杂性,帮助企业减少重复劳动,让聪明的人专注 于核心业务。
Install Ansible
Install
Control Node
安装之前,需了解是否具备安装条件:
操作系统要求:Red Hat, Debian, CentOS, macOS, any of the BSDs等,不支持Windows Python要求:Python 2 (version 2.7) or Python 3 (versions 3.5 and higher) installed
条件具备之后,只需要一条命令即可安装:
pip install ansible
或
yum install ansible
或
apt install ansible
Managed node
- 采用 SSH(默认为SFTP)与 Control Node 通信,如果SSH不可用,通过修改 ansible.cfg 更改通信协议
- Python支持:Python 2 (version 2.6 or later) or Python 3 (version 3.5 or later).
配置项
Ansible 作为一个自动化解决方案包,与其他成熟的软件系统一样,支持自定义各种环境参数。
配置项不仅可通过 ansible.cfg 文件进行定义,大多数参数也可通过 ANSIBLE_ 开头的环境变量进行配置:
export ANSIBLE_SUDO_USER=root
运行 cat /etc/ansible/ansible.cfg
文件,便可以查看几乎所有的配置项。
# config file for ansible -- https://ansible.com/
# ===============================================
# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first
[defaults]
# some basic default values...
#inventory = /etc/ansible/hosts
#library = /usr/share/my_modules/
#module_utils = /usr/share/my_module_utils/
#remote_tmp = ~/.ansible/tmp
#local_tmp = ~/.ansible/tmp
#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
#forks = 5
#poll_interval = 15
#sudo_user = root
#ask_sudo_pass = True
#ask_pass = True
#transport = smart
#remote_port = 22
#module_lang = C
#module_set_locale = False
# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit
# This only affects the gathering done by a play's gather_facts directive,
# by default gathering retrieves all facts subsets
# all - gather all subsets
# network - gather min and network facts
# hardware - gather hardware facts (longest facts to retrieve)
# virtual - gather min and virtual facts
# facter - import facts from facter
# ohai - import facts from ohai
# You can combine them using comma (ex: network,virtual)
# You can negate them using ! (ex: !hardware,!facter,!ohai)
# A minimal set of facts is always gathered.
#gather_subset = all
# some hardware related facts are collected
# with a maximum timeout of 10 seconds. This
# option lets you increase or decrease that
# timeout to something more suitable for the
# environment.
# gather_timeout = 10
# Ansible facts are available inside the ansible_facts.* dictionary
# namespace. This setting maintains the behaviour which was the default prior
# to 2.5, duplicating these variables into the main namespace, each with a
# prefix of 'ansible_'.
# This variable is set to True by default for backwards compatibility. It
# will be changed to a default of 'False' in a future release.
# ansible_facts.
# inject_facts_as_vars = True
# additional paths to search for roles in, colon separated
#roles_path = /etc/ansible/roles
# uncomment this to disable SSH key host checking
#host_key_checking = False
# change the default callback, you can only have one 'stdout' type enabled at a time.
#stdout_callback = skippy
## Ansible ships with some plugins that require whitelisting,
## this is done to avoid running all of a type by default.
## These setting lists those that you want enabled for your system.
## Custom plugins should not need this unless plugin author specifies it.
# enable callback plugins, they can output to stdout but cannot be 'stdout' type.
#callback_whitelist = timer, mail
# Determine whether includes in tasks and handlers are "static" by
# default. As of 2.0, includes are dynamic by default. Setting these
# values to True will make includes behave more like they did in the
# 1.x versions.
#task_includes_static = False
#handler_includes_static = False
# Controls if a missing handler for a notification event is an error or a warning
#error_on_missing_handler = True
# change this for alternative sudo implementations
#sudo_exe = sudo
# What flags to pass to sudo
# WARNING: leaving out the defaults might create unexpected behaviours
#sudo_flags = -H -S -n
# SSH timeout
#timeout = 10
# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
#remote_user = root
# logging is off by default unless this path is defined
# if so defined, consider logrotate
#log_path = /var/log/ansible.log
# default module name for /usr/bin/ansible
#module_name = command
# use this shell for commands executed under sudo
# you may need to change this to bin/bash in rare instances
# if sudo is constrained
#executable = /bin/sh
# if inventory variables overlap, does the higher precedence one win
# or are hash values merged together? The default is 'replace' but
# this can also be set to 'merge'.
#hash_behaviour = replace
# by default, variables from roles will be visible in the global variable
# scope. To prevent this, the following option can be enabled, and only
# tasks and handlers within the role will see the variables there
#private_role_vars = yes
# list any Jinja2 extensions to enable here:
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
# if set, always use this private key file for authentication, same as
# if passing --private-key to ansible or ansible-playbook
#private_key_file = /path/to/file
# If set, configures the path to the Vault password file as an alternative to
# specifying --vault-password-file on the command line.
#vault_password_file = /path/to/vault_password_file
# format of string {{ ansible_managed }} available within Jinja2
# templates indicates to users editing templates files will be replaced.
# replacing {file}, {host} and {uid} and strftime codes with proper values.
#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
# {file}, {host}, {uid}, and the timestamp can all interfere with idempotence
# in some situations so the default is a static string:
#ansible_managed = Ansible managed
# by default, ansible-playbook will display "Skipping [host]" if it determines a task
# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
# messages. NOTE: the task header will still be shown regardless of whether or not the
# task is skipped.
#display_skipped_hosts = True
# by default, if a task in a playbook does not include a name: field then
# ansible-playbook will construct a header that includes the task's action but
# not the task's args. This is a security feature because ansible cannot know
# if the *module* considers an argument to be no_log at the time that the
# header is printed. If your environment doesn't have a problem securing
# stdout from ansible-playbook (or you have manually specified no_log in your
# playbook on all of the tasks where you have secret information) then you can
# safely set this to True to get more informative messages.
#display_args_to_stdout = False
# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False
# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
#system_warnings = True
# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
#deprecation_warnings = True
# (as of 1.8), Ansible can optionally warn when usage of the shell and
# command module appear to be simplified by using a default Ansible module
# instead. These warnings can be silenced by adjusting the following
# setting or adding warn=yes or warn=no to the end of the command line
# parameter string. This will for example suggest using the git module
# instead of shelling out to the git command.
# command_warnings = False
# set plugin path directories here, separate with colons
#action_plugins = /usr/share/ansible/plugins/action
#become_plugins = /usr/share/ansible/plugins/become
#cache_plugins = /usr/share/ansible/plugins/cache
#callback_plugins = /usr/share/ansible/plugins/callback
#connection_plugins = /usr/share/ansible/plugins/connection
#lookup_plugins = /usr/share/ansible/plugins/lookup
#inventory_plugins = /usr/share/ansible/plugins/inventory
#vars_plugins = /usr/share/ansible/plugins/vars
#filter_plugins = /usr/share/ansible/plugins/filter
#test_plugins = /usr/share/ansible/plugins/test
#terminal_plugins = /usr/share/ansible/plugins/terminal
#strategy_plugins = /usr/share/ansible/plugins/strategy
# by default, ansible will use the 'linear' strategy but you may want to try
# another one
#strategy = free
# by default callbacks are not loaded for /bin/ansible, enable this if you
# want, for example, a notification or logging callback to also apply to
# /bin/ansible runs
#bin_ansible_callbacks = False
# don't like cows? that's unfortunate.
# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
#nocows = 1
# set which cowsay stencil you'd like to use by default. When set to 'random',
# a random stencil will be selected for each task. The selection will be filtered
# against the `cow_whitelist` option below.
#cow_selection = default
#cow_selection = random
# when using the 'random' option for cowsay, stencils will be restricted to this list.
# it should be formatted as a comma-separated list with no spaces between names.
# NOTE: line continuations here are for formatting purposes only, as the INI parser
# in python does not support them.
#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
# hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www
# don't like colors either?
# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
#nocolor = 1
# if set to a persistent type (not 'memory', for example 'redis') fact values
# from previous runs in Ansible will be stored. This may be useful when
# wanting to use, for example, IP information from one group of servers
# without having to talk to them in the same playbook run to get their
# current IP information.
#fact_caching = memory
#This option tells Ansible where to cache facts. The value is plugin dependent.
#For the jsonfile plugin, it should be a path to a local directory.
#For the redis plugin, the value is a host:port:database triplet: fact_caching_connection = localhost:6379:0
#fact_caching_connection=/tmp
# retry files
# When a playbook fails a .retry file can be created that will be placed in ~/
# You can enable this feature by setting retry_files_enabled to True
# and you can change the location of the files by setting retry_files_save_path
#retry_files_enabled = False
#retry_files_save_path = ~/.ansible-retry
# squash actions
# Ansible can optimise actions that call modules with list parameters
# when looping. Instead of calling the module once per with_ item, the
# module is called once with all items at once. Currently this only works
# under limited circumstances, and only with parameters named 'name'.
#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper
# prevents logging of task data, off by default
#no_log = False
# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
#no_target_syslog = False
# controls whether Ansible will raise an error or warning if a task has no
# choice but to create world readable temporary files to execute a module on
# the remote machine. This option is False by default for security. Users may
# turn this on to have behaviour more like Ansible prior to 2.1.x. See
# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user
# for more secure ways to fix this than enabling this option.
#allow_world_readable_tmpfiles = False
# controls the compression level of variables sent to
# worker processes. At the default of 0, no compression
# is used. This value must be an integer from 0 to 9.
#var_compression_level = 9
# controls what compression method is used for new-style ansible modules when
# they are sent to the remote system. The compression types depend on having
# support compiled into both the controller's python and the client's python.
# The names should match with the python Zipfile compression types:
# * ZIP_STORED (no compression. available everywhere)
# * ZIP_DEFLATED (uses zlib, the default)
# These values may be set per host via the ansible_module_compression inventory
# variable
#module_compression = 'ZIP_DEFLATED'
# This controls the cutoff point (in bytes) on --diff for files
# set to 0 for unlimited (RAM may suffer!).
#max_diff_size = 1048576
# This controls how ansible handles multiple --tags and --skip-tags arguments
# on the CLI. If this is True then multiple arguments are merged together. If
# it is False, then the last specified argument is used and the others are ignored.
# This option will be removed in 2.8.
#merge_multiple_cli_flags = True
# Controls showing custom stats at the end, off by default
#show_custom_stats = True
# Controls which files to ignore when using a directory as inventory with
# possibly multiple sources (both static and dynamic)
#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo
# This family of modules use an alternative execution path optimized for network appliances
# only update this setting if you know how this works, otherwise it can break module execution
#network_group_modules=eos, nxos, ios, iosxr, junos, vyos
# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as
# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain
# jinja2 templating language which will be run through the templating engine.
# ENABLING THIS COULD BE A SECURITY RISK
#allow_unsafe_lookups = False
# set default errors for all plays
#any_errors_fatal = False
[inventory]
# enable inventory plugins, default: 'host_list', 'script', 'auto', 'yaml', 'ini', 'toml'
#enable_plugins = host_list, virtualbox, yaml, constructed
# ignore these extensions when parsing a directory as inventory source
#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry
# ignore files matching these patterns when parsing a directory as inventory source
#ignore_patterns=
# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise.
#unparsed_is_failed=False
[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
[paramiko_connection]
# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently of the
# host key checking setting above.
#record_host_keys=False
# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
# line to disable this behaviour.
#pty=False
# paramiko will default to looking for SSH keys initially when trying to
# authenticate to remote devices. This is a problem for some network devices
# that close the connection after a key failure. Uncomment this line to
# disable the Paramiko look for keys function
#look_for_keys = False
# When using persistent connections with Paramiko, the connection runs in a
# background process. If the host doesn't already have a valid SSH key, by
# default Ansible will prompt to add the host key. This will cause connections
# running in background processes to fail. Uncomment this line to have
# Paramiko automatically add host keys.
#host_key_auto_add = True
[ssh_connection]
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it, -C controls compression use
#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
# The base directory for the ControlPath sockets.
# This is the "%(directory)s" in the control_path option
#
# Example:
# control_path_dir = /tmp/.ansible/cp
#control_path_dir = ~/.ansible/cp
# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
# port and username (empty string in the config). The hash mitigates a common problem users
# found with long hostnames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
# In those cases, a "too long for Unix domain socket" ssh error would occur.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path =
# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False
# Control the mechanism for transferring files (old)
# * smart = try sftp and then try scp [default]
# * True = use scp only
# * False = use sftp only
#scp_if_ssh = smart
# Control the mechanism for transferring files (new)
# If set, this will override the scp_if_ssh option
# * sftp = use sftp to transfer files
# * scp = use scp to transfer files
# * piped = use 'dd' over SSH to transfer files
# * smart = try sftp, scp, and piped, in that order [default]
#transfer_method = smart
# if False, sftp will not use batch mode to transfer files. This may cause some
# types of file transfer failures impossible to catch however, and should
# only be disabled if your sftp version has problems with batch mode
#sftp_batch_mode = False
# The -tt argument is passed to ssh when pipelining is not enabled because sudo
# requires a tty by default.
#usetty = True
# Number of times to retry an SSH connection to a host, in case of UNREACHABLE.
# For each retry attempt, there is an exponential backoff,
# so after the first attempt there is 1s wait, then 2s, 4s etc. up to 30s (max).
#retries = 3
[persistent_connection]
# Configures the persistent connection timeout value in seconds. This value is
# how long the persistent connection will remain idle before it is destroyed.
# If the connection doesn't receive a request before the timeout value
# expires, the connection is shutdown. The default value is 30 seconds.
#connect_timeout = 30
# The command timeout value defines the amount of time to wait for a command
# or RPC call before timing out. The value for the command timeout must
# be less than the value of the persistent connection idle timeout (connect_timeout)
# The default value is 30 second.
#command_timeout = 30
[accelerate]
#accelerate_port = 5099
#accelerate_timeout = 30
#accelerate_connect_timeout = 5.0
# The daemon timeout is measured in minutes. This time is measured
# from the last activity to the accelerate daemon.
#accelerate_daemon_timeout = 30
# If set to yes, accelerate_multi_key will allow multiple
# private keys to be uploaded to it, though each user must
# have access to the system via SSH to add a new key. The default
# is "no".
#accelerate_multi_key = yes
[selinux]
# file systems that require special treatment when dealing with security context
# the default behaviour that copies the existing context or uses the user default
# needs to be changed to use the file system dependent context.
#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p,vfat
# Set this to yes to allow libvirt_lxc connections to work without SELinux.
#libvirt_lxc_noseclabel = yes
[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan
[diff]
# Always print diff when running ( same as always running with -D/--diff )
# always = no
# Set how many context lines to show in diff
# context = 3
原理
本节尽量全面的介绍 Ansible 的原理,将常用的知识点汇聚在一起,以帮助用户在实践中能够充分准确的用好 Ansible。
Why Ansible?
Ansible 诞生于 2012 年,目前是 RedHat 旗下的产品,是 Github 上受欢迎的自动化运维工具。
从事过运维相关工作的小伙伴,对 Shell 应该是“有爱有恨”的。一方面我们爱它的无所不能,另外一方面我们恨它的语法晦涩难懂,且难以模块化利用。
Ansible 的作者兼创始人Michael DeHaan 曾经供职于Puppet Labs、RedHat、Michael,在配置管理和架构设计方面有丰富的经验。其在RedHat任职期间主要开发了Cobble,经历了各种系统简化、自动化基础架构操作的失败和痛苦,在尝试了Puppet、Chef、Cfengine、Capistrano、Fabric、Function、Plain SSH等各式工具后,决定自己打造一款能结合众多工具优点的自动化工具,Ansible由此诞生。
Ansible 为何如此火爆?首先是当前云计算的大规模应用所驱动,然后就是它的核心特点决定的:
- 无需代理也可以控制多台受控机并行管理
- 采用描述性的语言来使用,非常易读、易编写
- 兼容 Python 的语法
- 安装过程简单,学习曲线很平坦
一种解释排版型语言,易读性极强:
- block:
- name: Create credentials Folder
file:
path: /credentials
state: directory
- name: Write Databases Password
template:
src: password.txt.jinja2
dest: /credentials/password.txt
mode: 644
一句话总结:简单易用,功能强大,用途广泛。
应用领域
Ansible 究竟有什么用呢?
我们从运维工程师或开发人员日常工作中最常见的部署来说:为了部署一个应用,我们需要提前安装 Web 服务、应用程序服务、消 息队列、缓存系统、数据库、负载均衡等基础软件,与此同时我们还需要通过手工配置,将各个组件连接起来,让它们发挥功效。甚至,站在软件维护的角度,还需要部署日志系统、监控系统、数据库分析系统、数据库审计等维护工具。如果使用手工来完成这些任务,从购买一台云服务器,再到 SSH 登录直至完成所有任务,需要数百个步骤,而且每一个步骤不能出错。
如果有一个自动化程序能够完成上述任务,那一定会收到用户的热烈欢迎。幸运的,Ansible 就是这样的工具,它比 Shell 简单,它可以轻轻松松处理:
配置管理
配置管理即部署部署应用程序环境,包括对 Linux 和 Windows 上进行各种程序安装,系统操作,Web服务管理、应用服务管理、数据库配置等
管理云资源
Ansible 提供包括 AWS,Azure等数十个云资源的创建、操作。即无需用户了解每个云平台的 API,也可以轻松管理云资源。
监控告警
Ansible 支持对 Grafana、Nagios、Zabbix、Datalog 等系统监控软件进行直接操作。
消息发送
Ansible 提供了大量的 Notification modules 用于帮助应用程序发送消息,支持:邮件、Mattermost、RabbitMQ、syslogger等常见的应用。
硬件管理
Ansible 可以对网络设备、存储设备进行直接操作,所支持网络品牌包括 Cisco、Aruba、Check Point等多达几十个,支持的存储品牌包括:IBM、Netapp、EMC等
一句话总结:Ansible 可以完成 DevOps 全过程所需的自动化配置工作:
架构
技术架构
Ansible 技术架构
我们知道,让合适的程序以合适的方式在合适的主机上高效率的运行,是技术架构的出发点。
Ansible 的技术架构同样遵循这个原理,下面我们从如下三个方面阐述 Ansible 的技术架构:
- 主机:运行 Ansible 程序的服务器,分为主控端和受控端两种类型的主机,主机的在架构中的表现形式为 Host Inventory(主机清单)
- 程序:官方内置的软件包被成为为模块和插件,用户自己编写的程序被称之为 Playbook (多个 Playbook 以某种形式组合在一起被成为 roles)
- 连接:主控端和受控端之间的连接与控制,一般采用 SSH 连接,支持认证
为什么主机分为主控端(Control Node)受控端(Managed Node)?
主要是 Ansible 的用途决定的,由于 Ansible 需要考虑同一个程序在同一个时间部署到多个主机上,故在设计上引入的主控端这种角色,用于以集中式的方式向多个受控主机发布配置任务。
如果不考虑这种场景,Ansible 也支持在本机上运行程序,即主控模式并不是必须的。
Ansible 工作原理
用户登录到 Ansible 所在的服务器,便可以使用命令行运行 Ansible 程序。
这里需注意的是前面多次提到过的 Inventory 的概念,Ansible 程序在运行的时候,一定提前准备 Inventory 文件,如果缺少这个文件,Ansible 就只能在本机上运行。
下面就是一个在本机运行 ping 模块的程序,localhost
参数告之 Ansible 目标主机是本机
$ansible localhost -m ping
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
如果没有准备好 Inventory,而选用所有主机 (all
参数),系统就会报错
$ansible all -m ping
43.128.22.14 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
Ansible 代码执行过程
生态架构
Ansible 是一个卓越的技术产品,也是一个成功的商业软件,它的成功与其商业生态布局有密切关系。
生态架构的技术基础:
- 模块
- 插件
- Python 语法语法兼容性
- Roles
生态商业整合:
-
Ansible Galaxy:开发者共享的代码库(role库)
-
Ansible Collection:开发者共享的应用程序库(完整的 Ansible 应用程序)
-
Ansible Tower:企业级可视化 Ansible 管理工具,支持 API。
-
Ansible Automation Platform:由 RedHat 托管的 Ansible 项目,包含 Ansible Tower 以及自动化资源、自动化分析等企业级功能。
主机清单
Ansible 经常用于管理大量的服务器,如果企业有数千台服务器,那么一定需要对这些服务器进行分门别类。与此同时,服务器的用户账号信息等也各不相同。
Ansible Inventory (主机清单) 就是为了灵活的管理主机信息的技术标准。
格式
静态文件是相对于动态文件来说,Ansible 的静态主机文件是指提前准备好并有完整内容的主机清单
默认的文件是:/etc/ansible/hosts
下面先展示一个包含丰富信息的 hosts 文件示例:
[webservers]
86.21.14.177
web1.websoft9.com
we2.websoft9.com
192.165.2.50
[database]
86.21.14.172
host1.websoft9.com
host2.websoft9.com
192.165.2.51
[monitor]
86.21.14.173 ansible_user=root ansible_ssh_pass=123456 proxy=proxy.websoft9.com
86.21.14.174 ansible_user=root ansible_ssh_private_key_file=/root/mykey
host1.websoft9.com
www[01:50].example.com
[monitor:vars]
proxy=proxy.websoft9.com
从以上文件可得知 Inventory 的特点:
- 可分组(分组还可以嵌套)
- 可存放用户账号和密码
- 可以为主机和分组指定变量
- 同一个主机可以归属多个组
- 主机名称支持范表达式 www[01:50] 表示:www01,www02...
另外,Ansible 还支持多个 inventory 文件。只需在 ansible.cfg 文件中让 inventory 配置项指向一个目录即可。不过,多个 inventory 文件与单个并没有区别,毕竟可以单个文件由于支持分组,已经完全可以满足各种分类场景了。
参数
ansible_ssh_host | 远程主机 |
---|---|
ansible_ssh_port | 指定原创主机ssh端口 |
ansible_ssh_root | ssh连接远程主机的用户 |
ansible_ssh_pass | ssh连接远程主机的密码 |
ansible_sudo_pass | sudo密码 |
ansible_connection | 指定连接类型:local、ssh、paramiko |
ansible_ssh_private_key_file | ssh连接使用的私钥 |
ansible_shell_type | 指定连接端的shell类型,sh、csh、fish |
ansible_python_interpreter | 指定远程主机使用的python路径 |
变量
Ansible 支持向 inventory 文件中添加变量,变量适用范围可以是单个主机,也可以是分组。
清单插件
Playbook
本章我们讲解 Ansible 最核心的组件 playbook。
playbook 是什么?
如果把 Ansible 比作开发语言,那么 playbook 就是一个程序文件。程序代码在程序文件中按顺序执行,最终完成所需处理的任务。
为什么称之 playbook 而不是程序文件呢?
playbook 翻译过来就是剧本的意思。如果你是文艺爱好者,可能阅读过电影/舞台剧的剧本(下图)
剧本是个导演组织演员、道具、拍摄等资源一种编排叙述性的文字说明。
Ansible 的程序代码与电影具备具有类似性,故被作者命名为 playbook
格式
playbook 有着规范化 的格式,下面就是一个典型的可以被直接运行的 playbook 范例:
- hosts: localhost
remote_user:root
vars:
https_port: 443
tasks:
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
- name: Create file2
ansible.builtin.file:
state: touch
path: /tmp/file2
- name: Create file3
ansible.builtin.file:
state: touch
path: /tmp/file3
playbook 包含几个关键字,每个关键字的含义如下:
- hosts: 主机IP 或 主机组名 或 all
- remote_user: 以某个用户身份运行,通常设置为 root
- vars: 变量
图:playbook, paly, task 三者关系
模块
模块 Ansible 官方已经编写好了的软件包,是 Ansible 的功能核心。
正是由于 Ansible 提供了大量的模块,才大大简化运维工作,可以让运维人员只需要少量的 Shell 知识便可以完成复杂的运维任务。
有两种使用模块的方式:
在 Playbook 中使用模块:
- name: restart webserver
service:
name: httpd
state: restarted
在 命令行中使用模块:
ansible localhost -m service -a "name=httpd state=started"
查看所有模块
插件
插件是对模块功能的一种补充。
变量
循环
条件
Ansible 中使用 when
作为条件判断的关键词,条件判断注意事项:
- 变量名不需要双大括号
“{{}}”
- 运算符兼容 jinja2 格式:
==, !=, >, >=
- 支持逻辑运算符:
and, or, not
- 支持变量的定义判断:
defined, undefined, none
详情参考:Ansible条件判断详解
过滤
模板
查询
交互
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
Role
Ansible role 是用于规范化管理 playbook 程序文件以及附带的其他文件的一种软件包组织机制。
结构
运行 ansible-galaxy init myrole
可以创建名称为 myrole 的 role。
使用 tree
命令查看包的结构:
$ tree myrole
myrole
|-- defaults
| `-- main.yml
|-- files
|-- handlers
| `-- main.yml
|-- meta
| `-- main.yml
|-- README.md
|-- tasks
| `-- main.yml
|-- templates
|-- tests
| |-- inventory
| `-- test.yml
`-- vars
`-- main.yml
8 directories, 8 files
- tasks/main.yml 存放项目的主文件
- templates 存放 jinjia2 模板文件
- files 存放程序所需的文件
- defaults/vars 存放变量文件,vars 下的变量优先级更高
- tests 存放本 role 入口 playbook
- handlers 存放 task 的 handler
- meta 存放 role 的元数据,包括系统兼容性、role 依赖等
运行
以上创建的 role 模板也可以使用 ansible-playbook 运行。
ansible-playbook myrole/tests/test.yml
这种运行方式是很多初学者可能会忽视的
Ansible Galaxy
Ansible Galaxy 是官方提供的帮助用户分享 role 的网站平台。
同时官方也提供了一套 cli 命令:
$ ansible-galaxy role -h
usage: ansible-galaxy role [-h] ROLE_ACTION ...
positional arguments:
ROLE_ACTION
init Initialize new role with the base structure of a role.
remove Delete roles from roles_path.
delete Removes the role from Galaxy. It does not remove or alter the
actual GitHub repository.
list Show the name and version of each role installed in the
roles_path.
search Search the Galaxy database by tags, platforms, author and
multiple keywords.
import Import a role
setup Manage the integration between Galaxy and the given source.
info View more details about a specific role.
install Install role(s) from file(s), URL(s) or Ansible Galaxy
optional arguments:
-h, --help show this help message and exit
Collection
Ansible Collection 是完整的 Ansible 应用,它包含了 role 以及各种其他所需的配置,可以 role 编排后处理更为复杂的任务。
Websoft9 提供了包括 WordPress, GitLab, Odoo, LAMP 等数十个常见应用的开源自动化 Ansible Collection,可免费使用。
Ansible Tower
Ansible Tower 是 Ansible 可视化工具,它由一个上游的开源版本 AWX 可供用户免费使用。
问题解答
Ansible 是否有可视化工具?
有,Ansible Tower 即可视化工具
Ansible 可以管理远程服务器吗?
可以管理远程主机,也可以管理本机
Ansible 是否有系统服务?
没有,Ansible 是一套开发语言工具,主 要提供 CLI 供用户使用
如何成为Ansible程序高手?
Shell命令是根本,夯实基础稳步走;
晦涩理论看一遍,动手实验是正道。
经典教材床头放,官方文档经常看;
闲时看书有收获,勤动笔来总结多。
三人成行有我师,学会提问收获多;
疑难问题要会诊,切莫独钻死胡同。
稳定简约见功底,数据结构来撑腰;
软件没有终结日,长久迭代价值高。
学会驾驭Ansible,用通用的软件方法论去理解Ansible,千万不要被Ansible的技术术语所牵制。
如何成为 Ansible 应用高手?
系统变量 ansible_os_family 支持哪些值?
OS_FAMILY = dict(
RedHat = 'RedHat',
Fedora = 'RedHat',
CentOS = 'RedHat',
Scientific = 'RedHat',
SLC = 'RedHat',
Ascendos = 'RedHat',
CloudLinux = 'RedHat',
PSBM = 'RedHat',
OracleLinux = 'RedHat',
OVS = 'RedHat',
OEL = 'RedHat',
Amazon = 'RedHat',
XenServer = 'RedHat',
Ubuntu = 'Debian',
Debian = 'Debian',
SLES = 'Suse',
SLED = 'Suse',
OpenSuSE = 'Suse',
SuSE = 'Suse',
Gentoo = 'Gentoo',
Archlinux = 'Archlinux',
Mandriva = 'Mandrake',
Mandrake = 'Mandrake',
Solaris = 'Solaris',
Nexenta = 'Solaris',
OmniOS = 'Solaris',
OpenIndiana = 'Solaris',
SmartOS = 'Solaris',
AIX = 'AIX',
Alpine = 'Alpine',
MacOSX = 'Darwin',
FreeBSD = 'FreeBSD',
HPUX = 'HP-UX'
)
Ansible 是否支持动态主机清单?
支持。由于在实际生产场景中,如果清单采用手动维护这些列表将是一个非常繁琐的任务。
Ansible 支持动态生产主机清单,即 ansible.cfg 指向一个生产主机清单的程序,再由程序产生符合格式的清单列表。
Ansible 有没有默认分组?
有。默认有包含文件所有主机的 all 组,同时还有没有归属的 ungrouped 组。
Ansible受控端是否必须提前安装Python?
不是。Ansible的raw模块和script模块不依赖于客户端安装的Python来运行。从技术上讲,您可以使用Ansible使用raw模块安装兼容版本的Python ,然后使用该模块使用其他所有模块。例如,如果需要将Python 2引导到基于RHEL的系统上,则可以按以下方式安装它:
ansible myhost --become -m raw -a "yum install -y python2"
主控端如何安装Ansible最方便?
推荐采用 pip install ansible
Ansible 的应用模块好用吗?例如:Docker, MySQL等
建议弃用,直接使用命令更为稳定可靠,这样可以避免这边模块的版本兼容性问题
Ansible中的变量优先级有哪些?
有高到低:ansible命令带入的变量 > cfg配置文件的变量 > 主项目的var变量 > role中的var变量 > role default 变量
Ansible有全局变量的概念吗?
没有,但我们可以将:ansible命令带入的变量 | cfg配置文件的变量 | 主项目的var变量 视为全局变量。但特别需要注意的是:Ansible项目中即使有同名变量,它们不会共享一个内存区域,而是各自独占内存(区别于Java等语言变量指针的概念)。
Ansible 如何实现模块化?
Ansible Galaxy 就是模块化唯一的方案
Ansible 中的条件判断有哪些可能性?
True, not False, !=none, !="",
Ansbile 中Python Pip apt/yum 总结
- 客户端和服务端 python版本可以不一致
- 升级最新pip版本会导致 pip 命令无法使用 官方解释使用 python3 -m pip install xxx
- apt lock 问题可以在脚本中预处理
Ansbile 客户端和服务端 Python版本是否可以不一致?
可以
pip和pip3共存吗?
可以共存。但建议通过:python3 -m pip install xxx 这样的方式使用Python3下的pip,启用pip3这种表达方式
为什么Ansible中apt升级容易导致 lock?
AWS上非常容易出错,建议在脚本中预处理
Ansible 之PIP模块是否可以 制定Python版本?
可以,参考如下
# Install (Bottle) for Python 3.3 specifically,using the 'pip3.3' executable.
- pip:
name: bottle
executable: pip3.3
dnf 模块现在可以用吗?
现在不建议使用dnf模块
一个Ansible项目中,主入口文件中 vars_files 与 vars 哪个变量优先级高?
vars_files的优先级更高。需要注意的是Ansible的变量是无法覆盖的,即同名变量在内存中都有单独的存储区域,而Ansible只是通过优先级的方式使用。
如何从一个裸机快速运行Ansible项目?
下面以 CentOS 为例列出运行 Ansible 项目的步骤:
yum install ansible git -y
git clone https://github.com/Websoft9/ansible-activemq.git
cd ansible-activemq
ansible-galaxy install -r requirements.yml -f
ansible-playbook activemq.yml -c local
条件判断中变量 none,null.undefined 有什么区别?
- undefined 代表变量未定义,即变量不存在
- null 即空字符,varA="" 就代表定义个 null 变量 varA
- none 空值是Python里一个特殊的值,varA=None 就代表定义了一个 None 的变量 varA。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。
jinja2 模板中如何判断一个变量 varA 是否未定义,为空或 false:
使用 {% if varA %}
即可,等同于 {% if varA is defined and varA is not none %}
本项目中 Ansible 采用何种安装方式?
采用 rpm/deb 包的安装方式
如何以调试模式启动Ansible服务?
systemctl stop ansible-server
ansible-server console
python_interpreter=auto 时的选择逻辑?
Ansible 主控端有一个 Python interpreter 解析器的选择表,Ansible 会维护不同的操作系统发行版对应的选择。具体参考:Interpreter Discovery
可以通过 Ansible 的 ansible_python_interpreter 变量修改默认值。
Ansible 有哪些内置变量?
Ansible 有三种类型的内置变量:
- 用于 ansible.cfg 配置的系统变量(环境变量)
- 用于工作过程的特殊变量
- 收集到受控端的 Facts 变量