Blog von Obi Madu
Zurück zu allen Artikeln
InfrastructureDevOpsProjects

Auto-Deploy von Jenkins auf einem Remote-Server (mit kostenlosem SSL) über Ansible (HowTo)

Erfahren Sie, wie Sie einen selbst gehosteten Jenkins-Server mit kostenlosen, sich automatisch verlängernden SSL-Zertifikaten mithilfe von Ansible automatisch bereitstellen können.

Auto-Deploy von Jenkins auf einem Remote-Server (mit kostenlosem SSL) über Ansible (HowTo)

Jenkins ist eines der beliebtesten CI/CD-Tools (Continuous Integration und Continuous Deployment) auf dem Markt. Es wird in der Tech-Community sehr geschätzt, da es kostenlos, Open Source und selbst gehostet ist. Es gibt viele Gründe, warum Sie einen Jenkins-Server bereitstellen möchten: zum Erstellen persönlicher Projekte, zum Üben mit der Software, für den produktiven Einsatz im Unternehmen usw. Was auch immer Ihr Grund ist, eines ist sicher: Wenn Sie Ihren Server mit Ansible bereitstellen, wird der Prozess automatisch, wiederholbar, wiederverwendbar, versionierbar und teilbar. Heute werden wir durchgehen, wie man ein Ansible-Playbook erstellt, das Jenkins auf einem Remote-Server bereitstellt, mit einem kostenlosen Letsencrypt-SSL-Zertifikat (das sich automatisch verlängert).

Ich habe die gesamte Codebasis für dieses Projekt hier auf Github hochgeladen.

Lassen Sie uns eintauchen 🚀

1. Einen Remote-Server erwerben

Der erste Schritt zur Bereitstellung von Jenkins auf einem Server besteht darin, den Server selbst zu erwerben. Ein Remote-Server kann an vielen verschiedenen Orten bereitgestellt werden. Cloud-Anbieter wie DigitalOcean, Google Cloud, AWS usw. bieten einige der kostengünstigsten VPS-Pläne (Virtual Private Server) auf dem Markt an. Abhängig von Ihren Anforderungen können Sie einen Server entweder manuell oder über IaC (Infrastructure as Code) von einem dieser Anbieter bereitstellen.

Stellen Sie für dieses Tutorial sicher, dass Sie Ihre Serverinstanz mit Zugriff auf das öffentliche Internet (und damit mit einer öffentlichen IPv4-Adresse) bereitstellen.

Es gilt als Best Practice, ansible-Befehle auf einem Node über einen dedizierten Benutzer auszuführen. Um den Prozess der Erstellung eines dedizierten Benutzers mit den entsprechenden Berechtigungen für Ansible zu beschleunigen, verwenden Sie das folgende userdata-Startskript, um Ihren VPS zu starten.

#cloud-config
ssh_pwauth: false

users:
  - name: ansible
    gecos: Ansible User
    groups: users,admin,wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
      - "ssh-public-key"

Ersetzen Sie "ssh-public-key" durch den entsprechenden öffentlichen SSH-Schlüssel für die Verbindung zu Ihrem Server.

2. Setup für Ansible

Bevor wir uns in die Erstellung unseres Playbooks stürzen, müssen wir einige Dinge einrichten, um sicherzustellen, dass unser Workflow erfolgreich ist.

2.1 SSH-Konnektivität zum Remote-Server überprüfen

Ansible verbindet sich mit Node-Servern über SSH. Da wir uns in diesem Tutorial mit einem einzelnen Server befassen, müssen wir uns zunächst manuell mit ihm verbinden, um die Konnektivität zu überprüfen.

Ein Teil des Prozesses der Bereitstellung eines VPS ist die Einrichtung des SSH-Zugriffs darauf. Wo auch immer Sie Ihren VPS bereitstellen, sollten Sie Zugriff auf die private Schlüsseldatei für die SSH-Verbindung zu Ihrem Server haben.
Initiieren Sie eine SSH-Verbindung zu Ihrem Server mit

ssh -i location/to/keyfile username@server_ip 

Stellen Sie sicher, dass Sie Ihren privaten SSH-Schlüssel sicher auf Ihrem Ansible-Host speichern und seinen Speicherort im Auge behalten, da er in einem nächsten Schritt verwendet wird, um Ansible Zugriff auf Ihren VPS zu gewähren.

2.2 DNS-Einträge für unsere Jenkins-Domain einrichten

Um den Zugriff auf Ihren Jenkins-Server über die entsprechende Domain sicherzustellen, müssen Sie Ihren Domainnamen auf die IP-Adresse Ihres Servers verweisen lassen. Dies wird durch das Erstellen eines DNS-A record erreicht. Dies kann auf verschiedene Weise (über eine Vielzahl von Schnittstellen) erfolgen, abhängig von dem Domain Name Service, der für Ihre Domain zuständig ist. Die genauen Details sprengen den Rahmen dieses Tutorials.

Ohne den richtig gesetzten DNS-Eintrag wird Ihr Playbook kein SSL für Ihren Jenkins-Server ausstellen können, da Certbot (der Client, der zur Ausstellung der Zertifikate verwendet wird) nicht überprüfen kann, ob Sie die Domain kontrollieren, für die das SSL-Zertifikat ausgestellt werden soll. Dies wird dazu führen, dass Ihre Bereitstellung fehlschlägt. Stellen Sie also sicher, dass Sie den entsprechenden DNS-Eintrag einrichten.

2.3 Erstellen einer Ansible-Konfigurationsdatei

Damit Ansible richtig funktioniert, müssen einige wichtige Dinge konfiguriert werden. Wir machen dies in der Datei ansible.cfg. Ansible sucht vor der Ausführung automatisch nach dieser Datei, um die wichtigen Dinge zu finden, die es für seine Funktion benötigt. Unsere ansible.cfg-Datei wird folgenden Inhalt haben.

[defaults]
inventory = inventory.txt
private_key_file = key.txt
remote_user = ansible

Diese Datei konfiguriert die folgenden wichtigen Dinge:

  • Sie teilt Ansible mit, dass unsere Inventardatei, die Datei, die die Adressen aller Server auflistet, die Ansible verwalten soll, eine Datei namens inventory.txt ist und im aktuellen Verzeichnis zu finden ist.
  • Sie teilt Ansible mit, dass die private Schlüsseldatei für die SSH-Konnektivität zum Host eine Datei namens key.txt ist, die sich ebenfalls im aktuellen Verzeichnis befindet.
  • Sie konfiguriert den Benutzernamen ansible für die SSH-Verbindung zum Server. Dies ist der Fall, wenn Sie Ihren Server mit dem zuvor bereitgestellten cloud-init-Skript konfiguriert haben. Wenn nicht, können Sie diesen Wert gerne in den tatsächlichen username ändern.

In Übereinstimmung mit dieser Konfiguration sollten Sie die private SSH-Schlüsseldatei für die Verbindung zum Server in einer Datei namens key.txt im aktuellen Verzeichnis speichern, damit Ansible sie finden kann.

Denken Sie daran, Ihre key.txt-Datei NICHT in die Versionskontrolle einzuchecken. Insbesondere nicht in ein öffentliches Repository auf Github.

Sobald Sie die SSH-Konnektivität zu Ihrem Server überprüft, den entsprechenden DNS-Eintrag eingerichtet und eine Konfigurationsdatei erstellt haben, können Sie Ihr Playbook erstellen.

3. Das Playbook erstellen

Es ist Zeit, Ihr Ansible-Playbook zu erstellen. Um unsere Lösung einfach und modular zu halten, werden wir Ansible-Rollen verwenden. Hier ist eine Zusammenfassung der Schritte, die zu unserem gewünschten Ergebnis führen:

  • Richten Sie einen Nginx-Proxy für Jenkins ein
  • Erwerben Sie ein SSL-Zertifikat für unseren Server
  • Installieren Sie Jenkins


Jetzt im Code:

---
- hosts: all
  become: true
  roles:
    - nginx-proxy

- hosts: all
  become: true
  roles:
    - certbot

- hosts: all
  become: true
  roles:
    - jenkins

4. Dazugehörige Rollen erstellen

Als Nächstes müssen Sie die Rollen für die verschiedenen Aspekte des Workflows erstellen. Die Verzeichnisstruktur für Ihre Rollen sollte wie folgt aussehen:

tree

4.1 Innerhalb der Rolle nginx-proxy

Erstellen Sie eine tasks/mail.yml-Datei

Mit folgendem Inhalt:

- name: Install NginX
  tags: nginx,install
  ansible.builtin.apt:
    name: nginx
    state: latest
    update_cache: true

- name: Enable NginX
  ansible.builtin.service:
    name: nginx
    enabled: true

- name: Config Jenkins Proxy
  ansible.builtin.template:
    src: "jenkins-proxy.j2"
    dest: /etc/nginx/sites-available/jenkins-proxy.conf
    owner: root
    group: root
    mode: 0644
  notify: restart_nginx

- name: Enable Proxy
  ansible.builtin.file:
    src: /etc/nginx/sites-available/jenkins-proxy.conf 
    dest: /etc/nginx/sites-enabled/jenkins-proxy.conf
    state: link
  notify: restart_nginx

Erstellen Sie eine handlers/mail.yml-Datei

Mit folgendem Inhalt:

- name: restart_nginx
  ansible.builtin.service:
    name: nginx
    state: restarted

Erstellen Sie eine templates/jenkins-proxy.j2-Datei

Mit folgendem Inhalt:

server { 

    listen 80; # Port for incoming traffic (e.g., 80 for HTTP, 443 for HTTPS)
    server_name {{ server_name }}; # Your domain name or IP address

    # Location block for handling proxied requests

    location / {
        proxy_pass {{ jenkins_localhost }}; # Upstream server address (e.g., http://127.0.0.1:8080)
        
        # Optional settings:
        
        proxy_set_header Host $host; # Preserve host header for upstream server
        proxy_set_header X-Real-IP $remote_addr; # Forward client IP address
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Forward proxy chain

        # Additional settings as needed (e.g., caching, rewrites)
    }

}

4.2 Innerhalb der Rolle certbot

Erstellen Sie eine tasks/mail.yml-Datei

Mit folgendem Inhalt:

- name: Install Snapd
  ansible.builtin.apt:
    name: snapd
    state: latest

- name: Install Certbot
  community.general.snap:
    name: certbot
    classic: true
    state: present

- name: Create symlink    
  ansible.builtin.file:
    src: /snap/bin/certbot 
    dest: /usr/bin/certbot
    state: link

- name: Run certbot
  ansible.builtin.shell: 
    cmd: certbot --nginx -n --agree-tos --email {{ server_email }} --domains {{ server_name }}

4.3 Innerhalb der Rolle jenkins

Erstellen Sie eine tasks/mail.yml-Datei

Mit folgendem Inhalt:

- name: Install JDK
  ansible.builtin.apt:
    pkg:
      - fontconfig
      - openjdk-17-jre

- name: Get Jenkins Repo Key
  ansible.builtin.get_url:
    url: https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
    dest: /usr/share/keyrings/jenkins-keyring.asc

- name: Set Jenkins Source
  ansible.builtin.lineinfile:
    path: /etc/apt/sources.list.d/jenkins.list
    line: deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/
    create: yes

- name: Install Jenkins
  ansible.builtin.apt:
    name: jenkins
    state: present
    update_cache: true

- name: Enable Jenkins
  ansible.builtin.service:
    name: jenkins
    enabled: true

5. Jenkins-Debug-Ausgaben konfigurieren

Wenn Sie Ihren Jenkins-Server zum allerersten Mal einrichten, müssen Sie, um die Web-Benutzeroberfläche nutzen zu können, einen geheimen Schlüssel aus einer Datei auf dem Jenkins-Server abrufen, um zu beweisen, dass Sie den Server kontrollieren. Wir können diesen Schlüssel abrufen und ihn uns von Ansible ausgeben lassen, indem wir die folgenden Zeilen zu unserer role/jenkins/tasks/main.yml-Datei hinzufügen:

- name: Check Jenkins Status
  ansible.builtin.shell:
    cmd: systemctl status jenkins
  register: check_jens

- name: Debug
  debug:
    msg: "{{ check_jens.stdout_lines }}"

Diese Ergänzung gibt den Wert von aus

systemctl status jenkins

6. Ihr Playbook bereitstellen

Hier stellen Sie Ihr Playbook bereit und lassen Jenkins automatisch für sich einrichten. Um Ihr Playbook bereitzustellen, müssen Sie den 3 Variablen, die über unsere Rollen hinweg definiert sind, geeignete Werte zuweisen.

  • Die Variable {{jenkins_localhost}} verweist auf die localhost-Adresse von Jenkins. Dies ist normalerweise http://localhost:8080
  • Die Variable {{server_name}} gibt den Domainnamen an, unter dem der Jenkins-Server erreichbar sein soll. Dies ist die Domain, für die Sie zuvor DNS-Einträge vorgenommen haben.
  • Die Variable {{server_email}} gibt die E-Mail-Adresse des Serveradministrators an (dies ist erforderlich).

Sie können den Wert für diese Variablen auf verschiedene Weise angeben. Sie können sie zur Laufzeit angeben, in diesem Fall würden Sie Ihr Playbook mit dem folgenden Befehl bereitstellen:

ansible-playbook --extra-vars "server_name=domain-name jenkins_localhost=http://127.0.0.1:8080 
server_email=email" playbook.yml 

Ersetzen Sie domain-name und email durch geeignete Werte.

Hier ist ein Beispiel:

ansible-playbook --extra-vars "server_name=jen.obimadu.pro 
jenkins_localhost=http://127.0.0.1:8080 
server_email=hello@obimadu.pro" playbook.yml

Für weitere Möglichkeiten, den Wert der Variablen bereitzustellen, besuchen Sie Using Variables

UND DAS IST ES 🎉 🎉 Sie haben nun einen vollständig automatisierten Weg, Ihren Jenkins-Server bereitzustellen (und/oder neu bereitzustellen)!