Für unser SaaS-Angebot nutzen wir die Cloud-Dienste von AWS. Diese richten wir mittels Ansible-Skripten ein. Dabei sind wir auf ein interessantes Problem beim Einbinden zusätzlicher EBS – Volumes auf einer Instanz gestoßen:
Wir binden die Volumes mit dem ec2_vol
Plugin ein:
- name: Create EC2 volume
amazon.aws.ec2_vol:
volume_size: "16G"
volume_type: "gp2"
instance: "{{ instance_id }}"
device_name: sdf
name: "some-data"
tags:
purpose: "{{ purpose }}"
state: present
register: ec2_volume
Laut Dokumentation sollte das Volume nun unter dem device_name
, in diesem Fall also /dev/sdf
, erreichbar sein. Allerdings klappt das nicht wie gedacht. Zum einen werden diese Namen vom Kernel vergeben und sind nicht vorhersagbar. Zum anderen werden NVMe-SSDs mit dem Namensschema /dev/nvmeXnY
versehen. Die Dokumentation von AWS zum Thema EBS/NVMe weist darauf hin und klärt auf, dass der device_name
eigentlich in ein Feld des NVMe-Controllers geschrieben wird.
Wie bindet man ein Volume ein, das soeben erst dynamisch erzeugt wurde?
Der Device Name wird zufällig zugewiesen, und Eigenschaften wie die UUID sind bei neuen Volumes noch unbekannt: wie können wir zuverlässig das richtige Volume einbinden? Dazu gibt es einige Lösungen, die jedoch recht viele Shellskripte erfordern. Ich möchte einen einfacheren Weg mit den Bordmitteln von Ansible aufzeigen:
Dabei können wir ausnutzen, dass unser Volume von AWS eine ID der Form vol-abcdef0123456
erhält. Diese wird dem Volume als Seriennummer zugewiesen, aber ohne den Bindestrich, volabcdef0123456
.
Fragen wir von AWS die Volumeinformationen ab…
- name: Get EC2 volume details
delegate_to: localhost
amazon.aws.ec2_vol_info:
filters:
"tag:Name": "some-data"
register: ec2_vol_info
… und extrahieren die Volume-ID:
- name: Store volume serial number
ansible.builtin.set_fact:
ebs_volume_id: "{{ ec2_vol_info.volumes[0].id | replace('-', '') }}"
Nun können wir mit lsblk
den Pfad zu dem Volume mit dieser „Seriennummer“ ermitteln:
- name: List Linux block devices
ansible.builtin.shell:
cmd: "lsblk -o PATH,SERIAL | grep {{ ebs_volume_id }} | cut -d' ' -f1"
register: lsblk
- name: Store EBS volume device name
ansible.builtin.set_fact:
ebs_volume_device_name: "{{ lsblk.stdout }}"
Und voilà, in ebs_volume_device_name
ist nun der volle Pfad zu unseren Volume gespeichert. Diesen kann man nun nutzen um das Volume zu formatieren und zu mounten:
- name: Format EC2 volume
community.general.filesystem:
fstype: ext4
dev: "{{ ebs_volume_device_name }}"
force: no
- name: Mount EC2 volume
ansible.posix.mount:
path: /mnt/ebs
src: "{{ ebs_volume_device_name }}"
fstype: ext4
boot: yes
state: mounted
Abgesehen von dem Shell-Einzeiler um lsblk
kamen übersichtliche, gut lesbare und wartbare Ansible-Plugins zum Einsatz.
Dieser Ansatz funktioniert auch über Neustarts der Instanz hinweg, so wie es Ansibles idempotentes Design vorsieht.