虽说有了docker或者shell脚本等等一系列自动化运维工具,但是使用后往往会发现难以维护。
shell脚本只能单纯的执行,而且编写起来也非常麻烦
docker在部署好docker之前每次安装docker等等一系列操作也是完全重复的
CI/CD更多是程序的测试和发布和后续的持续集成,对于全新的机器等待使用CI/CD部署初期环境总感觉违反了单一原则啥的,而且也麻烦,不好复用等等
所以我们需要一个更好的专门的自动化运维工具。
要求
基础运维知识
基础python或者编程基础
如果你没有服务器,最好知道一点docker来方便测试
内容很多很杂,完全记录太麻烦了,请更多参考官方文档
Ansible简介
Ansible 就是新出现的自动化运维工具,免去那些复杂的描述,简单的说明它可以储存一些预设的主机,可以对他们进行分组,然后可以对单个主机或者一个组执行一系列的操作。
而且最棒的是,Ansible只需要在主机上安装,在你需要部署的机器上不需要任何安装,Ansible会使用ssh自行连接进行操作,所以需要做的就是在远程主机上放好ssh的key。
基本概念
Inventory: 记录远程主机的信息(IP,端口,密码等等)
Modules: 基本的模块功能(如apt,ping)
Tasks:任务
Playbooks:定义一系列任务
组件
Hosts:同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为/etc/ansible/hosts
Tasks:任务,由模板定义的操作列表
Variable:变量
Templates:模板,一般是.j2为文件后缀
Handlers:处理器,当某条件满足时,出发执行的操作
Roles:角色
安装
1 sudo apt install ansible
即可在ubuntu上安装,你可以在这里 查看更多的安装方法。
当然我是推荐使用docker进行安装的,但是ansible稍微特殊一点,而且纯ansible也不方便使用,后面我们会使用Ansible AWX,但是Ansible AWX需要使用ansible进行安装(禁止套娃),所以无奈我们先安装ansible。
配置文件目录:/etc/ansible/
模组目录:
~/.ansible/plugins/modules
/usr/share/ansible/plugins/modules
可执行文件:/usr/bin/ansible-playbook
Ansible如何工作
Ansible由节点和控制机器组成。 控制机器是安装Ansibles的地方,节点由这些机器通过SSH管理。 借助SSH协议,控制机器可以部署临时存储在远程节点上的模块。
控制机器使用ansible或者ansible-playbooks在服务器终端输入的Ansible命令集或者playbook后,Ansible会遵循预先编排的规则将playbook逐条拆解为Play,再将Play组织成Ansible可以识别的任务tasks,随后调用任务涉及到的所有Module及PLUGINS,根据主机清单Inventory中定义的主机列表通过SSH协议将任务集以临时文件或者命令的形式传输到远程节点并返回结果,如果是临时文件则执行完毕后自动删除。
Inventory
首先是/etc/ansible/hosts
,也就是Inventory
,我们可以定义主机
1 名字 ansible_port=端口 ansible_host=IP
这样就可以定义一个主机,其他的参数还有
ansible_host
将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
ansible_port
ssh端口号.如果不是默认的端口号,通过此变量设置.
ansible_user
ansible_pass
ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)
ansible_sudo_pass
sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)
ansible_sudo_exe
(new in version 1.8)
ansible_connection
与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 ‘smart’,‘smart’ 方式会根据是否支持 ControlPersist, 来判断’ssh’ 方式是否可行.
ansible_private_key_file
ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
ansible_shell_type
目标系统的shell类型.默认情况下,命令的执行使用 ‘sh’ 语法,可设置为 ‘csh’ 或 ‘fish’.
ansible_python_interpreter
目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 *BSD, 或者 /usr/bin/python
不是 2.X 版本的 Python.我们不使用 “/usr/bin/env” 机制,因为这要求远程用户的路径设置正确,且要求 “python” 可执行程序名不可为 python以外的名字(实际有可能名为python26).
与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径
Ansible 2.0 has deprecated the “ssh” from ansible_ssh_user, ansible_ssh_host, and ansible_ssh_port to become ansible_user, ansible_host, and ansible_port. If you are using a version of Ansible prior to 2.0, you should continue using the older style variables (ansible_ssh_*). These shorter variables are ignored, without warning, in older versions of Ansible.
对于一个组的定义
1 2 3 [group_name] 名字 ansible_port=端口 ansible_host=IP 名字 ansible_port=端口 ansible_host=IP
这样就可以定义一个组
/etc/ansible/hosts
不是唯一的,你可以创建自己的hosts
文件,然后指定文件使用
1 2 $ ansible-playbook -i hosts xxx.yml $ ansible <pattern_goes_here> -m <module_name> -a <arguments>
主机匹配
all
和*
代表目标为仓库(inventory)中的所有机器
也可以写IP地址或系列主机名192.168.1.*
必须隶属webservers组但同时不在phoenix组webservers:!phoenix
正则表达式shiyong~
开头~(web|db).*\.example\.com
从文件读取hosts,文件名以@为前缀开头@retry_hosts.txt
1 2 3 *.example.com webservers[0] webservers[0-25]
ad-hoc命令
Ansible提供两种方式去完成任务
ad-hoc命令
Ansible playbook
如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来,这样的命令就叫做ad-hoc命令
Ansible 能够以并行的方式同时运行ad-hoc命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ ansible atlanta -a "/sbin/reboot" -f 10 $ ansible atlanta -a "/usr/bin/foo" -u username $ ansible atlanta -a "/usr/bin/foo" -u username --sudo [--ask-sudo-pass] $ ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts" $ ansible webservers -m file -a "dest=/srv/foo/a.txt mode=600" $ ansible webservers -m yum -a "name=acme state=present" $ ansible webservers -m yum -a "name=acme-1.5 state=present" $ ansible webservers -m yum -a "name=acme state=absent" $ ansible webservers -m service -a "name=httpd state=started" $ ansible all -m setup
Playbooks
Playbooks是Ansible的配置、部署和编排语言。他们可以描述您希望远程系统实施的策略。我们把操作定义在Playbooks
里面,Playbooks
是一个yaml文件,文件名随意。playbook由一个或多个‘plays’组成.它的内容是一个以‘plays’为元素的列表.
每一个play包含了一个task列表(任务列表),一个task在其所对应的所有主机上执行完毕之后,下一个task才会执行
如果一个host执行task失败,这个host将会从整个playbook的rotation中移除. 如果发生执行失败的情况,请修正playbook中的错误,然后重新执行即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 - hosts: webservers vars: http_port: 80 max_clients: 200 remote_user: root tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started - name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True handlers: - name: restart apache service: name=httpd state=restarted
如果写过Drone的CI/CD文件大概就可以知道,我们先定义了哪个hosts,然后哪个用户去执行
下面就是我们要执行的一系列tasks,全部tasks是一个Playbooks ,他们是按照从上到下依次执行的
ansible会去指定的inventory文件里寻找对应的webservers主机
每个tasks有一个name和一个module
其实和docker-compose.yml很相似,只不过这里定义的是动作
1 2 ansible-playbook playbook.yml -f 10
Ansible-Pull
Ansible-pull是一个小脚本,它从git上checkout一个关于配置指令的repo,然后以这个配置指令来运行ansible-playbook.
include语句
用include
语句引用task文件的方法,可允许你将一个配置策略分解到更小的文件中。使用include语句引用tasks是将tasks从其他文件拉取过来。因为handlers也是tasks,所以你也可以使用include语句去引用handlers文件。
Playbook同样可以使用include引用其他playbook文件中的play。这时被引用的play会被插入到当前的playbook中,当前的playbook中就有了一个更长的的play列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tasks: - include: tasks/foo.yml - include: wordpress.yml wp_user=timmy - { include: wordpress.yml , wp_user: timmy , ssh_keys: [ 'keys/one.txt' , 'keys/two.txt' ] } - include: wordpress.yml vars: wp_user: timmy some_list_variable: - alpha - beta - gamma handlers: - include: handlers/handlers.yml
Include 语句也可用来将一个 playbook 文件导入另一个 playbook 文件。这种方式允许你定义一个 顶层的 playbook,这个顶层 playbook 由其他 playbook 所组成。
1 2 3 4 5 6 7 8 9 10 11 12 13 - name: this is a play at the top level of a file hosts: all remote_user: root tasks: - name: say hi tags: foo shell: echo "hi..." - include: load_balancers.yml - include: webservers.yml - include: dbservers.yml
当你在 playbook 中引用其他 playbook 时,不能使用变量替换。
变量
1 2 3 4 5 6 7 8 --- - name: Hello World hosts: localhost tasks: - name: Hello World debug debug: msg: "Hello World"
上面可以输出一个Hello World
,但是如果我们想设置一些变量且输出,我们可以这样
1 2 3 4 5 6 7 8 9 10 11 --- - name: Hello World hosts: localhost vars: greeting: "hello from vars" tasks: - name: Hello World debug debug: msg: "{{ greeting }} "
我们可以定义类似字典和列表的变量
1 2 3 4 5 6 7 vars: greeting: "hello from vars" demo: a: - a: 1 - b: 2 b: test
如果我们想很好的规划变量,我们可以把变量放进单独文件里面,比如vars/demo.yml
里,内容为
1 greeting: "hello from vars"
然后Playbooks
里面写上
1 2 vars_files: - "vars/demo.yml"
我们可以定义多个变量文件,如果变量名相同,最下方的变量文件会覆盖之前的
Host/组变量
上方Inventory 设置的就是host级别的变量,如果一组服务器用户相同,我们一一设置起来非常麻烦,我们就可以设置组级别的变量
1 2 3 4 5 6 7 [all] host1 http_port=80 host2 http_port=443 [all:vars] ansible_user =rootansible_password =ansible
同理,为了方便维护,我们可以把组变量等分开,再这个项目根目录下,我们这样创建文件夹
1 2 3 4 5 6 . ├── group_vars │ └── all.yml ├── host_vars │ └── host1.yml └── host
将文件名对应正确的组或主机名即可。于是定义我们需要管理的主机可以变为
hosts
host1.yml
1 2 ansible_user=root ansible_password=ansible
host2.yml
1 2 ansible_user=root ansible_password=ansible
等等各种方式
ansible.cfg
ansible.cfg
在一个项目级别上定义配置
1 2 [default] inventory = inventory/hosts
一般情况下,我们需要指定inventory
文件,这样很麻烦,遵循一切皆代码的原则,我们可以把这些也弄成配置
于是就有了ansible.cfg
文件,默认我们在执行ansible-playbook
的时候,我们会按照一下顺序搜索ansible.cfg
文件
当前目录下ansible.cfg
环境变量ANSIBLE_CONFIG
当前用户home下
/etc/ansible.cfg
然后我们就可以直接运行
1 ansible-playbook main.yml
详细参见这里Ansible配置文件
项目结构设计
目前
1 2 3 4 5 6 7 8 9 10 . ├── inventory │ ├── group_vars │ │ └── all.yml │ ├── host_vars │ │ ├── host1.yml │ │ └── host2.yml │ └── host ├── ansible.cfg └── main.yml
对于测试环境和生成环境,我们可以这样设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 . ├── inventory │ ├── production │ │ ├── group_vars │ │ │ └── all.yml │ │ ├── host_vars │ │ │ ├── host1.yml │ │ │ └── host2.yml │ │ └── host │ └── test │ ├── group_vars │ │ └── all.yml │ ├── host_vars │ │ ├── host1.yml │ │ └── host2.yml │ └── host ├── ansible.cfg └── main.yml
目前流程
graph TD
写hosts --> 写hosts变量;
写hosts变量 --> 写playhooks;
写playhooks --> 运行
模板
对于一些配置,我们可能针对不同情况需要不同的配置,就像渲染html页面一样,不过基本的框架是一样的
于是我们可以使用模板
1 2 3 4 5 6 7 - name: template template: src: templates/config.j2 dest: /etc/file.conf owner: bin group: wheel mode: '0664'
而对于config.j2
文件,采取和django一样的模板语法(jinja )
1 2 3 [default] http_port = {{ http_port }}
执行条件
对于不同系统,我们可以有不同的安装指令,我们就需要对task进行条件设置
1 2 3 4 tasks: - name: "shut down Debian flavored systems" command: /sbin/shutdown -t now when: ansible_facts['os_family'] == "Debian"
还有条件引入。如果操作系统是CentOS
,Ansible导入的第一个文件将是vars/CentOS.yml
,紧接着是/var/os_defaults.yml
,如果这个文件不存在.而且在列表中没有找到,就会报错. 在Debian最先查看的将是vars/Debian.yml
而不是vars/CentOS.yml
, 如果没找到,则寻找默认文件vars/os_defaults.yml
1 2 3 vars_files: - "vars/common.yml" - [ "vars/{{ ansible_os_family }} .yml" , "vars/os_defaults.yml" ]
循环
对于某一些操作,可能要对不同值执行多次,所以需要用到循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - name: add several users user: name: "{{ item.name }} " state: present groups: "{{ item.groups }} " loop: - { name: 'testuser1' , groups: 'wheel' } - { name: 'testuser2' , groups: 'root' } - name: add several users user: name={{ item }} state=present groups=wheel with_items: - testuser1 - testuser2 - name: give users access to multiple databases mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo with_nested: - [ 'alice' , 'bob' ] - [ 'clientdb' , 'employeedb' , 'providerdb' ]
字典循环
1 2 3 4 5 6 7 8 9 10 11 users: alice: name: Alice Appleworth telephone: 123 -456 -7890 bob: name: Bob Bananarama telephone: 987 -654 -3210 --- - name: Print phone records debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" with_dict: "{{users}} "
Do-Until循环
1 2 3 4 5 - action: shell /usr/bin/foo register: result until: result.stdout.find("all systems go") != -1 retries: 5 delay: 10
Block
对于一些指令,可能会导致执行失败,如果失败了,我们希望进行一些补救或者处理,类似python里面的
同时,可以定义一个task块,对于这一部分task块,同一使用某些条件或者权限等等
比如我们接下来一系列操作都需要sudo,或者只能在ubuntu上执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 tasks: - name: Install, configure, and start Apache block: - name: install httpd and memcached yum: name: - httpd - memcached state: present - name: apply the foo config template template: src: templates/src.j2 dest: /etc/foo.conf when: ansible_facts['distribution'] == 'CentOS' become: true
而对于错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tasks: - name: Handle the error block: - debug: msg: 'I execute normally' - name: i force a failure command: /bin/false - debug: msg: 'I never execute, due to the above task failing, :-(' rescue: - debug: msg: 'I caught an error, can do stuff here to fix it, :-)' always: - debug: msg: "This always executes, :-)"
角色
playbook里面有一系列task,很多都可以复用,而且把每个步骤的task分开管理也是很方便的,所以就有了role
我们在根目录下创建一个roles文件夹,然后再创建一个文件夹,名字随意,即为一个role,这里记为demo
demo
下按照规范会有这些文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 . └── demo ├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── README.md ├── tasks │ └── main.yml ├── templates ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml
下面的每一个子文件夹都对应着一些配置
tasks
: 任务
defaults
: 基本的常量变量
vars
: 这个任务专用变量
templates
: 模板文件
files
: 要传输的文件
handlers
: tasks的触发处理器
我们可以在主文件里面引用role
如果roles/x/tasks/main.yml
存在, 其中列出的tasks
将被添加到 play 中
如果roles/x/handlers/main.yml
存在, 其中列出的handlers
将被添加到 play 中
如果roles/x/vars/main.yml
存在, 其中列出的variables
将被添加到 play 中
如果roles/x/meta/main.yml
存在, 其中列出的角色依赖
将被添加到 roles 列表中 (1.3 and later)
所有copy tasks
可以引用roles/x/files/
中的文件,不需要指明文件的路径。
所有script tasks
可以引用roles/x/files/
中的脚本,不需要指明文件的路径。
所有template tasks
可以引用roles/x/templates/
中的文件,不需要指明文件的路径。
所有include tasks
可以引用roles/x/tasks/
中的文件,不需要指明文件的路径。
如果 roles 目录下有文件不存在,这些文件将被忽略。比如 roles 目录下面缺少了 ‘vars/’ 目录,这也没关系。
1 2 3 4 5 --- - hosts: webservers roles: - common - webservers
或者其他语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 --- - hosts: webservers roles: - common - role: foo_app_instance vars: dir: '/opt/a' app_port: 5000 - role: foo_app_instance vars: dir: '/opt/b' app_port: 5001 --- - hosts: webservers tasks: - import_role: name: example - include_role: name: example --- - hosts: webservers roles: - role: '/path/to/my/roles/common' --- - hosts: webservers tasks: - include_role: name: foo_app_instance vars: dir: '/opt/a' app_port: 5000
如果你希望定义一些 tasks,让它们在 roles 之前以及之后执行,你可以这样做
1 2 3 4 5 6 7 8 9 10 11 12 13 - hosts: webservers pre_tasks: - shell: echo 'hello' roles: - { role: some_role } tasks: - shell: echo 'still busy' post_tasks: - shell: echo 'goodbye'
handlers
对于某一些操作,可能我们希望它执行过一次后就不要再执行了,我们就可以使用handlers。
对于每一个tasks,它可以是changed
或者是ok
的状态(还有其他状态),如果是ok
则表示没有实际运行
因为它已经运行过了,比如安装过git了
1 2 3 4 5 6 7 - hosts: webservers tasks: - name: clone git: repo: '{{ repo_url }} ' dest: ~/demo notify: test handlers
然后在handles/main.yml
里面
1 2 3 - name: test handlers debug: msg: "Message from handles"
这样test handlers
只会在clone
是changed
的时候运行
异步操作和轮询
默认情况下playbook中的任务执行时会一直保持连接,直到该任务在每个节点都执行完毕.有时这是不必要的,比如有些操作运行时间比SSH超时时间还要长.
有些任务可能需要执行很长时间,但是又不需要一直看着他,就可以使用异步模式
1 2 3 4 5 tasks: - name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec command: /bin/sleep 15 async: 45 poll: 5
对于要求排它锁的操作,如果你需要在其之后对同一资源执行其它任务,那么你不应该对该操作使用”启动并忽略”.比如yum事务.
Module
Module是基本功能的单位,这里介绍一些常见的模块
debug
debug – Print statements during execution
1 2 3 - name: print variable debug: msg: "Hello World"
1 2 3 - name: print variable debug: msg: "Hello World"
复制文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 - name: Copy file with owner and permissions copy: src: /srv/myfiles/foo.conf dest: /etc/foo.conf owner: foo group: foo mode: '0644' - name: Copy file with owner and permission, using symbolic representation copy: src: /srv/myfiles/foo.conf dest: /etc/foo.conf owner: foo group: foo mode: u=rw,g=r,o=r - name: Another symbolic mode example, adding some permissions and removing others copy: src: /srv/myfiles/foo.conf dest: /etc/foo.conf owner: foo group: foo mode: u+rw,g-wx,o-rwx - name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version copy: src: /mine/ntp.conf dest: /etc/ntp.conf owner: root group: root mode: '0644' backup: yes - name: Copy a new " sudoers" file into place, after passing validation with visudo copy: src: /mine/sudoers dest: /etc/sudoers validate: /usr/sbin/visudo -csf %s - name: Copy a "sudoers" file on the remote machine for editing copy: src: /etc/sudoers dest: /etc/sudoers.edit remote_src: yes validate: /usr/sbin/visudo -csf %s - name: Copy using inline content copy: content: '# This file was moved to /etc/other.conf' dest: /etc/mine.conf - name: If follow=yes, /path/to/file will be overwritten by contents of foo.conf copy: src: /etc/foo.conf dest: /path/to/link follow: yes - name: If follow=no, /path/to/link will become a file and be overwritten by contents of foo.conf copy: src: /etc/foo.conf dest: /path/to/link follow: no
gather_facts
收集机器的相关的基本信息,默认是开启的,并且可以在变量中直接引用gather_facts里面的变量
1 2 3 4 5 6 7 8 - name: test hosts: all gather_facts: no tasks: - name: print facts debug: msg: "{{ ansible_deta_time }} "
yum
yum-module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 - name: install the latest version of Apache yum: name: httpd state: latest - name: ensure a list of packages installed yum: name: "{{ packages }} " vars: packages: - httpd - httpd-tools - name: remove the Apache package yum: name: httpd state: absent - name: install the latest version of Apache from the testing repo yum: name: httpd enablerepo: testing state: present - name: install one specific version of Apache yum: name: httpd-2.2.29-1.4.amzn1 state: present - name: upgrade all packages yum: name: '*' state: latest - name: upgrade all packages, excluding kernel & foo related packages yum: name: '*' state: latest exclude: kernel*,foo* - name: install the nginx rpm from a remote repo yum: name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state: present - name: install nginx rpm from a local file yum: name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state: present - name: install the 'Development tools' package group yum: name: "@Development tools" state: present - name: install the 'Gnome desktop' environment group yum: name: "@^gnome-desktop-environment" state: present - name: List ansible packages and register result to print with debug later. yum: list: ansible register: result - name: Install package with multiple repos enabled yum: name: sos enablerepo: "epel,ol7_latest" - name: Install package with multiple repos disabled yum: name: sos disablerepo: "epel,ol7_latest" - name: Install a list of packages yum: name: - nginx - postgresql - postgresql-server state: present - name: Download the nginx package but do not install it yum: name: - nginx state: latest download_only: true
apt
apt-module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 - name: Install apache httpd (state=present is optional) apt: name: apache2 state: present - name: Update repositories cache and install "foo" package apt: name: foo update_cache: yes - name: Remove "foo" package apt: name: foo state: absent - name: Install the package "foo" apt: name: foo - name: Install a list of packages apt: pkg: - foo - foo-tools - name: Install the version '1.00' of package "foo" apt: name: foo=1.00 - name: Update the repository cache and update package "nginx" to latest version using default release squeeze-backport apt: name: nginx state: latest default_release: squeeze-backports update_cache: yes - name: Install latest version of "openjdk-6-jdk" ignoring "install-recommends" apt: name: openjdk-6-jdk state: latest install_recommends: no - name: Upgrade all packages to the latest version apt: name: "*" state: latest - name: Update all packages to the latest version apt: upgrade: dist - name: Run the equivalent of "apt-get update" as a separate step apt: update_cache: yes - name: Only run "update_cache=yes" if the last one is more than 3600 seconds ago apt: update_cache: yes cache_valid_time: 3600 - name: Pass options to dpkg on run apt: upgrade: dist update_cache: yes dpkg_options: 'force-confold,force-confdef' - name: Install a .deb package apt: deb: /tmp/mypackage.deb - name: Install the build dependencies for package "foo" apt: pkg: foo state: build-dep - name: Install a .deb package from the internet. apt: deb: https://example.com/python-ppq_0.1-1_all.deb - name: Remove useless packages from the cache apt: autoclean: yes - name: Remove dependencies that are no longer required apt: autoremove: yes
pip
pip-module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 - pip: name: bottle - pip: name: bottle==0.11 - pip: name: bottle>0.10,<0.20,!=0.11 - pip: name: - django>1.11.0,<1.12.0 - bottle>0.10,<0.20,!=0.11 - pip: name: six environment: HTTP_PROXY: '127.0.0.1:8080' HTTPS_PROXY: '127.0.0.1:8080' - pip: name: svn+http://myrepo/svn/MyApp#egg=MyApp - pip: name: git+http://myrepo/app/MyApp - pip: name: file:///path/to/MyApp.tar.gz - pip: name: bottle virtualenv: /my_app/venv - pip: name: bottle virtualenv: /my_app/venv virtualenv_site_packages: yes - pip: name: bottle virtualenv: /my_app/venv virtualenv_command: virtualenv-2.7 - pip: name: bottle extra_args: --user - pip: requirements: /my_app/requirements.txt - pip: requirements: /my_app/requirements.txt virtualenv: /my_app/venv - pip: requirements: /my_app/requirements.txt extra_args: -i https://example.com/pypi/simple - pip: requirements: /my_app/requirements.txt extra_args: "--no-index --find-links=file:///my_downloaded_packages_dir" - pip: name: bottle executable: pip3.3 - pip: name: bottle state: forcereinstall - pip: name: bottle umask: "0022" become: True
get_url
get_url – Downloads files from HTTP, HTTPS, or FTP to node
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 - name: Download foo.conf get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf mode: '0440' - name: Download file and force basic auth get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf force_basic_auth: yes - name: Download file with custom HTTP headers get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf headers: key1: one key2: two - name: Download file with check (sha256) get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c - name: Download file with check (md5) get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: md5:66dffb5228a211e61d6d7ef4a86f5758 - name: Download file with checksum url (sha256) get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf checksum: sha256:http://example.com/path/sha256sum.txt - name: Download file from a file path get_url: url: file:///tmp/afile.txt dest: /tmp/afilecopy.txt - name: < Fetch file that requires authentication. username/password only available since 2.8 , in older versions you need to use url_username/url_password get_url: url: http://example.com/path/file.conf dest: /etc/foo.conf username: bar password: '{{ mysecret }} '
ansible-galaxy
这个东西就像python里面的pip,管理的对象是role或者collection,我们可以发布自己role,或者下载别人的role
ansible-galaxy role init name
: 初始化一个模块
ansible-galaxy role remove name
: 移除一个模块
我们可以在根目录下创建一个文件requirements.yml
1 2 3 4 roles: - src: https://..... scm: git version: master
安装指令:
1 ansible_galaxy install -r requirements.yml
ansible-galaxy
Ansible AWX
通过Ansible AWX ,可以利用网页界面可视化管理Ansible
Ansible AWX安装
通过github的方法,我们采取docker-compose安装,但是docker-compose需要文件需要通过官方的安装程序生成
先clone
库
1 git clone https://github.com/ansible/awx.git
按照官方的配置方法 ,配置一些数据库密码,或者一些初始的东西,然后进入到installer
文件夹里,生成配置文件
1 2 3 4 5 cd installeransible-playbook -i inventory install.yml
不过官方默认会启动容器,且绑定80端口,如果你已经有占用80端口的容器了,那么应该会启动失败
如果启动了部分容器我们先把他们删除掉,包括volume。
通过配置docker_compose_dir
的文件夹找到生成的docker-compose.yml
文件,手动配置一下反向代理或者其他一些需要自定义的地方,然后就可以up -d
了,启动后需要等待一段时间,网站才会初始化好。然后就能登录进去了。
普通的使用:
清单:对应Inventory
凭证:添加密钥密码等等
Ansible AWX在Ansible基础上添加了更多功能,就像管理docker的程序一样
实战
一键创建一个sudoer用户,并添加ssh密钥,关闭root登录并更改默认ssh端口,还更新所有包,达到安装包前开箱即用状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 --- - hosts: all gather_facts: no vars: ansible_user: root ansible_port: "22" tasks: - name: print host debug: msg: "{{ ansible_host }} :{{ ansible_port }} " - name: Check if we're using the default SSH port wait_for: port: "22" state: "started" host: "{{ ansible_host }} " connect_timeout: "5" timeout: "10" delegate_to: "localhost" ignore_errors: "yes" register: default_ssh - name: Set inventory ansible_port to default set_fact: ansible_port: "22" when: default_ssh is defined and default_ssh.state == "started" register: ssh_port_set - name: Check if we're using the inventory-provided SSH port wait_for: port: "{{ ansible_port }} " state: "started" host: "{{ ansible_host }} " connect_timeout: "5" timeout: "10" delegate_to: "localhost" ignore_errors: "yes" register: configured_ssh when: default_ssh is defined and default_ssh.state is undefined - name: SSH port is configured properly debug: msg: "SSH port is configured properly" when: configured_ssh is defined and configured_ssh.state is defined and configured_ssh.state == "started" register: ssh_port_set - name: Fail if SSH port was not auto-detected (unknown) fail: msg: "The SSH port is neither 22 or {{ ansible_port }} ." when: ssh_port_set is undefined - name: Confirm host connection works ping: - name: Make sure we have a "wheel" group group: name: wheel state: present - name: Allow "wheel" group to have passwordless sudo lineinfile: dest: /etc/sudoers state: present regexp: "^%wheel" line: "%wheel ALL=(ALL) NOPASSWD: ALL" validate: "visudo -cf %s" - name: Create a sudoer login user user: name: "{{ username }} " password: "{{ '{{ password }} ' | password_hash('bcrypt') }}" state: present shell: /bin/bash append: yes groups: - wheel system: no create_home: yes home: /home/{{ username }} ssh_key_bits: 2048 ssh_key_file: .ssh/id_rsa - name: Set authorized keys for the user copying it from current user authorized_key: user: "{{ username }} " key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }} " comment: my key - name: Run deferred setup to gather facts setup: - include_role: name: robertdebock.update vars: update_autoremove: yes update_upgrade_command: dist - name: test to see if selinux is running command: getenforce register: sestatus changed_when: false when: ansible_facts['distribution'] == "CentOS" - block: - name: ensure a list of packages installed yum: name: "{{ packages }} " vars: packages: - libselinux-python - policycoreutils-python when: ansible_facts['distribution'] == "CentOS" - name: Setup selinux for alternate SSH port seport: ports: "2022" proto: "tcp" setype: "ssh_port_t" state: "present" ignore_errors: "yes" when: ansible_facts['distribution'] == "CentOS" and sestatus is defined and sestatus.stdout is defined and '"Enabled" in sestatus.stdout' - include_role: name: arillso.sshd vars: ssh_server_ports: ['2022' ] ssh_client_password_login: true ssh_server_enabled: true ssh_server_password_login: false ssh_sftp_enabled: true
参考