Получение логов из контейнера докеров внутри jenkins

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

Итак, я хочу запустить его как конвейер и использовать jenkinsfile. Это то, что я получил до сих пор:

pipeline {
    agent any
    stages {
        stage('build war') {
            agent {
                docker { 
                    image 'gradle:latest'
                    reuseNode true 
                }
            }
            steps {
                sh 'gradle war -b oven/build.gradle'
            }
        }
        stage('test') {
            steps {
                script {
                    docker.image('mysql:latest').withRun('-e "MYSQL_ROOT_PASSWORD=password" -e "MYSQL_USER=root" -e "MYSQL_DATABASE=highlygroceries"') { c -> 
                        docker.image('munhunger/highly-oven').withRun('-e "test=test"') { h -> 
                            docker.image('mysql:latest').inside("--link ${c.id}:db") {
                                sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
                            }
                            docker.image('munhunger/highly-oven').inside("--link ${c.id}:db -e 'DB_URL=db:3306' -e 'DB_PASS=password' -e 'DB_USER=root'") {
                                sh 'sleep 5'
                            }
                            docker.image('gradle:latest').inside("--link ${h.id}:backend -e 'OVEN_URL=http://backend:8080'") {
                                sh 'gradle test -b oven/build.gradle'
                            }
                            sh "docker logs ${h.id}"
                        }
                    }
                }
            }
        }
        stage('build dockerimage') {
            steps {
                script {
                    dir('oven') {
                        def image = docker.build("munhunger/highly-oven")

                        docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') {
                            image.push("${env.BUILD_NUMBER}")
                            image.push("latest")
                        }
                    }
                }
            }
        }
    }
}

Но, похоже, проблема с соединением между моим бэкэндом и базой данных...

Все, что я получаю из журналов сборки, это:

se.munhunger.oven.rest.UserTest > System is up and running, Creating a user, it returns 204 upon creation FAILED
    java.lang.AssertionError at UserTest.java:38

что указывает на:

                Assert.assertEquals("non 204 from backend", 204,
                                    client.target(baseURL + "/api/user")
                                        .request()
                                        .header("email", "[email protected]")
                                        .post(Entity.json(null))
                                        .getStatus());

Я считаю, что связь между тестером и серверной частью работает, потому что следующий тест прошел успешно

            Assert.assertEquals(200,
                                client.target(baseURL + "/swagger")
                                    .request()
                                    .get()
                                    .getStatus());

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

Изменить Я получил несколько журналов, но не в то время. Если я изменю на следующее:

                docker.image('mysql:latest').withRun('-e "MYSQL_ROOT_PASSWORD=password" -e "MYSQL_USER=root" -e "MYSQL_DATABASE=highlygroceries"') { c -> 
                    docker.image('munhunger/highly-oven').withRun('-e "test=test"') { h -> 
                        docker.image('mysql:latest').inside("--link ${c.id}:db") {
                            sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
                        }
                        docker.image('munhunger/highly-oven').inside("--link ${c.id}:db -e 'DB_URL=db:3306' -e 'DB_PASS=password' -e 'DB_USER=root'") {
                            sh 'sleep 5'
                        }
                        sh "docker logs ${h.id}"
                        docker.image('gradle:latest').inside("--link ${h.id}:backend -e 'OVEN_URL=http://backend:8080'") {
                            sh 'gradle test -b oven/build.gradle'
                        }
                        sh "docker logs ${h.id}"
                    }
                }

Я получаю все журналы с запуска. но он не распечатывает журналы после сбоя теста


person munHunger    schedule 12.02.2018    source источник


Ответы (2)


Это помогает, когда вы можете запускать интеграционные тесты без использования вашего файла Jenkins, поэтому вместо использования этих вложенных операторов docker.image вы должны использовать docker-compose.

I для интеграционного тестирования следующим образом:

stage('Run integration tests') {
  steps {
    script {
      try {
        timeout(30) {
          // Tear up integration test environment
          sh "docker-compose up -d"
          // Wait until it is ready
          waitUntil {
            "healthy" == sh(returnStdout: true,
              script: "docker inspect CONTAINER_NAME --format=\"{{ .State.Health.Status }}\"").trim()
          }

          docker.image('IMAGENAME').inside('--network projectname_default') {
            sh "gradle integrationTest"
          }
        }
      } finally {
        try {
          step([$class: 'JUnitResultArchiver', testResults: '**/build/integrationTest-results/TEST-*.xml'])
        } catch (Exception e) {
          // Ignore exception when there are no test results
        }
        sh "docker-compose logs >integration-test.log"
        sh "docker-compose down --rmi local --volumes --remove-orphans"
        archive 'integration-test.log'
      }
    }
  }
}

Как видите, я подключаю тестовый контейнер Gradle к сети, настроенной с помощью docker-compose. Это позволяет повторно использовать вашу композицию не только для тестирования.

Теперь вы должны убедиться, что все контейнеры, которые вы используете, выводятся на стандартный вывод. В конце вы получаете все свои логи в архиве integration-test.log. Конечно, вы также можете расширить это, чтобы получить отдельный файл журнала для каждого контейнера.

person Hendrik M Halkow    schedule 13.02.2018
comment
Я заметил, что когда вы выполняете docker-compose logs >integration-test.log, он записывает файл со всеми управляющими символами терминала ANSI, и Jenkins не очень хорошо отображает этот журнал. Было бы здорово, если бы вы могли каким-то образом заархивировать его с флагом или типом mime, указывающим, что в файле могут быть управляющие символы ANSI, чтобы Дженкинс мог правильно его отображать. Кстати, вы можете добавить флаг --no-ansi, чтобы заархивировать только текстовую версию. - person Wyck; 19.09.2018

Оказывается, вы можете прямо поймать это дерьмо:

pipeline {
    agent any
    stages {
        stage('build war') {
            agent {
                docker { 
                    image 'gradle:latest'
                    reuseNode true 
                }
            }
            steps {
                sh 'gradle war -b oven/build.gradle'
            }
        }
        stage('test') {
            steps {
                script {
                    docker.image('mysql:latest').withRun('-e "MYSQL_ROOT_PASSWORD=password" -e "MYSQL_USER=root" -e "MYSQL_DATABASE=highlygroceries"') { c -> 
                        docker.image('munhunger/highly-oven').withRun('-e "test=test"') { h -> 
                            docker.image('mysql:latest').inside("--link ${c.id}:db") {
                                sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
                            }
                            docker.image('munhunger/highly-oven').inside("--link ${c.id}:db -e 'DB_URL=db:3306' -e 'DB_PASS=password' -e 'DB_USER=root'") {
                                sh 'sleep 5'
                            }
                            try {
                                docker.image('gradle:latest').inside("--link ${h.id}:backend -e 'OVEN_URL=http://backend:8080'") {
                                        sh 'gradle test -b oven/build.gradle'

                                }
                            }
                            catch (exc) {
                                sh "docker logs ${h.id}"
                                throw exc
                            }
                        }
                    }
                }
            }
        }
        stage('build dockerimage') {
            steps {
                script {
                    dir('oven') {
                        def image = docker.build("munhunger/highly-oven")

                        docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials') {
                            image.push("${env.BUILD_NUMBER}")
                            image.push("latest")
                        }
                    }
                }
            }
        }
    }
}
person munHunger    schedule 12.02.2018
comment
Это интригует, но я не понимаю, в чем отличие и как это приведет к появлению более поздних журналов. - person Lee Meador; 07.06.2018
comment
Разница в том, что ранее, когда он запускал sh 'gradle test...., он выдавал исключение, которое заставляло jenkins выходить из шага и, таким образом, не получать никаких значимых журналов. С оператором try/catch это не имеет значения, поскольку он перехватывает ошибки из теста и печатает из него все журналы. - person munHunger; 08.06.2018
comment
Что делают все --link? Кажется, это как-то связано с тем, что журнал доступен для печати с тех пор, как h.id - person Lee Meador; 08.06.2018
comment
Аргументы --link сопоставляют контейнеры Docker с пригодными для использования именами DNS. с ними мое серверное приложение может получить доступ к базе данных, используя URL-адрес db. Было бы довольно сложно получить доступ к базе данных из бэкэнда без этой ссылки. В этом примере кода они на самом деле не связаны с получением журналов, вместо этого они только удостоверяются, что код внутри разных контейнеров может получить доступ друг к другу. - person munHunger; 11.06.2018