Соединение сервер-клиент в кластере Kubernetes с использованием сокета Python

#python #kubernetes #websocket

Вопрос:

Я пытаюсь наладить связь сервер-клиент между модулями в кластере K8s, используя библиотеку python socket .

При запуске вне кластера соединение сервер-клиент работает, однако в k8s сервер даже не настроен:

 import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("myApp", 30152))  # it breaks here
server_socket.listen()
 

А вот мои конфигурационные YAMLs:

 ---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myApp
  labels:
    app: myApp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myApp
  template:
    metadata:
      labels:
        app: myApp
    spec:
      containers:
        - name: myApp
          image: app_image:version
          ports:
            - containerPort: 30152
          imagePullPolicy: Always

---
apiVersion: v1
kind: Service
metadata:
  name: myApp
  labels:
    app: myApp
spec:
  selector:
    app: myApp
  ports:
    - name: myApp
      protocol: TCP
      port: 30152
  type: ClusterIP
---
 

service type ClusterIP Это означает, что соединение будет осуществляться только между модулями в одном кластере. Кто-нибудь знает, откуда может взяться проблема?

Комментарии:

1. когда вы говорите «это прерывается здесь», что именно вы имеете в виду? есть ли ошибка? время ожидания? Вы пробовали запускать в клиенте и скручивать имя службы вручную?

2. Ошибка, которую я получаю,: [Errno -3] Temporary failure in name resolution

3. И когда я использую curl myApp или curl myApp:30152 изнутри модуля, ничего не появляется (хотя я не уверен, правильно ли я использую curl).

4. Подождите секунду, ваше развертывание MyApp — это сервер или клиент? Исходя из приведенного выше кода, кажется, что это сервер, тогда каков ваш клиент? откуда вы подключаетесь? Почему вы привязываете сервер к его имени службы?

5. Даже если вы измените это на ‘0.0.0.0’, сокет. прослушивание позволяет серверу принимать подключения, но это неблокирующая операция, я думаю, что ваш код (как есть) будет завершен сразу после запуска.

Ответ №1:

Здесь я построил небольшой пример с клиентским и серверным приложениями python, взаимодействующими друг с другом через службу k8s. Почти с нуля (клонируйте все файлы отсюда, если хотите продолжить)

Сервер

server.py

 import socket
import sys
import os

PORT = int(os.getenv('LISTEN_PORT'))

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('0.0.0.0', PORT)
print('Starting up on {} port {}'.format(*server_address))
sock.bind(server_address)
sock.listen()

while True:
    print('nWaiting for a connection')
    connection, client_address = sock.accept()
    try:
        print('Connection from', client_address)
        while True:
            data = connection.recv(64)
            print('Received {!r}'.format(data))
            if data:
                print('Sending data back to the client')
                connection.sendall(data)
            else:
                print('No data from', client_address)
                break
    finally:
        connection.close()
 

Dockerfile

 FROM python:3-alpine
WORKDIR /app
COPY server.py .
CMD ["/usr/local/bin/python3", "/app/server.py"]
 

building an image, tagging, pushing to container repo (GCP):

 docker build --no-cache -t q69936079-server .
docker tag q69936079-server gcr.io/<project_id>/q69936079-server
docker push gcr.io/<project_id>/q69936079-server
 

Client

client.py

 import socket
import os
import sys
import time

counter = 0

SRV = os.getenv('SERVER_ADDRESS')
PORT = int(os.getenv('SERVER_PORT'))

while 1:
    if counter != 0:
        time.sleep(5)

    counter  = 1
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (SRV, PORT)
    print("Connection #{}".format(counter))
    print('Connecting to {} port {}'.format(*server_address))
    try:
        sock.connect(server_address)
    except Exception as e:
        print("Cannot connect to the server,", e)
        continue

    try:
        message = b'This is the message. It will be repeated.'
        print('Sending:  {!r}'.format(message))
        sock.sendall(message)

        amount_received = 0
        amount_expected = len(message)

        while amount_received < amount_expected:
            data = sock.recv(64)
            amount_received  = len(data)
            print('Received: {!r}'.format(data))
    finally:
        print('Closing socketn')
        sock.close()
 

Dockerfile

 FROM python:3-alpine
WORKDIR /app
COPY client.py .
CMD ["/usr/local/bin/python3", "/app/client.py"]
 

создание образа, пометка, отправка в репозиторий контейнера (в моем случае GCP):

 docker build --no-cache -t q69936079-client .
docker tag q69936079-client gcr.io/<project_id>/q69936079-client
docker push gcr.io/<project_id>/q69936079-client
 

K8S

развертывание сервера

 apiVersion: apps/v1
kind: Deployment
metadata:
  name: server-deployment
spec:
  selector:
    matchLabels:
      app: server
  replicas: 1
  template:
    metadata:
      labels:
        app: server
    spec:
      containers:
      - name: server
        image: "gcr.io/<project_id>/q69936079-server:latest"
        env:
        - name: PYTHONUNBUFFERED
          value: "1"
        - name: LISTEN_PORT
          value: "30152"
 

развертывание клиента

 apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  selector:
    matchLabels:
      app: client
  replicas: 1
  template:
    metadata:
      labels:
        app: client
    spec:
      containers:
      - name: client
        image: "gcr.io/<project_id>/q69936079-client:latest"
        env:
        - name: PYTHONUNBUFFERED
          value: "1"
        - name: SERVER_ADDRESS
          value: my-server-service
        - name: SERVER_PORT
          value: "30152"
 

серверная служба

 apiVersion: v1
kind: Service
metadata:
  name: my-server-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
  - protocol: TCP
    port: 30152
 

Проверка

k8s object

 k get all

NAME                                     READY   STATUS    RESTARTS   AGE
pod/client-deployment-7dd5d675ff-pvwd4   1/1     Running   0          14m
pod/server-deployment-56bd44cc68-w6jns   1/1     Running   0          13m

NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/kubernetes          ClusterIP   10.140.0.1      <none>        443/TCP     12h
service/my-server-service   ClusterIP   10.140.13.183   <none>        30152/TCP   38m

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/client-deployment   1/1     1            1           14m
deployment.apps/server-deployment   1/1     1            1           13m

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/client-deployment-7dd5d675ff   1         1         1       14m
replicaset.apps/server-deployment-56bd44cc68   1         1         1       13m
 

Журналы сервера

 k logs -f deployment.apps/server-deployment

Starting up on 0.0.0.0 port 30152

Waiting for a connection
Connection from ('10.136.1.11', 48234)
Received b'This is the message. It will be repeated.'
Sending data back to the client
Received b''
No data from ('10.136.1.11', 48234)

Waiting for a connection
Connection from ('10.136.1.11', 48246)
Received b'This is the message. It will be repeated.'
Sending data back to the client
Received b''
No data from ('10.136.1.11', 48246)
 

Журналы клиентов

 k logs -f deployment.apps/client-deployment

Connection #1
Connecting to my-server-service port 30152
Cannot connect to the server, [Errno 111] Connection refused

Connection #2
Connecting to my-server-service port 30152
Cannot connect to the server, [Errno 111] Connection refused

Connection #3
Connecting to my-server-service port 30152
Sending:  b'This is the message. It will be repeated.'
Received: b'This is the message. It will be repeated.'
Closing socket

Connection #4
Connecting to my-server-service port 30152
Sending:  b'This is the message. It will be repeated.'
Received: b'This is the message. It will be repeated.'
Closing socket
 

Комментарии:

1. Я следовал вашему коду, и все работает так, как должно! Добавление адреса сервера, порта в качестве переменных ENV и запуск сервера на 0.0.0.0 решили проблему. Большое вам спасибо за ваше время и помощь 😉