Как я и обещал когда-то, посмотрим теперь, как создать и настроить в гугловом облаке балансировщик нагрузки уже для внешнего траффика. External load balancer (ELB), то есть. Не кнопочками и формочками, а через файлы конфигурации и Deployment Manager, чтобы всё по-взрослому. Ведь хотя external балансировщик и будет похож на internal load balancer, это всё-таки совсем другой зверь:
- Во-первых, к external load balancer подключаются «снаружи» (Ваш КО. Не благодарите.). Но так как под «снаружи» попадает весь мир, основные компоненты балансировщика будут либо глобальные, либо хотя бы региональные.
- Во-вторых, ELB очень понимает, с каким траффиком он работает, и есть вдруг ему на вход подадут HTTP(S), то он в состоянии прочитать его заголовки и по URL выбрать тот бэкэнд сервис, которому этот траффик предназначался. Ага, на один ELB можно навесить много сервисов.
- В-третьих, ELB к тому же ещё и прокси, и если на вход ему приходит SSL, то на нём же SSL сессия и закончится. А дальше контент пойдёт либо с новой сессией, либо вообще нешифрованный.
- Ну и, наконец, убер бонус. Из-за того, что ELB умный, глобальный и исключительно софтварный, его нереально за DDoS’ить. По крайней мере, не сегодня. Серьёзно, самая мощная задокументированная DDoS атака была, кажется, что-то около 500 гигабит траффика в секунду. Всего один гугловый датацентр же может без особых проблем принять 1300. Датацентров много, ELB глобальный, так что have fun. После того как ELB поймёт, что траффик подозрительный, он его просто отсечёт от принимающих инстансов и «растворит» в себе.
На сегодня у гугла есть четыре породы внешних лоад балансеров: HTTP, HTTPS, SSL Proxy и TCP Proxy. Так как HTTPS самый замудрёный, его мы и будем сегодня препарировать.
Из чего сделан HTTPS ELB
Если вы помните, то internal load balancer выглядел примерно вот так:
Структурно, HTTPS ELB выглядит практически так же, плюс пару дополнительных компонентов посередине:
- Глобальный forwarding rule
- TargetHTTPSProxy
- SSLCertificate
- URLMap
- Бэкенд сервис (один или много) и его health check
- Региональная управляемая группа инстансов (виртуальных машин) и шаблон для неё.
Вместе это выглядит примерно вот так:
Да, в общем случае нам нужно будет добавить ещё пару вспомогательных ресурсов. Во-первых, нужно настроить файрвол для health check траффика. Во-вторых, возможно, придётся явно задать внешний IP адрес, если вдруг захочется использовать уже существующий или просто дать ему имя как ресурсу.
И ещё одно «да». Я же упомянул, что для HTTPS ELB на вход можно подавать HTTPS траффик, но дальше к конкретным сервисам и виртуальным машинам он уже может идти расшифрованным. Если наша сеть хорошо защищена, то почему бы и нет. Тот же гугл в пределах одного датацентра не всегда его шифрует, чем мы хуже. В общем, пусть в нашем примере будет именно такой сценарий.
Как обычно, мы рассмотрим все компоненты один за одним, и начнём с конца списка: с управляемой группы инстансов (managed instance group) и её шаблона.
Управляемая группа инстансов (виртуальных машин) и её шаблон
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
resources: - name: http-instance-group-manager type: compute.v1.regionInstanceGroupManager properties: instanceTemplate: $(ref.http-instance-template.selfLink) namedPorts: - name: http port: 80 targetSize: 2 region: us-east1 - name: http-instance-template type: compute.v1.instanceTemplate properties: properties: disks: - autoDelete: true boot: true deviceName: boot initializeParams: sourceImage: projects/ubuntu-os-cloud/global/images/family/ubuntu-1804-lts type: PERSISTENT machineType: f1-micro metadata: items: - key: startup-script value: | sudo apt-get update sudo apt-get install apache2 -y sudo service apache2 restart echo "host:`hostname`" | sudo tee /var/www/html/index.html networkInterfaces: - accessConfigs: - name: External NAT type: ONE_TO_ONE_NAT network: global/networks/default |
В конфигурации шаблона инстанса нет абсолютно ничего интересного. Обычная заготовка Ubuntu сервера с установленным на нём Apache. Хотя в виртуальной машине и задана внешняя айпишка, строго говоря, для ELB она не нужна. Но так как для выхода в интернет такая айпишка уже понадобится, а наш apt-get, устанавливающий Apache сервер, туда обязательно полезет, пришлось её оставить. Можно было бы конечно настроить какой-нибудь NAT Gateway, либо создать образ инстанса с уже предустановленным софтом, но ай.
Кстати, в этот раз управляемая группа инстансов (managed instance group, MIG) стала региональной, что очень полезно для high-availability. Мы скажем группе создать сразу две виртуальные машины, та автоматически раскидает их по разным зонам одного региона, и теперь внезапно мы и отказ одной из зон сможем пережить, и будет среди кого балансировать входящий траффик. Всё по-взрослому.
Наконец, в настройке MIG мы указали, что её инстансы будут раздавать контент на 80-м порту, и в дальнейшем его можно называть просто http
.
Бэкенд сервис
MIG — группа инстансов — в каком-то смысле образует сервис, так что почему бы не объявить его явно. К тому же, если в будущем у нас появится ещё один MIG, например, в другом регионе, но раздающий тот же контент, можно будет добавить его в этот же сервис и таким образом сделать последний глобальным.
1 2 3 4 5 6 7 8 9 10 |
- name: http-backend-service type: compute.v1.backendService properties: backends: - group: $(ref.http-instance-group-manager.instanceGroup) healthChecks: - $(ref.http-health-check.selfLink) loadBalancingScheme: EXTERNAL portName: http protocol: HTTP |
Последние три строки конфигурации сервиса — одновременно и самые интересные — подготавливают сервис к работе с балансировщиком нагрузки. Там мы указываем, что балансировщик будет EXTERNAL, что траффик для него будет идти к порту с именем http, и протокол того траффика — HTTP.
Так как сервис ссылается на health check, которого ещё нет, то создадим-ка мы и его. Ну и ещё правило для файрвола, чтобы траффик пропустили.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- name: http-health-check type: compute.v1.httpHealthCheck - name: allow-http-healthcheck type: compute.v1.firewall properties: sourceRanges: - 35.191.0.0/16 - 130.211.0.0/22 allowed: - IPProtocol: tcp ports: - 80 |
URL Map
А вот теперь что-то совсем новенькое. URL Map работает как роутер для траффика из ELB. Мы можем завести несколько бэкенд сервисов, каждый со своей логикой, а затем настроить URL Map перенаправлять траффик к тому либо другому сервису в зависимости от URL. Это очень круто, потому что API сервисы могут висеть на одних инстансах, медийный контент — на других, а ELB будет общий.
Но сервис у нас всего один, так что мы просто положим его в defaultService
параметр и пойдём гулять дальше.
1 2 3 4 |
- name: http-url-map type: compute.v1.urlMap properties: defaultService: $(ref.http-backend-service.selfLink) |
HTTPSTargetProxy and SSLCertificate
Почти закончили. Ресурс targetHttpsProxy
— это именно та штука, которая будет шифровать и расшифровывать нам траффик при помощи sslCertificate
, ну а так же управлять SSL сессией. Одним своим параметром он ссылается на сертификат-ресурс, а другим — на urlMap
, который будет принимать эстафету.
1 2 3 4 5 6 |
- name: elb-target-https-proxy type: compute.v1.targetHttpsProxy properties: sslCertificates: - $(ref.ssl-cert.selfLink) urlMap: $(ref.http-url-map.selfLink) |
sslCertificate
— это всего лишь ещё один тип ресурса, поддерживаемый Deployment Manager’ом. Я сгененировал сертификат, положил его в YAML, и отправил вместе с ресурсом прямо в бесконечное гугловое облако. Две минуты работы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
- name: ssl-cert type: compute.v1.sslCertificate properties: certificate: | -----BEGIN CERTIFICATE----- MIIC/zCCAeegAwIBAgIJAKYi4Cy0C/w7MA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV BAMMC2V4YW1wbGUuY29tMB4XDTE5MDIwMTIyNDM0MloXDTI5MDEyOTIyNDM0Mlow FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQCsJlalGt3kyfOkSOQ071Lk1we9j34UBzgbydAk8C3kLzv72uEHMGFG O6rdDuCo2/DocD8XZUCQclUWYqPsI34GdmmDMZk+axVi5ivp+BBurWhVJqxFeyTX gQiXOL0p9BgvTCgpw2INOIFwX7w+R7or5HYTIcx63Ag7zNR+BueOJyO4baL62jnE 0nPUEE4gIsrhsNwg4b1sU4u2o3KgmXLU3mn/cqmlUwGsEXeIxqXR924cJ8c+Js/B 27exelnZXP8WcxUClZ4M67o+m2Ztb/DwPWTBjeNes8jkMxHEZb1FEtdddhCeMMGO m6uVy2+myXG2VJT2bGzr2T/O98BG8XVnAgMBAAGjUDBOMB0GA1UdDgQWBBTJ1Ele bydjWrqcr8Ex7WyqCgS0YzAfBgNVHSMEGDAWgBTJ1ElebydjWrqcr8Ex7WyqCgS0 YzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAL1+0cAwaU/OhRCHD0 w3y+PbZovMn36nr4ZXKYPNylMYeLKEJPMLh1s71Lj3G2yQYQLF/FGPl2PHajpJDa vLcI09bF1Olfk7fzB4YfkrDi9bOq01Uii1Ocw3udVxekeHQgOsVqQ4iNHw13Kq4j ABgYBspeSKafwA9yNkdqeW3o7fQGnaUU9h/feoW08Cz5RIHbkmd3e5YIWhV8BxT+ V2aMBQwGQt9QnBRfWv6kNhrrXEGcpQ3uCKpCF2xXiYsncd/imbtPv2+IhVxz0l96 FEDYJi4Ctj4X6kklqiFlTEpFOuhKHCj5xvnLk9NWaqLo+oARfIO++gy6zKRGzTLs t6s2 -----END CERTIFICATE----- privateKey: | -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEArCZWpRrd5MnzpEjkNO9S5NcHvY9+FAc4G8nQJPAt5C87+9rh BzBhRjuq3Q7gqNvw6HA/F2VAkHJVFmKj7CN+BnZpgzGZPmsVYuYr6fgQbq1oVSas RXsk14EIlzi9KfQYL0woKcNiDTiBcF+8Pke6K+R2EyHMetwIO8zUfgbnjicjuG2i +to5xNJz1BBOICLK4bDcIOG9bFOLtqNyoJly1N5p/3KppVMBrBF3iMal0fduHCfH PibPwdu3sXpZ2Vz/FnMVApWeDOu6PptmbW/w8D1kwY3jXrPI5DMRxGW9RRLXXXYQ njDBjpurlctvpslxtlSU9mxs69k/zvfARvF1ZwIDAQABAoIBAQCNdMNlz/ndcgT+ TdcXmEBpQjheD3buRjBYxTB/6cwL4LRNc8HNAngsGgOAuiTpHDGNDg8Jzm2LRCee yVchRtjbvplc8HiXza45IiGbk/cMuvksXybXwSS44JKKkFkADE+DLfUivCXp7zCN gl1QX+gfAQ/1EKTRn9Q0L0+8bzf+mdjMVyz57Y9HLP5RzNde34JCQRc50ZrG4vXs ddng915j2jnQMFjFGd/8GdkgojSzMZv0PEglHB5PKudUnKoYZVNRksVJhyCbb3A+ 23KiV6PA0QdiE7Xl2dX3iQf3Z/7+HtCUT9gONu+PNZoZ05vwCui5j/8nwPpZtyL2 drdCa15hAoGBAOI3y0PScaV6JrgrDopoKqacC45vE8MHSi0WcM/jHLWF0Mn1+uJD pWKD7LZCesEZkg+pd7scl94JMdww+H+gqIgjCQzQh1X5VhgblqPc011hJoKNAt4x U9rNQvS9Fhoh2Ik5KUMkO7OhjHbSqEkkX+jHobtnOd2hjVmK7mpXi9RbAoGBAMLQ Sd6I3CWtbxFd65s8pJCmTW2DD8R3MA+8JqoCcvMk6bKfS+MxmHPoOaWOzdrrvqYW ihxJi7vCi6sq/xeFZT/LF6BG1lkaEYSiLINyAWwjddgOMaZD/CPIGy9WhzAa93RU Bp3B/bxhEJtjrGlxtBbgszoLBeb+dRBbh/fJPYDlAoGBAL5+A0GqbZ7N/NrrDwSH 8Rp5ntWjPb3mXpUXJ4o3kk5dT9MxusFb+2G4+9UCqEIBGVjs+PDshAoqLf1gk3FN xX1WG2HaG4zPOKt2V+TGqIoiq/4VZkvat+UxIefbbkg1JhVvuApc8ZUzPYg1nhZx df4cVVns8/Jo/xFfB6Mu84WvAoGBAKLn8E2Rnp43KICaTFH05Rw8pNSl20KL9HnD +YUDJUKjpHUE9j2XFIggMkx6XTPrHPLgOD+tVJb++TJ6cvQlTWSKHUie09GQlgOW ZajJZd0azgmM3QHPKgJ17B2qusOEWVdCiIHVXavwcyWttNg8B791yQoJe7cNI7E5 CTswYijtAoGAVioH74Yoz/XOpNStblNq1/6DtiHlemvZWp/PhnVmg0djo/6TJMx7 xuMSPimvIRZplQInoZmQeg1XX/9a0wJvBQ8CadnAPQ9hi4KsCeFvRK0tapPb+AXH 4lTE2F20zg/gl6PGRYckZbyA778QyKM2TgM8MBoZvqPJFclqyABSpYI= -----END RSA PRIVATE KEY----- |
Глобальный forwarding rule и внешняя айпишка
Наконец, точка входа в наш ELB — глобальный forwarding rule.
1 2 3 4 5 6 7 8 |
- name: elb-https-forwarding-rule type: compute.v1.globalForwardingRule properties: IPAddress: $(ref.elb-static-ip.selfLink) IPProtocol: TCP loadBalancingScheme: EXTERNAL portRange: 443 target: $(ref.elb-target-https-proxy.selfLink) |
Forwarding rule принимает трафик на 443-м порту из внешнего мира, передаёт его targetHttpsProxy
, и в конечном итоге получается external load balancer.
Если бы мы остановились на этом месте, гугл бы автоматически задал автосгенерированый внешний айпи, и история бы закончилась. Но так как я хочу, чтобы все ресурсы были явными, то добавим-ка ещё две строки для уже явного, но всё ещё автосгенерированного внешнего IP адреса.
1 2 |
- name: elb-static-ip type: compute.v1.globalAddress |
И наконец…
После того, как мы положим все куски конфигурации в какой-нибудь elb_https.yaml
, то можно запустить итоговую конфигурацию в гугловый космос и начать ждать. Сам Deployment Manager работает достаточно быстро, но всё-таки между там, как все куски ELB родятся на свет, и тем, когда они начнут возвращать HTTP 200, может пройти и пять минут, и десять.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
gcloud deployment-manager deployments create test-elb --config elb_https.yaml # .......... #NAME TYPE STATE ERRORS INTENT #allow-http-healthcheck compute.v1.firewall COMPLETED [] #elb-https-forwarding-rule compute.v1.globalForwardingRule COMPLETED [] #elb-static-ip compute.v1.globalAddress COMPLETED [] #elb-target-https-proxy compute.v1.targetHttpsProxy COMPLETED [] #http-backend-service compute.v1.backendService COMPLETED [] #http-health-check compute.v1.httpHealthCheck COMPLETED [] #http-instance-group-manager compute.v1.regionInstanceGroupManager COMPLETED [] #http-instance-template compute.v1.instanceTemplate COMPLETED [] #http-url-map compute.v1.urlMap COMPLETED [] #ssl-cert compute.v1.sslCertificate COMPLETED [] |
Но в конце-концов этот момент наступит. Health checks позеленеют:
Менеджер группы инстансов отрапортует, что все сервера живы-здоровы:
И наступит вселенская благодать:
Мораль
Я только вот автоматом собрался писать, что «ну вот видите, всё было просто», как спохватился. Ну какое оно нафик просто. Это же гугл! Там и от меньшего мозг сворачивало, а уж от балансировщиков нагрузки… Но, в принципе, все компоненты разумны, их связь понятна, просто с количеством перебор. А так, воспринять можно.
Но всё-таки конфигурация ELB это такая штука, которую нужно создать раз, и дальше копипастить, копипастить, и ещё раз копипастить. Во второй раз я с нуля эту штуку не напишу. И вам не советую. Ну разве что для учёбы.
Полезная штука. Интересно, сколько это стоит?
Первые 5 forwarding rule стоят что-то около 18 баксов в месяц за штуку. SSLCertificate, URL Map, сервисы и прокси, вроде, не стоят ничего. Дальше стоить уже будут инстансы, и там можно разгуляться. Один f1-micro (vcpu, 0.6 GB) в месяц идёт бесплатно, но самая дешёвая нормальная машинка будет стоить вроде 32 бакса в месяц. + Диски, тоже зависит от размера. Но теоретически самый дешёвый ELB на одной f1-micro машине можно собрать именно за 18 баксов. Просто непонятно, среди чего именно он будет балансировать траффик 🙂
То есть в принципе, на те 300 баксов кредита на год что они предлагают, можно развернуться и все протестить. Интересно, а потом, если втянешься в это дело, цена не вырастет на порядок?
Не, цены у них стандартные, что в бесплатные 300 баксов, что после них. + если ресурсы используются предсказуемо: если виртуальная машина непрерывно работала целый месяц, то даже идут скидки за постоянное использование (sustained use discounts), которые я совсем забыл учесть, когда думал, стоит ли переезжать в Digital Ocean на гугл. Ну и квоты в настройках проекта можно проставить, мол, если я потратил больше чем сотню, тормози всё.
Насколько я помню, AWS тоже даёт кредит денег на год, и тоже можно играться. Microsoft немного жмоты, там вроде всего на месяц, но я знаю кучу случаев, когда MS давали какой-нить промо сертификат на 10-100k на год на использование в Azure. Типа для продвижения в массы.
Тут главное ни в гугле, ни в любом другом облаке не засветить свои ключи от сервисных аккаунтов и тем более не закоммитить их на гитхабе. Народ репозитории сканит регулярно, мгновенно просекают такие ключи, создают при их помощи биткойн фермы, а в конце месяца приходит счёт на средних размеров новую Тойоту. Но в таких случаях часто можно договориться со службой поддержки, мол, то были злоумышленники, и счета прощают.