0 aws. Об AWS

Thank you for reading this post, don't forget to subscribe!

Реги­о­ны. Regions. Дата­цен­тры Ама­зо­на раз­бро­са­ны по все­му миру. Ну кро­ме Рос­сии. У реги­о­нов очень милые име­на. В Ирлан­дии — eu-west-1. Во Франк­фур­те — eu-central-1. В Оре­гоне (штат США такой, на запад­ном побе­ре­жье) — us-west-2. В Север­ной Вир­джи­нии (это уже восток США) — us-east-1. N. Virginia — это «домаш­ний» реги­он AWS, здесь рань­ше все­го появ­ля­ют­ся новые плюшки.

Как пра­ви­ло, все ресур­сы Ама­зо­на при­вя­за­ны к кон­крет­но­му реги­о­ну, и живут имен­но в нём. Даже там, где суще­ству­ют гло­баль­ные про­стран­ства имён, вро­де баке­тов S3, всё рав­но дан­ные хра­нят­ся в кон­крет­ном регионе.

Зоны доступ­но­сти. Availability Zones. Их несколь­ко (как пра­ви­ло три) в каж­дом реги­оне. Ама­зон вро­де как обе­ща­ет, что нико­гда не будет отклю­чать реги­он весь сра­зу, а толь­ко отдель­ные зоны доступ­но­сти. Поэто­му насто­я­тель­но реко­мен­ду­ет­ся раз­ме­щать отка­зо­устой­чи­вые кла­сте­ры одно­вре­мен­но в несколь­ких зонах. Имя зоны доступ­но­сти полу­ча­ет­ся из име­ни реги­о­на добав­ле­ни­ем буков­ки. В Оре­гоне, напри­мер, есть три зоны: us-west-2aus-west-2bus-west-2c.

Самый глав­ный (и исто­ри­че­ски пер­вый) ресурс в ама­зо­но­вом обла­ке — это вир­ту­аль­ные маши­ны EC2. Elastic Compute Cloud. Вот эти вот зоны доступ­но­сти и мно­гое осталь­ное, о чём я рас­ска­жу позд­нее, оно идёт от EC2. И даже если вы не рабо­та­е­те с EC2 напря­мую, навер­ня­ка почти все сер­ви­сы Ама­зо­на рабо­та­ют поверх EC2. И мно­гие сооб­ра­же­ния и огра­ни­че­ния из EC2 рас­про­стра­ня­ют­ся и на эти сервисы.

Иден­ти­фи­ка­то­ры ресур­сов.  Тут пол­ней­ший раз­но­бой. Чаще все­го встре­ча­ет­ся бук­вен­но-циф­ро­вое обо­зна­че­ние, где по пер­вым бук­вам мож­но дога­дать­ся, что это за ресурс.
i-0de8aad2fb20a47ce — это иден­ти­фи­ка­тор инстан­са EC2.
subnet-3447c77fvpc-5e757727 — это под­сеть и VPC. А вот такое:
E1VO622G1KKGV1 — это CloudFront Distribution. Но похо­же, что рано или позд­но всё перей­дёт на ARN (Amazon Resource Name) — иден­ти­фи­ка­то­ры, тех­ни­че­ски очень похо­жие на URN. Вот тот же самый CloudFront Distribution, но в виде ARNarn:aws:cloudfront::999999999999:distribution/E1VO622G1KKGV1.
А вот ARN Application Load Balancerа: arn:aws:elasticloadbalancing:us-west-2:999999999999:loadbalancer/app/sitec-test-alb/fb97bc09692c72ea.

Ничто не меша­ет заво­дить EC2 инстан­сы (и даже Fargate кон­тей­не­ры), и раз­да­вать им сра­зу пуб­лич­ные IP адре­са (пока они есть, и пока они не забло­ки­ро­ва­ны). Но когда у вас куча мик­ро­сер­ви­сов, вовсе не обя­за­тель­но выстав­лять каж­дый из них голой попой в Интер­не­ты (и пла­тить за пуб­лич­ный IP для каж­до­го). То же каса­ет­ся и вся­ких баз дан­ных и кэшей (кото­рые managed сер­ви­сы в Ама­зоне). Их все мож­но эле­гант­но объ­еди­нить в VPC, Virtual Private Cloud.

Мож­но поду­мать, что VPC — это про­сто серая сеть, кото­рая объ­еди­ня­ет создан­ные в ней ресур­сы. Так оно и есть. Но несколь­ко сложнее.

VPC созда­ёт­ся на весь реги­он. Но внут­ри долж­на содер­жать под­се­ти, subnets. А каж­дая под­сеть уже созда­ёт­ся для кон­крет­ной availability zone. У под­се­ти опре­де­лён CIDR блок адре­сов. Либо серые IPv4, какие выбе­ре­те. Либо насто­я­щие IPv6, какие раз­ре­шит Ама­зон. Ресур­сы в VPC все­гда созда­ют­ся в опре­де­лён­ной под­се­ти, и полу­ча­ют IP адрес из её блока.

Точ­нее не сами ресур­сы, а их сете­вые интер­фей­сы, Elastic Network Interfaces. Такие интер­фей­сы в VPC полу­ча­ют не толь­ко EC2 инстан­сы, но и RDS инстан­сы, и ElastiCache инстан­сы, и зада­чи Fargate, и даже NAT Gateway и Elastic Load Balancer. И ко всем ним при­ме­ни­мы пра­ви­ла, о кото­рых далее.

Из VPC нуж­но ходить в интер­не­ты. Что­бы паке­ты какие-нибудь ска­чать. Что­бы к DynamoDB или ECS под­ру­бить­ся. Хоть это и ама­зо­но­вые сер­ви­сы, но с точ­ки зре­ния «из VPC» они нахо­дят­ся «там».

Спо­соб вый­ти в интер­не­ты номер раз. Нужен Internet Gateway. Он один на весь VPC. Ну про­сто ресурс такой, у него даже настро­ек нет. Нуж­но, что­бы у EC2 инстан­са или ELB в под­се­ти был насто­я­щий IP адрес. Нуж­но про­пи­сать марш­рут в таб­ли­це марш­ру­ти­за­ции (Route Table), что, мол, в интер­не­ты ходить через этот Gateway. Ну и свя­зать дан­ную под­сеть с этой таб­ли­цей маршрутизации.

На каж­дую avaiability zone нам нуж­на своя под­сеть. Назо­вём их пуб­лич­ны­ми. Одна таб­ли­ца марш­ру­ти­за­ции, тоже пуб­лич­ная. Где будет ска­за­но, что в 0.0.0.0/0 и ::/0 (не забы­ва­ем про IPv6) нуж­но ходить через Internet Gateway. И Route Table Accosiations, что­бы ска­зать, что каж­дая из наших пуб­лич­ных под­се­тей рабо­та­ет с дан­ной таб­ли­цей марш­ру­ти­за­ции. Но это пока ещё не всё

Спо­соб вый­ти в интер­не­ты номер два. Здесь не нуж­ны пуб­лич­ные IP на каж­дом интер­фей­се. Пото­му что будет NAT. Нужен NAT Gateway. Этой шту­ке нуж­но выдать пуб­лич­ный Elastic IP и раз­ме­стить её Elastic Network Interface в одной из пуб­лич­ных под­се­тей, что мы созда­ли ранее. Доста­точ­но будет одно­го NAT Gateway на VPC.

А теперь мы можем создать ещё под­се­ти. Сно­ва в каж­дой availability zone. И ещё одну таб­ли­цу марш­ру­ти­за­ции. Толь­ко в марш­ру­тах теперь ука­зы­ва­ем, что в интер­не­ты ходить надо через NAT Gateway. Свя­зы­ва­ем новые под­се­ти с этой новой таб­ли­цей марш­ру­ти­за­ции. Теперь интер­фей­сам в этих под­се­тях не нуж­но иметь пуб­лич­ный адрес. Их тра­фик будет натить­ся на NAT Gateway. Так как пря­мые под­клю­че­ния из интер­не­тов в эти под­се­ти будут невоз­мож­ны, назо­вём их при­ват­ны­ми. Как и в слу­чае любо­го NAT, мы сэко­но­ми­ли на пуб­лич­ных адресах.

В слу­чае IPv6 эко­но­мить на адре­сах нуж­ды нет. Но огра­ни­чить вхо­дя­щие под­клю­че­ния тоже хочет­ся. От гре­ха подаль­ше. Для это­го нужен Egress-Only Internet Gateway. Он рабо­та­ет так же как обыч­ный Internet Gateway, но про­пус­ка­ет толь­ко исхо­дя­щие (egress) соединения

Ана­ло­гич­но рабо­та­ет Elastic Load Balancer (ELB). Его интер­фей­сы, где он слу­ша­ет вхо­дя­щий тра­фик, долж­ны быть рас­по­ло­же­ны в пуб­лич­ной под­се­ти, и иметь реаль­ный IP. Ему глав­ное эти под­се­ти ука­зать, а осталь­ное он сам сде­ла­ет. А даль­ше он будет про­ки­ды­вать запро­сы на ваши бэкен­ды уже в при­ват­ных под­се­тях. Толь­ко ему нуж­но, что­бы его интер­фейс и интер­фейс бэкен­да были в одной avaiability zone. Так что одним адре­сом на всю VPC не отде­ла­е­тесь. Нуж­но по адре­су и интер­фей­су на каж­дую зону доступности.

Ну а теперь послед­ний, но очень тол­стый нюанс. Security Groups.

Все эти наши Elastic Network Interface, что EС2 инстан­сов, что Fargate taskов, что load balancerов, что RDS или ElastiCache, обя­за­тель­но при­вя­за­ны к неко­ей Security Group. А часто даже к несколь­ким Security Group. Будем гово­рить, что интер­фейс (или сер­вис) нахо­дит­ся в Security Group.

А сами эти Security Group — это такой firewall. Точ­нее то, что в клас­си­че­ских фай­ер­во­лах назы­ва­ют «zone». Некая зона, или груп­па ресур­сов, для кото­рой зада­ны пра­ви­ла филь­тра­ции вхо­дя­ще­го и исхо­дя­ще­го тра­фи­ка, а так­же пра­ви­ла про­бро­са тра­фи­ка меж­ду зонами.

Вот и для Security Group мож­но задать пра­ви­ла для вхо­дя­ще­го тра­фи­ка. С каких IP адре­сов, на какие пор­ты мож­но. Из какой дру­гой Security Group мож­но. Мож­но задать пра­ви­ла для исхо­дя­ще­го тра­фи­ка. На какие IP адре­са, на какие пор­ты мож­но. На какие дру­гие Security Group мож­но. Отдель­но зада­ёт­ся, мож­но ли интер­фей­сам из одной Security Group ходить к дру­гим интер­фей­сам в этой же груп­пе, то есть раз­ре­шён ли self трафик.

Напри­мер, возь­мём load balancer. Пусть он живёт в сво­ей Security Group, а балан­си­ру­е­мые им сер­ви­сы — в дру­гой Security Group.

Load balancerу нуж­но раз­ре­шить вхо­дя­щий тра­фик ото­всю­ду (из сетей 0.0.0.0/0 и ::/0) на те пор­ты, что он слу­ша­ет (443 в слу­чае HTTPS). И раз­ре­шить исхо­дя­щий тра­фик в Security Group балан­си­ру­е­мых сер­ви­сов, явно ука­зав эту самую их груп­пу. При­чём раз­ре­шить нуж­но как пор­ты, куда будет идти балан­си­ру­е­мый тра­фик, так и пор­ты, куда будет делать­ся health check. В част­ном слу­чае мож­но все пор­ты на выход открыть.

А в Security Group, где рас­по­ло­же­ны ваши сер­ви­сы, нуж­но, наобо­рот, раз­ре­шить вхо­дя­щий тра­фик из Security Group load balancerа, на основ­ные пор­ты, и пор­ты health check. А если этим сер­ви­сам нужен доступ к како­му-нибудь DynamoDB, или это кон­тей­не­ры ECS, то им ещё пона­до­бит­ся раз­ре­шить исхо­дя­щий тра­фик куда угод­но (опять 0.0.0.0/0 и ::/0).

Конеч­но, про­ще поме­стить весь ваш малень­кий кла­стер, вме­сте с load balancers и вся­ки­ми ElastiCache, в одну един­ствен­ную Security Group. Раз­ре­шить в ней self, вхо­дя­щий тра­фик на пару пуб­лич­ных пор­тов балан­се­ра, и исхо­дя­щий тра­фик. И всё. Но это тоже нуж­но не забыть сделать.

о ECS и Fargate.

ECS. Elastic Container Service. Шту­ка для запус­ка Доке­ров в Ама­зоне. Запу­щен­ный там сер­вис мож­но свя­зать с load balancer. Луч­ше с Application Load Balancer, кото­рый пони­ма­ет HTTP(S) и может раз­ру­ли­вать тра­фик, исхо­дя из пути запро­са. Ну и тер­ми­на­ци­ей TLS зани­ма­ет­ся. И вот с момен­та, когда наш сер­вис свя­зал­ся с балан­се­ром, ELB начи­та­ет играть суще­ствен­ную роль в рабо­те ECS.

Во-пер­вых, у нас таки будет нор­маль­ная балан­си­ров­ка тра­фи­ка. Мож­но запу­стить несколь­ко экзем­пля­ров сер­ви­са (в ECS это назы­ва­ет­ся task), балан­сер будет обо всех них в кур­се, и будет делать чест­ный round-robin запро­сов на них. Мож­но даже вклю­чить какой-то stickyness.

Во-вто­рых, балан­сер будет про­ве­рять доступ­ность каж­до­го экзем­пля­ра сер­ви­са. Application Load Balancer уме­ет делать это одним един­ствен­ным спо­со­бом. Он посы­ла­ет HTTP запрос на ука­зан­ный порт и ука­зан­ный путь, и ожи­да­ет ответ «200 OK» (или любой дру­гой, какой настро­и­те). Если какой-то task не отве­тил, балан­сер рапор­ту­ет об этом ECS, а ECS при­би­ва­ет зада­чу (и запус­ка­ет новую).

Про­бле­мой тут может быть вре­мя холод­но­го стар­та. Те же Spring Boot при­ло­же­ния начи­на­ют слу­шать порт и отве­чать на запро­сы через мину­ту-дру­гую (зави­сит от доступ­но­го CPU) после запус­ка. Поэто­му в настрой­ках ECS сер­ви­са нуж­но тща­тель­но ука­зы­вать пара­метр «health check grace period». Мол, ненене, на мне­ние load balancer о здо­ро­вье зада­чи мы начи­на­ем обра­щать вни­ма­ние толь­ко спу­стя вот столь­ко секунд.

В-тре­тьих, балан­сер сле­дит за вре­ме­нем обра­бот­ки запро­са. Если ваш сер­вис не отве­тил в тече­ние 60 секунд, поль­зо­ва­тель, конеч­но, полу­чит свои «504 Gateway Timeout». Но балан­сер сно­ва нажа­лу­ет­ся ECS, а тот сно­ва при­бьёт про­ви­нив­шу­ю­ся задачу.

А если эта зада­ча — един­ствен­ный экзем­пляр это­го сер­ви­са? Пра­виль­но, поль­зо­ва­тель будет видеть «503 Service Unavailable» ещё пару минут, пока новая зада­ча не про­чу­ха­ет­ся и не ста­нет вид­на здо­ро­вой балансеру.

Что делать? Мож­но уве­ли­чить этот шести­де­ся­ти­се­кунд­ный тай­маут по умол­ча­нию. Но зачем? Если бэкенд не отве­ча­ет мину­ту, зна­чит, это какой-то непра­виль­ный бэкенд. Надо с этим что-то делать. Поды­мать несколь­ко задач на сер­вис, что­бы и нагруз­ка балан­си­ро­ва­лась, и уми­ра­ли не все сра­зу. Искать узкие места. Думать, как делать тяжё­лые опе­ра­ции асинхронными…

Fargate. Это новый спо­соб запус­ка кон­тей­не­ров в ECS. Совсем недав­но был досту­пен толь­ко в Север­ной Вир­джи­нии, теперь появил­ся в Ирлан­дии и Оре­гоне точно.

Рань­ше для запус­ка кон­тей­не­ров вы заво­ди­ли вир­ту­аль­ные машин­ки EC2. Запус­ка­ли их из спе­ци­аль­но­го обра­за, где уже есть Docker и ECS агент. Ска­жем, пару маши­нок t2.medium, с 2 vCPU и 4 гига­ми памя­ти на бор­ту. Запус­ка­е­те их в раз­ных availability zone, конеч­но же. (vCPU — это услов­ный попу­гай­ский CPU, кото­рым меря­ет­ся про­из­во­ди­тель­ность в Ама­зоне). И вот на этих машин­ках вы запус­ка­е­те столь­ко кон­тей­не­ров, сколь­ко влезет.

В слу­чае Fargate вы запус­ка­е­те ваши кон­тей­не­ры незна­мо где. Вы про­сто зада­ё­те для тас­ка ECS тре­бо­ва­ния по памя­ти и CPU (обя­за­тель­но оба). И Fargate запу­стит тас­ку на «желе­зе», соот­вет­ству­ю­ще­му этим тре­бо­ва­ни­ям. Выбор доступ­но­го «желе­за» — не силь­но боль­шой. Мини­мум — 0.25 vCPU и 512 мега­байт памяти.

0.25 vCPU — это силь­но мало. Тот же кон­тей­нер, когда вы запус­ка­ли его на EC2, если вы его не огра­ни­чи­ва­ли по CPU, мог вос­поль­зо­вать­ся все­ми 2 vCPU, доступ­ны­ми на этой вир­ту­ал­ке. А с Fargate — толь­ко 0.25. На вре­ме­ни запус­ка Spring Boot это ска­зы­ва­ет­ся ооочень дра­ма­тич­но. (Иро­ния ещё в том, что на t2 инстан­сах EC2 эти 2 vCPU доста­ют­ся очень дёше­во, там на пол­ную катуш­ку мож­но их юзать лишь где-то 20% вре­ме­ни, что более чем доста­точ­но, что­бы делать быст­рые холод­ные стар­ты, а потом не делать ниче­го, нагру­жа­ю­ще­го CPU)

512 мега­байт ОЗУ — это силь­но мно­го. А «мно­го» — это в обла­ках зна­чит, что ещё и доро­го. Вы что, серьёз­но счи­та­е­те, что каж­до­му экзем­пля­ру ваше­го мик­ро­сер­ви­са нуж­но 512 мега­байт памя­ти? Я, может быть, хочу 1 vCPU и 256 памя­ти. Но тако­го нет.

Мож­но воткнуть в одну задачу/сервис несколь­ко кон­тей­не­ров. В кон­це-кон­цов, зада­чи в ECS — это такие podы Kubernetes. Тогда полу­чит­ся как-то более разум­но обой­тись с памя­тью. Но на один сер­вис в ECS мож­но натра­вить толь­ко один load balancer. В резуль­та­те, в одну задачу/сервис вы може­те воткнуть лишь один «пуб­лич­ный» кон­тей­нер, плюс несколь­ко «при­ват­ных» кон­тей­не­ров. «При­ват­ные» смо­гут общать­ся толь­ко друг с дру­гом, но не смо­гут при­ни­мать запро­сы из внеш­не­го мира.

Далее. В Fargate поче­му-то сло­ма­ли то, что все­гда рабо­та­ло в Docker. Файл /etc/hosts.

В Docker у каж­до­го кон­тей­не­ра появ­ля­ет­ся уни­каль­ное имя. И это имя явля­ет­ся и домен­ным име­нем это­го кон­тей­не­ра. И это имя про­пи­сы­ва­ет­ся в /etc/hosts и ука­зы­ва­ет, да хоть бы даже на 127.0.0.1. Это — нормально.

В Fargate нуж­ной запи­си в /etc/hosts поче­му-то не появ­ля­ет­ся. В резуль­та­те, как мини­мум в Java, попыт­ки полу­чить адрес локаль­но­го интер­фей­са завер­ша­ют­ся UnknownHostException. Лечит­ся гряз­но, но лечится:

CMD echo "127.0.0.1 $HOSTNAME" >> /etc/hosts && exec java …

Далее. Как кон­тей­не­ру узнать IP адрес, на кото­ром он запу­щен? При запус­ке в EC2 мож­но было спро­сить http://169.254.169.254/latest/meta-data/local-ipv4 и полу­чить ответ. В Fargate этот фокус не работает.

Зато рабо­та­ет ECS Task Metadata. Запро­сив http://169.254.170.2/v2/metadata мож­но узнать почти всё об этой зада­че. Вклю­чая набор кон­тей­не­ров, и IP адрес, на кото­ром они все запущены.