Как использовать переменные окружения в CMD для аргументов ТОЧКИ входа в файле Dockerfile?

#docker #dockerfile

Вопрос:

У меня есть файл настройки, в котором я запускаю исполняемый файл с такими аргументами по умолчанию, как этот:

 ENTRYPOINT ["executable", "cmd"]
CMD ["--param1=1", "--param2=2"]
 

Это отлично работает, и я могу запустить контейнер с аргументами по умолчанию:

 docker run image_name
 

или с пользовательскими аргументами:

 docker run image_name --param1=a --param2=2
 

Теперь я хотел бы, чтобы параметр по умолчанию зависел от переменной среды или по умолчанию имел значение deafult (1) , подобное этому:

 --param1='${PARAM1:-1}'
 

Я понимаю, что

 ENTRYPOINT ["executable", "cmd"]
CMD ["--param1='${PARAM1:-1}'", "--param2=2"]
 

не работает, так как CMD находится в форме exec и не вызывает командную оболочку и не может заменять переменные среды.

Но если я использую CMD в форме оболочки:

 ENTRYPOINT ["executable", "cmd"]
CMD "--param1='${PARAM1:-1}' --param2=2"
 

Я получаю no such option: -c

Поэтому мой вопрос таков:

Как я могу архивировать замену переменных среды в аргументах по умолчанию в CMD для моей ТОЧКИ входа?

Ответ №1:

Одним из способов было бы потерять CMD и перенести все значения по умолчанию в пользовательскую точку входа. Я стараюсь избегать этого, но иногда это кажется самым чистым способом, и вы можете быть намного более гибкими:

Докерфайл:

     COPY 'my-entrypoint.sh' '/somewhere/in/path/my-entrypoint'
    ENTRYPOINT ['my-entrypoint']
 

my-entrypoint.sh

     #!/bin/sh
  
    ARGS="${@}"
    if [ -z "${ARGS}" ]; then
        ARGS="--param1=${PARAM1:-1} --param2=2"
    fi
  
    executable cmd $ARGS
 

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

1. Спасибо за эту подсказку, она хорошо работает, но мне пришлось изменить последнюю строку с executable cmd "${ARGS}" на executable cmd $ARGS . В противном случае строка ARGS была обрезана и, следовательно, неполной. Чего я не могу понять. Я проверял echo "${ARGS}" и echo $ARGS раньше executable cmd $ARGS , и оба выглядели завершенными.

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

Ответ №2:

Вы не можете сделать это так, как вы описываете, по причинам, которые вы изложили в вопросе. ENTRYPOINT И CMD просто объединяются вместе, образуя единую командную строку, и если одна или обе эти части являются строкой, а не массивом JSON, он автоматически преобразуется sh -c 'the string' .

 ENTRYPOINT ["executable", "cmd"]
CMD "--param1='${PARAM1:-1}' --param2=2"

# Equivalently:
ENTRYPOINT ["executable", "cmd", "/bin/sh", "-c", ""--param1=...""]
CMD []
 

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

В Docker и Kubernetes, как правило, оказывается более удобным передавать параметры через переменные среды, чем в командной строке. Это означает, что вашему приложению необходимо знать, как искать эти переменные, и указать некоторые из значений по умолчанию, которые вы описываете здесь. Некоторые библиотеки анализа аргументов поддерживают это из коробки, но не все. argparse Например, стандартная библиотека Python напрямую не поддерживает переменные среды, но вы все равно можете легко поддерживать их:

 import argparse
import os

parser = argparse.ArgumentParser()
parser.add_argument('param1', default=os.environ.get('PARAM1', '1'))

args = parser.parse_args()
print(args.param1)
# Uses --param1 option, or else $PARAM1 variable, or else default "1"
 

Другой подход, который я обычно рекомендую,-это создать CMD хорошо сформированную команду оболочки; не пытайтесь разделить команду между CMD и ENTRYPOINT . Это позволяет избежать проблемы, связанной с тем, что докер вставляет sh -c оболочку в середину строки.

 # no ENTRYPOINT
CMD executable cmd --param1="${PARAM1:-1}" --param2=2
 

ENTRYPOINT Шаблон, который я нахожу полезным, состоит в том, чтобы использовать сценарий-оболочку для предоставления значений по умолчанию и выполнения других настроек в первый раз. Если этот сценарий является сценарием оболочки Bourne и заканчивается exec "$@" , то он будет выполняться CMD как основной процесс контейнера.

 #!/bin/sh
# docker-entrypoint.sh

# In Docker specifically, default $PARAM1 to "docker", not "1".
: ${PARAM1:=docker}

# Run the main container command.
exec "$@"
 
 ENTRYPOINT ["/docker-entrypoint.sh"] # must be a JSON array
CMD executable cmd --param2=2
 

(Нет необходимости иметь ENTRYPOINT . Создание ENTRYPOINT интерпретатора и ввод имени сценария CMD не приносит никакой пользы и затрудняет выполнение таких команд отладки, как docker run --rm my-image ls -l /app .)