add jenkins

This commit is contained in:
oleg.vodyanov91@gmail.com 2025-11-23 03:59:42 +04:00
parent eb3dfbe970
commit 221ab4b432
15 changed files with 322 additions and 21 deletions

View File

@ -0,0 +1,6 @@
nginx_server_name: jenkins.home.lab.local
jenkins_backend: "http://127.0.0.1:8080" # контейнер проброшен наружу на 8080
tls_mode: "letsencrypt" # "letsencrypt" | "custom"
tls_cert_path: "/etc/ssl/jenkins/fullchain.pem" # для custom
tls_key_path: "/etc/ssl/jenkins/privkey.pem" # для custom
client_max_body_size: "1g"

View File

@ -18,6 +18,8 @@ homelab:
ansible_host: 192.168.0.101
vpn:
ansible_host: vpn.home.lab.local
jenkins:
ansible_host: jenkins.home.lab.local
children:
dns:
hosts:

View File

@ -4,3 +4,4 @@
become: true
roles:
- packages
- docker

33
ansible/jenkins.yml Normal file
View File

@ -0,0 +1,33 @@
---
- name: Jenkins in Docker (with JCasC & baked plugins)
hosts: jenkins
become: true
vars:
nginx_conf_path: /etc/nginx/sites-available/jenkins.conf
nginx_conf_link: /etc/nginx/sites-enabled/jenkins.conf
jenkins_version: "lts-jdk17" # можно weekly, но lts стабильнее
jenkins_image_name: "jenkins-custom-casc:1.0.0"
jenkins_root: "/srv/jenkins"
jenkins_data_dir: "{{ jenkins_root }}/data"
jenkins_build_dir: "{{ jenkins_root }}/build"
jenkins_http_port: 8080
jenkins_agent_port: 50000
jenkins_admin_user: "admin"
# jenkins_admin_password: "ChangeMe_UseVault!" # Хранить в Ansible Vault! - поставил на хосте
jenkins_url: "http://{{ ansible_default_ipv4.address }}:{{ jenkins_http_port }}"
jenkins_plugins:
- configuration-as-code
- job-dsl
- workflow-aggregator
- credentials
- credentials-binding
- git
- github
- ssh-credentials
- matrix-auth
- timestamper
- email-ext
- ws-cleanup
- pipeline-utility-steps
roles:
- jenkins

View File

@ -25,3 +25,4 @@ $TTL 604800
229.0 IN PTR libre.home.lab.local. ; 192.168.0.229
235.0 IN PTR monitoring.home.lab.local. ; 192.168.0.235
226.0 IN PTR wg.home.lab.local. ; 192.168.0.226
145.0 IN PTR jenkins.home.lab.local. ; 192.168.0.145

View File

@ -28,3 +28,4 @@ nas.home.lab.local. IN A 192.168.0.100
libre.home.lab.local. IN A 192.168.0.229
monitoring.home.lab.local. IN A 192.168.0.235
wg.home.lab.local. IN A 192.168.0.226
jenkins.home.lab.local. IN A 192.168.0.145

View File

@ -8,34 +8,50 @@
- lsb-release
state: present
- name: Add Docker official GPG key (Debian/Ubuntu)
ansible.builtin.apt_key:
url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
state: present
when: ansible_os_family == 'Debian'
- name: Ensure docker keyring dir
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
- name: Add Docker repo (Debian/Ubuntu)
ansible.builtin.apt_repository:
repo: "deb [arch={{ ansible_architecture }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
state: present
when: ansible_os_family == 'Debian'
- name: Add Docker GPG (dearmored)
ansible.builtin.get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.gpg
mode: "0644"
register: docker_gpg_raw
- name: Dearmor Docker GPG if needed
ansible.builtin.shell: |
gpg --dearmor < /etc/apt/keyrings/docker.gpg > /etc/apt/keyrings/docker.gpg.tmp
mv /etc/apt/keyrings/docker.gpg.tmp /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
args:
creates: /etc/apt/keyrings/docker.gpg.tmp
when: docker_gpg_raw is changed
- name: Map architecture to repo arch
ansible.builtin.set_fact:
repo_arch: "{{ 'arm64' if ansible_architecture in ['aarch64','arm64'] else 'amd64' }}"
- name: Add Docker repo (correct arch + signed-by)
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/docker.list
mode: "0644"
content: |
deb [arch={{ repo_arch }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable
- name: apt update
ansible.builtin.apt:
update_cache: true
- name: Install Docker Engine & Compose plugin
ansible.builtin.package:
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
update_cache: true
state: present
- name: Install Docker Python SDK for Ansible modules
ansible.builtin.pip:
name: docker
- name: Ensure docker service running
ansible.builtin.service:
name: docker
state: started
enabled: true

View File

@ -0,0 +1,4 @@
- name: reload nginx
systemd:
name: nginx
state: reloaded

View File

@ -0,0 +1,63 @@
- name: Create directories
file:
path: "{{ item }}"
state: directory
mode: "0755"
loop:
- "{{ jenkins_root }}"
- "{{ jenkins_build_dir }}"
- "{{ jenkins_data_dir }}"
- name: Template plugins list
template:
src: templates/plugins.txt.j2
dest: "{{ jenkins_build_dir }}/plugins.txt"
mode: "0644"
- name: Template Dockerfile
template:
src: templates/Dockerfile.j2
dest: "{{ jenkins_build_dir }}/Dockerfile"
mode: "0644"
- name: Build custom Jenkins image (plugins baked in)
community.docker.docker_image:
name: "{{ jenkins_image_name }}"
build:
path: "{{ jenkins_build_dir }}"
source: build
register: build_img
- name: Template JCasC config
template:
src: templates/jenkins-casc.yaml.j2
dest: "{{ jenkins_data_dir }}/casc.yaml"
mode: "0640"
- name: Ensure ownership for Jenkins data (uid/gid 1000)
file:
path: "{{ jenkins_data_dir }}"
state: directory
owner: "1000"
group: "1000"
recurse: true
- name: Template docker-compose.yaml
template:
src: templates/docker-compose.yaml.j2
dest: "{{ jenkins_root }}/docker-compose.yaml"
mode: "0644"
- name: Up jenkins stack
community.docker.docker_compose_v2:
project_src: "{{ jenkins_root }}"
files: ["docker-compose.yaml"]
state: present
register: dc
- name: Wait for Jenkins HTTP
wait_for:
host: 127.0.0.1
port: "{{ jenkins_http_port }}"
delay: 3
timeout: 180

View File

@ -0,0 +1,6 @@
FROM jenkins/jenkins:{{ jenkins_version }}
USER root
# Ускоряем установку плагинов и делаем образ детерминированным
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
USER jenkins

View File

@ -0,0 +1,23 @@
services:
jenkins:
image: "{{ jenkins_image_name }}"
container_name: jenkins
restart: unless-stopped
ports:
- "{{ jenkins_http_port }}:8080"
- "{{ jenkins_agent_port }}:50000"
environment:
JAVA_OPTS: "-Djenkins.install.runSetupWizard=false -Dcasc.jenkins.config=/var/jenkins_home/casc.yaml"
JENKINS_ADMIN_ID: "{{ jenkins_admin_user }}"
JENKINS_ADMIN_PASSWORD: ${JENKINS_ADMIN_PASSWORD}
volumes:
- "{{ jenkins_data_dir }}:/var/jenkins_home"
# Если нужно, чтобы pipeline-ы запускали Docker на хосте — расскомментируйте:
- /var/run/docker.sock:/var/run/docker.sock
# healthcheck, чтобы Ansible/оркестратор видел состояние
healthcheck:
test: ["CMD", "bash", "-lc", "curl -fsSL http://127.0.0.1:8080/login || exit 1"]
interval: 30s
timeout: 5s
retries: 10
start_period: 30s

View File

@ -0,0 +1,62 @@
jenkins:
systemMessage: "Managed by Ansible + JCasC. Do not edit via UI."
numExecutors: 2
mode: NORMAL
# ✅ Перенесено в jenkins.*
crumbIssuer:
standard:
excludeClientIPFromCrumb: false
# ✅ Перенесено в jenkins.*
securityRealm:
local:
allowsSignup: false
users:
- id: "${JENKINS_ADMIN_ID}"
password: "${JENKINS_ADMIN_PASSWORD}"
# ✅ Современный matrix-auth
authorizationStrategy:
globalMatrix:
grantedPermissions:
- "Overall/Administer:${JENKINS_ADMIN_ID}"
- "Overall/Read:authenticated"
unclassified:
# ✅ Разрешённый ключ
location:
url: "{{ jenkins_url }}"
# ✅ У плагина timestamper имя узла — 'timestamper'
timestamper:
systemTimeFormat: "yyyy-MM-dd HH:mm:ss"
elapsedTimeFormat: "'+'HH:mm:ss"
tool:
git:
installations:
- name: "Default"
home: "/usr/bin/git"
jobs:
- script: |
pipelineJob('hello-pipeline') {
definition {
cps {
script('''\
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo "It works on ${env.NODE_NAME}"
}
}
}
}
'''.stripIndent())
sandbox(true)
}
}
}

View File

@ -0,0 +1,73 @@
# HTTP → HTTPS редирект
server {
listen 80;
listen [::]:80;
server_name {{ nginx_server_name }};
# ACME challenge (если включен LE)
location ^~ /.well-known/acme-challenge/ {
root /var/www/jenkins;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS ↔ Jenkins (reverse proxy)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ nginx_server_name }};
{% if tls_mode == 'letsencrypt' %}
ssl_certificate /etc/letsencrypt/live/{{ nginx_server_name }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ nginx_server_name }}/privkey.pem;
{% else %}
ssl_certificate {{ tls_cert_path }};
ssl_certificate_key {{ tls_key_path }};
{% endif %}
# Базовая TLS-настройка
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# (Опционально) HSTS — включай, если домен всегда по HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Размер артефактов, long-running шаги
client_max_body_size {{ client_max_body_size }};
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Проксирование в Jenkins-контейнер
location / {
proxy_pass {{ jenkins_backend }};
proxy_http_version 1.1;
# WebSocket/HTTP/1.1
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Проброс хостов/протоколов/адресов
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
# Убираем буферизацию, Jenkins любит стримить логи билда
proxy_buffering off;
}
# Вспомогательное: корректная переменная для upgrade
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
access_log /var/log/nginx/jenkins.access.log;
error_log /var/log/nginx/jenkins.error.log;
}

View File

@ -0,0 +1,3 @@
{% for p in jenkins_plugins -%}
{{ p }}
{% endfor -%}

View File

@ -0,0 +1,7 @@
nginx_server_name: jenkins.home.lab.local
jenkins_backend: "http://127.0.0.1:8080" # контейнер проброшен наружу на 8080
tls_mode: "letsencrypt" # "letsencrypt" | "custom"
tls_cert_path: "/etc/ssl/jenkins/fullchain.pem" # для custom
tls_key_path: "/etc/ssl/jenkins/privkey.pem" # для custom
client_max_body_size: "1g"
email: oleg.vodyanov91@gmail.com