add jenkins
This commit is contained in:
parent
eb3dfbe970
commit
221ab4b432
6
ansible/group_vars/jenkins.yml
Normal file
6
ansible/group_vars/jenkins.yml
Normal 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"
|
||||||
@ -18,6 +18,8 @@ homelab:
|
|||||||
ansible_host: 192.168.0.101
|
ansible_host: 192.168.0.101
|
||||||
vpn:
|
vpn:
|
||||||
ansible_host: vpn.home.lab.local
|
ansible_host: vpn.home.lab.local
|
||||||
|
jenkins:
|
||||||
|
ansible_host: jenkins.home.lab.local
|
||||||
children:
|
children:
|
||||||
dns:
|
dns:
|
||||||
hosts:
|
hosts:
|
||||||
|
|||||||
@ -4,3 +4,4 @@
|
|||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
- packages
|
- packages
|
||||||
|
- docker
|
||||||
|
|||||||
33
ansible/jenkins.yml
Normal file
33
ansible/jenkins.yml
Normal 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
|
||||||
@ -25,3 +25,4 @@ $TTL 604800
|
|||||||
229.0 IN PTR libre.home.lab.local. ; 192.168.0.229
|
229.0 IN PTR libre.home.lab.local. ; 192.168.0.229
|
||||||
235.0 IN PTR monitoring.home.lab.local. ; 192.168.0.235
|
235.0 IN PTR monitoring.home.lab.local. ; 192.168.0.235
|
||||||
226.0 IN PTR wg.home.lab.local. ; 192.168.0.226
|
226.0 IN PTR wg.home.lab.local. ; 192.168.0.226
|
||||||
|
145.0 IN PTR jenkins.home.lab.local. ; 192.168.0.145
|
||||||
|
|||||||
@ -28,3 +28,4 @@ nas.home.lab.local. IN A 192.168.0.100
|
|||||||
libre.home.lab.local. IN A 192.168.0.229
|
libre.home.lab.local. IN A 192.168.0.229
|
||||||
monitoring.home.lab.local. IN A 192.168.0.235
|
monitoring.home.lab.local. IN A 192.168.0.235
|
||||||
wg.home.lab.local. IN A 192.168.0.226
|
wg.home.lab.local. IN A 192.168.0.226
|
||||||
|
jenkins.home.lab.local. IN A 192.168.0.145
|
||||||
|
|||||||
@ -8,34 +8,50 @@
|
|||||||
- lsb-release
|
- lsb-release
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Add Docker official GPG key (Debian/Ubuntu)
|
- name: Ensure docker keyring dir
|
||||||
ansible.builtin.apt_key:
|
ansible.builtin.file:
|
||||||
url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
|
path: /etc/apt/keyrings
|
||||||
state: present
|
state: directory
|
||||||
when: ansible_os_family == 'Debian'
|
mode: "0755"
|
||||||
|
|
||||||
- name: Add Docker repo (Debian/Ubuntu)
|
- name: Add Docker GPG (dearmored)
|
||||||
ansible.builtin.apt_repository:
|
ansible.builtin.get_url:
|
||||||
repo: "deb [arch={{ ansible_architecture }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
|
url: https://download.docker.com/linux/ubuntu/gpg
|
||||||
state: present
|
dest: /etc/apt/keyrings/docker.gpg
|
||||||
when: ansible_os_family == 'Debian'
|
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
|
- name: Install Docker Engine & Compose plugin
|
||||||
ansible.builtin.package:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- docker-ce
|
- docker-ce
|
||||||
- docker-ce-cli
|
- docker-ce-cli
|
||||||
- containerd.io
|
- containerd.io
|
||||||
- docker-buildx-plugin
|
- docker-buildx-plugin
|
||||||
- docker-compose-plugin
|
- docker-compose-plugin
|
||||||
|
update_cache: true
|
||||||
state: present
|
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
|
|
||||||
|
|||||||
4
ansible/roles/jenkins/handlers/main.yml
Normal file
4
ansible/roles/jenkins/handlers/main.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
- name: reload nginx
|
||||||
|
systemd:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
||||||
63
ansible/roles/jenkins/tasks/main.yml
Normal file
63
ansible/roles/jenkins/tasks/main.yml
Normal 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
|
||||||
6
ansible/roles/jenkins/templates/Dockerfile.j2
Normal file
6
ansible/roles/jenkins/templates/Dockerfile.j2
Normal 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
|
||||||
23
ansible/roles/jenkins/templates/docker-compose.yaml.j2
Normal file
23
ansible/roles/jenkins/templates/docker-compose.yaml.j2
Normal 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
|
||||||
62
ansible/roles/jenkins/templates/jenkins-casc.yaml.j2
Normal file
62
ansible/roles/jenkins/templates/jenkins-casc.yaml.j2
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
73
ansible/roles/jenkins/templates/nginx-jenkins-https.conf.j2
Normal file
73
ansible/roles/jenkins/templates/nginx-jenkins-https.conf.j2
Normal 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;
|
||||||
|
}
|
||||||
3
ansible/roles/jenkins/templates/plugins.txt.j2
Normal file
3
ansible/roles/jenkins/templates/plugins.txt.j2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% for p in jenkins_plugins -%}
|
||||||
|
{{ p }}
|
||||||
|
{% endfor -%}
|
||||||
7
ansible/roles/jenkins/vars/main.yml
Normal file
7
ansible/roles/jenkins/vars/main.yml
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user