使用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云模板)”的文章。

广告
将在 10 秒后关闭
bannerAds