Krótki wstęp do AWS i boto3

Jest sporo poradników na temat tego jak korzystać z chmury AWS. Oto kolejny 🙂

Przygotowanie środowiska

Na początek zainstaluj potrzebne pakiety:

apt update
apt install ipython3 python3-boto3

i załóż konto na aws.amazon.com. Po zalogowaniu się do console.aws.amazon.com wpisz w polu do przeszukiwania serwisów AWS IAM. IAM to narzędzie do zarządzania kluczami dostępu – Identity and Access Manager. Służą one do przydzielania dostępu do Twoich zasobów w AWS skryptom. Dzięki temu nie musisz zapisywać hasła na przykład na serwerze, lecz tylko klucz i ID klucza.

Następnie kliknij users i wybierz lub stwórz nowego użytkownika. W detalach, wybierz zakładkę Security credentials. Powinna tam być sekcja Access keys. Kliknij Create access key. W okienku dialogowym pokaże się tabelka z ID klucza oraz sam klucz (widoczny po kliknięciu show).

Zapisz te wartości w pliku ~/.aws/credentials na Twoim komputerze. Boto3 oraz inne narzędzia AWS, z których możesz korzystać będą szukać właśnie tam domyślnych danych do logowania w AWS:

[default]
aws_access_key_id = ABCDEFGHIJKL
aws_secret_access_key = joJ/fw4tgp98sjoif34faCSDSv459...

Pierwsze kroki

Odpal konsolę ipython3, zaimportuj boto3 i połącz się z AWS:

# ipython3
import boto3

ec2 = boto3.resource('ec2', 'us-east-1')

Za pomocą obiektu ec2 będziemy mogli odpytać API AWS o dostępne zasoby i uruchomić nowe instancje w danym regionie.

Listowanie zasobów

Poniższy fragment wyświetli obrazy, których właścicielem jest Canonical (użytkownik o ID 099720109477):

for i in ec2.images.filter(Owners=['099720109477']):
   print(i)

ec2.images, podobnie jak instances, vpcs itd. posiada kilka metod służących do selektywnego listowania zasobów w AWS. Aby wyświetlić wszystkie zasoby, można wykorzystać ec2.images.all(), jednak nie polecam tego akurat na images – zbyt dużo wyników. Powyższa metoda filter pozwala przekazać kilka rodzajów kryteriów do wyświetlenia. Aby zobaczyć pełną dokumentację wpisz w konsoli ipython:

In [11]: ec2.images.filter?

i naciśnij enter. Powinna pokazać się pełna dokumentacja funkcji:

Signature: ec2.images.filter(**kwargs)
Docstring:
Creates an iterable of all Image resources in the collection filtered by kwargs passed to method.

See also: `AWS API Documentation `_


**Request Syntax**
::

  image_iterator = ec2.images.filter(
      ExecutableUsers=[
          'string',
      ],
      Filters=[
          {
              'Name': 'string',
              'Values': [
                  'string',
              ]
          },
      ],
      ImageIds=[
          'string',
      ],
      Owners=[
          'string',
      ],
      DryRun=True|False
  )
...

Na kolejnych stronach dokumentacji

Tworzenie nowych obrazów

Użyj metody ec2.create_volume aby stworzyć nowy obraz dysku. Sprawdź jej parametry wywołując ją najpierw z pytajnikiem. Na początek możesz dodać parametr DryRun=True aby nie zaalokować niepotrzebnie zasobów:

v = ec2.create_volume(DryRun=True)

API boto3 w odpowiedzi powinno zwrócić wyjątek z informacją jakich parametrów brakuje. Pododawaj je aż uzyskasz prawidłową odpowiedź z API.

Jednym z parametrów, jaki będziesz musiał podać jest AvailabilityZone, która jest czymś dokładniejszym niż określenie samego regionu. Aby zobaczyć jakie są AvailabilityZones w Twoim regionie, będziemy potrzebowali kolejnego obiektu API – ec2.client. Za jego pomocą można zobaczyć nieco inne parametry niż z tego, co zwróciło nam poprzednio ec2.resource:

ec2client = boto3.client('ec2', 'us-east-1')
ec2client.describe_availability_zones()
Out[22]:
{'AvailabilityZones': [{'Messages': [],
   'RegionName': 'us-east-1',
   'State': 'available',
   'ZoneName': 'us-east-1xyz'
...
In [23]:

Wybierz jedną strefę i wróć do tworzenia obrazu. Finalnie powinieneś dostać taki komunikat z DryRun=True:

ClientError: An error occurred (DryRunOperation) when calling the CreateVolume operation: Request would have succeeded, but DryRun flag is set.

Oznacza to dokładnie tyle, że wszystkie checki przeszyły i pozostało tylko usunąć DryRun aby poprawnie wysłać żądanie do AWS. Sprawdź jeszcze tylko rozmiar tworzonego volumenu aby nie zjeść całej swojej quoty 🙂

Jeśli zapisałeś zwrócony wynik do zmiennej, to powinieneś móc odczytać ID obrazu:

print(v.id)

Jeśli nie, to prawdopodobnie dostałeś podobny do poniższego string:

Out[26]: ec2.Volume(id='vol-0348fjqpif...')

Możesz stworzyć nowy obiekt odpowiadający Twojemu obrazowi:

v2 = ec2.Volume(id='vol-0348fjqpif...')

Mając “w ręce” taki obiekt można sprawdzić też jaki obraz ma rozmiar (v2.size), jego status (v2.state) i podobne rzeczy. Jeśli chcesz zobaczyć w konsoli ipython wszystkie opcje, wpisz v2 i wciśnij tabulator:

In [31]: v2. [to wciśnij tabulator :) ]
            v.attach_to_instance         v.create_time                v.enable_io                  v.kms_key_id                 v.size                       v.volume_id
            v.attachments                v.delete                     v.encrypted                  v.load                       v.snapshot_id                v.volume_type
            v.availability_zone          v.describe_attribute         v.get_available_subresources v.meta                       v.snapshots
            v.create_snapshot            v.describe_status            v.id                         v.modify_attribute           v.state
            v.create_tags                v.detach_from_instance       v.iops                       v.reload                     v.tags

Strzałkami w górę/dół można wybrać sobie funkcję. Ot taki feature dla leniwych 🙂 Na koniec, żeby nie dostać po kieszeni można skasować wolumen:

v2.delete()

Tworzenie wirtualnej maszyny – EC2 instance

Przejdźmy przez dodanie wszystkich parametrów po kolei:

i = ec2.create_instances(DryDun=True, MinCount=1, MaxCount=1)

Funckja pozwala stworzyć wiele instancji na raz. Pierwszymi parametrami o jakie będzie krzyczyała są powyższe MinCount i MaxCount. Tutaj chcemy zrobić dokładnie jedną instancje, więc oba są równe 1.

Wybór obrazu
Drugi brakujący parametr to ImageId. Zmodyfikujmy nieco wcześniejszą pętle aby sprawdzić jakie są nazwy obrazów i ich ID:

for i in ec2.images.filter(Owners=['099720109477'], Filters=[{'Name': 'name', 'Values': ['*bionic*']}]):
    print(str(i.id) + ' - ' + str(i.name))

Dodatkowy parametr metody filter to Filter. Przyjmuje on listę filtrów opisanych słownikiem. Wymaganymi polami są Name, który określa nazwę pola, którego dotyczy dany filtr oraz Values który określa dopuszczalne wartości tego pola w dowolnym pasującym wpisie. W powyższym przykładzie filtrujemy po polu name i wymagamy, aby w nazwie obrazu pojawiło się słowo bionic w dowolnym otoczeniu (gwiazdki):

[
    {
        'Name': 'name',
        'Values': ['*bionic*']
    }
]

Można też dołożyć kolejne kryteria aby na przykład wylistować tylko publiczne obrazy:

[
    {
        'Name': 'name',
        'Values': ['*bionic*']
    },
    {
        'Name': 'is-public',
        'Values': ['true']
    }
]

i tak dalej… Wybierz swój ID obrazu z ubuntu i idźmy dalej. ami-0a313d6098716f372 to na przykład Ubuntu Bionic LTS w wersji server minimal.

Dodanie VPC i sieci

Kolejnym brakującym parametrem będzie VPC, o ile nie masz swojego. Stwórzmy nowe i dodajmy od razu do niego sieć. VPC to wirtualna prywatna sieć, w której można dodawać adresacje podsieci, routing i trochę innych fajnych rzeczy. To trochę tak jakbyś łączył kabelki do switcha, a później dodawał adresacje na serwerze dhcp jako subnet:

vpc = ec2.create_vpc(CidrBlock='10.0.0.0/16')
subnet = vpc.create_subnet(CidrBlock='10.0.1.0/24')
iface = subnet.create_network_interface()

Cały subnet powinien mieścić się w adresacji jaką podałeś w create_vpc. Obiekt iface odpowiada podłączeniu do naszej nowej sieci. Coś w rodzaju ethernetowego kabelka z przypiętym adresem IP znajdującym się w danej sieci (subnet). Przypisany losowy adres możesz sprawdzić wpisując:

print(iface.private_ip_address)

Jeśli nie zapisałeś któregoś wyniku w zmiennej, w ipythonowej sesji, w każdej chwili możesz taki obiekt odtworzyć:

vpc.create_subnet(CidrBlock='10.0.1.0/24')
Out[32]: ec2.Subnet(id='subnet-12345...')

s = ec2.Subnet(id='subnet-12345...')

Stworzenie instancji – po raz ostateczny

Z tymi dwoma obiektami możemy wrócić do tworzenia instancji:

i = ec2.create_instances(MinCount=1, MaxCount=1, ImageId='ami-0a313d6098716f372', InstanceType='c4.xlarge', SubnetId=s.id)

Create_instances zwraca listę wyników – można ustawić większą ilość maszyn w MaxCount. Jeśli nie AWS’owi nie uda się stworzyć takiej ilości jak MaxCount będzie próbował aż do osiągnięcia MinCount. Jeśli nie będzie mógł stworzyć MinCount maszyn, to zwróci błąd, np. przez ograniczenia Twojego konta.

Jeśli zastanawiasz się po co nam był nowy interfejs – zgadza się, nie był potrzebny 🙂 Można go skasować: iface.delete()

Aby sprawdzić w jakim stanie jest nasza wirtualna maszyna, możesz wpisać:

instance = i[0]
print(instance.state)

Za każdym razem w odpowiedzi dostaniesz “pending”. To przez fakt, że boto3 pamięta ostatnią odpowiedź API AWS. Aby odświeżyć stan instancji (to samo tyczy się obrazów, VPC itd.) wywołaj instance.reload().

Posprzątanie

Na koniec pozostaje posprzątanie wszystkiego:

inst.terminate()

# Reload subnet information to be able to release it. If there are ifaces attached, first remove them
subnet.reload()
subnet.delete()

vpc.delete()

Leave a Reply