Развертывание Google Cloud Build в частном кластере GKE

Я использую Google Kubernetes Engine с опцией «частный кластер». Я также определил «авторизованную главную сеть», чтобы иметь возможность удаленного доступа к среде - это прекрасно работает. Теперь я хочу настроить какой-то конвейер CI / CD с помощью Google Cloud Build - после успешного создания нового образа докера этот новый образ должен быть автоматически развернут в GKE. Когда я впервые запустил новый конвейер, развертывание на GKE завершилось неудачно - сообщение об ошибке было примерно таким: «Невозможно подключиться к серверу: наберите tcp xxx.xxx.xxx.xxx:443: тайм-аут ввода-вывода». Поскольку у меня была опция «авторизованные основные сети», которую подозревали в качестве основной причины тайм-аута подключения, я добавил 0.0.0.0/0 в разрешенные сети и снова запустил задание Cloud Build - на этот раз все прошло хорошо, и после образ докера был создан, он был развернут в GKE. Хороший.

Единственная проблема, которая остается, заключается в том, что я действительно не хочу, чтобы весь Интернет имел доступ к моему мастеру Kubernetes - это плохая идея, не так ли?

Существуют ли более элегантные решения для сужения доступа за счет использования разрешенных основных сетей, а также возможности развертывания с помощью облачной сборки?


person Mizaru    schedule 21.08.2018    source источник
comment
Проблема здесь в том, что Cloud Build API должен взаимодействовать с вашим кластером, поэтому он работал, когда вы изменили авторизованную сеть на 0.0.0.0. Вам нужно будет добавить диапазон IP-адресов, которые API Google используют, в вашу главную авторизованную сеть, что не кажется хорошей идеей. Вместо этого, может ли сборка вызвать что-то на вашем локальном компьютере, который, в свою очередь, инициирует вызов с вашего локального компьютера мастеру K8s для обновления образа?   -  person Patrick W    schedule 21.08.2018
comment
Я действительно стараюсь не вовлекать мою локальную машину в процесс сборки, так как мне не нравится, что я или моя локальная машина являются его жизненно важной частью. Я думаю, что основная причина проблемы очевидна - я подумал, что может быть внутренний способ Google установить соединение с моим кластером. Я также пытался сузить список диапазонов IP-адресов, используемых для облачного конструктора, но попытка сделать это все равно оставила мне довольно длинный список ...   -  person Mizaru    schedule 22.08.2018
comment
Вы определенно можете использовать внутренний способ (вы можете настроить виртуальную машину GCE в той же сети VPC, чтобы использовать внутреннюю конечную точку k8s вместо внешней), но облачный конструктор не будет иметь IP-адрес, который можно было бы считать внутренним, не открывая свой кластер. до гораздо большего количества IP-адресов, чем вы хотели бы   -  person Patrick W    schedule 22.08.2018


Ответы (4)


В настоящее время невозможно добавить машины Cloud Build в VPC. Точно так же Cloud Build не объявляет диапазоны IP-адресов машин сборки. Таким образом, вы не можете сделать это сегодня без создания «экземпляра бастиона ssh» или «экземпляра прокси» на GCE внутри этого VPC.

Я подозреваю, что это скоро изменится. GCB существовал до частных кластеров GKE, а частные кластеры все еще находятся в стадии бета-тестирования.

person Ahmet Alp Balkan    schedule 23.08.2018
comment
хорошо - спасибо за объяснение. Затем я выясню, как настроить ssh / прокси в моем VPC. Надеюсь, Cloud Build скоро научится обрабатывать частные кластеры GKE. - person Mizaru; 23.08.2018
comment
просто чтобы вы знали - я реализовал кое-что, что можно было бы назвать обходным путем: в основном я изменяю разрешенные главные сети прямо перед и после этапа развертывания кубернетов. Таким образом, мне все еще нужно разрешить доступ из 0.0.0.0/0, но - только на несколько секунд. Поскольку это касается только нашей среды разработки, я могу жить с этим компромиссом в области безопасности. - person Mizaru; 31.08.2018
comment
Вместо 0.0.0.0/0 вы можете добавить еще один шаг развертывания перед развертыванием k8s, который в основном ищет общедоступный IP-адрес работающего конвейера ... например, используя контейнер 'curl' для 'curl icanhazip.com', затем используйте это вместо 0.0.0.0 .. - person cosmicnag; 03.09.2018
comment
Очень хорошая идея! Я собираюсь это проверить. Также раньше не знал icanhazip.com - очень полезный сервис, который наверняка может пригодиться во многих других случаях. - person Mizaru; 04.09.2018
comment
Есть ли подходящее решение (предоставленное командой GCP) для этого? - person bluelabel; 13.12.2018
comment
Я нашел это в gcp: cloud.google. ru / solutions / - person bluelabel; 13.12.2018
comment
Помимо использования curl icanhazip.com для определения вашего эффективного общедоступного IP-адреса, я использовал curl checkip.amazonaws.com (или curl https://checkip.amazonaws.com, если у вашей компании разные исходящие IP-адреса для HTTP и HTTPS). Адрес checkip.amazonaws.com используется различными сценариями AWS, поэтому это стабильное долгосрочное имя хоста. (Это не для продвижения конкурента - я просто использую то, что работает.) Кроме того, новое решение, предложенное @dinvlad в прошлом месяце (16 января 2021 г.), выглядит как отличное решение. - person Vincent Yin; 21.02.2021

В итоге мы сделали следующее:

1) Удалите этап развертывания из cloudbuild.yaml

2) Установите Keel внутри частного кластера и дайте ему права редактора / паблика в проекте облачного построителя / реестра.

Кил будет отслеживать изменения в изображениях и автоматически развертывать их в соответствии с вашими настройками.

Это отлично сработало, поскольку теперь мы получаем обновления хешированных образов sha, без добавления vms или создания какого-либо хоста bastion / ssh.

person Farhan Husain    schedule 17.08.2019
comment
Это отличный способ выполнить развертывание кластера Kubernetes, я тоже использую его для пары клиентов. Помимо решения проблемы достижимости частной плоскости управления, это хорошее разделение между Kubernetes и сборками. С keel (keel.io) вы не только получаете автоматическое развертывание, но и можете вручную утверждать их в веб-интерфейсе. - person Overbryd; 03.05.2021

Обновленный ответ (22.02.2021)

К сожалению, хотя описанный ниже метод работает, похоже, что туннели IAP страдают от ограничения скорости. Если через kubectl развернуто много ресурсов, через некоторое время туннель отключается. Мне пришлось использовать еще один трюк: динамически занести IP-адрес Cloud Build в белый список через Terraform, а затем применить его напрямую, что срабатывает каждый раз.

Оригинальный ответ

Также можно создать туннель IAP внутри этапа сборки облака:

- id: kubectl-proxy
  name: gcr.io/cloud-builders/docker
  entrypoint: sh
  args:
  - -c
  - docker run -d --net cloudbuild --name kubectl-proxy
      gcr.io/cloud-builders/gcloud compute start-iap-tunnel
      bastion-instance 8080 --local-host-port 0.0.0.0:8080 --zone us-east1-b &&
    sleep 5

На этом шаге запускается фоновый контейнер Docker с именем kubectl-proxy в cloudbuild сеть, которая используется на всех остальных этапах сборки Cloud. Контейнер Docker устанавливает туннель IAP с использованием удостоверения учетной записи службы Cloud Build. Туннель подключается к экземпляру GCE с предварительно установленным прокси-сервером SOCKS или HTTPS (упражнение предоставляется читателю).

В последующих шагах вы можете получить доступ к кластеру просто как

- id: setup-k8s
  name: gcr.io/cloud-builders/kubectl
  entrypoint: sh
  args:
  - -c
  - HTTPS_PROXY=socks5://kubectl-proxy:8080 kubectl apply -f config.yml

Основные преимущества этого подхода по сравнению с другими, предложенными выше:

  • Нет необходимости иметь хост-бастион с общедоступным IP-адресом - kubectl-proxy хост может быть полностью частным, что обеспечивает конфиденциальность кластера.
  • Туннельное соединение полагается на учетные данные Google по умолчанию, доступные для Cloud Build, и поэтому нет необходимости хранить / передавать какие-либо долгосрочные учетные данные, такие как ключ SSH.
person dinvlad    schedule 16.01.2021
comment
Выглядит отлично! Мне потребовалось время, чтобы понять сетевой поток. Диаграмма, иллюстрирующая каждый переход и соответствующий протокол связи (например, https), очень помогла бы. - person Vincent Yin; 21.02.2021
comment
К сожалению, хотя этот метод работает, похоже, что туннели IAP страдают от ограничения скорости. Если через kubectl развернуто много ресурсов, через некоторое время туннель отключается. Мне пришлось использовать еще один трюк: занести IP Cloud Build в белый список через Terraform, а затем применить его напрямую, что срабатывает каждый раз. Я обновил ответ. - person dinvlad; 22.02.2021
comment
Итак, вы занесли в белый список (общедоступный) IP-адрес Cloud Build в master authorized networks CIDR, верно? Это работает только тогда, когда главный узел K8s имеет внешний IP-адрес, верно? Что, если главный узел имеет внутренний IP-адрес? Ваш метод туннелирования IAP (несмотря на ограничение скорости) обеспечивает подключение на уровне TCP в последнем сценарии. И это ключевое отличие от всех других решений, которые я видел. - person Vincent Yin; 22.02.2021
comment
Это правда, хотя я нашел хороший баланс для наших кластеров: сделать узлы кластера частными, в то время как главный узел останется общедоступным. Таким образом, основные авторизованные сети разрешают доступ только с нескольких избранных IP-адресов, а к мастеру нельзя получить доступ даже с узлов GCP, не внесенных в белый список, за пределами кластера. - person dinvlad; 23.02.2021
comment
как вы думаете, это может быть тот же ответ на этот вопрос? stackoverflow.com/questions/67251653/ - person Arrajj; 25.04.2021
comment
Да, я думаю, это тоже решило бы эту проблему! С нетерпением жду этого. - person dinvlad; 26.04.2021
comment
Я все еще работаю над этим, я установил прокси на экземпляр, и отсоединенный контейнер работает нормально ... Однако у меня есть вопрос, если у меня есть запрос, который должен быть выполнен внутри задания python через это соединение SOCKS5, URL-адрес в код должен быть: HTTP: // localhost: 8080 или в другом формате? - person Arrajj; 04.05.2021
comment
@Khaledarja Я думаю, что с socks5 обычно вы бы использовали socks5h://localhost:8080 или socks5://localhost:8080 (разница в том, как разрешается DNS - вы, вероятно, захотите последнее) - person dinvlad; 06.05.2021
comment
Привет, dinvlad, я попытался выполнить описанные выше шаги и установил прокси-сервер danted socks на моем хосте-бастионе, но когда я пытаюсь запустить его из облачной сборки, я получаю следующую ошибку - Соединение с локальным сервером: 8080 было отказано - вы указали правильный хост или порт? - person Rajathithan Rajasekar; 29.05.2021
comment
@RajathithanRajasekar, это зависит от того, какой порт использует ваш прокси-сервер, по умолчанию, я думаю, 1080. Так что вы можете попробовать это - я привел только пример, потому что у всех разные настройки. - person dinvlad; 29.05.2021
comment
Я изменил порт с 1080 на 8080, я могу установить соединение IAP-Tunnel с хостом-бастионом из моей облачной оболочки, а также могу выполнить команду kubectl, используя соединение SOCKS. Но в облачной сборке, когда я пытаюсь запустить команду kubectl после установления соединения с kubectl-bastion из контейнера kubectl-proxy, я не могу запустить команды kubectl. - person Rajathithan Rajasekar; 29.05.2021
comment
- name: gcr.io/cloud-builders/docker env: - CLOUDSDK_COMPUTE_ZONE = us-central1 - CLOUDSDK_CORE_PROJECT = PROJECTNAME аргументы: - '-c' - ›- docker run -d --net cloudbuild --name kubectl-proxy gcr. io / cloud-builders / gcloud compute start-iap-tunnel bastion 8080 --local-host-port 0.0.0.0:8080 --zone us-central1-a --project ИМЯ ПРОЕКТА && sleep 5 id: точка входа в бастион: sh - person Rajathithan Rajasekar; 29.05.2021
comment
@dinvlad - - имя: gcr.io/cloud-builders/kubectl env: - CLOUDSDK_COMPUTE_ZONE = us-central1 - CLOUDSDK_CORE_PROJECT = PROJECTNAME аргументы: - '-c' - 'HTTPS_PROXY = socks5: // kubelodsl-proxy -n идентификатор оператора: точка входа Deploy-to-k8s: sh - person Rajathithan Rajasekar; 29.05.2021
comment
Не уверен, tbh, если бы вы могли создать Github-суть со своей полной конфигурацией, я мог бы помочь. В комментариях немного сложно устранить эти проблемы ;-) - person dinvlad; 29.05.2021
comment
@dinvlad - Большое спасибо за помощь. Я добавил порт этапов сборки облака, где я установил соединение с kubectl bastion, но на следующем шаге, когда я пытаюсь выполнить команду kubectl, это не удается, gist.github.com/rajathithan/4faf04f94c40772a997e7c774a6fdb9e - person Rajathithan Rajasekar; 30.05.2021

Обновление: я полагаю, что это не будет работать с производительностью по той же причине, что и обновление @ dinvlad выше, то есть ограничение скорости в IAP. Я оставлю здесь свой исходный пост, потому что он решает проблему сетевого подключения и иллюстрирует основной сетевой механизм.

Более того, даже если мы не используем его для Cloud Build, мой метод предоставляет способ туннелирования с моего ноутбука на частный главный узел K8s. Поэтому я могу редактировать yaml-файлы K8s на своем ноутбуке (например, с помощью VS Code) и немедленно выполнять kubectl со своего ноутбука, вместо того, чтобы отправлять код на хост-бастион и выполнять kubectl внутри хоста-бастиона. Я считаю, что это большой стимул для продуктивности времени разработки.

Исходный ответ

================

Я думаю, что могу улучшить отличное решение, предоставленное @dinvlad выше.

Я думаю, что решение можно упростить без установки прокси-сервера HTTP. Еще нужен бастионный хозяин.

Я предлагаю следующее Подтверждение концепции (без HTTP-прокси-сервера). Этот PoC иллюстрирует основной сетевой механизм, не отвлекая внимание от Google Cloud Build (GCB). (Когда у меня будет время в будущем, я опробую полную реализацию в Google Cloud Build.)

Предполагать:

  1. У меня есть кластер GKE, главный узел которого является частным, например, с IP-адресом 10.x.x.x.
  2. У меня есть вычислительный инстанс-бастион с именем my-bastion. У него только частный IP, но не внешний IP. Частный IP-адрес находится в master authorized networks CIDR кластера GKE. Следовательно, изнутри my-bastion, kubectl работает против частного главного узла GKE. Поскольку у my-bastion нет внешнего IP-адреса, мой домашний ноутбук подключается к нему через IAP.
  3. Мой домашний портативный компьютер с моим домашним общедоступным IP-адресом в Интернете не всегда может подключиться к указанному выше частному главному узлу GKE.

Моя цель - выполнить kubectl на моем ноутбуке в этом частном кластере GKE. С точки зрения сетевой архитектуры мой домашний ноутбук похож на сервер Google Cloud Build.

Теория. Зная, что gcloud compute ssh (и связанный с ним IAP) является оболочкой для SSH, SSH Динамическое перенаправление портов должно достичь этой цели.

Практика:

## On laptop:
LAPTOP~$ kubectl get ns
^C            <<<=== Without setting anything up, this hangs (no connectivity to GKE).

## Set up SSH Dynamic Port Forwarding (SOCKS proxy) from laptop's port 8443 to my-bastion.
LAPTOP~$ gcloud compute ssh my-bastion --ssh-flag="-ND 8443" --tunnel-through-iap

В другом терминале моего ноутбука:

## Without using the SOCKS proxy, this returns my laptop's home public IP:
LAPTOP~$ curl https://checkip.amazonaws.com
199.xxx.xxx.xxx

## Using the proxy, the same curl command above now returns a different IP address, 
## i.e., the IP of my-bastion. 
## Note: Although my-bastion doesn't have an external IP, I have a GCP Cloud NAT 
## for its subnet (for purpose unrelated to GKE or tunneling).
## Anyway, this NAT is handy as a demonstration for our curl command here.
LAPTOP~$ HTTPS_PROXY=socks5://127.0.0.1:8443 curl -v --insecure https://checkip.amazonaws.com
* Uses proxy env variable HTTPS_PROXY == 'socks5://127.0.0.1:8443'  <<<=== Confirming it's using the proxy
...
* SOCKS5 communication to checkip.amazonaws.com:443
...
* TLSv1.2 (IN), TLS handshake, Finished (20):             <<<==== successful SSL handshake
...
> GET / HTTP/1.1
> Host: checkip.amazonaws.com
> User-Agent: curl/7.68.0
> Accept: */*
...
< Connection: keep-alive
<
34.xxx.xxx.xxx            <<<=== Returns the GCP Cloud NAT'ed IP address for my-bastion 

Наконец, момент истины для kubectl:

## On laptop:
LAPTOP~$ HTTPS_PROXY=socks5://127.0.0.1:8443 kubectl --insecure-skip-tls-verify=true get ns
NAME              STATUS   AGE
default           Active   3d10h
kube-system       Active   3d10h
person Vincent Yin    schedule 22.02.2021
comment
Я думаю, проблема в том, как запустить этот первый терминал в автономном режиме на этапах сборки облака? - person Arrajj; 04.05.2021
comment
Чтобы ответить на ваш вопрос ^^^, первый фрагмент кода @ dinvlad, запускающий фоновый контейнер Docker, делает свое дело. В его ответе есть несколько хороших самородков, которые не были подробно описаны - это настоящий трюк как в Docker, так и в сети. - person Vincent Yin; 05.05.2021
comment
Ага - извиняюсь, если это было слишком лаконично. Спасибо за уточнение! - person dinvlad; 06.05.2021