Jenkins Pipeline com Jenkinsfile
Tags: ci, jenkins, cd, braziliandevs
Published: 2023-03-13 13:44:27

### O Problema

O time está gastando muito tempo com implantações e atualizações de sistemas nos ambientes de homologação e produção, esse time faz essas operações de forma manual ou seja acessando o servidor e rodando todos os comandos necessários, vale lembrar que esse processo é repetido diversas vezes durante o mês.

Precisamos de alguma forma melhorar esse processo dando mais agilidade e deixando o time livre para trabalhar em outras tarefas.

### Quais práticas existem para ajudar ?

Em uma situação como essa podemos tirar proveito dos conceitos de CI/CD. Antes de entrar no significado dessas siglas, vamos deixar claro que DevOps não é CI/CD, DevOps é sobre pessoas, um desenvolvedor não pode simplesmente avisar que terminou e esperar que o time de Operação faça a implantação, o time de Operação quando se depara com um erro após a implantação não pode simplesmente abrir um card de erro e encerrar sua parte. Em caso de falhas sistêmicas logo após a implantação quem é o profissional mais apto para resolver o problema ? um profissional de Operações ou o desenvolvedor que criou determinada feature ?

Vamos imaginar agora que o time de operação não faz mais implantações, quem vai fazer agora é o próprio time de desenvolvimento. Mas por que isso? “Quem pariu Mateus que o balance” ou "cachorro com muitos donos morre de fome”, essas frases falam de responsabilidade, quando atualizamos uma aplicação no servidor do cliente não estamos apenas trocando o `.jar` ou `exe` , estamos entregando uma solução para resolver um problema, o acompanhamento dessa solução desde a implantação até está em uso diário deve ser do time que planejou e desenvolveu essa solução. Estruturas como Servidores, Sistema operacional, versão de libs e etc depois de configurados é muito difícil dar problema, o que está mais propício a dar problema é código desenvolvido, é aplicação.

### Ok, como podemos melhorar isso?

Vamos falar sobre como o Jenkins e Jenkinsfile conseguem ajudar na criação de um pipeline, a sintaxe que precisamos usar para conseguir escrever um pipeline é baseada em duas formas uma chamada de Declarativa e outra chamada de Script, nesse nosso papo vamos usar a forma Declarativa.

A Arquitetura ficará mais ou menos assim:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eizuw5qa39oi54bvzlcb.jpg)

### Iniciando com Jenkinsfile

Um jenkinsfile nada mais é do que um arquivo com código que será interpretado pelo Jenkins, esse código é baseado em [Groovy](https://groovy-lang.org/) uma linguagem de programação bastante usada no mercado e roda na JVM do Java. Um Jenkinsfile é como qualquer outro código do software,precisa ser versionado, pode evoluir e que provavelmente precisará de manutenções.

Vamos rapidamente voltar nos passos básicos para entregar um software em produção ou homologação. 

- Devemos rodar testes de unidade
- Devemos rodar build
- Devemos armazenar artefato
- Devemos implantar artefato no servidor

pronto, agora que mapeamos os passos básicos que fazemos para entregar um software, podemos começar a pensar em como automatizar e deixar uma máquina fazendo esse trabalho.

Para iniciarmos vamos criar um arquivo na raiz do projeto chamado Jenkinsfile, não precisa de extensão, basta Jenkinsfile, vamos colocar o conteúdo abaixo:

  

```java
pipeline {
    agent any

    stages {      
        stage('Test') {
            steps {
                echo 'Testando..'
            }
        }
				stage('Build') {
            steps {
	                echo 'Compilando..'
            }
        }
        stage('Store') {
            steps {
                echo 'Armazenando....'
            }
        }
				stage('DeploY') {
            steps {
                echo 'Implantando....'
            }
        }
    }
}
```

Analisando o conteúdo que colamos, vamos observar algumas palavras reservadas como, pipeline, agent, stages, stage e steps, vamos conhecer um pouco mais sobre eles nos blocos abaixo.

**Pipeline**

Quando estamos montando um Pipeline de forma Declarativa todo o conteúdo fica dentro desse `nó` principal que chamamos de  `pipeline {}`

**Agent**

Define o `Executor`que será responsável por executar os comando dos pepiline, no formato Declarativo essa definição é obrigatória pois sem não saberá como executar. Podemos definir um `agent`de forma global como em nosso exemplo, no topo do arquivo, ou dentro de cada `stage`dando mais possibilidades para nosso pipeline.

Temos alguns [tipos de Agent](https://www.jenkins.io/doc/book/pipeline/syntax/#agent), mas para não alongar muito vamos focar em none, any e Docker.

Tipo `none` é bem simples, não será executado por nenhum `Executor` mas obriga a definição nos `stage`.

Tipo `any`diz que será executado por qualquer `Executor`disponível, em nosso exemplo até o momento estamos usando esse valor.

```java
agent any
```

Tipo `Docker`diz que o `Executor`usará uma imagem docker especificada para que ele execute seu trabalho, essa forma é a que usaremos em nossos exemplos, usando esse tipo de agent conseguimos realizar várias operações bastando ter uma imagem docker que contenha o que precisamos e pronto, não precisaremos "encher” nosso Jenkins com Plugins.

Confira mais detalhes [aqui](https://www.jenkins.io/doc/book/pipeline/syntax/#agent).

**Stages e Stage**

Agrupa um ou vários `stage`cada stage representa uma etapa do nosso pipeline, como test,build, deploy e etc.

```java
...
    stages {      
        stage('Test') {
            steps {
                echo 'Testando..'
            }
        }
				stage('Build') {
            steps {
	                echo 'Compilando..'
            }
        }
        stage('Store') {
            steps {
                echo 'Armazenando....'
            }
        }
				stage('DeploY') {
            steps {
                echo 'Implantando....'
            }
        }
    }
...
```

**Steps**

Define como o stage será executado, em nosso caso estamos apenas executando o comando `echo`mas podemos ter vários outros comando para executar o objetivo do `stage`.

### Evoluindo colunas

Agora vamos evoluir mais ainda nosso Jenkinsfile preenchendo algumas colunas para entender como o Jenkins vai executar os passos que definimos.

**Evoluindo coluna de Teste**

Vamos ver como podemos evoluir a `stage`de teste, atualmente está super simples, apenas exibindo um texto no console, veja abaixo:

```java
...
stage('Test') {
      steps {
          echo 'Testando..'
      }
}
...
```

Nosso projeto foi escrito em java usando maven como gerenciador, para rodarmos os testes unitários executamos o comando:

```java
$ mvn test
```

Mas a partir de agora quem vai executar esse comando será o Jenkins, nesse nosso papo usaremos um `agent` do tipo `docker` com uma imagem contendo o Java já configurado, primeiro vamos mudar o `agent`global que está como `agent any`para `agent none` dessa forma vamos definir o `agent` apenas dentro do `stage` como no exemplo abaixo:

 

```java
...
agent none
    stages {      
        stage('Test') {
          agent { docker 'adoptopenjdk/openjdk11:jdk-11.0.9.1_1' }            
        }
}
...
```

agora basta implementar o `steps` passando o comando para rodar os testes, veja como fica:

```java
...
agent none

    stages {      
        stage('Test') {
          agent { docker 'adoptopenjdk/openjdk11:jdk-11.0.9.1_1' }
          sh './mvnw test'
        }
     }
}
...
```

Se observar usamos o comando `sh`   dentro da chave `steps`, pronto com isso finalizamos nossa primeira coluna, nosso arquivo está fincando assim:

```java
pipeline {
    agent none

    stages {      
        stage('Test') {
            agent { docker 'adoptopenjdk/openjdk11:jdk-11.0.9.1_1' }
            steps {
                sh './mvnw test'
	    }
    } 
    stage('Build') {
        agent any
        steps {
	    echo 'Compilando..'
        }
    }
    stage('Store') {
        agent any
        steps {
            echo 'Armazenando....'
        }
    }
    stage('DeploY') {
        agent any
        steps {
            echo 'Implantando....'
        }
    }
}
```

observe que nos demais `stage` adicionei um `agent` do tipo `any` pois como comentei, se colocarmos no `agent` global o tipo `none` seremos obrigados a definir um `agent` em cada `stage`.

**Evoluindo coluna de Build**

Como na coluna de teste, vamos definir um `agent` do tipo `docker`, passar uma imagem com java e executar um outro comando para fazer o build da aplicação.

```java
stage('Test') {
    agent { docker 'adoptopenjdk/openjdk11:jdk-11.0.9.1_1'}            
    steps {
        sh './mvnw package'
    }
}
```

pronto agora o Jenkins saberá empacotar toda nossa aplicação em um arquivo `jar` que ficará disponível para a próxima etapa que pode ser por exemplo criar uma imagem docker.

### Conclusão

Vimos com é simples criar um arquivo Jenkinsfile e começamos a entender como o Jenkins vai usa-lo para automatizar os passos das nossas entregas de Software.

{% link https://dev.to/cassunde/a-importancia-dos-logs-23c9 %}