Déployer Jenkins comme code

Remarque perev.: il s'agit d'une traduction d'un article du blog d'ingénierie de Preply sur la façon dont vous pouvez utiliser la configuration comme code pour un outil CI / CD aussi populaire que Jenkins.

Dans notre entreprise, nous essayons de suivre la pratique du "Tout est comme du code", cela s'applique non seulement aux ressources d'infrastructure, mais aussi à la surveillance, au travail de Jenkins, etc. Dans cet article, je vais vous expliquer comment nous utilisons cette pratique pour déployer et prendre en charge Jenkins. Et cela s'applique non seulement à l'infrastructure du serveur et des agents, mais également aux plug-ins, aux accès, au travail et à bien d'autres choses.

De plus, dans cet article, nous essaierons de trouver des réponses à des questions telles que:

  • Notre Jenkins est-il devenu plus stable?
  • Pouvons-nous effectuer des changements fréquents de configuration de serveur et de travail?
  • La mise à jour Jenkins est-elle toujours une douleur pour nous?
  • Pouvons-nous contrôler tous nos changements?
  • Peut-on restaurer rapidement Jenkins en cas de fakap?

image


introduction


Habituellement, la première chose qui vient à l'esprit lorsque l'on mentionne l'expression «outils DevOps» est le système CI / CD. Par exemple, nous utilisons Jenkins parce que nous exécutons des centaines de tâches chaque jour, et cela représente des dizaines de milliers de builds. Certaines fonctionnalités que nous utilisons dans Jenkins ne sont pas disponibles sur d'autres systèmes CI / CD ou ont des fonctionnalités limitées.


Nous aimerions contrôler Jenkins complètement à partir du code, y compris l'infrastructure, les configurations, le travail et les plug-ins. Nous avons essayé d'exécuter Jenkins dans Kubernetes, mais cela ne correspondait pas à nos besoins, et il n'était pas facile à mettre à l'échelle en raison de son architecture .

image
Cela sera discuté

Infrastructure pour Jenkins


imageNous utilisons AWS et configurons l'ensemble de l'infrastructure à l'aide de Terraform et d'autres outils de hachage tels que Packer et Vault .

Comme mentionné précédemment, nous avons essayé d'utiliser Jenkins dans Kubernetes et avons rencontré des problèmes avec la mise à l'échelle du PVC , des ressources et d'une architecture pas si bien conçue.

Ici, nous utilisons les ressources AWS habituelles: instances EC2, certificats SSL, équilibreurs, Cloudfront, etc. L'image du système d'exploitation ( AMI ) est configurée à l'aide de Packer, qui s'intègre parfaitement avec Terraform et Vault.

{
    "variables": {
        "aws_access_key": "{{vault `packer/aws_access_key_id` `key`}}",
        "aws_secret_key": "{{vault `packer/aws_secret_access_key` `key`}}",
        "aws_region": "{{vault `packer/aws_region` `key`}}",
        "vault_token": "{{env `VAULT_TOKEN`}}"
    },
    "builders": [{
        "access_key": "{{ user `aws_access_key` }}",
        "secret_key": "{{ user `aws_secret_key` }}",
        "region": "{{ user `aws_region` }}",
        "type": "amazon-ebs",
        "communicator": "ssh",
        "ssh_username": "ubuntu",
        "instance_type": "c5.xlarge",
        "security_group_id": "sg-12345",
        "iam_instance_profile": "packer-role-profile",
        "ami_name": "packer-jenkins-master-{{timestamp}}",
        "ami_description": "Jenkins master image",
        "launch_block_device_mappings": [{
            "device_name": "/dev/sda1",
            "volume_size": 50,
            "volume_type": "gp2",
            "delete_on_termination": true
        }],
        "source_ami_filter": {
            "filters": {
                "virtualization-type": "hvm",
                "name": "ubuntu/images/*ubuntu-bionic-18.04-amd64-server-*",
                "root-device-type": "ebs"
            },
            "owners": ["099720109477"],
            "most_recent": true
        }
    }],
    "provisioners": [{
        "type": "shell",
        "environment_vars": ["VAULT_TOKEN={{ user `vault_token` }}"],
        "scripts": ["packer_bootstrap.sh"]
    }]
}
Un exemple de l'apparence de la configuration d'une image de système d'exploitation dans Packer.

À son tour, le fichier packer_bootstrap.shcontient un ensemble de commandes avec lesquelles le logiciel est installé à l'intérieur de l'image. Par exemple, nous pouvons installer Docker, docker-compose et vaultenv ou Datadog-agent pour la surveillance. Concernant l'infrastructure de cette image, nous pouvons utiliser Terraform, Cloudformation, Pulumi ou même Ansible.

Voici un exemple d'une infrastructure AWS possible pour Jenkins: les

utilisateurs se connectent à Jenkins via un équilibreur interne et les hooks Github accèdent à un serveur via un serveur externe. Nous utilisons l'intégration Jenkins avec GitHub, donc certains liens vers le serveur doivent être accessibles depuis Internet. Il existe de nombreuses solutions différentes ici (par exemple, une liste blanche pour les adresses IP , les URL ou les jetons, etc.), dans notre cas, nous utilisons une combinaison d'URL autorisées et de validation des jetons.
Ainsi, après les manipulations que nous avons faites, nous avons déjà une infrastructure prête à l'emploi avec l'image du système d'exploitation assemblée, la possibilité de surveiller et d'accéder au magasin de secret d'entreprise.

Nous utilisons Docker pour installer Jenkins et ses plugins


imageLa prochaine chose que nous ferons est d'installer Jenkins et ses plugins. Nous avions constamment des problèmes pour mettre à jour les plugins, donc l'objectif principal était d'avoir une distribution claire des plugins installés et de leurs versions dans le code.
Et ici Docker va nous aider, car nous pouvons prendre une image Docker pré - installée prête à l' emploi et l'utiliser comme base pour notre configuration.

FROM jenkins/jenkins:2.215

ENV CASC_JENKINS_CONFIG /jenkins_configs
USER root

#   
RUN apt update && \
    apt install -y python3 python3-pip && \
    pip3 install awscli jenkins-job-builder jjb-reactive-choice-param --no-cache-dir

USER jenkins
VOLUME /jenkins_configs
VOLUME /var/jenkins_home

#  
COPY plugins.txt /usr/share/jenkins/ref/
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
Dockerfile

À l'intérieur de l'image Docker, certains packages sont installés comme Job Builder, dont je parlerai plus tard, les référentiels sont également enregistrés et les plugins spécifiés dans le fichier sont installés plugins.txt.

Jenkins.instance.pluginManager.plugins.each{
  plugin ->
    println ("${plugin.getShortName()}:${plugin.getVersion()}")
}
Vous pouvez obtenir la liste des plugins installés dans Jenkins en cliquant sur le lien https://our-jenkins-url/scriptet en enregistrant la sortie dans un fichier.Enfinplugins.txt

, la configuration de docker-compose, qui exécutera Jenkins dans Docker.

version: "3"
services:
  jenkins:
    build: .
    container_name: jenkins
    restart: always
    ports:
      - "50000:50000"
      - "8080:8080"
    volumes:
      - ./configs/:/jenkins_configs/:ro
      - ./jenkins_home/:/var/jenkins_home/:rw
    environment:
      - VAULT_TOKEN
      - GITHUB_TOKEN
      - AWS_ACCESS_KEY_ID
      - AWS_SECRET_ACCESS_KEY
      - JAVA_OPTS=-Xms4G -Xmx8G -Xloggc:/var/jenkins_home/gc-%t.log -XX:NumberOfGCLogFiles=5 -XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCCause -XX:+PrintTenuringDistribution -XX:+PrintReferenceGC -XX:+PrintAdaptiveSizePolicy -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:+ParallelRefProcEnabled -XX:+UseStringDeduplication -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:+UnlockDiagnosticVMOptions -XX:G1SummarizeRSetStatsPeriod=1
volumes:
  configs:
    driver: local
  jenkins_home:
    driver: local
Nous utilisons également vaultenv pour forger des secrets à partir de Vault.

Notez certaines options Java qui nous ont aidés avec le garbage collection et les limites de ressources. Cet article est très cool sur le réglage de Jenkins.

Et bien sûr, nous pouvons maintenant déployer localement une copie de Jenkins et expérimenter de nouvelles versions du serveur et des plugins. C'est très confortable.
Nous avons maintenant une installation propre de Jenkins et des plugins, qui peuvent facilement être lancés dans le prod. Ajoutons plus de configuration pour elle.

Configuration du Jenkins en tant que plugin de code (JCaSC) pour la configuration du serveur


imageEn général, il existe un plugin appelé Jenkins Configuration as Code ( JCasC ), qui vous permet de stocker la configuration du serveur au format texte lisible par l'homme.

En utilisant ce plugin, vous pouvez décrire les configurations de sécurité, les accès, les paramètres des plugins, les agents, les onglets et bien plus encore.

La configuration est présentée au format YAML et est divisée en 5 blocs:

  • informations d'identification (description des secrets du système)
  • jenkins (paramètres d'autorisation et de cloud, paramètres globaux, descriptions d'agent, certains paramètres et onglets de sécurité)
  • sécurité (paramètres de sécurité globale, tels que les scripts autorisés)
  • outil (configuration pour des outils externes comme git, allure, etc.)
  • non classés (autres paramètres, tels que l'intégration avec Slack)

imageimageLe plugin prend en charge l'importation de configurations à partir d'une installation Jenkins existante.

De plus, le plugin prend en charge divers fournisseurs de secrets , cependant, dans cet exemple, nous n'utiliserons que des variables d'environnement.

credentials:
  system:
    domainCredentials:
    - credentials:
      - usernamePassword:
          description: "AWS credentials"
          id: "aws-creds"
          password: ${AWS_SECRET_ACCESS_KEY}
          scope: GLOBAL
          username: ${AWS_ACCESS_KEY_ID}
      - string:
          description: "Vault token"
          id: "vault-token"
          scope: GLOBAL
          secret: ${VAULT_TOKEN}
      ...
Voici comment les secrets peuvent être décrits.

Nous utilisons également le plug-in Amazon EC2 pour générer des agents dans AWS, et sa configuration peut également être décrite à l'aide de ce plug-in. L'autorisation matricielle nous permet de configurer l'accès des utilisateurs à l'aide de code.

jenkins:
  authorizationStrategy:
    projectMatrix:
      permissions:
      - "Overall/Administer:ivan.a@example.org"
      - "Credentials/View:petr.d@example.org"
      ...
  clouds:
  - amazonEC2:
      cloudName: "AWS"
      privateKey: ${EC2_PRIVATE_KEY}
      region: "${AWS_REGION}"
      templates:
      - ami: "ami-12345678"
        amiType:
          unixData:
            sshPort: "22"
        connectionStrategy: PRIVATE_IP
        deleteRootOnTermination: true
        description: "jenkins_agent"
        idleTerminationMinutes: "20"
        instanceCapStr: "100"
        minimumNumberOfInstances: 0
        mode: EXCLUSIVE
        numExecutors: 1
        remoteAdmin: "jenkins"
        remoteFS: "/home/jenkins"
        securityGroups: "sg-12345678"
        subnetId: "subnet-12345678"
        type: C52xlarge
        ...
Description des agents et des accès

Le plugin prend en charge d'autres choses que nous utilisons. Avec un processus de test local Jenkins correctement organisé, vous pouvez trouver et corriger efficacement les bogues avant qu'ils ne puissent potentiellement entrer dans les ventes Jenkins.
Nous avons maintenant une configuration reproductible pour le serveur, cela reste le cas pour les petits, à savoir - le travail.

Nous utilisons Job Builder pour les projets freestyle


imageIl existe plusieurs façons de créer des emplois freestyle à Jenkins:

  • en utilisant l'interface web (la manière la plus simple, a sauté dessus et a continué)
  • en utilisant directement l' API REST
  • en utilisant des plugins comme Job DSL ou JJB wrapper

Jenkins Job Builder (JJB) vous permet de configurer des travaux à l'aide de YAML ou JSON. Et c'est assez pratique, car vous pouvez configurer tous les travaux et stocker leur état dans git conditionnel. Autrement dit, nous pouvons créer un processus CI / CD pour notre outil CI / CD à l'aide de JJB.

.
├── config.ini
├── jobs
│   ├── Job1.yaml
│   | ...
│   └── Job2.yaml
└── scripts
    ├── job1.sh
    | ...
    └── job2.sh
Voici à quoi (simplifié) la structure de la configuration du travail sur le FS ressemble

- job:
    name: Job1
    project-type: freestyle
    auth-token: mytoken
    disabled: false
    concurrent: false
    node: jenkins_agent
    triggers:
      - timed: '0 3 * * *'
    builders:
      - shell:
          !include-raw: ../scripts/job1.sh
Et voici à quoi ressemble le travail dans le fichier Job1.yaml, les étapes du script. job1.sh

Le fichier de configuration JJB est également simple.

$ cat config.ini
[job_builder]
ignore_cache=True
exclude=jobs/Job2

[jenkins]
url=https://jenkins.example.org
user=some_user
password=some_password

$ jenkins-jobs --conf config.ini test -r jobs/
$ jenkins-jobs --conf config.ini update -r jobs/
L'application de nouvelles modifications peut facilement être lancée à l'aide de la commande Parjenkins-jobs update

elle-même, l'utilisateur pour lequel le jeton a été créé doit disposer des privilèges appropriés pour créer et configurer le travail. Nous avons juste besoin d'appliquer un travail d'initialisation (travail de démarrage), qui appliquera les modifications à l'aide de JJB dans Jenkins.

Il convient de mentionner que JJB n'est pas une «solution miracle», car certains plugins peu populaires ne sont pas pris en charge. Cependant, c'est un outil très flexible pour stocker le travail dans le code, y compris le support des macros .

Sommaire


Maintenant que nous avons atteint la fin de cet article, je voudrais revenir au début et répondre aux questions posées au début. Nous pouvons répondre «oui» à chacune des questions posées.

Dans cet article, nous ne nous sommes pas penchés sur les subtilités de la configuration de certaines technologies ou sur la façon de configurer Jenkins correctement , nous partageons simplement notre expérience, qui peut également vous être utile.

PS Dans l'article, j'utilise souvent le mot "job" (de l'anglais. "Job", "task"), pour moi, cela semble plus familier que la "tâche" dans le contexte de CI / CD en général ou Jenkins.

All Articles