Як ми пробивали Великий Китайський Фаєрвол (ч.2)

Привіт!

З вами знову Микита - системний інженер з компанії SЕMrush. І цією статтею я продовжую історію про те, як ми вигадували рішення обходу Китайського Фаєрвола до нашого сервісу semrush.com.

В попередньої частини я розповів:

  • які з'являються проблеми після того, як приймається рішення «Нам потрібно зробити так, щоб наш сервіс працював у Китаї»
  • які проблеми є у китайського інтернету
  • навіщо потрібна ICP-ліцензія
  • як і чому ми вирішили тестувати наші тестові стенди за допомогою Catchpoint
  • який результат дав наш перший варіант рішення, що базується на Cloudflare China Network
  • як ми знайшли баг у DNS Cloudflare

Ця частина — найцікавіша, на мою думку, тому що зосереджена на конкретних технічних реалізаціях стейджингів. І почнемо ми, а точніше продовжимо, з Алібаба хмара.

Алібаба хмара

Алібаба хмара - Досить великий хмарний провайдер, в якому є всі сервіси, що дозволяють йому чесно називати себе cloud provider. Добре, що вони мають можливість зареєструватися іноземним користувачам, і що більшість сайту перекладено англійською (для Китаю це розкіш). У цій хмарі можна працювати з безліччю регіонів світу, материкового Китаю, а також Океанічної Азії (Гонконг, Тайвань тощо).

IPSEC

Почали з географії. Так як тестовий сайт у нас знаходився в Google Cloud, нам необхідно було зв'язати Alibaba Cloud c GCP, тому відкрили список локацій, в яких є Google. На той момент вони ще не мали свого датацентру в Гонконгу.
Найближчим регіоном виявився asia-east1 (Тайвань). У Ali найближчим регіоном континентального Китаю до Тайваню виявився cn-shenzhen (Шеньчжень).

За допомогою тераформа описали та підняли всю інфраструктуру в GCP та Ali. Тунель 100 мбіт/с між хмарами піднявся майже миттєво. На боці Шеньчженя та Тайваню підняли проксіруючі віртуалки. У Шеньчжені користувальницький трафік термінується, проксується через тунель в Тайвань, а звідти вже йде безпосередньо на зовнішній IP нашого сервісу us-east (Східне узбережжя США). Пінг між віртуалками за тунелем 24msщо не так погано.

Одночасно ми розмістили тестову зону в Хмарний DNS Alibaba. Після делегування зони на NS Ali час резолвінгу знизився з 470 ms до 50 мс. До цього зона теж була на Cloudlfare.

Паралельно з тунелем до asia-east1 підняли ще один тунель із Шеньчженя прямо в us-east4. Там створили ще віртуалок, що проксюють, і почали міряти обидва рішення, маршрутизуючи тестовий трафік за допомогою Cookies або DNS. Схематично тестовий стенд описаний на наступному малюнку:

Latency для тунелів вийшла наступною:
Ali cn-shenzhen <—> GCP asia-east1 - 24ms
Ali cn-shenzhen <-> GCP us-east4 - 200ms

Браузерні випробування Catchpoint рапортували про гарне поліпшення показників.

Порівняйте результати тестів для двох рішень:

Рішення
Uptime
Медіана
75 Percentile
95 Percentile

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

Це дані рішення, що використовує IPSEC-тунель через asia-east1. Через us-east4 результати були гіршими, та й помилок було більше, тому результати наводити не буду.

За результатами цього тесту двох тунелів, один з яких термінується в найближчому регіоні до Китаю, а інший у фінальному пункті призначення, стало зрозуміло, що важливо якнайшвидше виринати з-під китайського фаєрволу, а далі використовувати швидкі мережі (CDN-провайдерів). , хмарних провайдерів та ін.). Не треба намагатися одним махом пройти фаєрвол та потрапити до пункту призначення. Це не найшвидший шлях.

В цілому, результати непогані, однак, у semrush.com медіана 8.8s, а 75% 9.4s (на тому ж тесті).
І перш ніж рухатися далі, я хотів би зробити невеликий ліричний відступ.

Ліричний відступ

Після того, як користувач заходить на сайт www.semrushchina.cn, Який резолвується через “швидкі” китайські DNS-сервери, HTTP-запит йде через наше швидке рішення. Відповідь повертається тим же шляхом, але в усіх JS-скриптах, HTML-сторінках та інших елементах веб-сторінки вказаний домен semrush.com для додаткових ресурсів, які мають бути завантажені під час малювання сторінки. Тобто клієнт резолвіт “головний” А-запис www.semrushchina.cn і йде в швидкий тунель, швидко отримує response - HTML-сторінку, в якій зазначено:

  • скачай такий-то js з sso.semrush.com,
  • CSS файли забери з cdn.semrush.com,
  • і ще візьми картинок з dab.semrush.com
  • і так далі.

Браузер починає йти у "зовнішній" інтернет за цими ресурсами, проходячи щоразу через пожираючий час відповіді фаєрвол.

Але на попередньому тесті представлені результати, коли на сторінці немає ресурсів semrush.com, тільки semrushchina.cn, а *.semrushchina.cn пустує на адресу віртуалки в Шеньчжені, щоб потрапити потім у тунель.

Тільки так, максимально закидаючи весь можливий трафік через своє рішення швидкого проходу китайського фаєрволу, можна отримати прийнятні швидкості та показники доступності сайту, а також чесні результати тестів рішень.
Ми зробили це без жодного виправлення коду на стороні продуктів команд.

Subfilter

Рішення народилося практично відразу після того, як випливла дана проблема. Нам був потрібен PoC (Proof of Concept), що наші рішення проходу фаєрволу справді працюють добре. Для цього потрібно максимально загортати весь трафік сайту в це рішення. І ми застосували subfilter у nginx.

Subfilter — це досить простий модуль nginx, що дозволяє змінювати один рядок у тілі відповіді на інший рядок. Ось ми і змінили всі входження semrush.com на semrushchina.cn у всіх відповідях.

І… це не спрацювало, тому що від бекендів ми отримували стислий контент, відповідно subfilter не знаходив потрібний рядок. Довелося додати ще один локальний server в nginx, який розтискав відповідь і передавав його наступному локальному серверу, який вже займався заміною рядка, стисненням і віддачею наступного по ланцюжку проксі-серверу.

У результаті, де клієнт би отримав .semrush.com, він отримував .semrushchina.cn і слухняно йшов через наше рішення.

Однак недостатньо просто поміняти домен в один бік, адже бекенди все ж таки очікують semrush.com у наступних запитах від клієнта. Відповідно, на тому ж сервері, де робиться заміна в один бік, за допомогою простенького регулярного виразу ми отримуємо піддомен із запиту, а далі робимо proxy_pass зі змінною $хост, виставленої в $subdomain.semrush.com. Може здатися заплутано, але це працює. І працює добре. Для окремих доменів, що потребують іншої логіки, просто створюються свої server-блоки і робиться окрема конфігурація. Нижче представлені укорочені nginx конфіги для наочності та демонстрації цієї схеми.

Наступний конфіг обробляє всі запити з Китаю на .semrushchina.cn:

    listen 80;

    server_name ~^(?<subdomain>[w-]+).semrushchina.cn$;

    sub_filter '.semrush.com' '.semrushchina.cn';
    sub_filter_last_modified on;
    sub_filter_once off;
    sub_filter_types *;

    gzip on;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;

    location / {
        proxy_pass http://127.0.0.1:8083;
        proxy_set_header Accept-Encoding "";
        proxy_set_header Host $subdomain.semrush.com;
        proxy_set_header X-Accept-Encoding $http_accept_encoding;
    }
}

Цей конфіг проксує на локальний на 83 порт, а там чекає наступний конфіг:

    listen 127.0.0.1:8083;

    server_name *.semrush.com;

    location / {
        resolver 8.8.8.8 ipv6=off;
        gunzip on;
        proxy_pass https://$host;
        proxy_set_header Accept-Encoding gzip;
    }
}

Повторюся, це конфіги, що обрізають.

Приблизно так. Може бути складним, але це на словах. Насправді все простіше пареної ріпи 🙂

Кінець ліричного відступу

Якийсь час ми були щасливі, тому що міф про падаючі IPSEC-тунелі не підтвердився. Але потім тунелі почали падати. Декілька разів на добу по кілька хвилин. Небагато, але нас це не влаштовувало. Так як обидва тунелі термінувалися на боці Ali на одному роутері, ми вирішили, що, можливо, це регіональна проблема і потрібно підняти бекапний регіон.

Підняли. Тунелі почали падати у різний час, але у нас чудово відпрацьовував фейловер на рівні upstream у nginx. Але потім тунелі почали падати приблизно одночасно 🙂 І знову почалися 502 та 504. Uptime став погіршуватися, тому ми стали опрацьовувати варіант з Alibaba CEN (Cloud Enterprise Network).

CEN

CEN — це зв'язок двох VPC з різних регіонів усередині Alibaba Cloud, тобто можна з'єднати приватні мережі будь-яких регіонів усередині хмари між собою. І що найголовніше: у цього каналу є досить суворий SLA. Він дуже стабільний як за швидкістю, так і за uptime. Але ніколи не буває все так просто:

  • його ДУЖЕ непросто отримати, якщо ви не китайські громадяни або юрособи,
  • платити потрібно за кожен мегабіт пропускної спроможності каналу.

Отримавши можливість з'єднати Материковий Китай и За кордоном, ми створили CEN між двома регіонами Ali: cn-shenzhen и ми-схід-1 (Найближчою точкою до us-east4). В Ali ми-схід-1 підняли ще одну віртуалку, щоб був ще один хміль.

Вийшло так:

Результати браузерних тестів нижче:

Рішення
Uptime
Медіана
75 Percentile
95 Percentile

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

CEN
99.75
16s
21s
27s

Показники трохи кращі, ніж у IPSEC. Але через IPSEC потенційно можна качати зі швидкістю 100 Мбіт/c, а через CEN тільки зі швидкістю 5 Мбіт/c і дорожче.

Напрошується гібрид, так? Поєднати швидкість IPSEC та стабільність CEN.

Так ми і вчинили, пустивши трафік як через IPSEC, так і через CEN у разі падіння IPSEC-тунелю. Uptime став набагато вищим, але швидкість завантаження сайту поки що залишала бажати кращого. Тоді я намалював усі схеми, які ми вже використовували та тестували, і вирішив спробувати додати до цієї схеми ще трохи GCP, а саме GLB.

GLB

GLB - це Global Load Balancer (або Google Cloud Load Balancer). Він має важливу для нас перевагу: у контексті CDN у нього anycast IP, що дозволяє робити трафік в найближчий до клієнта датацентр, завдяки чому трафік швидше потрапляє в швидку мережу Google і менше йде по "звичайному" інтернету.

Недовго думаючи, ми підняли HTTP/HTTPS LB в GCP і бекенд поставили наші віртуалки з subfilter.

Було кілька схем:

  • використовувати Cloudflare China Network, але на цей раз Origin'ом вказати глобальний IP GLB.
  • Термінувати клієнтів у cn-shenzhen, а звідти проксувати трафік відразу в GLB.
  • Іти відразу з Китаю до GLB.
  • Термінувати клієнтів у cn-shenzhenзвідти проксирувати в asia-east1 через IPSEC (у us-east4 через CEN), звідти вже йти до GLB (спокійно, внизу буде картинка та пояснення)

Ми протестували всі ці варіанти та ще кілька гібридних:

  • Cloudflare + GLB

Ця схема не влаштувала нас за uptime та DNS-помилками. Але тест проводили до фіксу бага з боку CF, можливо, зараз стало краще (проте це не виключає HTTP-таймаути).

  • Ali + GLB

Така схема також не влаштувала нас по uptime, тому що GLB часто випадав з апстріму через неможливість коннекту в прийнятний час або тайм-аут, адже для сервера в Китаї адреса GLB так і залишається зовні, а значить, за китайським фаєрволом. Магії не сталося.

  • GLB only

Варіант, схожий на попередній, тільки в ньому не використовувалися сервери в Китаї: трафік йшов відразу в GLB (поміняли DNS-записи). Відповідно, результати не влаштували, оскільки у звичайних китайських клієнтів, які користуються послугами звичайних інтернет-провайдерів, ситуація з проходженням фаєрволу набагато гірша, ніж у Ali Cloud.

  • Шеньчжень -> (CEN/IPSEC) -> Proxy -> GLB

Тут ми вирішили використати найкраще від усіх рішень:

  • стабільність та гарантований SLA від CEN
  • високу швидкість від IPSEC
  • “швидку” мережу гугла та його anycast.

Схема виглядає приблизно так: трафік користувачів термінуються на віртуалці ch-shenzhen. Там налаштовані апстріми nginx, частина з яких посилається на приватні IP-сервери, що знаходяться на іншому кінці IPSEC-тунелю, а частина апстрімів - на приватні адреси серверів з іншого боку CEN. IPSEC налаштовувався до регіону asia-east1 у GCP (був найближчий регіон до Китаю на момент створення рішення. Зараз у GCP є також присутність у Гонконгу). CEN - до регіону us-east1 в Ali Cloud.

Далі трафік з обох кінців прямував на anycast IP GLB, тобто в найближчу точку присутності гугла, і йшов його мережами в регіон us-east4 в GCP, в якому стояли віртуалки, що підміняють (з subfilter в nginx).

Це гібридне рішення, як ми очікували, дозволило скористатися перевагами кожної технології. Загалом трафік йде через швидкий IPSEC, але якщо починаються проблеми, ми швидко і на кілька хвилин викидаємо ці сервери з апстрімів і шолом трафік тільки через CEN, поки тунель не стабілізується.

Впровадивши 4-е рішення зі списку вище, ми досягли того, чого хотіли і чого вимагав від нас бізнес на той момент часу.

Результати браузерних тестів для нового рішення порівняно з попередніми:

Рішення
Uptime
Медіана
75 Percentile
95 Percentile

Cloudflare
86.6
18s
30s
60s

IPsec
99.79
18s
21s
30s

CEN
99.75
16s
21s
27s

CEN/IPsec + GLB
99.79
13s
16s
25s

CDN

У впровадженому нами рішенні все гаразд, тільки немає CDN, який міг би акселерувати трафік на рівні регіонів і навіть міст. Це має прискорити роботу сайту для кінцевих користувачів за рахунок використання швидких каналів зв'язку CDN-провайдера. І ми весь час думали про це. І ось, настав час наступної ітерації щодо проекту: пошук та тестування CDN-провайдерів у Китаї.

І про це я розповім вам у наступній, заключній частині 🙂

Джерело: habr.com

Додати коментар або відгук