使用Ansible操作CloudStack (基础篇:创建和配置虚拟机)
最近很常听到的配置管理工具是Ansible,它不仅可以用于服务器设置,还可以处理CloudStack的各种资源。
这次我们将尝试使用Ansible进行CloudStack操作的基础,包括创建虚拟机和简单的配置。
由于IDCF Cloud Advent Calendar 2015的@snicker_jp的文章和内容非常重复,请务必阅读这篇文章。
只用Ansible从IDCF云上启动虚拟机,构建使用HAProxy的SSL卸载器。 #idcfrontier:原始鳗鱼屋
本次文章中使用的文件可以在下面的存储库中找到。
关于Ansible的CloudStack模块
Ansible 不仅可以操作操作系统中的文件和用户,还可以处理各种云服务中的虚拟机和存储资源。
CloudStack的模块正在开发中,并计划从Ansible 2.0开始默认安装。
虽然有些复杂,但CloudStack模块的开发在以下两个仓库中进行。 同一开发人员负责推进开发,并且具有一定的兼容性,但并不完全相同,所以需要注意。
ansible/ansible-modules-extras
Ansibleの一部として開発がすすめられているレポジトリ
Ansible 2.0のリリース時にはこちらが組み込まれる
resmo/ansible-cloudstack
CloudStackモジュール単体で開発が進められているレポジトリ
こちらで修正を行ってからansible/ansible-modules-extras に反映されるケースが多い
这次将使用resmo/ansible-cloudstack。
安装CloudStack模块
首先,准备一个可以使用CloudStack模块的环境。
在这里,我们介绍以下两种方法。
-
- Ansibleがインストールされている場合
- Dockerを使用する場合
如果已经安装了Ansible
如果已经安装了Ansible,则将CloudStack模块下载到与playbook相同的目录下的library目录中。
$ git clone https://github.com/resmo/ansible-cloudstack.git library
请安装CloudStack模块所需的Python包。
$ pip install cs sshpubkeys
如果使用Docker
因为有很多关于搭建Docker环境的资料,所以省略了详细步骤。
我认为使用Docker Machine比较简单。
我会准备一个用于创建Ansible和CloudStack模块镜像的Dockerfile。我将安装Python包,获取CloudStack模块,并添加到库路径中。
FROM python:2.7
RUN pip install cs ansible sshpubkeys
RUN mkdir -p /usr/share/ansible && \
cd /usr/share/ansible && \
git clone https://github.com/resmo/ansible-cloudstack.git
RUN mkdir -p /etc/ansible ; \
echo "[defaults]" > /etc/ansible/ansible.cfg; \
echo "host_key_checking = False" >> /etc/ansible/ansible.cfg; \
echo "library = /usr/share/ansible/ansible-cloudstack" >> /etc/ansible/ansible.cfg
使用Dockerfile构建镜像。
$ docker build -t ansible .
可以确认在使用docker run创建的镜像中已安装了Ansible。
$ docker run --rm ansible ansible --version
ansible 1.9.4
configured module search path = None
计算机科学的设置
进行CloudStack API的操作,需要设置CloudStack模块所使用的cs的端点、API密钥和密钥。
我們也可以使用設定檔案進行設定,但這次我們試試使用環境變數進行設定。
请将环境变量CLOUDSTACK_ENDPOINT、CLOUDSTACK_KEY和CLOUDSTACK_SECRET设置为相应的端点、API密钥和秘密密钥。
此外,由于超时设置较严格(默认为10秒),可能会导致操作失败。为安全起见,请将超时时间延长至1分钟。(CLOUDSTACK_TIMEOUT=60)
如果使用本地的Ansible,请按照以下方式进行导出。
$ export CLOUDSTACK_ENDPOINT=<APIのエンドポイント>
$ export CLOUDSTACK_KEY=<APIキー>
$ export CLOUDSTACK_SECRET=<シークレットキー>
$ export CLOUDSTACK_TIMEOUT=60
如果使用Docker,首先需要根据以下内容创建一个名为.env的文件。
CLOUDSTACK_ENDPOINT=<APIのエンドポイント>
CLOUDSTACK_KEY=<APIキー>
CLOUDSTACK_SECRET=<シークレットキー>
CLOUDSTACK_TIMEOUT=60
使用docker run命令的–env-file选项来指定该文件,在运行时设置环境变量。
如果设置正确,您可以通过cs命令执行API。
# ローカルのAnsibleを使用する場合は
$ cs listZones
# Dockerを使用する場合
$ docker run --env-file=.env --rm ansible cs listZones
生成SSH密钥
我会创建一个用于连接到主机的SSH密钥。
$ ssh-keygen -f ansible_ssh -N ""
使用Ansible来构建Nginx服务器。
因为准备工作已经完成,所以我将创建一个虚拟机并进行Nginx服务器的部署。
库存文件
在 Inventory 文件中写入要创建的主机和组的设置。
本次我们将在CloudStack上创建一个名为nginx01的主机,并安装Nginx。
在CloudStack上创建了一个名为nginx的主机组来安装Nginx,创建的云主机属于名为cloudstack的主机组,nginx01分别属于这两个主机组。(将整个nginx主机组都包含在cloudstack主机组中。)
[all:vars] 将登录用户和密钥等共同使用的设置写在该部分。
[nginx]
nginx01
[cloudstack:children]
nginx
[all:vars]
ansible_ssh_user=root
ansible_ssh_private_key_file=./ansible_ssh
ansible_python_interpreter=python
cache_dir=./cache
设置变量
在group_vars/cloudstack中,编写CloudStack的默认配置,例如要使用的区域和模板。
---
ssh_public_key_file: "./ansible_ssh.pub"
zone_name: "joule"
network_name: "joule-network1"
service_offering_name: "light.S1"
template_name: "CentOS 7.1 64-bit"
firewall_rules:
- { protocol: "tcp", start_port: 22, end_port: 22 }
port_forwarding_rules:
- { protocol: "tcp", public_port: 22, private_port: 22 }
关于Nginx组,不仅开放SSH端口,还开放HTTP端口。
---
firewall_rules:
- { protocol: "tcp", start_port: 22, end_port: 22 }
- { protocol: "tcp", start_port: 80, end_port: 80 }
port_forwarding_rules:
- { protocol: "tcp", public_port: 22, private_port: 22 }
- { protocol: "tcp", public_port: 80, private_port: 80 }
创建Playbook
这次我们会创建以下三个Playbook。
-
- deploy.yml
仮想マシンのデプロイ、IPアドレスの取得、ポートフォワーディングの設定を行う
nginx.yml
仮想マシンにNginxをインストールする
clean.yml
作成・取得したリソースを削除・解放する
Playbook可通过ansible-playbook命令执行。
$ ansible-playbook -i hosts PLAYBOOK.yml
如果使用Docker,则将当前目录挂载到容器中,以便Playbook在容器中可见。
$ docker run --env-file=.env -v $(pwd):/data -w /data --rm ansible ¥
ansible-playbook -i hosts PLAYBOOK.yml
我会简单地解释一下每个Playbook的内容。
有关详细信息,请参考代码库中的文件。
部署.yml
deploy.yml文件在CloudStack上创建主机,并进行防火墙和端口转发等设置,使主机可以通过SSH连接。
虚拟机的部署
可以使用cs_instance模块来管理虚拟机。
只需设置每个参数,便可将虚拟机状态设置为所指定的状态。
需要启动虚拟机才能进行SSH连接。如果设置为“started”,当执行deploy.yml时,如果虚拟机处于停止状态,将会启动。
- name: "VMの作成"
cs_instance:
name: "{{ inventory_hostname }}"
zone: "{{ zone_name }}"
networks: ["{{ network_name }}"]
service_offering: "{{ service_offering_name }}"
template: "{{ template_name }}"
ssh_key: "{{ inventory_hostname }}"
state: started
register: vm
获取IP地址
为了连接到虚拟机的SSH和web页面,需要获取IP地址。
IP地址的管理可通过cs_ip_address模块进行。
在获取IP地址时需要注意。
由于Ansible基本上不会记住上次执行的处理过程,因此如果仅使用state: present 进行获取,则每次执行都会获取IP地址。
我们有几种对策可供选择,但我决定保存一个文件,其中写有获取的IP地址。如果文件已经存在,那么表示已经获取过,我们将跳过处理。
- name: "キャッシュ用ディレクトリの作成"
file:
path: "{{ vm_cache_dir }}"
state: "directory"
mode: "0755"
- name: "取得済みIPアドレスの確認"
stat:
path: "{{ vm_cache_dir + '/ip_address' }}"
register: cache_ip_address
- name: "新規IPアドレスの取得"
cs_ip_address:
zone: "{{ zone_name }}"
network: "{{ network_name }}"
register: ip_address
when: not cache_ip_address.stat.exists
- name: "取得したIPアドレスをキャッシュへ書き込み"
copy:
content: "{{ ip_address.ip_address }}"
dest: "{{ vm_cache_dir + '/ip_address' }}"
when: not cache_ip_address.stat.exists
- name: "IPアドレスのキャッシュ読み込み"
set_fact:
ip_address: "{{ lookup('file', vm_cache_dir + '/ip_address') }}"
设置防火墙端口转发规则。
因为已获取到IP地址,所以将配置SSH和HTTP的防火墙以及端口转发规则。
管理防火墙和端口转发规则分别使用 cs_firewall 和 cs_portforward。
在使用这些模块时需要注意的是,务必要指定区域。
如果没有指定区域,可能会在意料之外的区域中寻找 IP 地址并导致错误。
- name: "ファイアウォールルールの設定"
cs_firewall:
zone: "{{ zone_name }}"
ip_address: "{{ ip_address }}"
protocol: "{{ item.protocol }}"
start_port: "{{ item.start_port }}"
end_port: "{{ item.end_port }}"
cidr: "{{ item.cidr|default('0.0.0.0/0') }}"
with_items: firewall_rules
等待直到SSH连接可用
等到虚拟机可通过SSH连接为止,最后才进行等待。
- name: "SSHで接続可能になるまで待機"
wait_for:
host: "{{ ip_address }}"
port: "{{ ansible_ssh_port|default(22) }}"
search_regex: "OpenSSH"
- name: "authorized_keysの設定に時間がかかる場合があるので余分に待機"
pause: minutes=2
when: vm|changed
登入已创建好的虚拟机
您可以使用以下命令登录到创建的虚拟机。
$ ssh -i ansible_ssh root@$(cat cache/nginx01/ip_address)
nginx.yml的中国翻译选项:
```yaml:nginx.yml
---
- hosts: nginx
gather_facts: no
pre_tasks:
- include: set_ansible_ssh_host.yml
tasks:
- name: "Nginxをインストール(Ubuntu)"
apt:
name: "nginx"
update_cache: yes
when: ansible_distribution == 'Ubuntu'
- name: "EPELをインストール(CentOS)"
yum:
name: "epel-release"
when: ansible_distribution == 'CentOS'
- name: "Nginxをインストール(CentOS)"
yum:
name: "nginx"
when: ansible_distribution == 'CentOS'
- name: "Nginxを起動"
service:
name: "nginx"
enabled: yes
state: started
在执行安装Nginx的任务之前,我们会在pre_tasks中运行set_ansible_ssh_host.yml。set_ansible_ssh_host.yml会将已获取的IP地址设置到ansible_ssh_host中,以确保可以通过SSH连接到目标主机。
set_ansible_ssh_host.yml 执行时还会获取主机信息。
这将根据主机的信息来更改处理方式。
nginx.yml可以根据不同的发行版更改使用的模块,
以适应CentOS和Ubuntu两种操作系统。
---
- set_fact:
vm_cache_dir: "{{ cache_dir + '/' + inventory_hostname }}"
- name: "取得済みIPアドレスの確認"
stat:
path: "{{ vm_cache_dir + '/ip_address' }}"
register: cache_ip_address
connection: local
failed_when: not cache_ip_address.stat.exists
- name: "ansible_ssh_hostに取得済みのIPをセット"
set_fact:
ansible_ssh_host: "{{ lookup('file', vm_cache_dir + '/ip_address') }}"
- name: "仮想マシンのステータスを確認"
cs_instance:
name: "{{ inventory_hostname }}"
zone: "{{ zone_name }}"
state: present
connection: local
register: instance
- name: "ホスト情報の取得"
setup:
when: instance.state == "Running"
如果配置成功,你可以通过浏览器打开 cache/nginx01/ip_address 的IP地址来查看Nginx的界面。
清洁配置文件.yml
clean.yml是用于释放和取消已创建或获取的资源的Playbook。
删除时,需要注意不要忘记指定区域。如果未指定区域,存在于多个区域的资源可能会被意外删除。
---
- hosts: cloudstack
connection: local
tasks:
- set_fact:
vm_cache_dir: "{{ cache_dir + '/' + inventory_hostname }}"
- name: "SSH公開鍵の削除"
cs_sshkeypair:
name: "{{ inventory_hostname }}"
state: absent
- name: "取得済みIPアドレスの確認"
stat:
path: "{{ vm_cache_dir + '/ip_address' }}"
register: cache_ip_address
- name: "IPアドレスのキャッシュ読み込み"
set_fact:
ip_address: "{{ lookup('file', vm_cache_dir + '/ip_address') }}"
when: cache_ip_address.stat.exists
- name: "IPアドレスの解放"
cs_ip_address:
zone: "{{ zone_name }}"
network: "{{ network_name }}"
ip_address: "{{ ip_address }}"
state: absent
when: cache_ip_address.stat.exists
- name: "IPアドレスキャッシュの削除"
file:
path: "{{ vm_cache_dir + '/ip_address' }}"
state: absent
when: cache_ip_address.stat.exists
- name: "VMの削除"
cs_instance:
name: "{{ inventory_hostname }}"
zone: "{{ zone_name }}"
state: expunged
- name: "キャッシュ用ディレクトリの削除"
file:
path: "{{ vm_cache_dir }}"
state: absent
最终、最后、到最后,最后来说,最终结果,最后一步,闭幕,最后总结,最终目标
我认为,如果使用Vagrant或Terraform,根据我们这次讨论的内容,创建和配置虚拟机会更好。因为这些工具可以通过ID来管理资源,减少了操作意外资源的风险,更加安全。此外,由于使用范围较为有限,设置也更加简单。
我认为Ansible在操作CloudStack方面的优点在于,它方便编写详细处理步骤,易于将现有资源设为可管理的对象,以及它支持的资源类型非常丰富。
下一次的计划是将这些优势应用到实例中,进行现有模板的定制化,作为应用篇的内容。
在Qiita上有一篇题为”使用Ansible来操作CloudStack(进阶篇:自定义IDCF云模板)”的文章。