web-dev-qa-db-fra.com

Comment exposer le service Kubernetes au public sans codage en dur à l'IP des sbires?

J'ai un cluster kubernetes fonctionnant avec 2 serviteurs. Actuellement, je rend mon service accessible en 2 étapes:

  1. Démarrer le contrôleur de réplication et le pod
  2. Obtenez l'IP des sbires (en utilisant kubectl get minions) et définissez-le comme publicIPs pour le service.

Quelle est la pratique suggérée pour exposer le service au public? Mon approche semble erronée car je code en dur les IP des IP de sbires individuels. Il semble également contourner les capacités d'équilibrage de charge des services kubernetes car les clients devraient accéder directement aux services s'exécutant sur des serviteurs individuels.

Pour configurer le contrôleur de réplication et le module que j'utilise:

id: frontend-controller
kind: ReplicationController
apiVersion: v1beta1
desiredState:
  replicas: 2
  replicaSelector:
    name: frontend-pod
  podTemplate:
    desiredState:
      manifest:
        version: v1beta1
        id: frontend-pod
        containers:
          - name: sinatra-docker-demo
            image: madisn/sinatra_docker_demo
            ports:
              - name: http-server
                containerPort: 4567
    labels:
      name: frontend-pod

Pour configurer le service (après avoir obtenu les ip des sbires):

kind: Service
id: frontend-service
apiVersion: v1beta1
port: 8000
containerPort: http-server
selector:
  name: frontend-pod
labels:
  name: frontend
publicIPs: [10.245.1.3, 10.245.1.4]
22
Madis Nõmme

Comme je l'ai mentionné dans le commentaire ci-dessus, le createExternalLoadBalancer est l'abstraction appropriée que vous recherchez, mais malheureusement il n'est pas encore implémenté pour tous les fournisseurs de cloud, et en particulier pour vagrant, que vous utilisez localement.

Une option consisterait à utiliser les adresses IP publiques pour tous les serviteurs de votre cluster pour tous les services que vous souhaitez externaliser. Le trafic destiné au service se retrouvera sur l'un des serviteurs, où il sera intercepté par le processus kube-proxy et redirigé vers un pod qui correspond au sélecteur d'étiquettes pour le service. Cela pourrait entraîner un bond supplémentaire sur le réseau (si vous atterrissez sur un nœud sur lequel le pod n'est pas exécuté localement), mais pour les applications qui ne sont pas extrêmement sensibles à la latence du réseau, cela ne sera probablement pas perceptible.

8
Robert Bailey

Comme Robert l'a dit dans sa réponse, c'est quelque chose qui arrive, mais qui n'est malheureusement pas encore disponible.

J'exécute actuellement un cluster Kubernetes sur notre réseau de centre de données. J'ai 1 maître et 3 sbires fonctionnant tous sur des systèmes virtuels CentOS 7 (vcenter). La façon dont j'ai géré cela était de créer un serveur "kube-proxy" dédié. En gros, j'exécute simplement le service Kube-Proxy (avec Flannel pour la mise en réseau), puis j'attribue des adresses IP "publiques" à la carte réseau connectée à ce serveur. Quand je dis public, je veux dire des adresses sur notre réseau de centre de données local. Ensuite, lorsque je crée un service auquel j'aimerais accéder en dehors du cluster, je définis simplement la valeur publicIPs sur l'une des adresses IP disponibles sur le serveur proxy de cube. Quand quelqu'un ou quelque chose tente de se connecter à ce service depuis l'extérieur du cluster, il va frapper le proxy de cube, puis être redirigé vers le serviteur approprié.

Bien que cela puisse sembler être une solution de contournement, cela est en fait similaire à ce à quoi je m'attendrais une fois qu'ils auront trouvé une solution intégrée à ce problème.

7
Todd Spayde

Si vous exécutez un cluster localement, une solution que j'ai utilisée consistait à exposer le service sur vos nœuds kubernetes à l'aide de la directive nodeport dans votre définition de service, puis à effectuer un round robin sur chaque nœud de votre cluster avec HAproxy.

Voici à quoi ressemble l'exposition du nodeport:

apiVersion: v1
kind: Service
metadata:
  name: nginx-s
  labels:
    name: nginx-s
spec:
  type: NodePort
  ports:
    # must match the port your container is on in your replication controller
    - port: 80
      nodePort: 30000
  selector:
    name: nginx-s

Remarque: la valeur que vous spécifiez doit se situer dans la plage configurée pour les ports de noeud. (par défaut: 30000-32767)

Cela expose le service sur le port de nœud donné sur chaque nœud de votre cluster. Ensuite, j'ai configuré une machine distincte sur le réseau interne exécutant haproxy et un pare-feu accessible en externe sur le ou les ports de noeud spécifiés que vous souhaitez exposer.

Si vous regardez votre table nat sur l'un de vos hôtes, vous pouvez voir ce qu'elle fait.

root@kube01:~# kubectl create -f nginx-s.yaml
You have exposed your service on an external port on all nodes in your
cluster.  If you want to expose this service to the external internet, you may
need to set up firewall rules for the service port(s) (tcp:30000) to serve traffic.

See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details.
services/nginx-s
root@kube01:~# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
KUBE-PORTALS-CONTAINER  all  --  anywhere             anywhere             /* handle ClusterIPs; NOTE: this must be before the NodePort rules */
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL
KUBE-NODEPORT-CONTAINER  all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL /* handle service NodePorts; NOTE: this must be the last rule in the chain */

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
KUBE-PORTALS-Host  all  --  anywhere             anywhere             /* handle ClusterIPs; NOTE: this must be before the NodePort rules */
DOCKER     all  --  anywhere            !127.0.0.0/8          ADDRTYPE match dst-type LOCAL
KUBE-NODEPORT-Host  all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL /* handle service NodePorts; NOTE: this must be the last rule in the chain */

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        anywhere

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere

Chain KUBE-NODEPORT-CONTAINER (1 references)
target     prot opt source               destination
REDIRECT   tcp  --  anywhere             anywhere             /* default/nginx-s: */ tcp dpt:30000 redir ports 42422

Chain KUBE-NODEPORT-Host (1 references)
target     prot opt source               destination
DNAT       tcp  --  anywhere             anywhere             /* default/nginx-s: */ tcp dpt:30000 to:169.55.21.75:42422

Chain KUBE-PORTALS-CONTAINER (1 references)
target     prot opt source               destination
REDIRECT   tcp  --  anywhere             192.168.3.1          /* default/kubernetes: */ tcp dpt:https redir ports 51751
REDIRECT   tcp  --  anywhere             192.168.3.192        /* default/nginx-s: */ tcp dpt:http redir ports 42422

Chain KUBE-PORTALS-Host (1 references)
target     prot opt source               destination
DNAT       tcp  --  anywhere             192.168.3.1          /* default/kubernetes: */ tcp dpt:https to:169.55.21.75:51751
DNAT       tcp  --  anywhere             192.168.3.192        /* default/nginx-s: */ tcp dpt:http to:169.55.21.75:42422
root@kube01:~#

En particulier cette ligne

DNAT       tcp  --  anywhere             anywhere             /* default/nginx-s: */ tcp dpt:30000 to:169.55.21.75:42422

Et enfin, si vous regardez netstat, vous pouvez voir que kube-proxy écoute et attend ce service sur ce port.

root@kube01:~# netstat -tupan | grep 42422
tcp6       0      0 :::42422                :::*                    LISTEN      20748/kube-proxy
root@kube01:~#

Kube-proxy écoutera sur un port pour chaque service, et fera la traduction des adresses réseau dans votre sous-réseau virtuel dans lequel vos conteneurs résident. (Je pense?) J'ai utilisé de la flanelle.


Pour un cluster à deux nœuds, cette configuration HAproxy peut ressembler à ceci:

listen sampleservice 0.0.0.0:80
    mode http
    stats enable
    balance roundrobin
    option httpclose
    option forwardfor
    server noname 10.120.216.196:30000 check
    server noname 10.155.236.122:30000 check
    option httpchk HEAD /index.html HTTP/1.0

Et votre service est désormais accessible sur le port 80 via HAproxy. Si l'un de vos nœuds tombe en panne, les conteneurs seront déplacés vers un autre nœud grâce aux contrôleurs de réplication et HAproxy ne routera que vers vos nœuds vivants.

Je suis curieux de savoir quelles méthodes les autres ont utilisées, c'est juste ce que j'ai trouvé. Je ne poste généralement pas sur le débordement de la pile, donc je m'excuse si je ne respecte pas les conventions ou le formatage approprié.

2
Carlin

C'est pour MrE. Je n'avais pas assez d'espace dans la zone des commentaires pour poster cette réponse, j'ai donc dû créer une autre réponse. J'espère que cela t'aides:

Nous avons en fait quitté Kubernetes depuis la publication de cette réponse. Si je me souviens bien, je n'avais qu'à exécuter l'exécutable kube-proxy sur une machine virtuelle CentOS dédiée. Voici ce que j'ai fait:

J'ai d'abord supprimé Firewalld et mis en place iptables. Kube-proxy s'appuie sur iptables pour gérer ses NAT et redirections.

Deuxièmement, vous devez installer flanneld afin d'avoir un adaptateur de pont sur le même réseau que les services Docker exécutés sur vos serviteurs.

Ensuite, j'ai attribué plusieurs adresses IP à la carte réseau locale installée sur la machine. Ce seront les adresses IP que vous pourrez utiliser lors de la configuration d'un service. Ce seront les adresses disponibles EN DEHORS de votre cluster.

Une fois que tout est réglé, vous pouvez démarrer le service proxy. Il se connectera au maître et récupérera une adresse IP pour le réseau de pont de flanelle. Ensuite, il synchronisera toutes les règles IPtables et vous devriez être défini. Chaque fois qu'un nouveau service est ajouté, il créera les règles de proxy et répliquera ces règles sur tous les serviteurs (et votre proxy). Tant que vous avez spécifié une adresse IP disponible sur votre serveur proxy, ce serveur proxy transmettra tout le trafic pour cette adresse IP au serviteur approprié.

J'espère que c'est un peu plus clair. N'oubliez pas que je ne fais pas partie du projet Kubernetes depuis environ 6 mois maintenant, donc je ne sais pas ce qui a changé depuis mon départ. Ils pourraient même avoir une fonctionnalité en place qui gère ce genre de chose. Si ce n'est pas à espérer, cela vous aidera à le prendre en charge.

1
Todd Spayde

Vous pouvez utiliser la ressource Ingress pour permettre aux connexions externes de l'extérieur d'un cluster Kubernetes d'atteindre les services du cluster.

En supposant que vous avez déjà déployé un pod, vous avez maintenant besoin d'une ressource de service, par exemple:

apiVersion: v1 kind: Service metadata: name: frontend-service labels: tier: frontend spec: type: ClusterIP selector: name: frontend-pod ports: - name: http protocol: TCP # the port that will be exposed by this service port: 8000 # port in a docker container; defaults to what "port" has set targetPort: 8000

Et vous avez besoin d'une ressource Ingress: apiVersion: extensions/v1beta1 kind: Ingress metadata: name: frontend-ingress spec: rules: - Host: foo.bar.com http: paths: - path: / backend: serviceName: frontend-service # the targetPort from service (the port inside a container) servicePort: 8000 Pour pouvoir utiliser les ressources Ingress, vous avez besoin de contrôleur d'entrée déployé.

Maintenant, à condition que vous connaissiez votre IP maître Kubernetes, vous pouvez accéder à votre application depuis l'extérieur d'un cluster Kubernetes avec: curl http://<master_ip>:80/ -H 'Host: foo.bar.com'


Si vous utilisez un serveur DNS, vous pouvez ajouter cet enregistrement: foo.bar.com IN A <master_ip> Ou ajouter cette ligne à votre fichier /etc/hosts: <master_ip> foo.bar.com Et maintenant vous pouvez simplement exécuter: curl foo.bar.com


Notez que de cette façon, vous accéderez toujours à foo.bar.com En utilisant le port 80. Si vous voulez utiliser un autre port, je recommande d'utiliser un service de type NodePort, uniquement pour ce port non 80. Cela rendra ce port résolvable, peu importe quelle Kubernetes VM IP vous utilisez (n'importe quel IP maître ou minion est très bien). Exemple d'un tel service: apiVersion: v1 kind: Service metadata: name: frontend-service-ssh labels: tier: frontend spec: type: NodePort selector: name: frontend-pod ports: - name: ssh targetPort: 22 port: 22 nodePort: 2222 protocol: TCP Et si vous avoir <master_ip> foo.bar.com dans votre fichier/etc/hosts, alors vous pouvez accéder à: foo.bar.com:2222

0
Ewa