编者按:本文节选自节选自《基于Linux的企业自动化》第五章。“第5章,使用Ansible构建用于部署的虚拟机模板,通过构建虚拟机模板来探索部署Linux的最佳实践,虚拟机模板将以实际操作的方式大规模部署在虚拟机管理程序上。”
名词解释:
- cloud-init: 提供云实例初始时自定义配置的能力,支持多个发行版和多个平台;
- docker-compose:业务只需要单个容器场时,可以用docker命令管理。如果业务需要多个容器,可以用docker-compose定义和运行它们;
- Ansible-vault:提供文件和变量的加密能力,可以用于保护密码等敏感数据。
以下是原文
5.3 使用Ansible来构建和标准化模板
你现在应该有一个基本的Linux映像,以便在企业中部署。如果你选择下载一个现成的模板(或者确实是使用公共云提供商提供的模板),那么你的映像将是一个非常空白的模板,随时可以定制。如果你之前选择构建自己的映像,那么你可能已经选择执行了少量定制,例如我们之前执行的cloud-init安装。然而,你会注意到,我们是手工完成这些的,这与我们在本书早期所称赞的可伸缩、可重复、可审核的流程很不一样。在我们继续阅读本章的这一节时,我们将了解如何使用Ansible自定义一个基本模板,而不管它来自何处。
没有适合所有人的普适的Linux映像,因此,本章介绍的方法并不一定是最佳的。但是,我们将研究一些与自定义为要部署的映像相关联的更常见的任务,例如:
将文件传输到映像中
安装软件包
编辑配置文件
验证映像
通过这些示例的组合,大多数读者都应该能够轻松地根据自己的需求定制自己的映像。让我们开始更深入地探讨这个问题,看看如何将文件传输到我们之前使用Ansible创建的虚拟机映像中。
5.3.1 将文件传输到映像中
根据作者的经验,通常需要将文件注入(inject)到操作系统映像中,以确保它满足给定的一组要求。这些文件可能是简单的文本文件,例如当前的企业标准每日消息(message of the day)、现有软件包的配置文件,甚至可能是软件包中不存在的二进制文件。Ansible可以轻松地处理所有这些问题,所以让我们看一些具体的例子。一般来说,在角色中编写Ansible代码以支持重用和可读性是一种很好的做法,因此我们将在这里为示例定义一个角色。在这个例子中,我做了以下假设:
我们已经下载/构建了本章上一节中概述的Linux模板。
我们正在虚拟机中运行此裸模板。
此虚拟机的IP地址为192.168.81.141。
虚拟机已使用以下凭据设置了用户账户:
用户名:imagebuild。
口令:Password。
此账户已启用sudo。
很自然,我们不会分发一个其中包含一个使用这样的弱口令的启用sudo的账户的云映像,因此我们假设我们只在构建阶段使用该账户,然后在清理阶段将其删除。Ansible需要能够连接到远程主机来执行它的工作,但是它使用的账户在本质上可能是暂时的,并且在使用后会被删除:
1.在我们的示例中,我们将创建一个类似于下面的清单文件。你的实际清单文件无疑会有所不同,为你的映像和环境定制它是留给你的一个练习:
[imagesetup]
192.168.81.141
[imagesetup:vars]
ansible_user=imagebuild
ansible_password=password
ansible_sudo_pass=password
这是一个非常简单的示例;在许多方面,当我们没有配置SSH密钥身份验证时,它是这个过程所需的最低限度的配置。SSH密钥通常是处理SSH身份验证的最佳方法,因为它们提供了一些好处,尤其是任务可以在没有口令提示的情况下运行。
提示
尽管此清单文件本质上是暂时的,但使用ansible-vault存储口令仍然是最佳实践,这里建议这样做。为了本章的简单性和减少你需要完成的步骤的数量,我们将不加密口令(采用明文)。
接下来,我们将为角色创建基本目录结构:
$ mkdir -p roles/filecopyexample/tasks
$ mkdir -p roles/filecopyexample/files
3.现在,让我们创建几个示例文件进行复制。首先,创建一个要附加到roles/filecopyexample/files/motd中的当天消息的自定义消息:
------------------------
Enteprise Linux Template
Created with Ansible
-----------------------
4.我们还要为chrony服务创建一个新的配置文件来同步我们的公司时间服务器,在roles/filecopyexample/files/chrony.conf中输入:
pool ntp.example.com iburst maxsources 4
keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift
logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1 3
我们打算将这两个文件复制到远程服务器。但是,Ansible并不局限于从Ansible主机复制文件,它还可以将文件从远程服务器直接下载到目标主机:
1.假设你的构建需要docker-compose,我们可以从内部服务器下载它,如果你的映像服务器可以访问internet,甚至可以直接从internet下载。假设我们想在映像中安装docker-compose 1.18.0,我们可以指示Ansible直接从https://github.com/docker/compose/releases/download/1.18.0/docker-compose-Linux-x86_64下载。
2.现在,让我们构建我们的角色来复制两个文件并把docker-compose下载到我们的映像中,这必须写在roles/filecopyexample/tasks/main.yml中。此角色的第一部分显示在以下代码中,用于跨我们前面讨论的两个配置文件进行复制:
---
- name: Copy new MOTD file, and backup any existing file if it exists
copy:
src: files/motd
dest: /etc/motd
owner: root
group: root
mode: '0644'
backup: yes
- name: Copy across new chrony configuration, and backup any existing file if it exists
copy:
src: files/chrony.conf
dest: /etc/chrony.conf
owner: root
group: root
mode: '0644'
backup: yes
然后此角色继续在VM映像上安装docker-compose的任务:
- name: Install docker-compose 1.18.0
get_url:
url: https://github.com/docker/compose/releases/download/1.18.0/docker-compose-Linux-x86_64
dest: /usr/local/bin/docker-compose
mode: 0755
owner: root
group: root
因此,我们的角色现在已经完成了,不过请确保为你的环境正确定制它。例如,docker-compose可能有较新的版本,这意味着前面的get_url模块的url参数将发生更改。
提示
chrony配置文件的路径可能会因操作系统而异。请在运行前面的剧本之前检查此项。示例中显示的路径适用于CentOS 7系统,正如我们先前构建的系统。
3.最后,我们将在顶级目录中(从中创建roles/目录)创建一个供调用的site.yml文件,并运行此角色。这应该包含以下内容:
---
- name: Run example roles
hosts: all
become: yes
roles:
- filecopyexample
4.最后,让我们用ansible-playbook -i hosts site.yml命令运行我们的示例并查看发生了什么:
如我们所见,changed的状态告诉我们所有三个文件都已成功传输或下载,作为示例,我们可以看到现在可以运行docker-compose了,它是在剧本运行期间安装的(尽管这需要Docker正确运行,在本例中没有安装它)。
显然,这个示例已经做出了一个基本假设,即在构建阶段,chrony包安装在我们的示例映像上。尽管出于我们前面讨论的原因,从一个最小的操作系统映像开始是有意义的,但几乎可以肯定的是,在基本构建之上安装一些补充软件包是有必要的,我们将在下一节中对此进行探讨。
5.3.2 安装软件包
我们在上一节中已经介绍了如何安装独立的二进制文件,如docker-compose,但是如果我们需要实际安装一些未安装在我们的基本映像中的其他操作系统软件包呢?例如,cloud-init在大多数云环境中非常有用,但它没有包含在我们之前执行的CentOS 7最小安装中。
在这里,Ansible同样可以提供帮助,这次,我们将定义一个角色来安装我们需要的软件包。我们将重用上一节中的清单文件,并以与之前相同的方式创建一个名为packageinstall的新角色:
1.现在,前面关于复制文件的示例将适用于所有Linux分发版,唯一需要注意的是目标文件存在的位置可能不同。例如,CentOS 7虚拟机映像将在/etc/chrony.conf中安装chrony配置文件,而Ubuntu 18.04 LTS服务器将在/etc/chrony/chrony.conf中安装它。除了对copy模块的dest:参数的这一小改动之外,代码将保持不变。
遗憾的是,软件包的安装会变得更复杂一些。
2.假设我们想在CentOS 7 2示例映像上安装cloud-init和docker,执行此操作所需的角色可能如下所示:
---
- name: Install the epel-release package
yum:
name: epel-release
state: present
- name: Install cloud-init and docker
yum:
name: "{{ item }}"
state: present
loop:
- cloud-init
- docker
3.我们必须先安装EPEL存储库,然后才能安装所需的软件包装。当我们运行它时,输出应该是这样的:
如果你使用的是不同的Linux发行版,那么你需要相应地改变包管理器。例如,在使用apt包管理器的发行版(如Debian或Ubuntu)上,等效的Ansible角色类似于以下代码块:
---
- name: Install cloud-init and docker
apt:
name: "{{ item }}"
state: present
loop:
- cloud-init
- docker.io
注意模块从yum到apt的变化,以及用于Docker容器服务的不同软件包名。除此之外,剧本几乎是一模一样的。
我们可以进一步改进,这种不同导致了需要为两种不同的操作系统基础维护两个不同的角色,但是如果我们可以智能地将它们组合成一个角色呢?幸运的是,Ansible在第一次运行时收集的事实可以用来识别操作系统,从而运行正确的代码。
我们将重新利用前面的示例代码,将这两个安装组合成一个Ansible角色:
1.代码的第一部分与前面的示例几乎相同,只是我们现在已经指定了when子句,以确保它只在基于Debian或Ubuntu的Linux发行版上运行:
---
- name: Install cloud-init and docker
apt:
name: "{{ item }}"
state: present
loop:
- cloud-init
- docker.io
when: ansible_distribution == 'Debian' or ansible_distribution =='Ubuntu'
2.我们再添加两个在CentOS或Red Hat Enterprise Linux上执行安装Docker 所需的步骤:
- name: Install the epel-release package
yum:
name: epel-release
state: present
when: ansible_distribution == 'CentOS' or ansible_distribution =='Red Hat enterprise Linux'
- name: Install cloud-init and docker
yum:
name: "{{ item }}"
state: present
loop:
- cloud-init
- docker
when: ansible_distribution == 'CentOS' or ansible_distribution =='Red Hat enterprise Linux'
再次注意每个任务下的when子句,这些具体示例用于根据Ansible在运行的初始部分获得的事实来确定是否应该运行任务。因此,如果我们现在在Ubuntu系统上运行这个角色,我们会看到以下内容:
3.如你所见,与apt相关的第一个任务是运行的,但是下面基于yum的的两个任务由于不满足when子句的条件,已被跳过。现在,如果我们在CentOS 7目标上运行它,我们会看到:
现在情况正好相反:apt任务被跳过,但运行了两个与yum相关的任务。
通过这种方式,即使在处理几个不同的基本操作系统时,也可以维护单个角色来安装一组通用的软件包需求。将when子句与Ansible事实相结合是一种非常有效的方法,可以确保单个代码库在跨各种系统时的正确行为,因此如果你的SOE确实扩展到基于Debian和Red Hat的系统,那么你仍然可以轻松简单地维护代码。
一旦安装了补充软件包,通常必须对其进行配置才能使其有用。在下一节中,我们将探讨Ansible在编辑配置文件中的用法。
全部0条评论
快来发表一下你的评论吧 !