как подключить контейнер kubernetes к внешнему миру без правила пересылки (движок контейнеров Google)

Я использую службу Google Container Engine, и у меня есть модуль, на котором запущен сервер, прослушивающий порт 3000. Я настроил службу для подключения порта 80 к порту 3000 этого модуля. Я могу свернуть службу, используя ее локальный и общедоступный IP-адрес из внутри узла, но не снаружи. Я установил правило брандмауэра, чтобы разрешить порт 80 и отправить его на узел, но я продолжаю получать сообщение «В соединении отказано» из-за пределов сети. Я пытаюсь сделать это без правила пересылки, так как есть только один модуль, и похоже, что правила пересылки стоят денег и выполняют балансировку нагрузки. Я думаю, что правило брандмауэра работает, потому что, когда я добавляю createExternalLoadBalancer: true в спецификацию службы, внешний IP-адрес, созданный правилом переадресации, работает должным образом. Мне нужно что-то еще делать? Проложить маршрут что ли?

controller.yaml

kind: ReplicationController
apiVersion: v1beta3
metadata:
    name: app-frontend
    labels:
        name: app-frontend
        app: app
        role: frontend
spec:
    replicas: 1
    selector:
        name: app-frontend
    template:
        metadata:
            labels:
                name: app-frontend
                app: app
                role: frontend
        spec:
            containers:
                - name: node-frontend
                  image: gcr.io/project_id/app-frontend
                  ports:
                    - name: app-frontend-port
                      containerPort: 3000
                      targetPort: 3000
                      protocol: TCP

service.yaml

kind: Service
apiVersion: v1beta3
metadata:
  name: app-frontend-service
  labels:
    name: app-frontend-service
    app: app
    role: frontend
spec:
  ports:
    - port: 80
      targetPort: app-frontend-port
      protocol: TCP
  publicIPs:
   - 123.45.67.89
  selector:
    name: app-frontend

Изменить (дополнительные сведения): при создании этой службы добавляются эти дополнительные правила, обнаруженные при запуске iptables -L -t nat

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination         
REDIRECT   tcp  --  anywhere             10.247.247.206       /* default/app-frontend-service: */ tcp dpt:http redir ports 56859
REDIRECT   tcp  --  anywhere             89.67.45.123.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http redir ports 56859
Chain KUBE-PORTALS-HOST (1 references)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             10.247.247.206       /* default/app-frontend-service: */ tcp dpt:http to:10.241.69.28:56859
DNAT       tcp  --  anywhere             89.67.45.123.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http to:10.241.69.28:56859

Я не совсем понимаю iptables, поэтому не уверен, насколько порт назначения соответствует моей службе. Я обнаружил, что DNS для 89.67.45.123.bc.googleusercontent.com разрешается в 123.45.67.89.

kubectl get services показывает указанный мной IP-адрес и порт:

NAME                             IP(S)               PORT(S)
app-frontend-service             10.247.243.151      80/TCP
                                 123.45.67.89

В /var/log/kube-proxy.log не отображается ничего недавнего с внешних IP-адресов.


person Reese    schedule 26.05.2015    source источник


Ответы (3)


TL; DR: используйте внутренний IP-адрес вашего узла в качестве общедоступного IP-адреса в определении вашей службы.


Если вы включите подробное ведение журнала на kube-proxy, вы увидите, что он, похоже, создает соответствующее правило IP-таблиц:

I0602 04:07:32.046823   24360 roundrobin.go:98] LoadBalancerRR service "default/app-frontend-service:" did not exist, created
I0602 04:07:32.047153   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 10.119.244.130/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
I0602 04:07:32.048446   24360 proxier.go:606] Opened iptables from-host portal for service "default/app-frontend-service:" on TCP 10.119.244.130:80
I0602 04:07:32.049525   24360 iptables.go:186] running iptables -C [KUBE-PORTALS-CONTAINER -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j REDIRECT --to-ports 36970]
I0602 04:07:32.050872   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-CONTAINER -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j REDIRECT --to-ports 36970]
I0602 04:07:32.052247   24360 proxier.go:595] Opened iptables from-containers portal for service "default/app-frontend-service:" on TCP 23.251.156.36:80
I0602 04:07:32.053222   24360 iptables.go:186] running iptables -C [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
I0602 04:07:32.054491   24360 iptables.go:186] running iptables -A [KUBE-PORTALS-HOST -t nat -m comment --comment default/app-frontend-service: -p tcp -m tcp -d 23.251.156.36/32 --dport 80 -j DNAT --to-destination 10.240.121.42:36970]
I0602 04:07:32.055848   24360 proxier.go:606] Opened iptables from-host portal for service "default/app-frontend-service:" on TCP 23.251.156.36:80

Перечисление записей iptables с использованием -L -t показывает, что общедоступный IP-адрес преобразован в обратное DNS-имя, как вы видели:

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination         
REDIRECT   tcp  --  anywhere             10.119.240.2         /* default/kubernetes: */ tcp dpt:https redir ports 50353
REDIRECT   tcp  --  anywhere             10.119.240.1         /* default/kubernetes-ro: */ tcp dpt:http redir ports 54605
REDIRECT   udp  --  anywhere             10.119.240.10        /* default/kube-dns:dns */ udp dpt:domain redir ports 37723
REDIRECT   tcp  --  anywhere             10.119.240.10        /* default/kube-dns:dns-tcp */ tcp dpt:domain redir ports 50126
REDIRECT   tcp  --  anywhere             10.119.244.130       /* default/app-frontend-service: */ tcp dpt:http redir ports 36970
REDIRECT   tcp  --  anywhere             36.156.251.23.bc.googleusercontent.com  /* default/app-frontend-service: */ tcp dpt:http redir ports 36970

Но добавление опции -n показывает IP-адрес (по умолчанию -L выполняет обратный поиск по IP-адресу, поэтому вы видите DNS-имя):

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination         
REDIRECT   tcp  --  0.0.0.0/0            10.119.240.2         /* default/kubernetes: */ tcp dpt:443 redir ports 50353
REDIRECT   tcp  --  0.0.0.0/0            10.119.240.1         /* default/kubernetes-ro: */ tcp dpt:80 redir ports 54605
REDIRECT   udp  --  0.0.0.0/0            10.119.240.10        /* default/kube-dns:dns */ udp dpt:53 redir ports 37723
REDIRECT   tcp  --  0.0.0.0/0            10.119.240.10        /* default/kube-dns:dns-tcp */ tcp dpt:53 redir ports 50126
REDIRECT   tcp  --  0.0.0.0/0            10.119.244.130       /* default/app-frontend-service: */ tcp dpt:80 redir ports 36970
REDIRECT   tcp  --  0.0.0.0/0            23.251.156.36        /* default/app-frontend-service: */ tcp dpt:80 redir ports 36970

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

$ curl 10.119.244.130:80
app-frontend-5pl5s
$ curl 23.251.156.36:80
app-frontend-5pl5s

Без добавления правила брандмауэра попытка удаленного подключения к общедоступному IP-адресу истекает. Если вы добавите правило брандмауэра, вы надежно получите отказ в соединении:

$ curl 23.251.156.36
curl: (7) Failed to connect to 23.251.156.36 port 80: Connection refused

Если вы включите ведение журнала iptables:

sudo iptables -t nat -I KUBE-PORTALS-CONTAINER -m tcp -p tcp --dport 
80 -j LOG --log-prefix "WTF: "

Затем выполните команду grep с выводом dmesg для WTF, ясно, что пакеты прибывают на 10. IP-адрес виртуальной машины, а не на временный внешний IP-адрес, который был установлен в качестве общедоступного IP-адреса службы.

Оказывается, проблема в том, что GCE имеет два типа внешних IP-адресов: ForwardingRules (которые пересылают DSTIP без изменений) и NAT 1-к-1 (который фактически перезаписывает DSTIP на внутренний IP-адрес). Внешний IP-адрес виртуальной машины - это более поздний тип, поэтому, когда узел получает пакеты, правило IP-таблиц не соответствует.

Исправление на самом деле довольно простое (но не интуитивно понятное): используйте внутренний IP-адрес вашего узла в качестве общедоступного IP-адреса в определении вашей службы. После обновления файла service.yaml для установки общедоступных IP-адресов на внутренний IP-адрес (например, 10.240.121.42) вы сможете подключаться к своему приложению извне сети GCE.

person Robert Bailey    schedule 02.06.2015
comment
Спасибо, у меня он работает с ip, возвращенным от ifconfig на узле. Я не знал, как включить подробное ведение журнала или как еще его получить. Применимо ли это к сценариям с несколькими узлами? Если бы все IP-адреса узлов были в списке, отправил бы он его тому, у кого есть контейнер, прослушивающий этот порт? Что бы он сделал, если бы два контейнера на двух разных узлах прослушивали этот порт? - person Reese; 02.06.2015
comment
Чтобы включить подробное ведение журнала, отредактируйте /etc/default/kube-proxy и измените --v=2 на --v=4. Затем запустите sudo service kube-proxy restart. Файл журнала записывается в /var/log/kube-proxy.log. - person Robert Bailey; 02.06.2015
comment
Это применимо к сценариям с несколькими узлами, как описал Алекс ниже - вы можете добавить несколько записей publicIP в свою службу и отправлять запросы всем узлам в вашем кластере (например, используя циклический перебор DNS). Kube-proxy на каждом узле перехватывает запросы и перенаправляет их в соответствующие поды, работающие в кластере. - person Robert Bailey; 02.06.2015
comment
Вам нужно будет использовать разные порты или разные общедоступные IP-адреса для разных внешних сервисов (хотя модули могут повторно использовать одни и те же номера портов внутри кластера, потому что каждый модуль получает отдельный IP-адрес). Это преимущество использования внешнего балансировщика нагрузки GCE - поскольку каждая служба получает свой IP-адрес, вы можете использовать один и тот же порт (например, порт 80 или 443) для всех своих служб. - person Robert Bailey; 02.06.2015
comment
@RobertBailey, похоже, это больше не работает. Если я установил внутренний IP-адрес как внешний в определении службы, я не могу получить доступ к своему приложению из ниоткуда, кроме как внутри моего кластера. Совершенно новый кластер GCE под управлением Kubernetes 1.1.7, кстати. Жаль, что LoadBalancing дороже самой vm, хрен по моим тестам. - person CESCO; 13.02.2016
comment
Я не могу повторить это в голове. Я установил службу с LB и externalIP внутри GCE IP виртуальной машины, и она работает. Может быть, вы можете прислать мне свой YAML? Вы можете написать мне, если не хотите публиковать это здесь. - person Tim Hockin; 22.02.2016
comment
Похоже, этот ответ больше не применим? Установка внутреннего IP-адреса узла в качестве loadbalancerIp или externalIP, похоже, работает для меня - person nambrot; 07.09.2016

Если вы добавите внешний IP-адрес узла в поле publicIPs службы, тогда вы сможете получить к нему доступ по IP-адресу узла. Если в вашем кластере несколько узлов, вы можете указать несколько их IP-адресов в поле, если хотите разрешить доступ к модулю на любом из них.

В следующем выпуске будет более простая встроенная опция для настройки внешней службы без балансировщика нагрузки. Если вам интересно или вы читаете это когда-нибудь в будущем, ознакомьтесь с обновленным разделом «Внешние службы» на странице этот документ, чтобы узнать, как вы сможете использовать NodePort для более простого выполнения того же самого.

person Alex Robinson    schedule 26.05.2015
comment
Думаю, я уже выполнил упомянутые вами шаги. Как вы можете видеть в моем service.yaml, у службы есть поле publicIPs, в котором установлен внешний IP-адрес узла. - person Reese; 26.05.2015
comment
Правило брандмауэра для порта 80 и добавление внешнего IP-адреса узла в поле publicIPs службы должно быть всем, что нужно для его работы. После того, как вы дважды проверили, что IP-адрес в службе является IP-адресом узла и что это тот же IP-адрес, что и тот, который вы тестируете, следующее место, на которое нужно обратить внимание, - запустить sudo iptables -L -t nat на узле, чтобы увидеть, есть ли правило iptables. ссылаясь на IP-адрес. Если это так, проверьте /var/log/kube-proxy.log, чтобы узнать, почему запросы к этой службе не маршрутизируются. - person Alex Robinson; 27.05.2015
comment
Подтверждено, что указанный внешний IP-адрес узла совпадает с указанным в спецификации службы. Я не видел указанный IP-адрес в iptables, но видел его в обратном направлении как префикс для домена назначения. Я обновил свой вопрос, добавив некоторые дополнительные сведения - person Reese; 27.05.2015
comment
Чтобы протестировать функцию NodePort, о которой вы говорите, могу ли я обновить кластер до последней версии? Или мне нужно создать новый кластер? - person Reese; 01.06.2015

Ответ @Robert Bailey абсолютно правильный. publicIPs устарел в kubernetes 1.5.1, вместо этого вы можете использовать externalIP.

  • получить внутренний IP-адрес узла, kubectl describe node | grep Address
    Addresses: 10.119.244.130,101.192.150.200,gke-...
  • или вы можете запустить ifconfig eth0 внутри терминала узла, чтобы получить внутренний ip

  • установить ip в service.yaml spec: type: NodePort externalIPs: - 10.119.244.130

  • может скручиваться с решимостью проверить
    curl --resolve 'example.com:443:23.251.156.36' https://example.com -k
person ken    schedule 02.02.2017
comment
Это работает только в том случае, если вы не собираетесь масштабировать свои поды (они могут оказаться в другом externalIp). Я бы подумал, что это сработает также в случае реконструкции (отмирание и воссоздание стручка, поскольку я думаю, что его можно было бы создать где-то еще). - person lowercase00; 23.01.2021