<a id="i-tcp-udp"></a>

# TCP- и UDP-балансировка для DNS

В этом примере разворачивается ANIC и DNS-сервер, а затем настраивается балансировка TCP и UDP
для DNS-сервиса с использованием ключа `stream-snippets` в ConfigMap.

Стандартные ресурсы Ingress в Kubernetes предполагают, что используется HTTP-трафик,
и не предназначены для обработки TCP- или UDP-трафика, поэтому,
чтобы встроить необходимую конфигурацию балансировки TCP/UDP в блок `stream {}`,
используется ключ `stream-snippets` из ConfigMap.

В ANIC можно использовать headless-сервис и его DNS-имя,
чтобы получить реальные IP-адреса подов и балансировать нагрузку между ними.
ANIC регулярно заново разрешает DNS-имя, чтобы автоматически обновлять его
при добавлении или удалении подов.

#### NOTE
- Для тестирования используется утилита **dig**. Убедитесь, что она установлена.
- Для настройки TCP/UDP-балансировки используется родная конфигурация Angie.

1. Установите ANIC. Убедитесь, что порт 5353 открыт как для TCP-, так и для UDP-трафика.
2. Сохраните публичный IP-адрес ANIC в переменной оболочки:
   ```console
   $ IC_IP=<ваш_IP-адрес>
   ```
3. Сохраните порт 5353, используемый ANIC, в переменной оболочки:
   ```console
   $ IC_5353_PORT=<номер порта>
   ```

   #### NOTE
   Если вы хотите настроить ANIC с помощью сервиса `LoadBalancer`, то в нем нельзя использовать
   протоколы TCP и UDP одновременно.
   В этом случае создайте два отдельных сервиса: для TCP и для UDP.
   Соответственно, у вас будет два разных публичных IP-адреса (см. примеры ниже).
4. Разверните DNS-сервер:
   - Разверните две реплики `CoreDNS`, настроенные на пересылку DNS-запросов на `8.8.8.8`.
   - Создайте два сервиса — `coredns` и `coredns-headless`.

   ### Пример

   ```yaml
   apiVersion: v1
   kind: ConfigMap
   metadata:
     name: coredns
   data:
     Corefile: |
       .:53 {
         forward . 8.8.8.8:53
         log
       }
   ---
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: coredns
   spec:
     replicas: 2
     selector:
       matchLabels:
         app: coredns
     template:
       metadata:
         labels:
           app: coredns
       spec:
         containers:
         - name: coredns
           image: coredns/coredns:1.10.0
           args: [ "-conf", "/etc/coredns/Corefile" ]
           volumeMounts:
           - name: config-volume
             mountPath: /etc/coredns
             readOnly: true
           ports:
           - containerPort: 53
             name: dns
             protocol: UDP
           - containerPort: 53
             name: dns-tcp
             protocol: TCP
           securityContext:
             allowPrivilegeEscalation: false
             capabilities:
               add:
               - NET_BIND_SERVICE
               drop:
               - all
             readOnlyRootFilesystem: true
         volumes:
           - name: config-volume
             configMap:
               name: coredns
               items:
               - key: Corefile
                 path: Corefile
   ---
   apiVersion: v1
   kind: Service
   metadata:
     name: coredns
   spec:
     selector:
      app: coredns
     ports:
     - name: dns
       port: 53
       protocol: UDP
     - name: dns-tcp
       port: 53
       protocol: TCP
   ---
   apiVersion: v1
   kind: Service
   metadata:
     name: coredns-headless
   spec:
     clusterIP: None
     selector:
      app: coredns
     ports:
     - name: dns
       port: 53
       protocol: UDP
     - name: dns-tcp
       port: 53
       protocol: TCP
   ```

   Примените настройки:
   ```console
   $ kubectl apply -f dns.yaml
   ```
5. Создайте конфигурацию балансировки нагрузки. В примере настраиваются два сервера:
   один принимает TCP-трафик на порту 5353, а второй — UDP-трафик на том же порту.
   Оба сервера балансируют входящий трафик между подами CoreDNS.

   ### Пример

   ```yaml
   kind: ConfigMap
   apiVersion: v1
   metadata:
     name: angie-config
     namespace: angie-ingress
   data:
       stream-snippets: |
         resolver kube-dns.kube-system.svc.cluster.local valid=5s;

         upstream coredns-udp {
             zone coredns-udp 64k;
             server coredns-headless.default.svc.cluster.local service=_dns._udp resolve;
         }

         server {
             listen 5353 udp;
             listen [::]:5353 udp;
             proxy_pass coredns-udp;
             proxy_responses 1;
             status_zone coredns-udp;
         }

         upstream coredns-tcp {
             zone coredns-tcp 64k;
             server coredns-headless.default.svc.cluster.local service=_dns-tcp._tcp resolve;
         }

         server {
             listen 5353;
             listen [::]:5353;
             proxy_pass coredns-tcp;
             status_zone coredns-tcp;
         }
   ```

   ANIC поддерживает повторное разрешение DNS-имен с помощью параметра `resolve` в директиве `upstream`.
   При использовании параметра `resolve`, ANIC не завершит перезагрузку с ошибкой,
   если имя upstream-хоста не удается разрешить.

   Помимо IP-адресов, ANIC также может получать порты через DNS-записи типа SRV.

   Для разрешения IP-адресов и портов ANIC использует Kube-DNS, который указывается в директиве `resolver`.
   Также устанавливается параметр `valid=5s`, чтобы ANIC повторно разрешал DNS-имена каждые 5 секунд.

   Вместо обычного сервиса `coredns` используется `coredns-headless`.
   Этот сервис создается как headless-сервис, то есть ему не выделяется виртуальный IP,
   и ANIC может разрешить все реальные IP-адреса подов CoreDNS.

   #### NOTE
   ANIC завершит перезагрузку с ошибкой, если DNS-имя, указанное в директиве `resolver`, не может быть разрешено.
   Чтобы избежать этого, рекомендуется использовать виртуальный IP-адрес сервиса kube-dns вместо его DNS-имени.
6. Обновите ConfigMap, добавив ключ `stream-snippets` с конфигурацией балансировки нагрузки.

   Используется ключ `stream-snippets` в объекте ConfigMap,
   чтобы настроить балансировку TCP- и UDP-трафика для наших подов CoreDNS.

   Примените настройки:
   ```console
   $ kubectl apply -f angie-config.yaml
   ```
7. Проверьте, что конфигурация успешно применена:
   ```console
   $ kubectl describe configmap angie-config -n angie-ingress
   . . .
   Events:
   Type    Reason   Age   From       Message
   ----    ------   ----  ----       -------
   Normal  Updated  3s    ANIC       Configuration from angie-ingress/angie-config was updated
   ```
8. Протестируйте DNS-сервер.

   Чтобы проверить, что балансировка TCP/UDP работает, выполните разрешение имени `kubernetes.io`
   с использованием нашего DNS-сервера, доступного через ANIC.

   Разрешение `kubernetes.io` через UDP:
   ```shell
   $ dig @$IC_IP -p $IC_5353_PORT kubernetes.io

   ; <<>> DiG 9.10.6 <<>> @<REDACTED>> -p 5353 kubernetes.io
   ; (1 server found)
   ;; global options: +cmd
   ;; Got answer:
   ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33368
   ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

   ;; OPT PSEUDOSECTION:
   ; EDNS: version: 0, flags:; udp: 512
   ;; QUESTION SECTION:
   ;kubernetes.io.                 IN      A

   ;; ANSWER SECTION:
   kubernetes.io.          299     IN      A       45.54.44.100

   ;; Query time: 111 msec
   ;; SERVER:<REDACTED>#5353(<REDACTED>)
   ;; WHEN: Fri Aug 17 12:49:54 BST 2018
   ;; MSG SIZE  rcvd: 71
   ```

   Разрешение `kubernetes.io` через TCP:
   ```shell
   $ dig @$IC_IP -p $IC_5353_PORT kubernetes.io +tcp

   ; <<>> DiG 9.10.6 <<>> @<REDACTED> -p 5353 kubernetes.io +tcp
   ; (1 server found)
   ;; global options: +cmd
   ;; Got answer:
   ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49032
   ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

   ;; OPT PSEUDOSECTION:
   ; EDNS: version: 0, flags:; udp: 512
   ;; QUESTION SECTION:
   ;kubernetes.io.                 IN      A

   ;; ANSWER SECTION:
   kubernetes.io.          146     IN      A       45.54.44.100

   ;; Query time: 95 msec
   ;; SERVER: <REDACTED>#5353(<REDACTED>)
   ;; WHEN: Fri Aug 17 12:52:25 BST 2018
   ;; MSG SIZE  rcvd: 71
   Look at the Ingress Controller logs:

   $ kubectl logs <angie-ingress-pod> -n angie-ingress
   . . .
   <REDACTED> [17/Aug/2018:11:49:54 +0000] UDP 200 71 42 0.016
   <REDACTED> [17/Aug/2018:11:52:25 +0000] TCP 200 73 44 0.098
   ```

   Посмотрите логи:
   ```shell
   $ kubectl logs <angie-ingress-pod> -n angie-ingress
   . . .
   <REDACTED> [17/Aug/2018:11:49:54 +0000] UDP 200 71 42 0.016
   <REDACTED> [17/Aug/2018:11:52:25 +0000] TCP 200 73 44 0.098
   ```
