Другое

Исправление ошибки Docker TTY в Jenkins: Полное руководство

Узнайте, как исправить ошибку 'The input device is not a TTY' при выполнении команд Docker в Jenkins. Полное руководство с решениями, лучшими практиками и примерами кода для бесперебойной работы CI/CD конвейеров.

Как исправить ошибку “The input device is not a TTY” при выполнении команд Docker в Jenkins?

Я сталкиваюсь с ошибкой “The input device is not a TTY” при выполнении следующей команды в моем Jenkinsfile:

docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh

Мне нужно запустить скрипт с именем script.sh внутри Docker-контейнера из Jenkins, но я хочу избежать использования интерактивного режима. Как правильно выполнить эту команду без вызова ошибки TTY?

Ошибка “устройство ввода не является TTY” возникает при использовании флага -t Docker (выделение псевдотерминала) в средах без терминала, таких как конвейеры Jenkins CI/CD. Чтобы исправить это, удалите флаг -t из команды Docker run, поскольку Jenkins не предоставляет интерактивный доступ к терминалу. В вашем конкретном случае измените docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh на docker run -v $PWD:/foobar -i cloudfoundry/cflinuxfs2 /foobar/script.sh или просто docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh.

Содержание

Понимание ошибки TTY

Ошибка “устройство ввода не является TTY” возникает, когда Docker пытается выделить псевдотерминал (TTY), но не может этого сделать, потому что среда не поддерживает выделение терминала. В Jenkins и других средах CI/CD процессы запускаются в неинтерактивном режиме без доступа к терминалам.

Когда вы используете флаг -t в командах Docker, например:

bash
docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh

Флаг -t указывает Docker выделить TTY для контейнера. Однако Jenkins запускает задания в неинтерактивных средах, где TTY недоступен, что вызывает ошибку.

Ключевое замечание: Флаг -t обычно используется для интерактивных сеансов, где вы хотите вводить команды непосредственно в контейнер. Для выполнения скриптов в средах CI/CD этот флаг обычно не требуется.

Немедленные решения

Простое удаление флага

Самый простой способ исправить проблему — удалить флаг -t из команды Docker:

bash
# До (вызывает ошибку в Jenkins)
docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh

# После (работает в Jenkins)
docker run -v $PWD:/foobar -i cloudfoundry/cflinuxfs2 /foobar/script.sh

Если вам не нужен ввод STDIN, вы можете удалить оба флага -i и -t:

bash
docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh

Условное выделение TTY

Для скриптов, которые должны работать как в интерактивных, так и в неинтерактивных средах, используйте условную логику для обнаружения доступности TTY:

bash
DOCKER_RUN_OPTIONS="--rm"
# Выделять tty только если обнаружен
if [ -t 0 ] && [ -t 1 ]; then
    DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -t"
fi

docker run $DOCKER_RUN_OPTIONS -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh

Этот подход позволяет одному и тому же скрипту работать как в терминальных средах, так и в конвейерах CI/CD.

Лучшие практики для конвейеров Jenkins

Использование конвейера с плагином Docker

Jenkins предоставляет встроенную поддержку Docker, которая автоматически обрабатывает выделение TTY:

groovy
pipeline {
    agent {
        docker {
            image 'cloudfoundry/cflinuxfs2'
            args '-v $PWD:/foobar'
        }
    }
    stages {
        stage('Запуск скрипта') {
            steps {
                sh '/foobar/script.sh'
            }
        }
    }
}

Этот подход предпочтителен, так как он делегирует управление Docker Jenkins и избегает проблем, связанных с TTY.

Выполнение скрипта в Docker-контейнере

Если вам нужно запускать Docker-команды из конвейера Jenkins, используйте следующий шаблон:

groovy
pipeline {
    agent any
    stages {
        stage('Запуск Docker-команды') {
            steps {
                script {
                    // Удаляем флаг -t для неинтерактивного выполнения
                    sh 'docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh'
                }
            }
        }
    }
}

Конфигурация, зависящая от среды

Для более сложных сценариев вы можете определять среду и соответствующим образом настраивать флаги Docker:

groovy
pipeline {
    agent any
    environment {
        IS_TTY = sh(script: 'test -t 0 && echo "true" || echo "false"', returnStdout: true).trim()
    }
    stages {
        stage('Конфигурация Docker') {
            steps {
                script {
                    def dockerArgs = "-v $PWD:/foobar"
                    if (env.IS_TTY == "true") {
                        dockerArgs += " -t"
                    }
                    sh "docker run ${dockerArgs} cloudfoundry/cflinuxfs2 /foobar/script.sh"
                }
            }
        }
    }
}

Альтернативные подходы

Использование Docker Exec

Если у вас уже запущен контейнер, рассмотрите возможность использования docker exec:

bash
# Запускаем контейнер без TTY
docker run -d --name my-container -v $PWD:/foobar cloudfoundry/cflinuxfs2 sleep 3600

# Выполняем скрипт в работающем контейнере
docker exec my-container /foobar/script.sh

# Очистка
docker stop my-container
docker rm my-container

Использование плагина Jenkins Docker

Плагин Jenkins Docker обеспечивает лучшую интеграцию:

groovy
pipeline {
    agent {
        docker {
            image 'cloudfoundry/cflinuxfs2'
            args '-v $PWD:/foobar'
            reuseNode true
        }
    }
    stages {
        stage('Выполнение скрипта') {
            steps {
                sh '/foobar/script.sh'
            }
        }
    }
}

Использование Docker Compose

Для сложных настроек Docker Compose может помочь управлять средой:

yaml
version: '3'
services:
    build:
        image: cloudfoundry/cflinuxfs2
        volumes:
            - .:/foobar
        command: /foobar/script.sh

Затем запустите его из Jenkins:

bash
docker-compose up --build

Расширенная конфигурация

Обработка интерактивных команд

Если ваш скрипт требует некоторой интерактивной функциональности, вы можете использовать обходные пути:

bash
# Используем expect или другие инструменты автоматизации
docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /bin/sh -c 'expect -c "spawn /foobar/script.sh; interact"'

Переменные окружения

Настройте переменные окружения для управления поведением Docker:

bash
# В вашем Jenkinsfile или скрипте
DOCKER_TTY_FLAG=""
if [ "${JENKINS_HOME}" != "" ]; then
    DOCKER_TTY_FLAG=""
else
    DOCKER_TTY_FLAG="-t"
fi

docker run ${DOCKER_TTY_FLAG} -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh

Логирование и вывод

Для лучшего отладки в Jenkins обеспечьте правильную обработку вывода:

bash
docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh > build.log 2>&1
cat build.log

Тестирование и валидация

Локальное тестирование

Тестируйте ваши Docker-команды локально перед развертыванием в Jenkins:

bash
# Тестируем в терминале (должно работать с -t)
docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh

# Тестируем без терминала (имитация среды Jenkins)
echo "exit" | docker run -v $PWD:/foobar -it cloudfoundry/cflinuxfs2 /foobar/script.sh

# Тестируем рекомендуемый подход
docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh

Валидация конвейера Jenkins

Создайте простой тестовый конвейер для валидации вашей Docker-конфигурации:

groovy
pipeline {
    agent any
    stages {
        stage('Тест Docker-команды') {
            steps {
                echo 'Тестирование Docker-команды без TTY...'
                sh '''
                    docker run --rm alpine echo "Docker-команда выполнена успешно"
                '''
            }
        }
    }
}

Обработка ошибок

Реализуйте правильную обработку ошибок в ваших конвейерах Jenkins:

groovy
pipeline {
    agent any
    stages {
        stage('Запуск Docker-скрипта') {
            steps {
                script {
                    try {
                        sh 'docker run -v $PWD:/foobar cloudfoundry/cflinuxfs2 /foobar/script.sh'
                    } catch (Exception e) {
                        error "Docker-команда не удалась: ${e.getMessage()}"
                    }
                }
            }
        }
    }
}

Заключение

Ошибка “устройство ввода не является TTY” — это распространенная проблема при выполнении Docker-команд в конвейерах Jenkins CI/CD. Вот основные выводы:

  1. Удалите флаг -t — это наиболее распространенное исправление для сред Jenkins, где интерактивные терминалы недоступны.

  2. Используйте плагин Jenkins Docker — встроенная поддержка Docker автоматически обрабатывает выделение TTY и является рекомендуемым подходом.

  3. Реализуйте условную логику — для скриптов, которые должны работать как в интерактивных, так и в неинтерактивных средах, обнаруживайте доступность TTY и соответствующим образом настраивайте флаги.

  4. Рассмотрите альтернативные подходы — используйте docker exec для уже работающих контейнеров или воспользуйтесь Docker Compose для сложных настроек.

  5. Тщательно тестируйте — валидируйте Docker-команды локально перед развертыванием в Jenkins, чтобы избежать неожиданных проблем.

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

Источники

  1. Stack Overflow - Error “The input device is not a TTY”
  2. Better Stack Community - Solved: The Input Device Is Not a TTY
  3. Medium - Fix: “The input device is not a TTY” Error in Docker
  4. Magetop Blog - Fixing “The input device is not a TTY” Error With Docker Run
  5. Jenkins Documentation - Using Docker with Pipeline
  6. Server Fault - Best practices to avoid Jenkins error: sudo: no tty present
Авторы
Проверено модерацией
Модерация