Ansible入门教程--自动化你的基础设施

584 阅读16分钟

这个AnsibleAnsible是一个开源的软件配置、配置管理和应用部署工具。它实现了基础设施即代码(IaC),这意味着它可以通过idempotent的变化来处理基础设施的状态,用一种容易阅读的、特定领域的语言来定义,而不是依赖Bash脚本。教程教授了我们最喜欢的开源软件配置、配置管理和应用部署工具的基本知识。

首先,我们将讨论 "基础设施即代码 "的概念,我们还将彻底了解目前可用的IaC工具情况。然后,我们将深入探讨什么是Ansible,它是如何工作的,以及其安装和配置的最佳实践。

你还将通过一个树莓派车队管理的例子,学习如何用Ansible轻松实现基础设施的自动化。

好了,让我们从理解IaC概念开始吧!

什么是基础设施即代码?

自从复杂的Linux服务器架构诞生以来,配置服务器的方式要么是使用命令行,要么是使用bash脚本。然而,bash脚本的问题是它们相当难读,但更重要的是,使用bash脚本是一种完全强制性的方式。

当依靠bash脚本时,实现细节或机器状态之间的小差异会破坏配置过程。还有一个问题是,如果有人通过SSH-s进入服务器,通过命令行配置了一些东西,然后后来有人试图运行一个脚本,期待旧的状态,会发生什么。

脚本可能会成功运行,也可能简单地中断,或者事情可能完全乱套。没有人能够说得清楚。

为了减轻通过bash脚本定义服务器配置的缺点所带来的痛苦,我们需要一种声明性的方法来对服务器的状态进行idempotent改变,也就是说,不管我们运行多少次我们的脚本,其结果都应该是达到完全相同的预期状态。

这就是 "基础设施即代码"(IaC)概念背后的想法:通过易读的、特定领域的语言定义的易变的变化,来处理基础设施的状态。

这些声明性方法是什么?

首先,Puppet诞生了,然后是Chef。它们都是对广泛采用的需要共同配置的虚拟机集群的回应。

Puppet和Chef都遵循所谓的 "基于拉动 "的配置管理方法。这意味着你要定义配置--使用他们各自领域的特定语言--并存储在服务器上。当新机器启动时,它们需要有一个配置好的客户端,从服务器上提取配置定义并应用于自身。

使用他们的特定领域语言肯定比写bash脚本更清晰,更有自述性。他们在启动机器后自动应用所需的配置,这也很方便。

然而,人们可以说,对预配置客户端的需求使它们有点笨拙。而且,这些客户端的配置仍然相当复杂,如果存储配置的主节点发生故障,如果我们需要快速更新服务器,我们能做的就是退回到旧的命令行/bash脚本方法。

ansible tutorial for beginners by risingstack

为了避免单点故障,Ansible应运而生。

Ansible和Puppet、Chef一样,采用的是声明式的特定领域语言,但与它们相比,Ansible采用的是 "基于推送 "的方法。这意味着,只要你安装了Python,并且在你想配置的主机上运行了SSH服务器,你就可以毫无问题地运行Ansible。我们可以有把握地说,期待服务器的SSH连接绝对不是不可想象的。

长话短说,Ansible为你提供了一种将你的声明性配置推送到机器上的方法。

后来又出现了SaltStack。它也采用了基于推送的方法,但它增加了很多功能,同时也增加了很多使用和维护方面的复杂性。

因此,虽然Ansible绝对不是四个最常见的解决方案中最强大的,但它是最容易上手的,而且它应该足以覆盖99%的可想象的使用情况。

如果你刚刚开始接触IaC的世界,Ansible应该是你的起点,所以现在让我们坚持使用它。

你应该知道的其他IaC工具

虽然上面提到的四种工具(Pupper、Chef、Salt、Ansible)可以批量处理单个机器的配置,但还有其他的IaC工具可以与之结合使用。为了完整起见,让我们快速列出这些工具,这样你就不会在风景中迷失。

Vagrant:它已经存在了很长时间。与Puppet、Chef、Ansible和Salt相反,Vagrant为你提供了一种创建虚拟机蓝图的方法。这也意味着你只能用Vagrant创建虚拟机,但你不能修改它们。因此,它可以成为你最喜欢的配置管理器的一个有用的伴侣,要么设置他们的客户端,要么设置SSH服务器,让他们开始工作。

Terraform:在你使用Ansible之前,如果你维护自己的虚拟机群,Vagrant会很方便。如果你在云中,Terraform可以用来声明性地提供虚拟机,设置网络,或者基本上任何你可以用你喜欢的云供应商的UI、API或CLI处理的东西。功能支持可能会有所不同,这取决于实际的供应商,而且他们大多也有自己的IaC解决方案,但如果你不想被锁定在一个平台上,Terraform可能是最好的解决方案。

KubernetesKubernetes(通常缩写为K8s)提供了一个框架来有效地运行分布式系统。它是一个帮助管理容器化工作负载和服务的平台,甚至还负责扩展。谷歌在2014年将其开源。 容器编排系统被认为是基础设施即代码,因为特别是Kubernetes,你可以控制内部网络、容器、实际机器的很多方面,基本上它更像一个操作系统,它自己的权利比什么都重要。然而,它要求你有一个运行中的虚拟机集群,并安装和配置了Kubernetes。

总而言之,你可以使用Vagrant或Terraform为你的虚拟机群打下基础,然后使用Ansible、Puppet、Chef或Salt来持续处理它们的配置。最后,Kubernetes可以为你提供一种方法,在其上协调你的服务。

你是否在寻找基础设施相关问题或项目的专家帮助?请查看我们的DevOps基础设施相关的服务,或者联系我们:info@risingstack.com

我们之前已经写了很多关于Kubernetes的文章,所以这次我们将更进一步,看看我们最喜欢的远程配置管理工具。

什么是Ansible?

让我们把我们已经知道的东西拆开。

Ansible是一个基于推送的IaC,提供了一种用户友好的特定领域语言,所以你可以用声明的方式来定义你想要的架构。

基于推送意味着Ansible使用SSH在运行Ansible的机器和被配置的机器之间进行通信。

我们希望用Ansible来配置的机器被称为管理节点或Ansible主机。在Ansible的术语中,主机的列表被称为清单

读取定义文件并运行Ansible将配置推送给主机的机器被称为控制节点

如何安装Ansible

只需要在一台机器上安装Ansible即可,即控制节点。

控制节点的要求如下。

  • 安装Python 2(2.7版)或Python 3(3.5版及以上)。
  • 不支持Windows作为控制节点,但你可以使用WSL在Windows 10上进行设置
  • 管理节点也需要安装Python。

RHEL和CentOS

sudo yum install ansible

基于Debian的发行版和WSL

sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install ansible

MacOS

在Mac上安装Ansible的首选方式是通过pip

pip install --user ansible

运行下面的Ansible命令来验证安装。

ansible --version

Ansible设置、配置和自动化教程

在本教程中,我们将用Ansible设置Raspberry Pi,所以即使SD卡损坏了,我们也可以快速地重新设置并继续使用它。

  1. Flash镜像(Raspbian)
  2. 用默认凭证登录(pi/raspberry)
  3. 更改默认密码
  4. 设置无密码的SSH
  5. 安装你想使用的软件包

有了Ansible,我们可以把这个过程自动化。

假设我们有几台Raspberry Pis,在它们身上安装完操作系统后,我们需要在所有设备上安装以下软件包。

  • vim
  • wget
  • curl
  • htop

我们可以在每台设备上逐一安装这些包,但那会很乏味。让Ansible来做这项工作吧。

首先,我们需要创建一个项目文件夹。

mkdir bootstrap-raspberry && cd bootstrap-raspberry

我们需要一个配置文件和一个hosts文件。让我们来创建它们。

touch ansible.cfg
touch hosts 		// file extension not needed 

Ansible可以通过一个名为ansible.cfg 的配置文件进行配置。你可以在这里找到一个包含所有选项的例子。

安全风险:如果你从一个可写入世界的文件夹中加载ansible.cfg ,另一个用户可以在那里放置自己的配置文件并运行恶意代码。关于这一点的更多信息请看这里

配置文件的查找顺序将按照以下顺序进行查找。

  1. ANSIBLE_CONFIG (如果设置了环境变量)
  2. ansible.cfg (在当前目录下)
  3. ~/.ansible.cfg (在主目录中)
  4. /etc/ansible/ansible.cfg

因此,如果我们有一个ANSIBLE_CONFIG 环境变量,Ansible将忽略所有其他的文件(2.,3.,4.)。另一方面,如果我们没有指定一个配置文件,/etc/ansible/ansible.cfg

现在我们将使用一个非常简单的配置文件,内容如下。

[defaults]
inventory = hosts
host_key_checking = False

这里我们告诉Ansible,我们使用我们的hosts 文件作为库存,并且不检查主机密钥。Ansible默认启用了主机密钥检查。如果一个主机被重新安装,并且在known_hosts 文件中有一个不同的密钥,这将导致一个错误信息,直到被纠正。如果主机最初不在known_hosts ,这将导致交互式地提示确认,如果你想自动化你的进程,这是不可取的。

现在让我们打开hosts 文件。

[raspberries]
192.168.0.74
192.168.0.75
192.168.0.76


[raspberries:vars]
ansible_connection=ssh  
ansible_user=pi 
ansible_ssh_pass=raspberry

我们在[raspberries] 块下列出树莓派的IP地址,然后给它们分配变量。

  • ansible_connection:与主机的连接类型。默认为ssh在这里查看其他连接类型
  • ansible_user:连接到主机时要使用的用户名
  • ansible_ssh_password:用来验证主机的密码

创建一个Ansible Playbook

现在我们已经完成了对Ansible的配置。我们可以开始设置我们想要自动化的任务了。Ansible把这些任务的列表称为 "playbook"。

在我们的例子中,我们要。

  1. 更改默认密码。
  2. 添加我们的SSH公钥到authorized_keys
  3. 安装一些软件包。

意味着,我们的playbook里有3个任务,我们称之为pi-setup.yml

默认情况下,Ansible会尝试在所有主机上并行运行一个playbook,但playbook中的任务是连续运行的,一个接一个。

让我们看一下我们的pi-setup.yml ,作为一个例子。

- hosts: all
  become: 'yes'
  vars:
    user:
      - name: "pi"
        password: "secret"
        ssh_key: "ssh-rsa …"
    packages:
      - vim
      - wget
      - curl
      - htop
  tasks:
    - name: Change password for default user
      user:
        name: '"{{ item.name }}"'
        password: '"{{ item.password | password_hash('sha512') }}"'
        state: present
      loop:
        - '"{{ user }}"'
    - name: Add SSH public key
      authorized_key:
        user: '"{{ item.name }}"'
        key: '"{{ item.ssh_key }}"'
      loop:
        - '"{{ user }}"'
    - name: Ensure a list of packages installed
      apt:
        name: '"{{ packages }}"'
        state: present
    - name: All done!
      debug:
        msg: Packages have been successfully installed

拆解Ansible Playbook的例子

我们来拆解一下这个游戏手册。

- hosts: all
  become: 'yes'
  vars:
    user:
      - name: "pi"
        password: "secret"
        ssh_key: "ssh-rsa …"
    packages:
      - vim
      - wget
      - curl
      - htop
  tasks:  [  ]

这一部分定义了与整个playbook相关的字段。

  1. hosts: all:这里我们告诉Ansible在hostfile中定义的所有主机上执行这个playbook。
  2. become: yes:以sudo用户身份执行命令。Ansible使用权限升级系统,以root权限或其他用户的权限执行任务。这可以让你成为另一个用户,所以叫这个名字。
  3. vars:用户定义变量。一旦你定义了变量,你就可以使用Jinja2模板系统在你的playbooks中使用它们。还有其他来源vars ,比如从系统中发现的变量。这些变量被称为事实
  4. tasks:我们要执行的命令的列表

让我们再看一下我们之前定义的第一个任务,不涉及用户模块的细节。如果你是第一次听到与Ansible有关的 "模块 "这个词,不要着急,我们会在后面详细讨论它们。

tasks:
  - name: Change password for default user
    user:
      name: '"{{ item.name }}"'
      password: '"{{ item.password | password_hash('sha512') }}"'
      state: present
    loop:
      - '"{{ user }}"'
  1. name:任务的简短描述,使我们的playbook自带文档。
  2. user:手头的任务所配置和运行的模块。每个模块都是一个封装了所需状态的对象。这些模块可以控制系统资源、服务、文件或基本上任何东西。例如,user 模块的文档可以在这里找到。它是用来管理用户账户和用户属性的。
  3. loop:循环使用变量。如果你想用不同的输入来多次重复一个任务。loops循环变量就会派上用场。比方说,我们有100个用户定义为变量,我们想注册他们。有了循环,我们不需要运行100次游戏程序,只需要一次。

了解Ansible用户模块

放大用户模块。

user:
  name: '"{{ item.name }}"'
  password: '"{{ item.password | password_hash('sha512') }}"'
  state: present
loop:
  - '"{{ user }}"'

Ansible有很多模块,每个模块都封装了一个特定任务/服务的逻辑。上面的用户模块定义了一个用户和它的密码。不管是需要创建还是已经存在,只是需要修改密码,Ansible都会帮我们处理。

请注意,Ansible只接受散列的密码,所以要么你提供预先散列的字符,要么--如上--使用一个散列过滤器

你是否在寻找基础设施相关问题或项目的专家帮助?请查看我们的DevOps基础设施相关的服务,或者通过info@risingstack.com 联系我们。

为了简单起见,我们将用户的密码存储在我们的示例游戏手册中,但你不应该在游戏手册中直接存储密码。相反,你可以在从CLI运行playbook时使用变量标志,或者使用密码存储,如Ansible Vault1Password模块

大多数模块都暴露了一个state 参数,最好的做法是在可能的情况下明确定义它。状态定义了模块是否应该使某些东西存在(添加、开始、执行)或不存在(删除、停止、清除)。例如,创建或删除一个用户,或启动/停止/删除一个Docker容器。

注意,用户模块将在循环的每一次迭代中被调用,传入user 变量的当前值。循环不是模块的一部分,它在外部缩进层,意味着它是与任务相关的。

授权的钥匙模块

authorized_keys 模块为一个特定用户的账户添加或删除SSH授权密钥,从而实现无密码的SSH连接。

- name: Add SSH public key
  authorized_key:
    user: '"{{ item.name }}"'
    key: '"{{ item.ssh_key }}"'

上面的任务将采取指定的key ,并将其添加到指定的user's~/.ssh/authorized_keys 文件中,就像你用手,或用ssh-copy-id

Apt模块

我们需要一个新的vars 块来安装软件包。

vars:
  packages:
    - vim
    - wget
    - curl
    - htop

tasks:
  - name: Ensure a list of packages installed
    apt:
      name: '"{{ packages }}"'
      state: present

apt 模块负责管理apt软件包(比如Debian/Ubuntu)。name 字段可以接受一个要安装的软件包列表。在这里,我们定义了一个变量来存储所需的软件包列表,以保持任务的简洁,这也使我们能够在应用游戏手册时,如果觉得有必要,用命令行参数覆盖软件包列表,而不需要编辑实际的游戏手册。

state 字段被设置为存在,意味着Ansible应该在缺少软件包的情况下安装它,或者跳过它,如果它已经存在。换句话说,它确保了软件包的存在。它也可以设置为absent (确保它不在那里),latest (确保它在那里,而且是最新的版本,build-deps (确保它的构建依赖是存在的),或者fixed (试图纠正一个有破损依赖的系统的地方)。

让我们运行我们的Ansible Playbook

重申一下,这里是整个playbook的合集。

- hosts: all
  become: 'yes'
  vars:
    user:
      - name: "pi"
        password: "secret"
        ssh_key: "ssh-rsa …"
    packages:
      - vim
      - wget
      - curl
      - htop
  tasks:
    - name: Change password for default user
      user:
        name: '"{{ item.name }}"'
        password: '"{{ item.password | password_hash('sha512') }}"'
        state: present
      loop:
        - '"{{ user }}"'
    - name: Add SSH public key
      authorized_key:
        user: '"{{ item.name }}"'
        key: '"{{ item.ssh_key }}"'
      loop:
        - '"{{ user }}"'
    - name: Ensure a list of packages installed
      apt:
        name: '"{{ packages }}"'
        state: present
    - name: All done!
      debug:
        msg: Packages have been successfully installed

现在我们已经准备好运行这个游戏手册了。

ansible-playbook pi-setup.yml

或者我们可以通过覆盖配置文件来运行它。

$ ANSIBLE_HOST_KEY_CHECKING=False

$ ansible-playbook - i192.168.0.74, 192.168.0.75” ansible_user=john  ansible_ssh_pass=johnspassword” -e  ‘{“user”: [{ “name”: “pi”, “password”: “raspberry”, “state”: “present” }] }’ -e '{"packages":["curl","wget"]}' pi-setup.yml

上面的片段中使用的命令行标志是。

  • -i(库存):指定库存。它可以是上面的逗号分隔的列表,也可以是一个库存文件。
  • -e(或-extra-vars):通过这个标志可以添加或覆盖变量。在我们的例子中,我们正在覆盖我们的hosts 文件中布置的配置(ansible_user,ansible_ssh_pass)以及我们之前在playbook中设置的变量userpackages

用Ansible做什么

当然,Ansible并不只是用来设置自制的服务器。

Ansible可以用来批量管理虚拟机群,确保每个新创建的虚拟机都有和其他虚拟机一样的配置。它还可以通过对一个playbook的修改,轻松地将整个机群的配置一起改变。

但是Ansible也可以用于其他大量的任务。如果你只有一台服务器在云供应商中运行,你可以以一种其他人可以轻松阅读和使用的方式定义它的配置。你也可以定义维护游戏手册,比如创建新的用户,将新员工的SSH密钥添加到服务器上,这样他们也可以登录到机器上。

或者你可以使用AWXAnsible Tower来创建一个基于GUI的Linux服务器管理系统,提供与Windows服务器类似的体验。

The postGetting Started with Ansible Tutorial - Automate your Infrastructureappeared first onRisingStack Engineering.