Большинство знакомы со сценарием, когда переменные окружения определяются на уровне операционной системы или окружения конкретного пользователя. Однако мало кто знает, что переменные могут быть установлены в том числе на уровне конкретного процесса. О примерах таких сценариев и поговорим в статье.
Переменные окружения процесса
Сначала обсудим вопросы общего характера, а потом я расскажу пример про GitHub Actions из реальной практики.
Проблема
Приложения могут взаимодействовать с переменными окружения, например извлекать из них полезные данные, секреты, параметры конфигурации. Это особенно актуально для процессов CI/CD, которые часто запускаются в контейнерах.
В целях отладки необходимо точно понимать с какими переменными окружения и их значениями работает тот или иной процесс.
Решение
Для любого пайплайна CI/CD первой командой я всегда вывожу список переменных окружения:
1 |
env |
Если вы работаете с докер-контейнером, достаточно зайти в него:
1 |
docker exec -it <container_name> /bin/bash |
далее выполнить ту же команду – env или printenv.
Любой процесс в операционной системе также запускается с определенными переменными окружения. ОС хранит информацию о запущенных процессах в специальном каталоге (/proc), из которого можно вытащить в том числе данные о переменных окружения. Используйте для этого однострочник:
1 2 |
# use top or ps aux to check process PID cat /proc/<pid>/environ | tr '\0' '\n' |
Где <pid> это PID процесса. Посмотреть можно с помощью обычного top или ps aux. Также увидеть переменные окружения процесса можно и обычным ps:
1 |
ps e -p <pid> |
А теперь поговорим о примерах ситуаций, когда это может пригодиться.
Пример
Я использовал простейший пайплайн для сборки Java-приложения в GitHub Actions. Первым делом после клонирования репозитория нужно было установить требуемую версию Java. Для github-hosted раннеров это работало отлично, но проблемы начались при переходе на self-hosted раннеры. Вот пример пайплайна:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
name: "Test Java action" on: workflow_dispatch: jobs: test-java: runs-on: [self-hosted, home] container: image: ubuntu:20.04 steps: - name: Debug run: | set -x printenv - uses: actions/checkout@v3 - uses: actions/setup-java@v2 with: java-version: '8' distribution: 'zulu' java-package: jdk+fx cache: gradle |
Я получал очень странную ошибку, которая утверждала, что версия Java не передавалась:
1 |
Error: Input required and not supplied: java-version |
Пайплайн не менялся при переходе на свои раннеры и ошибка очевидно была в раннере или его окружении. Первым делом я обратился к логам на самом раннере и обнаружил, что номер версии действительно пустой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[2022-07-11 19:15:16Z INFO ActionManifestManager] Loaded action.yml file: { "name": "Setup Java JDK", "description": "Set up a specific version of the Java JDK and add the command-line tools to the PATH", "inputs": { "type": 2, "map": [ { "key": { "type": 0, "file": 3, "line": 6, "col": 3, "lit": "java-version" }, "value": "" } |
Ниже в логах был пример команды docker exec. Экшн использовал её для установки Java:
1 |
[2022-07-11 19:15:16Z INFO ProcessInvokerWrapper] Arguments: 'exec -i --workdir /__w/<repo-name>/<repo-name> -e INPUT_JAVA-VERSION -e INPUT_DISTRIBUTION -e INPUT_JAVA-PACKAGE -e INPUT_CACHE -e INPUT_ARCHITECTURE -e INPUT_JDKFILE -e INPUT_CHECK-LATEST -e INPUT_SERVER-ID -e INPUT_SERVER-USERNAME -e INPUT_SERVER*** INPUT_SETTINGS-PATH -e INPUT_OVERWRITE-SETTINGS -e INPUT_GPG-PRIVATE-KEY -e INPUT_GPG-PASSPHRASE -e INPUT_JOB-STATUS -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RETENTION_DAYS -e GITHUB_RUN_ATTEMPT -e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME -e GITHUB_SERVER_URL -e GITHUB_API_URL -e GITHUB_GRAPHQL_URL -e GITHUB_REF_NAME -e GITHUB_REF_PROTECTED -e GITHUB_REF_TYPE -e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e GITHUB_ACTION_REPOSITORY -e GITHUB_ACTION_REF -e GITHUB_PATH -e GITHUB_ENV -e GITHUB_STEP_SUMMARY -e RUNNER_DEBUG -e RUNNER_OS -e RUNNER_ARCH -e RUNNER_NAME -e RUNNER_TOOL_CACHE -e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN -e ACTIONS_CACHE_URL 20f3553f1dcb0aaddc33e81ad81dfb846d9f5c363437c259577d48cc21769e77 /__e/node12/bin/node /__w/_actions/actions/setup-java/v2/dist/setup/index.js' |
Команда содержала интересные переменные с дефисом в имени, например INPUT_JAVA-PACKAGE или INPUT_JAVA-VERSION. Если попробовать задать переменную с таким именем, то оболочка будет ругаться:
1 2 |
# export INPUT_JAVA-VERSION=8 bash: export: `INPUT_JAVA-VERSION=8': not a valid identifier |
Кажется проблема именно в этом, однако это ошибочные рассуждения, ведь на дефолтных раннерах все работало и в таком виде.
Тогда оставалось проверить с какими переменными окружения запускалась задача внутри docker-контейнера. До возникновения ошибки проходило совсем мало времени, всего несколько секунд, а контейнер после этого удалялся. Тем не менее я успевал вытащить данные нужного мне процесса и среди них не оказалось переменных с дефисом в имени:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=<hostname> HOME=/github/home GITHUB_ACTIONS=true CI=true INPUT_DISTRIBUTION=zulu INPUT_CACHE=gradle INPUT_ARCHITECTURE=x64 INPUT_JDKFILE= GITHUB_JOB=test-java GITHUB_REF=refs/heads/test-branch GITHUB_SHA=313186985ec554c3fbe2cc07e526f6917eda9d21 GITHUB_REPOSITORY=<owner>/<repo-name> GITHUB_REPOSITORY_OWNER=<owner> GITHUB_RUN_ID=2651821041 GITHUB_RUN_NUMBER=1 GITHUB_RETENTION_DAYS=90 GITHUB_RUN_ATTEMPT=6 GITHUB_ACTOR=<owner> GITHUB_WORKFLOW=Test Java action GITHUB_HEAD_REF= GITHUB_BASE_REF= GITHUB_EVENT_NAME=workflow_dispatch GITHUB_SERVER_URL=https://github.com GITHUB_API_URL=https://api.github.com GITHUB_GRAPHQL_URL=https://api.github.com/graphql GITHUB_REF_NAME=test-branch GITHUB_REF_PROTECTED=false GITHUB_REF_TYPE=branch GITHUB_WORKSPACE=/__w/<repo-name>/<repo-name> GITHUB_ACTION=__actions_setup-java GITHUB_EVENT_PATH=/github/workflow/event.json GITHUB_ACTION_REPOSITORY=actions/setup-java GITHUB_ACTION_REF=v2 GITHUB_PATH=/__w/_temp/_runner_file_commands/add_path_4c32a692-e509-478a-9290-36582fb04964 GITHUB_ENV=/__w/_temp/_runner_file_commands/set_env_4c32a692-e509-478a-9290-36582fb04964 GITHUB_STEP_SUMMARY=/__w/_temp/_runner_file_commands/step_summary_4c32a692-e509-478a-9290-36582fb04964 RUNNER_DEBUG=1 RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_NAME=<runner-name> RUNNER_TOOL_CACHE=/__w/_tool RUNNER_TEMP=/__w/_temp RUNNER_WORKSPACE=/__w/<repo-name> ACTIONS_RUNTIME_URL=<runtime-url> ACTIONS_RUNTIME_TOKEN=<token> ACTIONS_CACHE_URL=<cache-url> |
Проблема пряталась в запуске docker через обертку в виде bash-скрипта, которая не пропускала необходимые переменные окружения. Таким образом запускаются приложения, установленные через Snap в Ubuntu. Подробнее дискуссию можно почитать в задаче Java-version is not specified on self-hosted but ok on github-runner.
Разумеется проблема решилась переустановкой Docker через обычный apt, а в личной копилке добавилось причин не использовать Snap. Хоть и пришлось потратить некоторое время на дебаг, но опыт получился интересным. Пишите, если вдруг возникают сложности с Linux. Всем спасибо, всем успехов!