hidemium's blog

日々学んだことをアウトプットする。

Ansibleとcloud-initを利用した仮想マシンのカスタマイズ

以前、AnsibleのVMwareモジュールとcloud-initを利用した仮想マシンのカスタマイズを書きましたが、今回はAnsibleを使ったcloud-initを利用した仮想マシンのカスタマイズ方法について書いてみたいと思います。

hidemium.hatenablog.com

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Ubuntu 22.04 (テンプレートVM)

インストール

前回の手順と比べてAnsibleのバージョンがだいぶ新しくなっているため、再度インストール手順を載せておきます。

community.vmware を利用したAnsibleが実行できる環境を準備してみようと思います。

Ansible 7(ansible-core 2.14)以降をインストールする場合は、Python 3.9以降がインストールされている必要があります。

Releases and maintenance — Ansible Documentation

venvの環境をインストールします。(Ansibleの公式サイトでは、pipxによるインストール手順に変更されてました。)

$ python3 --version
Python 3.9.5
$ sudo apt install python3.9-venv
$ mkdir ansible-vmware
$ cd ansible-vmware
$ python3 -m venv venv (python3 -m venv [仮想環境ディレクトリ名])

venvというフォルダが作成されました。

仮想環境のアクティベートし、Ansibleをインストールします。

$ source venv/bin/activate
(venv) $ python -m pip install ansible

Collectionをインストールします。requirements.ymlを用意し、インストールしてみます。

(venv) $ vi requirements.yml
collections:
- name: community.vmware
(venv) $ ansible-galaxy collection install -r requirements.yml

collectionのフォルダ内にrequirements.txtがあり、こちらを利用し、pyvmomiとvSphere Automation Python SDKをインストールします。

(venv) $ python -m pip install -r ~/.ansible/collections/ansible_collections/community/vmware/requirements.txt
(venv) $ pip list
Package                            Version
---------------------------------- ---------
ansible                            8.5.0
ansible-core                       2.15.5
certifi                            2023.7.22
cffi                               1.16.0
charset-normalizer                 3.3.0
cryptography                       41.0.4
idna                               3.4
importlib-resources                5.0.7
Jinja2                             3.1.2
lxml                               4.9.3
MarkupSafe                         2.1.3
nsx-policy-python-sdk              4.1.0.1.0
nsx-python-sdk                     4.1.0.1.0
nsx-vmc-aws-integration-python-sdk 4.1.0.1.0
nsx-vmc-policy-python-sdk          4.1.0.1.0
packaging                          23.2
pip                                23.3
pkg_resources                      0.0.0
pycparser                          2.21
pyOpenSSL                          23.2.0
pyvmomi                            8.0.2.0
PyYAML                             6.0.1
requests                           2.31.0
resolvelib                         1.0.1
setuptools                         44.0.0
six                                1.16.0
urllib3                            2.0.6
vapi-common-client                 2.44.0
vapi-runtime                       2.44.0
vcenter-bindings                   4.2.0
vmwarecloud-aws                    1.64.0
vmwarecloud-draas                  1.23.0
vsphere-automation-sdk             1.86.0

前回のAnsibleの構成をもとに、cloud-initを利用した仮想マシンをカスタマイズするロールを書いていきます。

hidemium.hatenablog.com

まず、rolesに guest_cloud_init というロールを作成します。ロールの作成はansible-galaxyコマンドを利用します。

$ cd ~/ansible-vmware
$ cd roles
$ ansible-galaxy init guest_cloud_init
$ tree -L 2
.
└── guest_cloud_init
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml

taskのmain.ymlを編集します。テンプレートからのクローンと、仮想マシンメタデータとユーザデータを設定を行っています。

$ cd guest_cloud_init
$ vi task/main.yml
---
- name:  Clone a virtual machine from Linux template
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ datacenter_name }}"
    validate_certs: no
    state: present
    template: "{{ template }}"
    folder: "/{{ datacenter_name }}/vm"
    name: "{{ vm_name }}"
    cluster: "{{ cluster_name }}"
  delegate_to: localhost

- name:  Set the metadata and userdata for the virtual machine
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    state: present
    name: "{{ vm_name }}"
    advanced_settings:
      - key: guestinfo.metadata
        value: "{{ lookup('template', 'metadata.j2') | b64encode }}"
      - key: guestinfo.metadata.encoding
        value: base64
      - key: guestinfo.userdata
        value: "{{ lookup('template', 'userdata.j2') | b64encode }}"
      - key: guestinfo.userdata.encoding
        value: base64
  delegate_to: localhost

templateのmetadata.j2を編集します。このようにすることで、仮想マシン名とゲストOS内のホスト名を同一にすることができます。テンプレート内ではネットワーク設定は固定としていますが、変数として定義も可能です。

$ vi template/metadata.j2
---
instance-id: {{ vm_name }}
local-hostname: {{ vm_name }}
hostname: {{ vm_name }}
network:
  version: 2
  ethernets:
    ens192:
      dhcp4: false
      addresses:
      - 192.168.1.2/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 192.168.1.1

templateのuserdata.j2を編集します。

$ vi template/userdata.j2
---
#cloud-config

users:
  - default
  - name: username
    ssh_authorized_keys:
      - 公開鍵をペースト
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo, wheel
    lock_passwd: true
    shell: /bin/bash
 
packages:
    - 必要なパッケージ名を追加

次に、taskの実行に必要な変数について homelab_vm グループの情報として取得できるように、group_vars配下にフォルダを作成して記載していきます。

$ cd ~/ansible-vmware/group_vars
$ mkdir homelab_vm
$ cd homelab_vm
$ vi main.yml
---
vcenter_hostname: vCenter Name
datacenter_name: Datacenter Name
cluster_name: Cluster Name
template: Template Name
vm_name: VM Name

playbookを実行できるか確認します。

$ ansible-playbook -i hosts pb_guest_cloud_init.yml
PLAY [homelab_vm] ************************************************************************************************************************************************************************

TASK [guest_cloud_init : Clone a virtual machine from Linux template] ******************************************************************************************************
changed: [VM name -> localhost]

TASK [guest_cloud_init : Set the metadata and userdata for the virtual machine] ******************************************************************************************
changed: [VM name -> localhost]

PLAY RECAP *****************************************************************************************************************************************************************
VM name : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

全体構成として以下のようになりました。

$ tree -L 4
.
├── ansible.cfg
├── group_vars
│   ├── all
│   │   ├── main.yml
│   │   └── vault.yml
│   └── homelab_vm
│       └── main.yml
├── host_vars
├── hosts
├── pb_guest_cloud_init.yml
├── requirements.yml
├── roles
│   └── guest_cloud_init
│       ├── defaults
│       │   └── main.yml
│       ├── files
│       ├── handlers
│       │   └── main.yml
│       ├── meta
│       │   └── main.yml
│       ├── README.md
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   ├── metadata.j2
│       │   └── userdata.j2
│       ├── tests
│       │   ├── inventory
│       │   └── test.yml
│       └── vars
│           └── main.yml
└── venv
    ├── bin
~ 中略 ~

VMが起動後に、ホスト名が変更されたことや、公開鍵認証を利用してsshでログインできることを確認します。

$ ssh username@192.168.1.2
$ hostnamectl

Ansibleのvmware_guestモジュールは、カスタマイズ仕様を使ったゲストOSの設定が可能ですが、cloud-initには対応はしていなかったため、このような方法でcloud-initが利用できるようになり、より自動化がしやすくなっていくのでは思います。

参考