Construção do Jenkins para Android usando o docker

Olá pessoal!

Eu trabalho como desenvolvedor Android, e há pouco tempo realizamos algumas tarefas rotineiras em nosso projeto que gostaríamos de automatizar. Por exemplo, temos 5 sabores diferentes, para cada um dos quais precisamos fazer upload de nossa compilação no tecido, às vezes para carrinhos diferentes várias vezes ao dia. Sim, essa tarefa pode ser realizada com a tarefa gradle, mas eu gostaria de não executar esse processo na máquina do desenvolvedor, mas de alguma forma centralizada. Ou, por exemplo, faça upload automaticamente da compilação no google play para a versão beta. Bem, eu só queria escolher o sistema de CI. O que saiu disso e como o configuramos, por que existe o Docker, mais adiante neste artigo.



No meu entendimento, toda a tarefa foi dividida em aproximadamente duas etapas:

  1. Instale e configure o próprio Jenkins com o SDK do Android
  2. Configurar tarefas já dentro do Jenkins

Neste artigo, quero abordar o primeiro ponto e, se isso for de interesse de alguém, no próximo artigo, descreverei o processo de configuração de tarefas de montagem no próprio Jenkins.

Portanto, o primeiro ponto é a instalação e configuração do sistema Jenkins


Habré já tem um artigo maravilhoso sobre esse assunto, mas ela já tem alguns anos e algumas coisas estão um pouco desatualizadas (por exemplo, sdkmanager), embora ela tenha me ajudado muito a descobrir o que e como fazer nos estágios iniciais.

Se você olhar a documentação oficial para a instalação do Jenkins, veremos três maneiras diferentes de fazer isso: iniciar uma imagem do docker pronta para download, baixar e executar um arquivo de guerra e também instalar o jenkins no sistema à moda antiga (por exemplo, apt-get install jenkins usando o ubuntu como exemplo). A primeira opção é a mais correta, porque não carrega configurações e dependências desnecessárias para o sistema host e, a qualquer momento, mesmo que algo dê errado, é fácil e simples excluir tudo e começar de novo. Mas a imagem padrão do docker para jenkins contém alguns dados que não precisamos (por exemplo, o plug-in blueocean) e não contêm o que definitivamente precisaremos (por exemplo, android sdk). Foi decidido criar nossa própria imagem do docker que, dentro dele, fará o download e executará o arquivo war, baixará e instalará o android sdk, além de definir todas as outras configurações necessárias. Para iniciar mais tarde, precisamos de um sistema host com o docker instalado. Sugiro aqui não reinventar a roda e usar o DigitalOcean.

Crie e configure uma máquina virtual


Para começar, se alguém não estiver registrado lá, sugiro que se registre (aqui, no momento da redação do artigo, havia um link de referência, mas depois de ler as regras, joguei fora). Após o registro, você pode pesquisar no Google um ou outro código promocional na Internet e obter cerca de 10 dólares para começar.

Depois, precisamos obter uma nova gota. Selecione o item Gotas e, em seguida, Criar gota .

imagem

O sistema host é o Ubuntu 18.04. Você pode escolher uma imagem com o Docker já instalado e configurado, mas faremos tudo por conta própria. Como a montagem de compilações do Android ainda exige muitos recursos, precisamos escolher uma configuração de pelo menos 20 dólares para que as compilações sejam coletadas normalmente e relativamente rapidamente.

imagem

Escolheremos um local mais próximo (por exemplo, na Alemanha). Depois, existem duas opções de como nos conectaremos ao nosso servidor virtual. Podemos adicionar uma chave ssh ou ficar sem ela. Se neste local não indicarmos qual chave usar, a senha do usuário root será enviada para o nosso correio.

imagem

Aqui podemos alterar o nome do servidor e concluir a criação clicando no botão Criar .

imagem

Agora vamos para o droplet criado e copiamos o endereço IP para nós, para mais conexão e configuração.

imagem

Precisamos de um cliente ssh. Se você trabalha com uma papoula, pode usar o terminal padrão; se, no Windows, podemos usar massa para trabalhar ou usar o subsistema Linux (somente para Windows 10). Eu pessoalmente uso a última opção, e ela me convém completamente.

Conecte-se ao nosso servidor usando o seguinte comando

 ssh root@YOUR_IP_ADDRESS 

O console oferecerá que você salve a chave, concordamos com isso. Após a conexão, criaremos um novo usuário para nós mesmos, adicionaremos aos superusuários (e daremos a ele a oportunidade de usar o sudo sem senha), copiaremos a chave de acesso via ssh e dizeremos que ele é o proprietário desses arquivos (caso contrário, não funcionará). O nome de usuário é alterado para qualquer conveniente para você.

 useradd -m -s /bin/bash username \ && echo 'username ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \ && mkdir /home/username/.ssh \ && cp /root/.ssh/authorized_keys /home/username/.ssh/authorized_keys \ && chown username:username -R /home/username/.ssh 

Desconecte da raiz com o comando

 exit 

E vamos reconectar já com a ajuda do novo usuário criado

 ssh username@YOUR_IP_ADDRESS 

Depois de atualizar o sistema e reiniciar o servidor (se durante o processo de atualização o sistema solicitar algo, basta escolher sempre os valores padrão nesse caso).

 sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove -y && sudo reboot 

A configuração básica está concluída. Do ponto de vista do sistema de combate, ele não é muito seguro, mas dentro da estrutura deste artigo é completamente adequado.

Instale o Docker.


Para instalar o Docker em nosso sistema, usaremos a documentação oficial . Como temos um sistema recém-instalado, pularemos esse ponto e, se você tiver um sistema em que algo esteja em execução há muito tempo, por recomendação dos caras do Docker, exclua possíveis versões antigas

 sudo apt-get remove docker docker-engine docker.io containerd runc 

Não se esqueça de primeiro conectar-se novamente via ssh ao nosso servidor. A instalação do Docker em si é descrita em detalhes na documentação; darei comandos gerais para facilitar isso para você. O que eles fazem pode ser lido lá. Primeiro adicione o repositório.

 sudo apt update \ && sudo apt install -y apt-transport-https ca-certificates \ curl gnupg-agent software-properties-common \ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - \ && sudo apt-key fingerprint 0EBFCD88 \ && sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 

E instale o próprio Docker:

 sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io 

Para que, no futuro, possamos chamar comandos do docker sem o prefixo sudo, execute o seguinte comando (que também é cuidadosamente descrito nas instruções ).

 sudo usermod -aG docker username 

Depois disso, é necessário efetuar login novamente (usando o comando exit e reconectando-se ao servidor) para que isso funcione.

O Docker está instalado, o que podemos verificar com o comando

 docker run hello-world 

Ela baixa a imagem de teste e a executa no contêiner. O contêiner, após iniciar, imprime uma mensagem informativa e sai.

Parabéns, terminamos o estágio de preparação do servidor para o trabalho!

Criando sua imagem do Docker


Criaremos a imagem do Docker escrevendo nosso próprio Dockerfile. Exemplos de como fazer isso corretamente na Internet: uma carroça e um carrinho pequeno, mostrarei minha versão final e tentarei comentar o máximo possível. Há também um manual de instruções do docker em si, com exemplos sobre a ortografia correta e canônica do dockerfile.

Crie e abra seu Dockerfile para edição

 touch Dockerfile && nano Dockerfile 

Nele, por exemplo, colocaremos o conteúdo do meu Dockerfile

Meu dockerfile inteiro com comentários
 #    . FROM ubuntu:18.04 #      LABEL author="osipovaleks" LABEL maintainer="osipov.aleks.kr@gmail.com" LABEL version="1.0" LABEL description="Docker image for Jenkins with Android SDK" #  ,  Jenkins    ENV TZ=Europe/Kiev RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # i386    ia32-libs RUN dpkg --add-architecture i386 #      RUN apt-get update && apt-get install -y git \ wget \ unzip \ sudo \ tzdata \ locales\ openjdk-8-jdk \ libncurses5:i386 \ libstdc++6:i386 \ zlib1g:i386 #  ,       RUN apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt #  RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 #   Android Sdk     ARG android_home_dir=/var/lib/android-sdk/ ARG sdk_tools_zip_file=sdk-tools-linux-4333796.zip RUN mkdir $android_home_dir RUN wget https://dl.google.com/android/repository/$sdk_tools_zip_file -P $android_home_dir -nv RUN unzip $android_home_dir$sdk_tools_zip_file -d $android_home_dir RUN rm $android_home_dir$sdk_tools_zip_file && chmod 777 -R $android_home_dir # environment    ENV ANDROID_HOME=$android_home_dir ENV PATH="${PATH}:$android_home_dir/tools/bin:$android_home_dir/platform-tools" ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/ #   Android SDK RUN yes | sdkmanager --licenses #    Jenkins ENV JENKINS_HOME=/var/lib/jenkins RUN mkdir $JENKINS_HOME && chmod 777 $JENKINS_HOME #     jenkins,   ,         RUN useradd -m jenkins && echo 'jenkins ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers USER jenkins WORKDIR /home/jenkins #   war     Jenkins RUN wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war -nv CMD java -jar jenkins.war #      EXPOSE 8080/tcp 


Alguns esclarecimentos:

  • No começo, havia um desejo de usar um alpino mais leve em vez do ubuntu, mas ele não possui suporte a ia32-libs , necessário para criar projetos usando o Android SDK.
  • Instalamos o openjdk-8-jdk, não o openjdk-8-jdk-headless, mais leve, porque algumas funções do Jenkins precisam de um sistema completo (por exemplo, exibindo resultados de teste de unidade).
  • É necessário instalar localidades, devido ao fato de que em alguns projetos, sem eles, o conjunto gradle falha sem erros e logs claros, e eu passei vários dias para chegar ao fundo desse motivo (em um ubuntu normal que não está no docker, todos os locais são preenchidos por padrão) .
  • Precisamos aceitar imediatamente todas as licenças do Android SDK, para que, durante o processo de compilação, o Jenkins possa instalar independentemente os componentes necessários (por exemplo, os SDKs necessários para diferentes versões da API). Se necessário, posteriormente, dentro do contêiner do docker, será possível gerenciar o SDK usando o sdkmanager, por exemplo, o sdkmanager --list permite exibir todos os componentes disponíveis e todos os componentes instalados e o sdkmanager --install "platforms;android-26" instalará o SDK para a versão 26 da API.
  • Em geral, era possível não iniciar o usuário jenkins e ficar com o usuário root, mas de alguma forma não está certo, você também não podia conceder direitos de superusuário, mas isso era feito em termos de conveniência, se algo precisasse ser instalado no estágio de instalação e depuração.
  • O tamanho básico da imagem acabou sendo bastante grande (quase 800 mb), mas no geral cheguei à conclusão de que isso não é muito crítico para mim e é mais fácil fazer o download deste formulário do que gastar tempo pesquisando e removendo pacotes dos quais não preciso.

Após escrever o Dockerfile, precisamos transformá-lo em uma imagem pronta para o Docker, com base em quais contêineres serão criados. Isso é feito simplesmente por uma equipe

 docker build -t jenkins-image 

em que o parâmetro -t jenkins-image é responsável pelo nome da sua imagem e o ponto no final do comando indica que você precisa procurar o Dockerfile para o assembly dentro deste diretório. O processo de montagem em si leva algum tempo e, após a montagem, deve haver algo assim no console.
9fd8f5545c27 construído com sucesso
Marcado com sucesso jenkins-image: mais recente
O que nos diz que nossa imagem foi montada com sucesso e podemos prosseguir para a próxima etapa, a saber, o lançamento de nosso contêiner.

Docker Hub e imagens prontas


Sim, é claro, podemos usar nossa imagem pronta para iniciar o contêiner, mas se precisarmos fazer isso em mais de vários dispositivos, criar um Dockerfile a cada vez e criar uma imagem pronta a partir dele não será muito conveniente. E se também atualizarmos o conteúdo do nosso Dockerfile, implementar as alterações em todos os nós não será conveniente. Para esses fins, há um repositório público de imagens do Docker Hub . Ele permite que você não colete uma imagem toda vez, em cada nó, mas simplesmente faça o download para si mesmo do repositório público e use-a igualmente em todas as máquinas. Por exemplo, a imagem que serviu de exemplo para este artigo está disponível no repositório denominado osipovaleks / docker-jenkins-android e, posteriormente, no artigo, trabalharemos com ela.

Este artigo não implica um estudo detalhado do Docker Hub, não entenderemos como enviar nossas imagens para lá (embora isso não seja muito difícil) e o que pode ser feito com elas, não entenderemos que ainda pode haver seus próprios repositórios públicos ou privados, Neste, tudo pode ser resolvido independentemente, se necessário.

Lançamento de contêiner


Existem duas maneiras de iniciar um contêiner.

  1. A primeira maneira, simplesmente usando o docker run , permite fazer isso com facilidade e rapidez da seguinte maneira

     docker run --name jenkins -d -it -v jenkins-data:/var/lib/jenkins -v jenkins-home:/home/jenkins -p 8080:8080 --restart unless-stopped osipovaleks/docker-jenkins-android 

    onde o comando run possui os seguintes parâmetros

    • --name jenkins - nome do futuro contêiner
    • -d - inicia o contêiner em segundo plano
    • -it - sinalizadores para trabalhar com STDIN e tty
    • -v jenkins-data:/var/lib/jenkins e -v jenkins-home:/home/jenkins - create (se não for criado) e mapeie arquivos de volume especiais para as seções internas do contêiner que permitirão salvar nosso Jenkins personalizado mesmo após a reconstrução container
    • -p 8080:8080 - mapeie a porta do host para a porta do contêiner para que tenhamos acesso à interface da web (sim, essa é a porta que especificamos no Dockerfile)
    • --restart unless-stopped - a opção determina a política de execução automática do contêiner após a reinicialização do host (nesse caso, inicie automaticamente se o contêiner não tiver sido desativado manualmente)
    • osipovaleks/docker-jenkins-android - imagem para implantação.

    Na saída para o console do Docker, devemos obter o ID do contêiner criado e também mostrar informações sobre como a imagem é carregada no sistema (é claro, se ainda não estiver carregada), algo como isto
    Não foi possível encontrar a imagem 'osipovaleks / docker-jenkins-android: latest' localmente
    mais recente: Puxando de osipovaleks / docker-jenkins-android
    6cf436f81810: Puxar concluído
    987088a85b96: Puxar concluído
    b4624b3efe06: Puxar concluído
    d42beb8ded59: Puxar concluído
    b3896048bb8c: Puxar concluído
    8eeace4c3d64: extração concluída
    d9b74624442c: Puxar concluído
    36bb3b7da419: Puxar concluído
    31361bd508cb: tração concluída
    cee49ae4c825: puxar concluído
    868ddf54d4c1: Puxar concluído
    361bd7573dd0: Puxar concluído
    bb7b15e36ae8: Puxar concluído
    97f19daace79: Puxar concluído
    1f5eb3850f3e: Puxar concluído
    651e7bbedad2: Puxar concluído
    a52705a2ded7: Puxar concluído
    Digest: sha256: 321453e2f2142e433817cc9559443387e9f680bb091d6369bbcbc1e0201be1c5
    Status: imagem mais recente baixada para osipovaleks / docker-jenkins-android: mais recente
    ef9e5512581da66d66103d9f6ea6ccd74e5bdb3776747441ce6a88a98a12b5a4
  2. A segunda maneira de começar envolve a gravação de um arquivo de composição especial, em que o comando executar é simplesmente descrito usando a linguagem YAML e iniciado usando o Docker Compose.

    Para fazer isso, precisamos instalá-lo:

     sudo apt update && sudo apt install -y docker-compose 

    Em seguida, crie um diretório para o projeto (isso é importante se você se importa como os volumes criados automaticamente para o contêiner serão chamados) e vá para ele

     mkdir jenkinsProject && cd jenkinsProject 

    e por dentro criamos o próprio arquivo de composição e entramos no modo de edição

     touch docker-compose.yml && nano docker-compose.yml 

    e coloque o seguinte conteúdo nele

     version: '3' services: jenkins: container_name: jenkins image: osipovaleks/docker-jenkins-android ports: - "8080:8080" restart: unless-stopped volumes: - "jenkins-data:/var/lib/jenkins" - "jenkins-home:/home/jenkins" volumes: jenkins-data: jenkins-home: 

    Nela, talvez, apenas a primeira linha levante questões ( version: '3' ) que indica a versão dos recursos do arquivo de composição, bem como uma seção com o bloco de volumes que lista as usadas neste contêiner

    Execute seu contêiner com o comando:

     docker-compose up -d 

    onde o sinalizador -d também indica que o contêiner será criado e lançado em segundo plano. Como resultado, o Docker deve mostrar algo como o seguinte:
    Criando o volume "jenkinsproject_jenkins-data" com o driver padrão
    Criando o volume "jenkinsproject_jenkins-home" com o driver padrão
    Puxando jenkins (osipovaleks / docker-jenkins-android: mais recente) ...
    mais recente: Puxando de osipovaleks / docker-jenkins-android
    6cf436f81810: Puxar concluído
    987088a85b96: Puxar concluído
    b4624b3efe06: Puxar concluído
    d42beb8ded59: Puxar concluído
    b3896048bb8c: Puxar concluído
    8eeace4c3d64: extração concluída
    d9b74624442c: Puxar concluído
    36bb3b7da419: Puxar concluído
    31361bd508cb: tração concluída
    cee49ae4c825: puxar concluído
    868ddf54d4c1: Puxar concluído
    361bd7573dd0: Puxar concluído
    bb7b15e36ae8: Puxar concluído
    97f19daace79: Puxar concluído
    1f5eb3850f3e: Puxar concluído
    651e7bbedad2: Puxar concluído
    a52705a2ded7: Puxar concluído
    Digest: sha256: 321453e2f2142e433817cc9559443387e9f680bb091d6369bbcbc1e0201be1c5
    Status: imagem mais recente baixada para osipovaleks / docker-jenkins-android: mais recente
    Criando jenkins ...
    Criando jenkins ... concluído
    Lembre-se, eu disse que o nome dos volumes criados dependerá do nome do projeto? Execute o comando:

     docker volume ls 

    e temos essa saída
    NOME DO VOLUME DO CONDUTOR
    local jenkinsproject_jenkins-data
    local jenkinsproject_jenkins-home
    onde veremos que, apesar do nome do volume ter sido escolhido por jenkins-home , na realidade, um prefixo do nome do projeto grudou nele e o volume do nome acabou sendo jenkinsproject _jenkins-home


Qual opção de inicialização usar? Aqui você pode escolher por si mesmo, acredita-se que o Docker Compose seja mais uma ferramenta para iniciar vários contêineres de uma só vez, vinculados entre si, e se você precisar iniciar apenas um contêiner, poderá usar o docker run .

Agora, após estas subpasso para iniciar e configurar o servidor, bem como iniciar o contêiner com Jenkins, podemos prosseguir para sua configuração inicial

Configuração inicial do Jenkins


Pegue o endereço IP do nosso servidor, adicione a porta 8080 indicada por nós e siga este link no navegador.

http://YOUR_IP_ADDRESS:8080/

Se antes disso tudo estava configurado e iniciado corretamente, então aqui veremos a figura a seguir



Para a primeira configuração, precisamos digitar a senha que o sistema gerou durante a instalação. Para fazer isso, basta olhar para o conteúdo do arquivo /var/lib/jenkins/secrets/initialAdminPassword . Mas esse arquivo está dentro do contêiner em execução e, para lê-lo, precisamos nos conectar ao contêiner usando o seguinte comando:

 docker exec -it jenkins /bin/bash 

onde a opção -it é semelhante à execução do docker run , jenkins é o nome do nosso contêiner e /bin/bash executará o /bin/bash para nós no contêiner e dará acesso a ele. Depois disso, podemos ver a senha inicial para Jenkins:

 cat /var/lib/jenkins/secrets/initialAdminPassword 

o seguinte aparece no console
91092b18d6ca4492a2759b1903241d2a
Essa é a senha.

O usuário ALexhha sugeriu uma opção mais simples de ler essa senha, sem conectar-se ao próprio contêiner. O fato é que, no momento do lançamento do próprio Jenkins, essa senha é mostrada nos logs. Acontece que tudo o que precisamos é ler os logs do contêiner. No nosso caso, isso é feito com o seguinte comando:

 docker logs jenkins 


onde jenkins nome do nosso contêiner e, nos logs, você pode ver o seguinte:

******************************************************* ***********
******************************************************* ***********
******************************************************* ***********

A configuração inicial do Jenkins é necessária. Um usuário administrador foi criado e uma senha gerada.
Por favor, use a seguinte senha para prosseguir para a instalação:

91092b18d6ca4492a2759b1903241d2a

Isso também pode ser encontrado em: / var / lib / jenkins / secrets / initialAdminPassword

******************************************************* ***********
******************************************************* ***********

******************************************************* ***********

Esta opção é um pouco mais simples e rápida.

Copie-o, cole-o no campo Senha do administrador na interface da web e clique em Continuar . Na próxima tela, selecione Instalar plug - ins sugeridos e instale um conjunto de plug-ins padrão.





Após instalar os plugins, crie um usuário para nós mesmos e clique em Salvar e Concluir



Concordamos com a seção Configuração da instância, na qual é solicitado que você preencha o URL no qual o Jenkins funcionará (no nosso caso, deixe tudo como está)



E na próxima tela, clique no botão Iniciar, usando o querido Jenkins.



Então, instalamos e lançamos o Jenkins!



Já é possível trabalhar com ele, mas para coletar nossas compilações do Android, você precisará configurar mais alguns pontos. A localização do Jenkins está relacionada ao idioma selecionado do seu navegador e, é claro, a tradução para o russo não está completamente concluída, e temos uma mistura infernal de russo e inglês. Se você conseguiu exatamente da mesma maneira e isso o enfurece, você pode usar o plug-in especial e definir o idioma da interface padrão. Bem, ou mude seu navegador para a interface em inglês.

Vá para as configurações do Jenkins e selecione Configuração do sistema.



Verifique as variáveis ​​de ambiente e insira o nome ANDROID_HOME no campo e especifique / var / lib / android-sdk / no campo (especificamos esses dados no Dockerfile como o diretório inicial do Android SDK).



Clique no botão Salvar , saia desta seção de configurações e vá para a seção Configuração Global de Ferramentas .



Configure a partição JDK (onde a variável JAVA_HOME também foi preenchida por nós no Dockerfile e podemos usar seu valor / usr / lib / jvm / java-8-openjdk-amd64 / aqui ).



Também aqui ainda precisamos preencher a seção Gradle . Selecionamos e instalamos a versão do Gradle usada nos projetos que você construirá usando esse sistema de IC.Você pode ter várias versões. Você também não pode iniciar a variável Gradle se tiver gradlew no repositório, por exemplo, e puder construí-la com ela.



Com isso, podemos terminar nossa primeira etapa. O sistema Jenkins está totalmente operacional e podemos personalizar a tarefa de compilação. Observe que o sistema foi ajustado às nossas necessidades e aqui pode não fornecer o que você precisa - por exemplo, não há emuladores Android para testes e NDK.

Se este artigo interessar a alguém, continuarei na segunda parte com um exemplo de um ou dois aborrecimentos, descreverei a integração de Jenkins e Bitbucket (é ele, não o Github, porque é mais fácil com repositórios particulares gratuitos e artigos na Internet sobre é menor, mas talvez mais divertido), mostrarei como fazer amizade com a chave ssh do repositório do contêiner, sobre notificações por email e vários outros chips. Em geral, sobre tudo o que configuramos.

Peço-lhe para não chutar muito, este é o meu primeiro artigo sobre Habr. Bom para todos!

Source: https://habr.com/ru/post/pt443606/


All Articles