Решил сделать небольшую заметку о том, как работать с AWS EBS. Существует несколько способов. Расскажу как можно это сделать несколькими способами.
Работа с AWS EBS в Unix/Linux (Ручная)
Покажу на наглядном примере как можно создать, примаунтить том к созданной машине. Начну с того, что создам раздел (AWS EBS волюму), для этого логинимся в AWS Console. Затем, переходим в EC2 раздел. Затем, Volume раздел. После этого, нажимаем на «Create Volume»:
После создание нового раздела, его стоит приатачить на нужный инстанс(машину), для этого, — открываем EC2 -> в блоке ELASTIC BLOCK STORE находим «Volumes», затем находим созданный раздел. Кликаем на нужную волюму правой клавишей мышки и выбираем «Attach Volume» (Так же можно нажать на «Actions -> Attach Volume»):
Зайду на сервер, у меня на нем крутиться Jenkins.
Создаем папку:
$ mkdir /var/build/garson_home
Смотрим какие диски есть в системе:
root@ip-10-101-44-43:~# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 12.6M 1 loop /snap/amazon-ssm-agent/295
loop1 7:1 0 87M 1 loop /snap/core/5145
xvda 202:0 0 100G 0 disk
`-xvda1 202:1 0 100G 0 part /
xvdf 202:80 0 1.5T 0 disk
root@ip-10-101-44-43:~#
ВНИМАНИЕ! Определите, есть ли файловая система на томе(волюме). Новые тома являются необработанными блочными устройствами, и вы должны создать на них файловую систему, прежде чем сможете их монтировать и использовать. Тома, которые были восстановлены из снапшетов, вероятно, уже имеют файловую систему; если вы создаете новую файловую систему поверх существующей файловой системы, операция перезаписывает ваши данные.
Используйте команду «file -s», чтобы получить информацию об устройстве, например, тип файловой системы:
root@ip-10-101-44-43:~# file -s /dev/xvdf
/dev/xvdf: Linux rev 1.0 ext4 filesystem data, UUID=aee3cf72-5a23-4ea6-9c50-af100dfc0912 (needs journal recovery) (extents) (large files) (huge files)
root@ip-10-101-44-43:~#
Сейчас можем смонитировать:
root@ip-10-101-44-43:~# mount /dev/xvdf /var/build/garson_home
root@ip-10-101-44-43:~#
Если получили ошибку:
mount: /var/build/garson_home: wrong fs type, bad option, bad superblock on /dev/xvdf, missing codepage or helper program, or other error.
То решение — форматирование волюмы:
# mkfs -t ext4 /dev/xvdf
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 157286400 4k blocks and 39321600 inodes
Filesystem UUID: e91fac36-3163-415c-a583-b390cce264c7
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000
Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done
и потом можно сново монтировать:
# mount /dev/xvdf /var/build/garson_home
Чтобы отмонтировать раздел, стоит выполнить:
# umount /var/build/garson_home
Получил ошибку при удалении папки:
rm: cannot remove '/var/build/garson_home': Device or resource busy
Фикс:
root@ip-10-101-40-26:~# losetup -d /dev/loop0
root@ip-10-101-40-26:~# fuser -vm /var/build/garson_home
USER PID ACCESS COMMAND
/var/build/garson_home:
root kernel mount /var/build/garson_home
root@ip-10-101-40-26:~# losetup -d /dev/loop1
root@ip-10-101-40-26:~# fuser -vm /var/build/garson_home
USER PID ACCESS COMMAND
/var/build/garson_home:
root kernel mount /var/build/garson_home
root@ip-10-101-40-26:~# losetup -a
/dev/loop1: [66309]:62992 (/var/lib/snapd/snaps/core_5145.snap)
/dev/loop0: [66309]:62995 (/var/lib/snapd/snaps/amazon-ssm-agent_295.snap)
root@ip-10-101-40-26:~# umount /var/build/garson_home
root@ip-10-101-40-26:~#
Как-то так. Все вроде бы логично и работает, но…. при следующем перезапуске ОС, раздел не будет автоматически атачиться к файловой системе, для этого — нужно добавить некоторые действия в fstab.
Для начала, скопируем конфиг:
# cp /etc/fstab /etc/fstab.orig
Это не обязательно, но если что-то пойдет не так — можно будет откатиться назад.
Используйте команду blkid, чтобы найти UUID устройства:
# blkid
Открываем fstab:
# vim /etc/fstab
И прописываем:
UUID=YOUR_UUID_HERE /YOUR_MOUNT_PATH_HERE TYPE_FS_HERE defaults,nofail DUMP_HERE PASS_HERE
Где:
- YOUR_UUID_HERE — Это UUID, который получили с вывода команды blkid для вашего раздела (Пример: bc756bb8-a3cb-4c07-90b9-8827e73687c9).
- YOUR_MOUNT_PATH_HERE — Это путь для монтирования (Например: /var/build/garson_home).
- TYPE_FS_HERE — Это тип монтируемой ФС (Например: xfs).
- defaults,nofail — Это опции монтирования.
- DUMP_HERE — Используется утилитой dump для того чтобы определить, когда делать резервную копию. После установки, dump проверяет эту запись и использует значение, чтобы решить, подключать ли файловую систему. Возможные значения 0 или 1. Если 0, dump игнорирует файловую систему, если 1, dump сделает резервную копию. У большинства пользователей dump не установлен, поэтому в поле следует задать 0. Например: 0.
- PASS_HERE — Это номер прохода. Утилита fsck проверяет число, подставленное в поле и решает, в каком порядке проверять файловую систему. Возможные значения 0, 1 и 2. Файловые системы со значением , равным 0, не будут проверены утилитой fsck. У корневой системы должен быть наибольший приоритет, 1, остальные файловые системы должны иметь приоритет 2. Например: 2.
В интернете видел как некоторые юзают:
YOUR_SECTION_HERE /YOUR_MOUNT_PATH_HERE TYPE_FS_HERE defaults,nofail DUMP_HERE PASS_HERE
Где:
- YOUR_SECTION_HERE — это название раздела. Например: /dev/xvdf. Получить можно с помощью утилиты lsblk.
Ну что, отмонтируем волюму:
# umount /YOUR_MOUNT_PATH_HERE
И примонтируем все:
# mount -a
Работа с AWS EBS в Unix/Linux (Terraform)
Так же, можно написать Терраформ модуль для работы с AWS EBS. У меня он уже имеется и его можно стянуть:
$ git clone git@github.com:SebastianUA/terraform.git
В данном репозитории лежат все мои модули что я писал. Конечно же, там есть и для работы с AWS EBS.
Работа с AWS EBS в Unix/Linux (Python + boto3)
Написал скрипт (Название скрипта будет ebs_volumes.py) и его содержание:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import boto3
import time
import re
from ec2_metadata import ec2_metadata
class bgcolors:
def __init__(self):
self.colors = {
'PURPURE': '\033[95m',
'BLUE': '\033[94m',
'GREEN': '\033[92m',
'YELLOW': '\033[93m',
'RED': '\033[91m',
'ENDC': '\033[0m',
'BOLD': '\033[1m',
'UNDERLINE': '\033[4m'
}
def ec2_connector(b3_client, region, prof_name):
try:
session = boto3.session.Session(profile_name=prof_name)
ec2 = session.client(b3_client, region_name=region)
return ec2
except Exception as err:
print("Failed to create a boto3 client connection to EC2:\n", bgcolors().colors['RED'] + str(err),
bgcolors().colors['ENDC'])
return False
def ec2_describe_parameters():
instance_id = ec2_metadata.instance_id
availability_zone = ec2_metadata.availability_zone
parameters = {'Instance_ID': instance_id, 'Availability_Zone': availability_zone}
return parameters
def ec2_describe_volumes(b3_client, region, prof_name):
volumes_array = []
ec2 = ec2_connector(b3_client, region, prof_name)
if ec2:
volumes = ec2.describe_volumes(Filters=[])
for volume in volumes['Volumes']:
volume_id = volume['VolumeId']
if 'Tags' in volume:
tag_keys = volume['Tags']
for tag_key in tag_keys:
tk = tag_key['Key']
if tk == "Name":
tv = tag_key['Value']
data_tags = {'Volume_Name': tv, 'Volume_ID': volume_id}
volumes_array.append(data_tags)
else:
data_tags = {'Volume_Name': 'None', 'Volume_ID': volume_id}
volumes_array.append(data_tags)
else:
exit(-1)
return volumes_array
def ec2_create_volume(b3_client, region, prof_name, vol_name, vol_env, vol_size):
ec2 = ec2_connector(b3_client, region, prof_name)
if ec2:
volumes = ec2_describe_volumes(b3_client, region, prof_name)
availability_zone = ec2_describe_parameters()['Availability_Zone']
# availability_zone = 'us-west-2a'
if not volumes:
print('None, I will create a new volume!')
try:
new_volume = ec2.create_volume(Size=int(vol_size),
Encrypted=False,
AvailabilityZone='{0}'.format(availability_zone),
VolumeType='gp2',
TagSpecifications = [
{
"ResourceType": 'volume',
'Tags': [
{
'Key': 'Name',
'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
},
{
'Key': 'Environment',
'Value': '{0}'.format(vol_env)
},
{
'Key': 'Orchestration',
'Value': 'py-script'
},
{
'Key': 'Createdby',
'Value': 'Vitaliy Natarov'
}
],
},
]
)
ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
except Exception as err:
print(
"I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
bgcolors().colors['ENDC'])
else:
circle = 0
founded_volumes = []
while circle < len(volumes):
if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
founded_volumes.append(volumes[circle])
circle += 1
if not founded_volumes:
print('I will create a new volume!')
try:
new_volume = ec2.create_volume(Size=int(vol_size),
Encrypted=False,
AvailabilityZone='{0}'.format(availability_zone),
VolumeType='gp2',
TagSpecifications=[
{
"ResourceType": 'volume',
'Tags': [
{
'Key': 'Name',
'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
},
{
'Key': 'Environment',
'Value': '{0}'.format(vol_env)
},
{
'Key': 'Orchestration',
'Value': 'py-script'
},
{
'Key': 'Createdby',
'Value': 'Vitaliy Natarov'
}
],
},
]
)
ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
except Exception as err:
print(
"I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
bgcolors().colors['ENDC'])
else:
print('Woops.... I cant create a new volume. Please use another name to it!!!!!')
else:
exit(-1)
return ec2_create_volume
def ec2_attaching_volumes(b3_client, region, prof_name, vol_name):
ec2 = ec2_connector(b3_client, region, prof_name)
if ec2:
instance_id = ec2_describe_parameters()['Instance_ID']
volumes = ec2_describe_volumes(b3_client, region, prof_name)
#
# instance_id = 'i-0f6f70b38d1ccf0c1'
#
# volumes = [{'Volume_Name': 'test-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1da'},
# {'Volume_Name': 'test2-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1db'}]
if not volumes:
print('None')
exit(0)
else:
circle = 0
founded_volumes = []
while circle < len(volumes):
if re.search(r'{0}'.format(vol_name), volumes[circle]['Volume_Name']):
founded_volumes.append(volumes[circle])
circle += 1
symbols_for_volumes = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']
circle2 = 0
while circle2 < len(founded_volumes):
volume_device_name = '/dev/xvd{0}'.format(symbols_for_volumes[circle2])
try:
print('I will try attach!')
print(volumes[circle2]['Volume_ID'], instance_id, volume_device_name)
ec2.attach_volume(
VolumeId='{0}'.format(volumes[circle2]['Volume_ID']),
InstanceId='{0}'.format(instance_id),
Device='{0}'.format(volume_device_name))
except Exception as err:
print("I can not attach [{0}] volume to [{1}] node:\n".format(volumes[circle2]['Volume_ID'], instance_id),
bgcolors().colors['RED'] + str(err), bgcolors().colors['ENDC'])
circle2 += 1
else:
exit(-1)
return ec2_attaching_volumes
def ec2_delete_volume(b3_client, region, prof_name, vol_name, vol_env):
ec2 = ec2_connector(b3_client, region, prof_name)
if ec2:
volumes = ec2_describe_volumes(b3_client, region, prof_name)
circle = 0
if volumes:
while circle < len(volumes):
if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
volume_id = volumes[circle]['Volume_ID']
try:
delete_volume = ec2.delete_volume(VolumeId='{0}'.format(volume_id))
# ec2.get_waiter('delete_complete').wait(VolumeIds=[delete_volume['VolumeId']])
except Exception as err:
print(
"I can not delete a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
bgcolors().colors['ENDC'])
circle += 1
else:
print('I dont have needed volume name to delete! Please use another one....')
else:
exit(-1)
return ec2_delete_volume
def main():
start__time = time.time()
parser = argparse.ArgumentParser(prog='python3 script_name.py -h',
usage='python3 script_name.py {ARGS}',
add_help=True,
prefix_chars='--/',
epilog='''created by Vitalii Natarov''')
parser.add_argument('--version', action='version', version='v0.5.0')
parser.add_argument('--bclient', '--boto3-client', dest='boto3_client', help='Set boto3 client', default='ec2')
parser.add_argument('--region', dest='region', help='Set AWS region for boto3', default='us-west-2')
parser.add_argument('--pname', '--profile-name', dest='profile_name', help='Set profile name',
default='default')
parser.add_argument('--vol-name', '--volume-name', dest='volume_name', help='Set volume name to find|attach it',
default='test')
parser.add_argument('--vol-env', '--volume-env', dest='volume_env', help='Set env for volume', default='nonprod')
parser.add_argument('--vol-size', '--volume-size', dest='volume_size', help='Set size for volume', default=6)
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--d', dest='describe', help='Describe volumes', action='store_true')
group.add_argument('--describe', dest='describe', help='Describe volumes', action='store_true')
group2 = parser.add_mutually_exclusive_group(required=False)
group2.add_argument('--c', dest='create', help='Create volume', action='store_true', default=argparse.SUPPRESS)
group2.add_argument('--create', dest='create', help='Create volume', action='store_true')
group3 = parser.add_mutually_exclusive_group(required=False)
group3.add_argument('--a', dest='attach', help='Attach volume(s)', action='store_true', default=argparse.SUPPRESS)
group3.add_argument('--attach', dest='attach', help='Attach volume(s)', action='store_true')
group4 = parser.add_mutually_exclusive_group(required=False)
group4.add_argument('--del', dest='delete', help='Delete volume', action='store_true', default=argparse.SUPPRESS)
group4.add_argument('--delete', dest='delete', help='Delete volume', action='store_true')
results = parser.parse_args()
boto3_client = results.boto3_client
region = results.region
profile_name = results.profile_name
volume_name = results.volume_name
volume_env = results.volume_env
volume_size = results.volume_size
if results.describe:
print(ec2_describe_volumes(boto3_client, region, profile_name))
elif results.create:
ec2_create_volume(boto3_client, region, profile_name, volume_name, volume_env, volume_size)
elif results.attach:
ec2_attaching_volumes(boto3_client, region, profile_name, volume_name)
elif results.delete:
ec2_delete_volume(boto3_client, region, profile_name, volume_name, volume_env)
else:
print(bgcolors().colors['GREEN'],
'Please add [--describe] for describe or [--create] for create or [--attach] for attach',
bgcolors().colors['ENDC'])
print(bgcolors().colors['RED'], 'For help, use: script_name.py -h', bgcolors().colors['ENDC'])
exit(0)
end__time = round(time.time() - start__time, 2)
print("--- %s seconds ---" % end__time)
print(bgcolors().colors['GREEN'], "============================================================",
bgcolors().colors['ENDC'])
print(bgcolors().colors['GREEN'], "==========================FINISHED==========================",
bgcolors().colors['ENDC'])
print(bgcolors().colors['GREEN'], "============================================================",
bgcolors().colors['ENDC'])
if __name__ == '__main__':
main()
Вот такой вот скрипт. Чтобы вызвать помощь, выполните:
$ python3 ebs_volumes.py --help
Вот и все, статья «Работа с AWS EBS в Unix/Linux» завершена.