Como compilar projetos no Jenkins, se precisar de muitos ambientes diferentes

Como compilar projetos no Jenkins, se precisar de muitos ambientes diferentes

No Hub há muitos artigos sobre Jenkins, mas poucos descrevem um exemplo de funcionamento do Jenkins e dos agentes Docker. Todas as ferramentas de construção de projetos populares como Drone.io, Bitbucket Pipeline, GitLab, GitHub actions e outras, podem compilar tudo em contêineres. Mas e o Jenkins?

Atualmente, existe uma solução para este problema: o Jenkins 2 consegue trabalhar muito bem com agentes Docker. Neste artigo, quero compartilhar minha experiência e mostrar como você pode fazer isso também.

Por que eu decidi resolver esse problema?

Como nós, na empresa Citronium usamos muitas tecnologias diferentes, o servidor de construção precisa manter várias versões do Node.JS, Gradle, Ruby, JDK e outros. Mas muitas vezes, conflitos de versão são inevitáveis. Sim, você estaria certo se dissesse que existem diferentes gerenciadores de versão como nvm, rvm, mas nem tudo é tão simples com eles e essas soluções têm problemas:

  • grande volume de runtimes que os desenvolvedores esquecem de limpar;
  • existem conflitos entre diferentes versões de um mesmo runtime;
  • cada desenvolvedor necessita de um conjunto diferente de componentes.

Existem outros problemas, mas melhor eu falar sobre a solução.

Jenkins no Docker

Dado que o Docker já está bem implantado na esfera de desenvolvimento, quase tudo pode ser executado com Docker. Minha solução é que o Jenkins esteja no Docker e possa executar outros contêineres Docker. Essa questão começou a ser debatida em 2013 em um artigo chamado “Docker can now run within Docker«.

Resumindo, é necessário instalar o próprio Docker no contêiner de trabalho e montar o arquivo /var/run/docker.sock.

Aqui está um exemplo de Dockerfile que eu criei para o Jenkins.

FROM jenkins/jenkins:lts

USER root

RUN apt-get update && 

apt-get -y install apt-transport-https 
     ca-certificates 
     curl 
     gnupg2 
     git 
     software-properties-common && 
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && 
add-apt-repository 
   "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") 
   $(lsb_release -cs) 
   stable" && 
apt-get update && 
apt-get -y install docker-ce && 
usermod -aG docker jenkins

RUN curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose 

RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/

USER jenkins

Assim, obtivemos um contêiner Docker que pode executar comandos Docker na máquina hospedeira.

Configuração de Build

Recentemente, o Jenkins obteve a capacidade de descrever suas regras usando Pipeline uma sintaxe que permite alterar facilmente o script de build e armazená-lo no repositório.

Vamos então colocar no próprio repositório um Dockerfile especial que conterá todas as bibliotecas necessárias para a construção. Dessa forma, o próprio desenvolvedor pode preparar um ambiente reproduzível e não será necessário pedir à equipe de OPS para instalar uma versão específica do Node.JS no host.

FROM node:12.10.0-alpine

RUN npm install yarn -g

Esse template de construção é adequado para a maioria dos aplicativos Node.JS. E se você precisar de uma imagem para um projeto JVM com o scanner Sonar incluído? Você pode escolher os componentes necessários para a construção por conta própria.

FROM adoptopenjdk/openjdk12:latest

RUN apt update 
    && apt install -y 
        bash unzip wget

RUN mkdir -p /usr/local/sonarscanner 
    && cd /usr/local/sonarscanner 
    && wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip 
    && unzip sonar-scanner-cli-3.3.0.1492-linux.zip 
    && mv sonar-scanner-3.3.0.1492-linux/* ./ 
    && rm sonar-scanner-cli-3.3.0.1492-linux.zip 
    && rm -rf sonar-scanner-3.3.0.1492-linux 
    && ln -s /usr/local/sonarscanner/bin/sonar-scanner /usr/local/bin/sonar-scanner

ENV PATH $PATH:/usr/local/sonarscanner/bin/
ENV SONAR_RUNNER_HOME /usr/local/sonarscanner/bin/

Descrevemos o ambiente de construção, mas qual é a relação com o Jenkins? Os agentes do Jenkins conseguem trabalhar com essas imagens Docker e realizar a construção internamente.

stage("Build project") {
    agent {
        docker {
            image "project-build:${DOCKER_IMAGE_BRANCH}"
            args "-v ${PWD}:/usr/src/app -w /usr/src/app"
            reuseNode true
            label "build-image"
        }
    }
    steps {
        sh "yarn"
        sh "yarn build"
    }
}

Diretiva agente usa a propriedade docker, onde você pode especificar:

  • o nome do container de construção de acordo com sua política de nomenclatura;
  • os argumentos necessários para iniciar o container de construção, onde, no nosso caso, montamos o diretório atual como um diretório dentro do container.

E já nos passos de construção, indicamos quais comandos executar dentro do agente Docker de construção. Isso pode ser qualquer coisa, também uso assim para implantar aplicativos com o ansible.

Abaixo, quero mostrar um Jenkinsfile genérico que pode construir um simples aplicativo Node.JS.

def DOCKER_IMAGE_BRANCH = ""
def GIT_COMMIT_HASH = ""

pipeline { 
    options {
        buildDiscarder(
            logRotator(
                artifactDaysToKeepStr: "",
                artifactNumToKeepStr: "",
                daysToKeepStr: "",
                numToKeepStr: "10"
            )
        )
        disableConcurrentBuilds()
    }

    agent any

    stages {

        stage("Prepare build image") {
            steps {
                sh "docker build -f Dockerfile.build . -t project-build:${DOCKER_IMAGE_BRANCH}"
            }
        }

        stage("Build project") {
            agent {
                docker {
                    image "project-build:${DOCKER_IMAGE_BRANCH}"
                    args "-v ${PWD}:\/usr\/src\/app -w \/usr\/src\/app"
                    reuseNode true
                    label "build-image"
                }
            }
            steps {
                sh "yarn"
                sh "yarn build"
            }
        }

    post {
        always {
            step([$class: "WsCleanup"])
            cleanWs()
        }
    }

}

O que saiu disso?

Graças a esse método, resolvemos os seguintes problemas:

  • o tempo de configuração do ambiente de build é reduzido para 10 a 15 minutos por projeto;
  • um ambiente de build do aplicativo totalmente reproduzível, pois é possível compilar localmente também;
  • não há problemas com conflitos entre diferentes versões das ferramentas de compilação;
  • workspace sempre limpo, que não fica sobrecarregado.

A solução em si é simples e óbvia e permite obter apenas vantagens. Sim, a barreira de entrada aumentou um pouco em comparação com os comandos simples de build, mas agora há a garantia de que a construção será sempre feita, e o desenvolvedor pode escolher tudo o que é necessário para seu processo de compilação.

Você também pode utilizar a imagem que criei Jenkins + Docker. Todos os códigos-fonte são abertos e estão disponíveis em rmuhamedgaliev\/jenkins_docker.

Durante a redação do artigo, surgiu uma discussão sobre o uso de agentes em servidores remotos, para não sobrecarregar o nó mestre usando o plugin docker-plugin. Mas sobre isso falarei no futuro.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster