Работа с AWS EBS в Unix/Linux

Решил сделать небольшую заметку о том, как работать с 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» завершена.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.