Разверните OpenClaw в облаке в один клик
Вход/ Регистрация
На главную
Облачные сервисы

Приватные и публичные сервисы

Если в кластере нужно публиковать часть сервисов в интернет, а часть оставлять доступной только из приватной сети, удобнее всего разделить входящий трафик на уровне ingress-контроллеров. Для этого можно развернуть два экземпляра Traefik: один будет обслуживать публичные Ingress, второй — приватные.

В этой статье разберем такой сценарий. В качестве основы будем использовать два Helm-релиза Traefik с разными IngressClass, а затем покажем, как маршрутизировать запросы к публичным и внутренним сервисам.

Идея в том, что в кластере запускаются два независимых экземпляра Traefik:

  • traefik-public — обрабатывает только Ingress с ingressClassName: public;

  • traefik-private — обрабатывает только Ingress с ingressClassName: private.

Каждый экземпляр создает собственный сервис типа LoadBalancer, но с разными параметрами:

  • публичный Traefik получает внешний IP-адрес и принимает трафик из интернета;

  • приватный Traefik создает внутренний балансировщик без публичного IP и доступен только внутри приватной сети.

За счет этого можно использовать один и тот же кластер для внешних и внутренних приложений, не смешивая их точки входа.

Перед началом убедитесь, что:

Подготовка конфигурации Traefik

Оба экземпляра Traefik разворачиваются из одного Helm-чарта, но используют разные values-файлы.

Конфигурация публичного Traefik

Создайте файл traefik-public-values.yaml со следующим содержимым:

    
fullnameOverride: traefik-public ingressClass: enabled: true isDefaultClass: false name: public service: enabled: true type: LoadBalancer annotations: external-dns.alpha.kubernetes.io/hostname: "example.com" external-dns.alpha.kubernetes.io/ttl: "1200" providers: kubernetesCRD: enabled: true allowCrossNamespace: false ingressClass: public kubernetesIngress: enabled: true ingressClass: public publishedService: enabled: true

Этот файл настраивает публичный экземпляр Traefik, который будет обрабатывать ingress-ресурсы класса public и создавать обычный LoadBalancer с внешним IP.

Здесь важно следующее:

  • ingressClass.name: public — задает имя класса, на который будут ссылаться публичные Ingress;

  • providers.kubernetesCRD.ingressClass и providers.kubernetesIngress.ingressClass — ограничивают Traefik только ресурсами своего класса;

  • publishedService.enabled: true — позволяет корректно публиковать адрес ingress-сервиса.

Аннотации external-dns.alpha.kubernetes.io/* нужны только в том случае, если в кластере используется external-dns.

В значении external-dns.alpha.kubernetes.io/hostname нужно указать реальный домен или поддомен, который вы хотите направить на публичный ingress, например app.example.com.

Если вы используете external-dns, DNS-запись будет создаваться автоматически. Если external-dns в кластере нет, эти аннотации можно убрать и создать DNS-запись вручную. В нашем примере проверка будет выполняться через curl --resolve. В таком случае реальные DNS-записи не обязательны.

Конфигурация приватного Traefik

Создайте файл traefik-private-values.yaml со следующим содержимым:

    
fullnameOverride: traefik-private ingressClass: enabled: true isDefaultClass: false name: private service: enabled: true type: LoadBalancer annotations: k8s.timeweb.cloud/attached-loadbalancer-no-external-ip: "true" providers: kubernetesCRD: enabled: true allowCrossNamespace: false ingressClass: private kubernetesIngress: enabled: true ingressClass: private publishedService: enabled: true

Этот файл настраивает приватный экземпляр Traefik, который будет обрабатывать ingress-ресурсы класса private и создавать внутренний балансировщик без публичного IP.

Ключевой параметр здесь — k8s.timeweb.cloud/attached-loadbalancer-no-external-ip: "true". Он указывает, что балансировщик должен быть внутренним и не получать внешний публичный IP.

Установка двух экземпляров Traefik

Сначала добавьте репозиторий Helm:

    
helm repo add traefik https://helm.traefik.io/traefik helm repo update

После этого разверните публичный экземпляр:

    
helm install traefik-public traefik/traefik \ -n traefik-public --create-namespace \ -f traefik-public-values.yaml

Затем разверните приватный экземпляр:

    
helm install traefik-private traefik/traefik \ -n traefik-private --create-namespace \ -f traefik-private-values.yaml

После установки сразу проверьте, что оба сервиса типа LoadBalancer созданы:

    
kubectl get svc -n traefik-public kubectl get svc -n traefik-private

Создание балансировщиков может занимать до 10 минут. Пока ресурс создается, в колонке EXTERNAL-IP может отображаться состояние pending.

Ожидаемое поведение будет разным:

  • у traefik-public должен появиться внешний IP-адрес;

  • у traefik-private внешний IP не появится, так как он использует внутренний балансировщик.

Проверить состояние балансировщиков можно не только через kubectl, но и в панели управления Timeweb Cloud. Дождитесь завершения создания балансировщиков.

На этом же этапе полезно убедиться, что оба класса ingress зарегистрированы в кластере:

    
kubectl get ingressclass

В выводе должны быть классы public и private. 

Публикация публичных и приватных сервисов

После развертывания двух экземпляров Traefik достаточно указывать нужный IngressClass в манифесте Ingress.

Для публичного сервиса:

    
spec: ingressClassName: public

Для приватного сервиса:

    
spec: ingressClassName: private

Дальше весь трафик будет маршрутизироваться через соответствующий ingress-контроллер.

Практический пример

Ниже приведен полный набор манифестов для демонстрации. В примере будут:

  • два публичных сервиса service1 и service2;

  • один приватный сервис service3;

  • два ingress-ресурса с разными IngressClass.

ConfigMap с тестовыми HTML-страницами

Создайте файл config-map.yaml:

    
apiVersion: v1 kind: ConfigMap metadata: name: service-config namespace: ingress-example data: service1.html: | <html> <head><title>Service 1</title></head> <body><h1>Welcome to Service 1!</h1></body> </html> service2.html: | <html> <head><title>Service 2</title></head> <body><h1>Welcome to Service 2!</h1></body> </html> service3.html: | <html> <head><title>Service 3</title></head> <body><h1>Welcome to Service 3!</h1></body> </html>

Публичный сервис service1

Создайте файл service1-deployment.yaml:

    
apiVersion: apps/v1 kind: Deployment metadata: name: service1 namespace: ingress-example spec: replicas: 2 selector: matchLabels: app: service1 template: metadata: labels: app: service1 spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /usr/share/nginx/html volumes: - name: config-volume configMap: name: service-config items: - key: service1.html path: service1.html --- apiVersion: v1 kind: Service metadata: name: service1 namespace: ingress-example spec: selector: app: service1 ports: - protocol: TCP port: 80 targetPort: 80

Публичный сервис service2

Создайте файл service2-deployment.yaml:

    
apiVersion: apps/v1 kind: Deployment metadata: name: service2 namespace: ingress-example spec: replicas: 2 selector: matchLabels: app: service2 template: metadata: labels: app: service2 spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /usr/share/nginx/html volumes: - name: config-volume configMap: name: service-config items: - key: service2.html path: service2.html --- apiVersion: v1 kind: Service metadata: name: service2 namespace: ingress-example spec: selector: app: service2 ports: - protocol: TCP port: 80 targetPort: 80

Приватный сервис service3

Создайте файл service3-deployment.yaml:

    
apiVersion: apps/v1 kind: Deployment metadata: name: service3 namespace: ingress-example spec: replicas: 2 selector: matchLabels: app: service3 template: metadata: labels: app: service3 spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /usr/share/nginx/html volumes: - name: config-volume configMap: name: service-config items: - key: service3.html path: index.html --- apiVersion: v1 kind: Service metadata: name: service3 namespace: ingress-example spec: selector: app: service3 ports: - protocol: TCP port: 80 targetPort: 80

Публичный ingress

Создайте файл ingress-public.yaml:

    
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-public-ingress namespace: ingress-example spec: ingressClassName: public rules: - host: ingress1.example.com http: paths: - path: /service1 pathType: Prefix backend: service: name: service1 port: number: 80 - path: /service2 pathType: Prefix backend: service: name: service2 port: number: 80

Обратите внимание, что в spec.ingressClassName указано значение public — это означает, что Ingress будет обрабатываться публичным ingress-контроллером.

Приватный ingress

Создайте файл ingress-private.yaml:

    
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-private-ingress namespace: ingress-example spec: ingressClassName: private rules: - host: ingress2.example.com http: paths: - path: / pathType: Prefix backend: service: name: service3 port: number: 80

В spec.ingressClassName указано значение private, поэтому Ingress будет обрабатываться приватным ingress-контроллером.

Создайте неймспейс ingress-example:

    
kubectl create namespace ingress-example

После этого примените все манифесты:

    
kubectl apply -f config-map.yaml kubectl apply -f service1-deployment.yaml kubectl apply -f service2-deployment.yaml kubectl apply -f service3-deployment.yaml kubectl apply -f ingress-public.yaml kubectl apply -f ingress-private.yaml

Теперь проверьте, что деплойменты, сервисы и ingress-ресурсы созданы:

    
kubectl get deploy,svc,ingress -n ingress-example

В результате вы должны увидеть три сервиса, три деплоймента и два ingress-ресурса. Если какого-то ресурса нет, значит соответствующий манифест не применился или был изменен.

Запросы к ingress1.example.com/service1... будут попадать в service1, а запросы к ingress1.example.com/service2... — в service2.

Запросы к ingress2.example.com/ будут попадать в service3.

Такой сервис будет доступен только из приватной сети, если запрос приходит через внутренний балансировщик Traefik.

Проверка работы

Перед проверкой запросами полезно еще раз посмотреть, какой класс назначен каждому ingress:

    
kubectl describe ingress example-public-ingress -n ingress-example kubectl describe ingress example-private-ingress -n ingress-example

Убедитесь, что у example-public-ingress указан Ingress Class: public, а у example-private-ingressIngress Class: private. Если класс не совпадает, Traefik не будет обрабатывать такой ресурс.

В примере используются имена ingress1.example.com и ingress2.example.com. Это условные хосты. Для проверки через curl --resolve их можно использовать без настройки реального DNS. Если вы хотите открывать публичный ingress в браузере, замените ingress1.example.com на реальный домен или поддомен и настройте для него DNS-запись.

Когда публичный балансировщик получит внешний IP, проверьте маршруты командами:

    
curl http://ingress1.example.com/service1.html \ --resolve ingress1.example.com:80:PUBLIC_LB_EXTERNAL_IP curl http://ingress1.example.com/service2.html \ --resolve ingress1.example.com:80:PUBLIC_LB_EXTERNAL_IP

Где PUBLIC_LB_EXTERNAL_IP — публичный IP, выделенный для балансировщика.

Если в ответ приходит 404, проверьте, что вы используете именно хост ingress1.example.com и пути /service1 или /service2

Для приватного сервиса выполните проверку с машины, которая находится в той же приватной сети, что и кластер:

    
curl http://ingress2.example.com/ \   --resolve ingress2.example.com:80:PRIVATE_LB_IP

Где PRIVATE_LB_IP — приватный IP балансировщика.

В ответ вы должны получить HTML-страницу service3.

Если приватный маршрут не открывается, проверьте, что запрос идет из приватной сети. Внешний интернет-трафик на такой ingress не попадет, даже если сам ресурс в кластере настроен правильно.

Была ли статья полезна?
Ваша оценка очень важна
Пока нет комментариев