Как предотвратить запуск моего скрипта каждый раз при запуске контейнера Docker? - PullRequest
2 голосов
/ 19 февраля 2020

Я бы хотел запустить скрипт (заполнить мой MySql Docker контейнер) только тогда, когда мои docker контейнеры созданы. Я запускаю следующий файл docker -compose.yml, который содержит контейнер Django.

version: '3'

services:
  mysql:
    restart: always
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: 'maps_data'
      # So you don't have to use root, but you can if you like
      MYSQL_USER: 'chicommons'
      # You can use whatever password you like
      MYSQL_PASSWORD: 'password'
      # Password for root access
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - "3406:3406"
    volumes:
      - my-db:/var/lib/mysql

  web:
    restart: always
    build: ./web
    ports:           # to access the container from outside
      - "8000:8000"
    env_file: .env
    environment:
      DEBUG: 'true'
    command: /usr/local/bin/gunicorn maps.wsgi:application -w 2 -b :8000
    depends_on:
      - mysql

  apache:
    restart: always
    build: ./apache/
    ports:
      - "80:80"
    #volumes:
    #  - web-static:/www/static
    links:
      - web:web

volumes:
  my-db:

У меня есть этот web / Dockerfile

FROM python:3.7-slim

RUN apt-get update && apt-get install

RUN apt-get install -y libmariadb-dev-compat libmariadb-dev
RUN apt-get update \
    && apt-get install -y --no-install-recommends gcc \
    && rm -rf /var/lib/apt/lists/*

RUN python -m pip install --upgrade pip
RUN mkdir -p /app/

WORKDIR /app/

COPY requirements.txt requirements.txt
RUN python -m pip install -r requirements.txt

COPY entrypoint.sh /app/
COPY . /app/
RUN ["chmod", "+x", "/app/entrypoint.sh"]

ENTRYPOINT ["/app/entrypoint.sh"]

, и это содержимое моей точки входа. sh file

#!/bin/bash
set -e

python manage.py migrate maps
python manage.py loaddata maps/fixtures/country_data.yaml
python manage.py loaddata maps/fixtures/seed_data.yaml

exec "$@"

Проблема заключается в том, что, когда я несколько раз запускаю "docker -compose up", точка входа. sh скрипт запускается с его командами. Я бы предпочел, чтобы команды запускались только при первом сборке контейнера docker, но, похоже, они всегда запускаются при восстановлении контейнера. Есть ли способ настроить то, что мне нужно для этого?

Ответы [ 5 ]

1 голос
/ 27 февраля 2020

Идея заполнения базы данных при первом запуске - очень распространенный случай. Как и предлагали другие, вы можете изменить свой entrypoint.sh сценарий и применить к нему некоторые условные логи c и сделать так, как вы хотите, чтобы они работали.

Но я думаю, что это действительно лучшая практика, если Вы разделяете логи c для seeding the database и running services и не держите их запутанными друг с другом. Это может привести к нежелательному поведению в будущем.

Я собирался предложить обходной путь, используя docker-compose, и начал искать какой-то синтаксис для исключения некоторых служб при выполнении docker-compose up, но обнаружил, что это все еще открытый выпуск . Но я нашел этот ответ переполнения стека Ведьма предложила очень хороший подход.

version: '3'

services:
  all-services:
    image: docker4w/nsenter-dockerd # you want to put there some small image
    command: sh -c "echo start"
    depends_on:
      - mysql
      - web
      - apache

  mysql:
    restart: always
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: 'maps_data'
      # So you don't have to use root, but you can if you like
      MYSQL_USER: 'chicommons'
      # You can use whatever password you like
      MYSQL_PASSWORD: 'password'
      # Password for root access
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - "3406:3406"
    volumes:
      - my-db:/var/lib/mysql

  web:
    restart: always
    build: ./web
    ports:           # to access the container from outside
      - "8000:8000"
    env_file: .env
    environment:
      DEBUG: 'true'
    command: /usr/local/bin/gunicorn maps.wsgi:application -w 2 -b :8000
    depends_on:
      - mysql

  apache:
    restart: always
    build: ./apache/
    ports:
      - "80:80"
    #volumes:
    #  - web-static:/www/static
    links:
      - web:web

  seed:
    build: ./web
    env_file: .env
    environment:
      DEBUG: 'true'
    entrypoint: /bin/bash -c "/bin/bash -c \"$${@}\""
    command: |
      /bin/bash -c "
        set -e
        python manage.py loaddata maps/fixtures/country_data.yaml
        python manage.py loaddata maps/fixtures/seed_data.yaml
        /bin/bash || exit 0
      "
    depends_on:
      - mysql

volumes:
  my-db:

Если вы воспользуетесь чем-то подобным выше, вы сможете запустить seeding stage перед запуском docker-compose up.

Для заполнения вашей базы данных выполните:

docker-compose up seed

Для запуска всего стека используйте:

docker-compose up -d all-services

Я думаю, что это чистый подход и , может быть расширен до многих различных сценариев ios и вариантов использования.

ОБНОВЛЕНИЕ

Если вы действительно хотите иметь возможность запустить весь стек полностью, а также предотвратить Неожиданное поведение, вызванное многократным выполнением команды loaddata, я бы посоветовал вам определить новую команду управления django для проверки существующих данных. Посмотрите на это:

checkseed.py

from django.core.management.base import BaseCommand, CommandError
from project.models import Country  # or whatever model you have seeded

class Command(BaseCommand):
    help = 'Check if seed data already exists'

    def handle(self, *args, **options):
        if Country.objects.all().count() > 0:
            self.stdout.write(self.style.WARNING('Data already exists .. skipping'))
            return False
        # do all the checks for your data integrity
        self.stdout.write(self.style.SUCCESS('Nothing exists'))
        return True

И после этого вы можете изменить seed часть docker-compose, как показано ниже:

  seed:
    build: ./web
    env_file: .env
    environment:
      DEBUG: 'true'
    entrypoint: /bin/bash -c "/bin/bash -c \"$${@}\""
    command: |
      /bin/bash -c "
        set -e
        python manage.py checkseed &&
        python manage.py loaddata maps/fixtures/country_data.yaml
        python manage.py loaddata maps/fixtures/seed_data.yaml
        /bin/bash || exit 0
      "
    depends_on:
      - mysql

Таким образом, вы можете быть уверены, что если кто-нибудь запустит docker-compose up -d по ошибке, это не вызовет ошибок целостности и подобных проблем.

1 голос
/ 23 февраля 2020

Подход, который я использовал ранее, заключается в том, чтобы обернуть ваши loaddata вызовы в вашу собственную команду управления, которая сначала проверяет, есть ли какие-либо данные в базе данных, и, если есть, ничего не делает. Примерно так:

# your_app/management/commands/maybe_init_data.py

from django.core.management import call_command
from django.core.management.base import BaseCommand

from address.models import Country

class Command(BaseCommand):

    def handle(self, *args, **options):
        if not Country.objects.exists():
            self.stdout.write('Seeding initial data')
            call_command('loaddata', 'maps/fixtures/country_data.yaml')
            call_command('loaddata', 'maps/fixtures/seed_data.yaml')

И затем измените сценарий точки входа на:

python manage.py migrate
python manage.py maybe_init_data

(Предположим, что у вас есть модель Country - замените ее на модель, которую вы на самом деле делаете есть в ваших светильниках.)

0 голосов
/ 27 февраля 2020

Обычно build и deploy разделены.

Ваш ENTRYPOINT является частью deploy . Если вы хотите сконфигурировать вручную с помощью команды deploy, выполните команды migrate и просто замените контейнеры на новые (возможно, из fre sh image), тогда вы можете разделить их на отдельные команды

start database (если не работает)

docker-compose -p production -f docker-compose.yml up mysql -d

выполнить миграцию

docker run \
        --rm \
        --network production_default \
        --env-file docker.env \
        --entrypoint python \
        my-backend-image-name:prod python manage.py migrate maps

, а затем развернуть fre sh image

docker-compose -p production -f docker-compose.yml up -d

И каждый раз вручную принимайте решение о запуске шага миграции или не

0 голосов
/ 26 февраля 2020

У меня недавно был похожий случай. Поскольку «ENTRYPOINT» содержит команду, которая будет выполняться при каждом запуске контейнера, решение будет включать в себя некоторые logi c в точке входа . sh сценарий, чтобы избежать применения обновлений (в вашем случае миграция и загрузка данных), если эффекты этих операций уже присутствуют в базе данных.

Например:

#!/bin/bash
set -e

#Function that goes to verify if effects of migration and load data are present on database
function checkEffects() {
  IS_UPDATED=0
  #Check effects and set to 1 IS_UPDATED if effects are not present
}

checkEffects
if [[ $IS_UPDATED == 0 ]]
then
   echo "Database already initialized. Nothing to do"
else
   echo "Database is clean. Initializing it"
   python manage.py migrate maps
   python manage.py loaddata maps/fixtures/country_data.yaml
   python manage.py loaddata maps/fixtures/seed_data.yaml
fi

exec "$@"

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

Пример: Сегодня вы работаете с локальным Dockerfile для своей службы web , но я думаю, что в процессе работы вы начнете создавать версии этой службы для загрузки это в Docker реестре. Поэтому, когда вы загрузите свой первый выпуск (например, 1.0.0 версия ), вы укажите следующее в вашем docker -compose.yml:

web:
   restart: always
   image: <DOCKER_REGISTRY_HOST>:<DOCKER_REGISTRY_PORT>/web:1.0.0 
   ports: # to access the container from outside
     - "8000:8000" 

Затем вы выпустите версию 1.2.0 * контейнера службы web , когда будете вносить другие изменения в схему, например, загружать другие данные в точку входа. sh:

#1.0.0 updates  
python manage.py migrate maps 
python manage.py loaddata maps/fixtures/country_data.yaml 
python manage.py loaddata maps/fixtures/seed_data.yaml
#1.2.0 updates   
python manage.py loaddata maps/fixtures/other_seed_data.yaml 

Здесь у вас будет 2 сценария ios ( давайте пока проигнорируем необходимость проверять эффекты на сценарии ):

1- Вы впервые развертываете свои сервисы с web: 1.2.0 : начиная с чистой базы данных, вы должны быть уверены, что все обновления выполнены (оба 1.1.0 и 1.2.0 ).

Решение этого случая простое, поскольку вы можете просто выполнить все обновления.
2 - вы обновляете веб-контейнер до 1.2.0 вкл. существующая среда, в которой 1.0.0 работала : поскольку ваша база данных была инициализирована с обновлениями от 1.0.0, вы должны быть уверены, что only 1.2.0 обновления выполняются

Здесь сложно, потому что вы должны иметь возможность проверить, какая версия базы данных применяется, чтобы пропустить обновления 1.0.0. Это означает, что вы должны хранить веб-версию где-нибудь в базе данных, например

Согласно всем этим обсуждениям, поэтому я думаю, что лучшее решение - это работать непосредственно над сценариями, которые идут для создания схемы и заполнения данные, чтобы сделать эти инструкции идемпотентными, обращая внимание на обновления.

Некоторые примеры:

1- Создайте таблицу

Вместо создания таблицы следующим образом:

CREATE TABLE country

используйте , если не существует , чтобы избежать таблица уже существует ошибка:

CREATE TABLE IF NOT EXISTS country

2- Вставьте данные по умолчанию

Вместо этого вставьте данные без указания первичного ключа:

INSERT INTO maps.country (name) VALUES ("USA");

Включите первичный ключ , чтобы избежать дубликатов :

INSERT INTO maps.country (id,name) VALUES (1,"USA");
0 голосов
/ 20 февраля 2020

Вместо использования файла entrypoint.sh, почему бы просто не запустить команды в web / Dockerfile?

RUN python manage.py migrate maps
RUN python manage.py loaddata maps/fixtures/country_data.yaml
RUN python manage.py loaddata maps/fixtures/seed_data.yaml

Таким образом, эти изменения будут запечены в образе и, когда вы запустите образ эти изменения будут уже выполнены.

...