Как создать кластер Kubernetes, обслуживающий собственный контейнер с SSL и NGINX

Я пытаюсь построить кластер Kubernetes со следующими сервисами внутри:

  • Docker-registry (который будет содержать мой образ Django Docker)
  • Nginx прослушивает порт 80 и 443
  • PostgreSQL
  • Несколько приложений django обслуживаются с помощью Gunicorn
  • Контейнер letsencrypt для создания и автоматического обновления подписанного SSL сертификаты

Моя проблема - это проблема с курицей и яйцом, которая возникает во время создания кластера:

Мои сертификаты SSL хранятся в секретном томе, который создается контейнером letsencrypt. Чтобы иметь возможность сгенерировать сертификат, нам нужно показать, что мы являемся владельцем доменного имени, и это делается путем проверки доступности файла по имени сервера (в основном это состоит из того, что Nginx может обслуживать статический файл через порт 80).

Итак, здесь возникает моя первая проблема: для обслуживания статического файла, необходимого letsencrypt, мне нужно запустить nginx. SSL-часть nginx не может быть запущена, если секрет не был смонтирован, а секрет создается только тогда, когда шифрование выполнено успешно ...

Итак, простым решением может быть 2 контейнера Nginx: один прослушивает только порт 80, который будет запущен первым, затем letsencrypt, затем мы запускаем второй контейнер Nginx, прослушивающий порт 443.

-> На мой взгляд, это выглядит пустой тратой ресурсов, но почему бы и нет.

Предполагая, что у меня есть 2 контейнера nginx, я хочу, чтобы мой реестр Docker был доступен через https.

Итак, в моей конфигурации nginx у меня будет файл docker-registry.conf, который выглядит примерно так:

upstream docker-registry {
  server registry:5000;
}

server {
  listen 443;
  server_name docker.thedivernetwork.net;

  # SSL
  ssl on;
  ssl_certificate /etc/nginx/conf.d/cacert.pem;
  ssl_certificate_key /etc/nginx/conf.d/privkey.pem;

  # disable any limits to avoid HTTP 413 for large image uploads
  client_max_body_size 0;

  # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
  chunked_transfer_encoding on;

  location /v2/ {
    # Do not allow connections from docker 1.5 and earlier
    # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;
    }

    # To add basic authentication to v2 use auth_basic setting plus add_header
    auth_basic "registry.localhost";
    auth_basic_user_file /etc/nginx/conf.d/registry.password;
    add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;

    proxy_pass                          http://docker-registry;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
  }
}

Важной частью является proxy_pass, который перенаправляет на контейнер реестра.

Проблема, с которой я столкнулся, заключается в том, что мой сервер Django Gunicorn также имеет файл конфигурации в той же папке django.conf:

upstream django {
    server django:5000;
}

server {
    listen 443 ssl;
    server_name example.com;
    charset     utf-8;

    ssl on;
    ssl_certificate /etc/nginx/conf.d/cacert.pem;
    ssl_certificate_key /etc/nginx/conf.d/privkey.pem;

    ssl_protocols        SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers          HIGH:!aNULL:!MD5;
    client_max_body_size 20M;

    location / {
        # checks for static file, if not found proxy to app
        try_files $uri @proxy_to_django;
    }

    location @proxy_to_django {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        #proxy_pass_header Server;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 65;
        proxy_read_timeout 65;

        proxy_pass   http://django;
    }

}

Таким образом, nginx успешно запустится только при трех условиях:

  • секрет установлен (это можно решить, разделив Nginx на 2 отдельных контейнера)
  • служба реестра запущена
  • служба django запущена

Проблема в том, что образ django извлекает свой образ из службы реестра, поэтому мы снова находимся в ситуации тупиковой блокировки.

Я не упоминал об этом, но и реестр, и django имеют разные ServerName, поэтому nginx может их обслуживать.

Решение, которое я думал об этом (но оно довольно грязное!), - это перезагрузить nginx несколько раз со все большим количеством конфигураций:

  • Я запускаю службу реестра докеров
  • Я запускаю Nginx только с файлом registry.conf
  • Я создаю свой django rc и сервис
  • Я перезагружаю nginx как с registry.conf, так и с django.conf

Если бы был способ заставить nginx игнорировать сбойную конфигурацию, это, вероятно, решило бы и мои проблемы.

Как я могу аккуратно выполнить эту настройку?

Спасибо за вашу помощь

Тибо


person thibserot    schedule 20.01.2016    source источник
comment
Можете ли вы просто иметь 3 контейнера в модуле и позволить другим 2 аварийно завершить работу, пока контейнер letsencrypt не запишет сертификат на общий том? Если вы хотите избежать зацикливания, вы можете обернуть фактический контейнер в другую точку входа с помощью --cmd = your-script, и иметь свой сценарий, например: продолжайте опрашивать конечную точку реестра, а затем запустить django. Для этого, конечно, требуется 3 nginx, но я считаю, что это чище, чем перезагружать одну конфигурацию снова и снова. Его также можно компоновать, если вы, например, хотите заменить letsencrypt nginx на веб-сервер go или что-то в этом роде.   -  person Prashanth B    schedule 21.01.2016


Ответы (1)


Вы используете Сервисы Kubernetes для своих Приложения?

С сервисом для каждого из ваших модулей у вас есть прокси-сервер для модулей. Даже если модуль не запущен, пока запущена служба, nginx найдет ее при поиске, поскольку службе назначен IP-адрес.

Итак, вы запускаете Службы, а затем запускаете nginx и любой Pod, который хотите, в нужном вам порядке.

person MrE    schedule 20.01.2016
comment
Прежде чем он сможет обслуживать ssl (на службе или иначе), ему нужен клиент letsencrypt для генерации сертификатов, но он, возможно, может использовать службу для приложения реестра. - person Prashanth B; 21.01.2016
comment
может потребоваться выполнить загрузку вручную с помощью сертификата, сгенерированного letsencrypt, но после этого все должно быть в порядке - person MrE; 21.01.2016
comment
Отличная идея для сервисной части! Я ими пользуюсь. У меня были подозрения на этот счет, но я не был уверен, что произойдет за сценой, и подумал, что вам нужно запустить RC перед службой. Как упоминал MrE, у меня все еще есть проблема с letcencrypt ... Это можно решить, если отсутствующий секрет не мешает запуску модулей ... Есть ли у нас возможность для этого? И как я могу проверить, запущен ли модуль, если ожидания запуска службы недостаточно? - person thibserot; 21.01.2016