Docker Swarm — Как установить переменные среды для задач на разных узлах

Я новичок в docker swarm и могу развертывать свои службы на различных узлах, однако переменные среды, которые экспортируются из сценария dockerfile ENTRYPOINT, не устанавливаются для задач, развернутых в кластере docker swarm.

Настройка

  • докер версии 18.09.1, сборка 4c52b90
  • docker-compose версии 1.23.2, сборка 1110ad01
  • Джанго 2.1.5
  • ПосгресSQL 10

Пытаясь выполнить одноразовую команду внутри задачи django, используя docker exec -t CONTAINER_ID sh для входа в контейнер, а затем выполняя python manage.py migrate, я получаю следующую ошибку:

Ошибка

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "manage.py", line 38, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 211, in fetch_command
    settings.INSTALLED_APPS
  File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 57, in __getattr__
    self._setup(name)
  File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 44, in _setup
    self._wrapped = Settings(settings_module)
  File "/usr/local/lib/python3.6/site-packages/django/conf/__init__.py", line 107, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/app/config/settings/production.py", line 15, in <module>
    DATABASES['default'] = env.db('DATABASE_URL')  # noqa F405
  File "/usr/local/lib/python3.6/site-packages/environ/environ.py", line 202, in db_url
    return self.db_url_config(self.get_value(var, default=default), engine=engine)
  File "/usr/local/lib/python3.6/site-packages/environ/environ.py", line 275, in get_value
    raise ImproperlyConfigured(error_msg)
django.core.exceptions.ImproperlyConfigured: Set the DATABASE_URL environment variable

Итак, DATABASE_URL не задано как переменная среды внутри моего контейнера докеров. Как указывалось ранее, это экспортируется из сценария ENTRYPOINT, который вызывается в файле докеров.

Файл Docker

FROM python:3.6-alpine

ENV PYTHONUNBUFFERED 1

RUN apk update \
  # psycopg2 dependencies
  && apk add --virtual build-deps gcc python3-dev g++ musl-dev \
  && apk add postgresql-dev \
  # Pillow dependencies
  && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
  # CFFI dependencies
  && apk add libffi-dev py-cffi \
  # Translations dependencies
  && apk add gettext \
  # https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell

# Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements
RUN pip install -r /requirements/production.txt \
    && rm -rf /requirements

COPY ./compose/production/django/entrypoint /entrypoint
RUN sed -i 's/\r//' /entrypoint
RUN chmod +x /entrypoint
RUN chown django /entrypoint

COPY ./compose/production/django/start /start
RUN sed -i 's/\r//' /start
RUN chmod +x /start
RUN chown django /start

COPY ./compose/production/django/celery/worker/start /start-celeryworker
RUN sed -i 's/\r//' /start-celeryworker
RUN chmod +x /start-celeryworker
RUN chown django /start-celeryworker

COPY ./compose/production/django/celery/beat/start /start-celerybeat
RUN sed -i 's/\r//' /start-celerybeat
RUN chmod +x /start-celerybeat
RUN chown django /start-celerybeat

COPY ./compose/production/django/celery/flower/start /start-flower
RUN sed -i 's/\r//' /start-flower
RUN chmod +x /start-flower

COPY . /app

RUN chown -R django /app

USER django

WORKDIR /app

ENTRYPOINT ["/entrypoint"]

Скрипт ENTRYPOINT

#!/bin/sh

set -o errexit
set -o pipefail
set -o nounset


# N.B. If only .env files supported variable expansion...
export CELERY_BROKER_URL="${REDIS_URL}"

if [ -z "${POSTGRES_USER}" ]; then
    base_postgres_image_default_user='postgres'
    export POSTGRES_USER="${base_postgres_image_default_user}"
fi
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"

postgres_ready() {
python << END
import sys

import psycopg2

try:
    psycopg2.connect(
        dbname="${POSTGRES_DB}",
        user="${POSTGRES_USER}",
        password="${POSTGRES_PASSWORD}",
        host="${POSTGRES_HOST}",
        port="${POSTGRES_PORT}",
    )
except psycopg2.OperationalError:
    sys.exit(-1)
sys.exit(0)

END
}
until postgres_ready; do
  >&2 echo 'Waiting for PostgreSQL to become available...'
  sleep 1
done
>&2 echo 'PostgreSQL is available'

exec "$@"

Это было взято из проекта django-cookie-cutter от pydanny. Все работает с использованием обычной установки без роя: docker-compose -f production.yml build и docker-compose -f production.yml up для развертывания в производственной среде с одним экземпляром.

Наконец, вот как выглядит мой файл docker-compose для роя:

Docker-compose.yml

version: '3.6'

volumes:
  production_postgres_data: {}
  production_postgres_data_backups: {}
  production_caddy: {}
  node-modules:

networks:
  webnet:
    driver: overlay
    attachable: true

services:
  django: &django
    image: registry:image
    depends_on:
      - postgres
      - redis
    env_file:
      - PATH to .env

    command: /start
    deploy:
      mode: replicated
      replicas: 2
      restart_policy:
        condition: on-failure
        delay: 5s
    networks:
      -  webnet

  postgres:
    image: registry:image
    volumes:
      - production_postgres_data:/var/lib/postgresql/data
      - production_postgres_data_backups:/backups
    env_file:
      - PATH to .env
    deploy:
      restart_policy:
        condition: on-failure
        delay: 5s
      placement:
        constraints:
          - node.role == manager
    networks:
      - webnet

  frontend:
    image: registry:image
    command: /start
    volumes:
      - node-modules:/app/node_modules
    ports:
      - "3000:3000"
    deploy:
      mode: replicated
      replicas: 2
      restart_policy:
        condition: on-failure
        delay: 5s
    networks:
      -  webnet

  caddy:
    image: registry:image
    depends_on:
      - django
      - frontend
    volumes:
      - production_caddy:/root/.caddy
    env_file:
       - PATH to .env
    ports:
      - "0.0.0.0:80:80"
      - "0.0.0.0:443:443"
    deploy:
      placement:    
        constraints:
          - node.role == manager
    networks:
      -  webnet

  redis:
    image: redis:3.2
    deploy:
      mode: replicated
      replicas: 2
    networks:
      -  webnet

Я не уверен, почему экспорт переменных среды из сценария точки входа не устанавливается, когда задачи развертываются на узлах с использованием docker stack deploy --with-registry-auth -c production.yml my_swarm.

Любая помощь будет оценена по этому или альтернативному решению для установки переменных env. Я не смог найти документацию, которая связывает сценарии точки входа dockerfile с задачами/службами docker swarm.

РЕДАКТИРОВАТЬ:

Я предполагаю, что мне нужно каким-то образом использовать https://docs.docker.com/engine/swarm/secrets/, но хотелось бы сохранить скрипт точки входа.

РЕДАКТИРОВАТЬ 2: Нашел ресурс, нужно адаптировать мой процесс. https://docs.docker.com/engine/swarm/secrets/#build-support-for-docker-secrets-into-your-images

РЕДАКТИРОВАТЬ 3: После дополнительной проверки все остальные переменные среды, кроме тех, которые указаны в сценарии точки входа, переносятся на каждую задачу. Я смог войти в контейнер django, используя docker exec, и выполнить те же команды для создания DATABASE_URL, а также CELERY_BROKER_URL, как показано в сценарии. Однако до сих пор не известно, почему сценарии точки входа нельзя использовать для создания переменных среды.


person Dan R    schedule 11.02.2019    source источник
comment
Если вы установите переменную с точкой входа, она не будет видна с docker exec, поскольку docker exec создает новую оболочку, которая не является дочерней для вашей точки входа.   -  person BMitch    schedule 12.02.2019
comment
А, понял. Итак, просто чтобы уточнить, переменные все еще устанавливаются, когда задача раскручивается в узле, и могут использоваться задачей. Однако мне не видно, когда я использую docker exec для выполнения одноразовых команд, и я должен установить их снова для этого экземпляра.   -  person Dan R    schedule 12.02.2019
comment
Я бы даже не назвал это задачей, это скорее дочерние процессы для сценария точки входа, которые имеют доступ к переменным. Задача заключается в создании контейнера, в котором выполняется сценарий точки входа, поэтому задача находится на несколько уровней выше.   -  person BMitch    schedule 12.02.2019
comment
Спасибо за разъяснение @BMitch! Наконец-то понял концепцию.   -  person Dan R    schedule 13.02.2019


Ответы (1)


Это решено благодаря bmitch, см. комментарии. Для всех, кто сталкивается с этим. Сценарии Entrypoint отлично работают, когда задача создает cotnainer/child process. Так что любые установленные в них переменные будут доступны контейнерам/дочернему процессу.

Не проблема заключалась в том, что когда я использовал docker exec для выполнения одноразовых команд внутри определенного контейнера/дочернего процесса, он создает новую оболочку, которая не вызывает сценарий точки входа, поэтому не имеет доступа к переменным, установленным в точке входа. Однако вы можете снова установить их в оболочке, и дочерний процесс будет иметь к ним доступ. например миграция базы данных и т.д.

person Dan R    schedule 12.02.2019