Cookie Warning

Tuesday 28 May 2019

Remove Route53 record on EC2 instance termination with Lambda

Creation auto scaling infrastructure requires connecting lot of small pieces, which in statically created infra are done once and than forgot. One of first problems, which you face, is how to name instances, created by auto scaling, in way that you can always access any of them in straightforward way. For this, I come during my work at Rocket Internet, to straightforward solution. Name of instance start always with it's role name, which in most cases is same as name of Auto Scaling Group and than after dash it contains instance id. With this setup we ensure, that each EC2 instance in ASG can be always easily identified and will have unique name. One of first and I think most common role is bastion host, which is entry point to access all resources in VPC. Therefore our bastion is named as bastion-1234567890abcdef0. Name might look long, but this is mostly due to longer IDs, introduced by AWS some time ago.

To get real use of hostnames, they are also added to our internal zone in Route53. This makes also much easier to connect to instance, by using it's name, rather than IP (and you are also independent, if you use IPv4, IPv6 or DualStack). Another topic is making auto completion of those name available in SSH, but this will be topic for another post.

Everything will work smoothly, but after some time and many scaling up and down, you will notice, that your Route53 is full of rubbish - entries of old instances, which are no longer existing, but still resolvable. What more you could hit some problems on Reverse DNS lookups, due to reuse of private IPs. How fast you will face this problem, depends on your subnet size and amount of scale up/down operations.

There are multiple possible solutions of that problem, some of them:

  • Running cronjob, which will clean up Route53 entries of non existing instances
  • Deleting Route53 records on instance termination via rc.d script
  • Using CloudWatch Events to run Lambda, removing entry after instance termination
Below will focus on third solution - using CloudWatch Events and Lambda - as it's most elegant and following AWS good practices. Guide would be to click through AWS Console or launch bunch of CLI commands to get that result, but currently it's not (or at least shouldn't be) way, how you manage your infrastructure. Of course, for smaller setups you can still do it, but even there you will loose quite fast overview of your infrastructure, not even talking about managing it with more than one person. One of best and I think most widely used, modern tool for such purposes is Terraform from Hashicorp. If you never heard about it, you should definitely check it out, as makes managing infrastructure much easier.


Code on first look seems to be long, for such easy feature, like delete record on instance termination, but all happens due to granular AWS permissions, which must be assigned to each role via policy, setup of logging and other features. As you will look through code, you should easily see, that bit less than first 100 lines are to setup permissions to Lambda, CloudWatch Events and CloudWatch Logs. After that there is creation of zipped code, what is required by AWS for each Lambda function. Security Group is required, as our Lambda function must have access to VPC, so must be also located in it. This happens by passing vpc_config parameter. Last step is to grant CloudWatch Events permission to invoke Lambda function, Without that, each step will work well, but when instance will terminate, you will see only in CloudWatch Logs, that invocation failed. Solving this problem took me actually some time and was cherry on top.
Keep in mind, that there some parts, like your aws_vpc and probably paths to python script may differ.

As talking about Python script, below you will find also code for Lambda function, which delete record, for instance, which was terminated. It assume, that record is created in form instance_name.internal_domain, where domain is configured at the top of file.


Code for Terraform was written with version 0.11.7, which was latest stable during it creation, but should work with 0.12, which is current stable as well. Python code was created for 3.7, as Python 2.X support will be finished with end of this year.

Thursday 27 June 2013

XEN a duże wykorzystanie CPU przez maszyny wirtualne

Wirtualizacja zawsze niesie za sobą przynajmniej częściowe wykorzystanie procesora na potrzeby nadzorcy. Przy niskim i umiarkowanym obciążeniu, wirtualne procesorz nie są zbyt często przełączane pomiędzy fizycznymi procesorami i ich rdzeniami, przez co obciążenie jest prawie identyczne, jak dla zlokalizowania wirtualnych maszyn na fizycznych urządzeniach. W przypadku dużego obciążenia procesora, różnice te są już znacznie bardziej widoczne, szczególnie gdy wykonywany jest ten sam proces (np. jest to webserver, na którym główne obciążenie generuje PHP).

W takim przypadku obciążenie przy wirtualizacji będzie dużo wyższe ze względu na potrzebe importowania do pamięci podręcznej procesora (cache L2, L3) samego PHP. Wynika to z częstego przełączania wirtualnego procesora pomiędzy rzeczywistymi procesorami, przez nadzorcę wirtualizacji. Każde takie przełączenie powoduje potrzebę załadowania do pamięci podręcznej procseora ponownie instrukcji niezbędnych danemu systemowi, używanych przy przetwarzaniu danych.

Rozwiązaniem tego problemu jest przypisanie do konkretnego fizycznego rdzenia procesora, wirtualnego procesora. W przypadku gdy suma wszystkich wirtualnych procesorów jest większa od ilości fizycznych rdzeni, należy to wykonać tylko dla najbardziej obciążonych serwerów. W przypadku XEN służy do tego komenda xm vcpu-pin <host> <vcpus> <cpus>. Przykłady jej użycia poniżej:

xm vcpu-set host 8
xm vcpu-set host2 8
xm vcpu-set host3 8
xm vcpu-pin host all 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
xm vcpu-pin host2 all 0,1,2,3,4,5,6,7
xm vcpu-pin host3 all 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31

W powyższym przykładzie deklarujemy dla obu hostów po 8 wirtualnych procesorów, a następnie przypisujemy host2 do jego wszystkich procesorów, 8 fizycznych procesorów. Pozostałe 2 serwery mogą korzystać ze wszystkich pozostałych procesorów.

Zmiana ilości wirtualnych procesorów dla hosta lub modyfikacja ilości maszyn wirtualnych na serwerze spowoduje zresetowanie ustawień przypisania procesorów do domyślnych wartości, czyli każdy wirtualny procesor ma przypisane wszystkie fizyczne procesory, czyli może być migrowany przez zarządzcę między nimi bez ograniczeń.

Tuesday 21 August 2012

Notatki z PostgreSQL

Ponizsze zapytania przydaja sie w codziennym administrowanie PostgreSQL, jednak kazde z nich jest za małe na osobny wpis. Dlatego tez umieszczone sa razem jako "notatki".

database=# SELECT relname, reltuples, relpages * 8 / 1024 AS "size in MB" FROM pg_class ORDER BY relpages DESC;
                   relname                  | reltuples | size in MB 
--------------------------------------------+-----------+------------
 message_post                               |     54409 |        440
 report_data                                |     29533 |         87
 player_stat                                |    434848 |         68
 message_status                             |     33073 |         64
 message_head                               |     26032 |         62
 bank_log                                   |    680535 |         55
 player_stat_pkey                           |    434848 |         53
 player_stat_stat_idx                       |    434848 |         53
 pg_toast_38039                             |     25378 |         52

Jak widac w powyzszym przykładzie mamy tu tez do czynienia z systemowa relacja o nazwie pg_toast_<id>TOAST to mechanizm składowania duzych danych w PostgreSQL. Sama nazwa nie mowi nam, dane ktorej relacji sa przechowywane w tym obiekcie. Na szczescie mozemy to bardzo łatwo sprawdzic
database=# select 38039::oid::regclass;
    regclass     
-----------------
 town_profile
(1 row)

Dzieki temu wiemy, ze obiekt pg_toast_38039 zawiera dane z relacji town_profile.

Thursday 2 August 2012

gsh czyli grupowe SSH

GSH (obecnie przemianowany na Polysh), ktorego nazwa pochodzi od Group Shell, to skrypt umozliwiajacy rownolegle polaczenie z dowolna (ograniczeniem sa tutaj mozliwosci systemu) iloscia serwerow poprzez SSH. Dzieki temu mozemy wykonac dowolne polecenie na wielu hostach naraz. Oczywiscie fani Bash'a zapytaja "ale w czym to lepsze od prostej petli for?":
for i in `db0{01...20}`; do uname -a; done; 
Chocby w tym, ze powyzszy skrypt bedzie wykonywal sie dla kazdego hostu po kolei. W przypadku malej liczby serwerow i szybko wykonujacego sie polecenia nie ma wiekszych problemow. Zaczynaja sie one, gdy mamy wiele (powiedzmy ponad 50) serwerow i skrypt ktory wykonuje sie kilkanascie/kilkadziesiat sekund. W takim przypadku docenimy mozliwosc rownoleglego wykonania tego polecenia na wszystkich hostach. Oczywiscie mozemy z pomoca bash'a wykonac to rownolegle, jednak naklad pracy jest zdecydowanie wiekszy. Do tego wykonanie tego samego polecenia za pomoca gsh jest duzo prostsze:
gsh db0"<01-20>" --command='uname -a' 
#lub
gsh db0"<01-20>"
Ready(20) > uname -a
Pierwszy przykład stosuje rzadko - praktycznie tylko gdy wynik dzialania skryptu na wielu hostach jest dodatkowo przetwarzany dalej (sort, wc, grep etc.). Drugi przyklad umozliwia nam dzialanie jak na zwyklej konsoli, z ta roznica ze polecenie jest wykonywane na wszystkich hostach jednoczesnie. W przypadku maszyn wirtualnych, na kotrych wywolanie danego polecenia/skryptu wywoluje duze obciazenie warto pamietac o zadbaniu by skrypt nie uruchamial sie w tym samym czasie na wszystkich wirtualkach:
sleep $(( $RANDOM%120));

Tuesday 31 July 2012

PostgreSQL 8.4 & lower()

Postgresql 8.4, a dokładniej jego funkcja lower() ma spore problemy z niektorymi jezykami. Przykładem moze byc hiszpanski, dla ktorego blad przedstawiaja ponizsze wywolania tej samej komendy, ale w 2 roznych wersja PostgreSQL (oraz roznych wersjach Debiana).

Postgres 8.4  (on Debian Lenny):
postgres=# select lower('JORGE PEÑA');
   lower    
------------
 jorge peÑa
(1 row)

Postgres 9.0 (on Debian Squeeze):
postgres=# select lower('JORGE PEÑA');
   lower    
------------
 jorge peña
(1 row)
Bład nie miał wpływu na samo działanie bazy danych -  problemy zaczynaja sie dopiero podczas aktualizacji do wersji >=9.0, gdzie bład jest juz poprawiony. Wtedy, w przypadku posiadania ograniczen typu uniqe index migracja danych zakonczy sie porazka i bedzie wymagala naszej recznej interwencji lub skryptu by usunac ten problem. 

Sam blad nie jest nowoscia, nie mniej niektorzy wciaz moga korzystac z dosyc przestarzałej juz wersji 8.4, gdy stabilne wydanie to w chwili obecnej 9.1.4.