Stellen Sie Jenkins als Code bereit

Hinweis perev .: Dies ist eine Übersetzung eines Artikels aus dem technischen Blog von Preply darüber, wie Sie die Konfiguration als Code für ein so beliebtes CI / CD-Tool wie Jenkins verwenden können.

In unserem Unternehmen versuchen wir, die Praxis von "Alles ist wie Code" zu befolgen. Dies gilt nicht nur für Infrastrukturressourcen, sondern auch für Überwachung, Jenkins-Arbeit usw. In diesem Artikel werde ich darüber sprechen, wie wir diese Vorgehensweise verwenden, um Jenkins bereitzustellen und zu unterstützen. Dies gilt nicht nur für die Infrastruktur für den Server und die Agenten, sondern auch für Plug-Ins, Zugriffe, Arbeit und viele andere Dinge.

Darüber hinaus werden wir in diesem Artikel versuchen, Antworten auf folgende Fragen zu finden:

  • Sind unsere Jenkins stabiler geworden?
  • Können wir häufige Änderungen an der Server- und Jobkonfiguration vornehmen?
  • Ist das Jenkins-Update immer noch ein Schmerz für uns?
  • Können wir alle unsere Änderungen kontrollieren?
  • Können wir Jenkins im Falle eines Fakaps schnell wiederherstellen?

Bild


Einführung


Normalerweise fällt bei der Erwähnung des Ausdrucks „DevOps-Tools“ als erstes das CI / CD-System ein. Zum Beispiel verwenden wir Jenkins, weil wir jeden Tag Hunderte von Aufgaben ausführen, und das sind Zehntausende von Builds. Einige Funktionen, die wir in Jenkins verwenden, sind entweder in anderen CI / CD-Systemen nicht verfügbar oder verfügen nur über eingeschränkte Funktionen.


Wir möchten Jenkins vollständig vom Code aus steuern, einschließlich Infrastruktur, Konfigurationen, Jobs und Plug-Ins. Wir haben versucht, Jenkins in Kubernetes auszuführen, aber es entsprach nicht unseren Anforderungen und war aufgrund seiner Architektur nicht einfach zu skalieren .

Bild
Dies wird diskutiert

Infrastruktur für Jenkins


BildWir verwenden AWS und konfigurieren die gesamte Infrastruktur mit Terraform und anderen Hash-Tools wie Packer und Vault .

Wie bereits erwähnt, haben wir versucht, Jenkins in Kubernetes zu verwenden, und hatten einige Probleme mit der Skalierung von PVC , Ressourcen und einer nicht so gut gestalteten Architektur.

Hier verwenden wir die üblichen AWS-Ressourcen: EC2-Instanzen, SSL-Zertifikate, Balancer, Cloudfront usw. Das OS-Image ( AMI ) wird mit Packer konfiguriert, das sich perfekt in Terraform und Vault integrieren lässt.

{
    "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"]
    }]
}
Ein Beispiel für die Konfiguration des Betriebssystemabbilds in Packer.

Die Datei packer_bootstrap.shenthält wiederum eine Reihe von Befehlen, mit denen Software im Abbild installiert wird. Zum Beispiel können wir Docker, Docker-Compose und Vaultenv oder Datadog-Agent zur Überwachung installieren . In Bezug auf die Infrastruktur für dieses Image können wir Terraform, Cloudformation, Pulumi oder sogar Ansible verwenden.

Hier ist ein Beispiel für eine mögliche AWS-Infrastruktur für Jenkins:

Benutzer melden sich über einen internen Balancer bei Jenkins an, und Github-Hooks gelangen über einen externen Balancer zu einem Server. Wir verwenden die Jenkins-Integration mit GitHub, daher müssen einige Server-Links über das Internet zugänglich sein. Hier gibt es viele verschiedene Lösungen (z. B. eine weiße Liste für IP-Adressen , URLs oder Token usw.). In unserem Fall verwenden wir eine Kombination aus zulässigen URLs und Token-Validierung.
Nach den vorgenommenen Manipulationen verfügen wir bereits über eine vorgefertigte Infrastruktur mit dem zusammengestellten Betriebssystem-Image, der Möglichkeit, den geheimen Unternehmensspeicher zu überwachen und darauf zuzugreifen.

Wir verwenden Docker, um Jenkins und seine Plugins zu installieren


BildAls nächstes installieren wir Jenkins und seine Plugins. Wir hatten ständig Probleme beim Aktualisieren der Plugins, daher war das Hauptziel, die installierten Plugins und ihre Versionen im Code klar zu definieren.
Und hier hilft uns Docker, da wir ein vorgefertigtes vorinstalliertes Docker-Image als Basis für unsere Konfiguration verwenden können.

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


Docker- Datei Im Docker-Image werden einige Pakete wie Job Builder installiert, auf die ich später noch eingehen werde. Die Repositorys werden ebenfalls registriert und die in der Datei angegebenen Plugins installiert plugins.txt.

Jenkins.instance.pluginManager.plugins.each{
  plugin ->
    println ("${plugin.getShortName()}:${plugin.getVersion()}")
}
Sie können eine Liste der in Jenkins installierten Plugins abrufen, indem Sie auf den Link klicken https://our-jenkins-url/scriptund die Ausgabe in einer Datei speichernplugins.txt

. Schließlich die Konfiguration für Docker-Compose, mit der Jenkins in Docker ausgeführt wird.

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
Wir verwenden vaultenv auch, um Geheimnisse aus Vault zu fälschen.

Beachten Sie einige Java-Optionen, die uns bei der Speicherbereinigung und den Ressourcenbeschränkungen geholfen haben. Dieser Artikel ist sehr cool über das Stimmen von Jenkins.

Und natürlich können wir jetzt lokal eine Kopie von Jenkins bereitstellen und mit neuen Versionen des Servers und der Plugins experimentieren. Es ist sehr bequem.
Jetzt haben wir eine saubere Installation von Jenkins und Plugins, die einfach im Produkt gestartet werden können. Lassen Sie uns mehr Konfiguration für sie hinzufügen.

Konfigurieren des Jenkins als Code (JCaSC) -Plugin für die Serverkonfiguration


BildIm Allgemeinen gibt es ein Plugin namens Jenkins Configuration as Code ( JCasC ), mit dem Sie die Serverkonfiguration in einem für Menschen lesbaren Textformat speichern können.

Mit diesem Plugin können Sie Sicherheitskonfigurationen, Zugriffe, Einstellungen für Plugins, Agenten, Registerkarten und vieles mehr beschreiben.

Die Konfiguration wird im YAML-Format dargestellt und ist in 5 Blöcke unterteilt:

  • Anmeldeinformationen (Beschreibung der Systemgeheimnisse)
  • Jenkins (Autorisierungs- und Cloud-Einstellungen, globale Einstellungen, Agentenbeschreibungen, einige Sicherheitseinstellungen und Registerkarten)
  • Sicherheit (globale Sicherheitseinstellungen, z. B. zulässige Skripte)
  • Tool (Konfiguration für externe Tools wie Git, Allure usw.)
  • nicht klassifiziert (andere Einstellungen, wie z. B. Integration in Slack)

BildBildDas Plugin unterstützt das Importieren von Konfigurationen aus einer vorhandenen Jenkins-Installation.

Darüber hinaus bietet das Plugin Unterstützung für verschiedene geheime Anbieter . In diesem Beispiel werden jedoch nur Umgebungsvariablen verwendet.

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}
      ...
So können Geheimnisse beschrieben werden.

Wir verwenden auch das Amazon EC2- Plugin , um Agenten in AWS zu aktivieren, und seine Konfiguration kann auch mit diesem Plugin beschrieben werden. Mit der Matrix-Autorisierung können wir den Benutzerzugriff mithilfe von Code konfigurieren.

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
        ...
Beschreibung der Agenten und Zugriffe

Das Plugin unterstützt einige andere Dinge, die wir verwenden. Mit einem ordnungsgemäß organisierten lokalen Jenkins-Testprozess können Sie Fehler effektiv finden und beheben, bevor sie möglicherweise in den Jenkins-Vertrieb gelangen.
Jetzt haben wir eine reproduzierbare Konfiguration für den Server, es bleibt der Fall für kleine, nämlich - den Job.

Wir verwenden Job Builder für Freestyle-Projekte


BildEs gibt verschiedene Möglichkeiten, Freestyle-Jobs in Jenkins zu erstellen:

  • über das Webinterface (der einfachste Weg, sprang auf und ging weiter)
  • direkt mit der REST-API
  • mit Plugins wie Job DSL oder JJB Wrapper

Mit Jenkins Job Builder (JJB) können Sie Jobs mit YAML oder JSON konfigurieren. Dies ist sehr praktisch, da Sie alle Jobs konfigurieren und ihren Status in einem bedingten Git speichern können. Das heißt, wir können mithilfe von JJB einen CI / CD-Prozess für unser CI / CD-Tool erstellen.

.
├── config.ini
├── jobs
│   ├── Job1.yaml
│   | ...
│   └── Job2.yaml
└── scripts
    ├── job1.sh
    | ...
    └── job2.sh
So sieht (vereinfacht) die Struktur der Konfiguration des Jobs auf dem FS aus

- 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
Und so sieht der Job in der Datei aus Job1.yaml, Schritte im Skript. job1.sh

Die JJB- Konfigurationsdatei sieht auch einfach aus.

$ 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/
Die Anwendung neuer Änderungen kann einfach mit dem Befehl "Selbst" gestartet werden. Derjenkins-jobs update

Benutzer, für den das Token erstellt wurde, muss über die entsprechenden Berechtigungen zum Erstellen und Konfigurieren des Jobs verfügen. Wir müssen nur einen Initialisierungsjob (Seed-Job) anwenden, der die Änderungen mit JJB in Jenkins anwendet.

Es ist erwähnenswert, dass JJB keine „Silberkugel“ ist, da einige nicht sehr beliebte Plugins nicht unterstützt werden. Es ist jedoch ein sehr flexibles Tool zum Speichern von Jobs im Code, einschließlich Makrounterstützung .

Zusammenfassung


Nachdem wir das Ende dieses Artikels erreicht haben, möchte ich zum Anfang zurückkehren und die am Anfang gestellten Fragen beantworten. Wir können jede der gestellten Fragen mit „Ja“ beantworten.

In diesem Artikel haben wir uns nicht mit den Feinheiten des Einrichtens bestimmter Technologien oder der korrekten Konfiguration von Jenkins befasst . Wir teilen lediglich unsere Erfahrungen, die auch für Sie nützlich sein können.

PS In dem Artikel verwende ich oft das Wort "Job" (aus dem Englischen "Job", "Aufgabe"), für mich klingt es vertrauter als die "Aufgabe" im Kontext von CI / CD im Allgemeinen oder Jenkins.

All Articles