jenkins: drop helm, run via raw manifests

This commit is contained in:
Brad Stein 2025-12-19 18:31:48 -03:00
parent fa44a00d0b
commit b97b22fc01
9 changed files with 465 additions and 359 deletions

View File

@ -0,0 +1,24 @@
# services/jenkins/configmap-init-scripts.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: jenkins-init-scripts
namespace: jenkins
data:
theme.groovy: |
import jenkins.model.Jenkins
import org.codefirst.SimpleThemeDecorator
def instance = Jenkins.get()
def decorators = instance.getExtensionList(SimpleThemeDecorator.class)
if (decorators?.size() > 0) {
def theme = decorators[0]
theme.setCssUrl("https://jenkins-contrib-themes.github.io/jenkins-material-theme/dist/material-ocean.css")
theme.setJsUrl("")
theme.setTheme("")
instance.save()
println("Applied simple-theme-plugin dark theme")
} else {
println("simple-theme-plugin not installed; skipping theme configuration")
}

View File

@ -0,0 +1,165 @@
# services/jenkins/configmap-jcasc.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: jenkins-jcasc
namespace: jenkins
data:
securityrealm.yaml: |
jenkins:
securityRealm:
oic:
clientId: "${OIDC_CLIENT_ID}"
clientSecret: "${OIDC_CLIENT_SECRET}"
serverConfiguration:
wellKnown:
wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration"
scopesOverride: "openid profile email"
logoutFromOpenIdProvider: true
postLogoutRedirectUrl: "https://ci.bstein.dev"
sendScopesInTokenRequest: true
rootURLFromRequest: true
userNameField: "preferred_username"
fullNameFieldName: "name"
emailFieldName: "email"
groupsFieldName: "groups"
authorization.yaml: |
jenkins:
authorizationStrategy:
loggedInUsersCanDoAnything:
allowAnonymousRead: false
creds.yaml: |
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
scope: GLOBAL
id: gitea-pat
username: "${GITEA_PAT_USERNAME}"
password: "${GITEA_PAT_TOKEN}"
description: "Gitea PAT for pipelines"
- usernamePassword:
scope: GLOBAL
id: harbor-robot
username: "${HARBOR_ROBOT_USERNAME}"
password: "${HARBOR_ROBOT_PASSWORD}"
description: "Harbor robot for pipelines"
jobs.yaml: |
jobs:
- script: |
pipelineJob('harbor-arm-build') {
triggers {
scm('H/5 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/harbor-arm-build.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
}
}
}
pipelineJob('ci-demo') {
triggers {
scm('H/1 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/ci-demo.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
scriptPath('Jenkinsfile')
}
}
}
pipelineJob('bstein-dev-home') {
triggers {
scm('H/2 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/bstein-dev-home.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
scriptPath('Jenkinsfile')
}
}
}
base.yaml: |
jenkins:
disableRememberMe: false
mode: NORMAL
numExecutors: 0
labelString: ""
projectNamingStrategy: "standard"
markupFormatter:
plainText
clouds:
- kubernetes:
containerCapStr: "10"
connectTimeout: "5"
readTimeout: "15"
jenkinsUrl: "http://jenkins.jenkins.svc.cluster.local:8080"
jenkinsTunnel: "jenkins-agent.jenkins.svc.cluster.local:50000"
skipTlsVerify: false
maxRequestsPerHostStr: "32"
retentionTimeout: "5"
waitForPodSec: "600"
name: "kubernetes"
namespace: "jenkins"
restrictedPssSecurityContext: false
serverUrl: "https://kubernetes.default"
credentialsId: ""
podLabels:
- key: "jenkins/jenkins-jenkins-agent"
value: "true"
templates:
- name: "default"
namespace: "jenkins"
containers:
- name: "jnlp"
args: "^${computer.jnlpmac} ^${computer.name}"
envVars:
- envVar:
key: "JENKINS_URL"
value: "http://jenkins.jenkins.svc.cluster.local:8080/"
image: "jenkins/inbound-agent:3355.v388858a_47b_33-3"
privileged: "false"
resourceLimitCpu: 512m
resourceLimitMemory: 512Mi
resourceRequestCpu: 512m
resourceRequestMemory: 512Mi
ttyEnabled: false
workingDir: /home/jenkins/agent
idleMinutes: 0
instanceCap: 2147483647
label: "jenkins-jenkins-agent "
nodeUsageMode: "NORMAL"
podRetention: Never
serviceAccount: "default"
slaveConnectTimeoutStr: "100"
yamlMergeStrategy: override
inheritYamlMergeStrategy: false
slaveAgentPort: 50000
crumbIssuer:
standard:
excludeClientIPFromCrumb: true

View File

@ -0,0 +1,17 @@
# services/jenkins/configmap-plugins.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: jenkins-plugins
namespace: jenkins
data:
plugins.txt: |
kubernetes
workflow-aggregator
git
pipeline-utility-steps
configuration-as-code
configuration-as-code-support
oic-auth
job-dsl
simple-theme-plugin

View File

@ -0,0 +1,195 @@
# services/jenkins/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins
labels:
app: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
strategy:
type: Recreate
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: default
nodeSelector:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 90
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi5"]
- weight: 50
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi4"]
hostAliases:
- ip: 38.28.125.112
hostnames:
- sso.bstein.dev
securityContext:
fsGroup: 1000
initContainers:
- name: install-plugins
image: jenkins/jenkins:2.528.3-jdk21
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- |
set -euo pipefail
jenkins-plugin-cli --plugin-file /plugins/plugins.txt
volumeMounts:
- name: plugins
mountPath: /plugins/plugins.txt
subPath: plugins.txt
- name: plugin-dir
mountPath: /usr/share/jenkins/ref/plugins
containers:
- name: jenkins
image: jenkins/jenkins:2.528.3-jdk21
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
- name: agent-listener
containerPort: 50000
env:
- name: JAVA_OPTS
value: "-Xms512m -Xmx2048m"
- name: JENKINS_OPTS
value: "--webroot=/var/jenkins_cache/war"
- name: JENKINS_SLAVE_AGENT_PORT
value: "50000"
- name: CASC_JENKINS_CONFIG
value: /config/jcasc
- name: ENABLE_OIDC
value: "true"
- name: OIDC_ISSUER
value: "https://sso.bstein.dev/realms/atlas"
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: clientId
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: clientSecret
- name: OIDC_AUTH_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: authorizationUrl
- name: OIDC_TOKEN_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: tokenUrl
- name: OIDC_USERINFO_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: userInfoUrl
- name: OIDC_LOGOUT_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: logoutUrl
- name: HARBOR_ROBOT_USERNAME
valueFrom:
secretKeyRef:
name: harbor-robot-creds
key: username
- name: HARBOR_ROBOT_PASSWORD
valueFrom:
secretKeyRef:
name: harbor-robot-creds
key: password
- name: GITEA_PAT_USERNAME
valueFrom:
secretKeyRef:
name: gitea-pat
key: username
- name: GITEA_PAT_TOKEN
valueFrom:
secretKeyRef:
name: gitea-pat
key: token
resources:
requests:
cpu: 750m
memory: 1536Mi
limits:
cpu: 1500m
memory: 3Gi
livenessProbe:
httpGet:
path: /login
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
readinessProbe:
httpGet:
path: /login
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /login
port: http
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 20
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
- name: jenkins-cache
mountPath: /var/jenkins_cache
- name: jcasc
mountPath: /config/jcasc
- name: init-scripts
mountPath: /usr/share/jenkins/ref/init.groovy.d
- name: plugin-dir
mountPath: /usr/share/jenkins/ref/plugins
- name: tmp
mountPath: /tmp
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins
- name: jenkins-cache
emptyDir: {}
- name: plugin-dir
emptyDir: {}
- name: plugins
configMap:
name: jenkins-plugins
- name: jcasc
configMap:
name: jenkins-jcasc
- name: init-scripts
configMap:
name: jenkins-init-scripts
- name: tmp
emptyDir: {}

View File

@ -1,358 +0,0 @@
# services/jenkins/helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: jenkins
namespace: jenkins
spec:
interval: 30m
chart:
spec:
chart: jenkins
version: 5.8.114
sourceRef:
kind: HelmRepository
name: jenkins
namespace: flux-system
install:
timeout: 15m
remediation:
retries: 3
upgrade:
timeout: 15m
remediation:
retries: 3
remediateLastFailure: true
cleanupOnFail: true
rollback:
timeout: 15m
values:
controller:
nodeSelector:
kubernetes.io/arch: arm64
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: ["arm64"]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 90
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi5"]
- weight: 50
preference:
matchExpressions:
- key: hardware
operator: In
values: ["rpi4"]
resources:
requests:
cpu: 750m
memory: 1.5Gi
limits:
cpu: 1500m
memory: 3Gi
javaOpts: "-Xms512m -Xmx2048m"
startupProbe:
httpGet:
path: /login
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 20
jenkinsUrl: https://ci.bstein.dev
ingress:
enabled: true
hostName: ci.bstein.dev
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
tls:
- secretName: jenkins-tls
hosts:
- ci.bstein.dev
hostAliases:
- ip: 38.28.125.112
hostnames:
- sso.bstein.dev
installPlugins:
- kubernetes
- workflow-aggregator
- git
- pipeline-utility-steps
- configuration-as-code
- oic-auth
- job-dsl
- configuration-as-code-support
- simple-theme-plugin
containerEnv:
- name: ENABLE_OIDC
value: "true"
- name: OIDC_ISSUER
value: "https://sso.bstein.dev/realms/atlas"
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: clientId
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: clientSecret
- name: OIDC_AUTH_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: authorizationUrl
- name: OIDC_TOKEN_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: tokenUrl
- name: OIDC_USERINFO_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: userInfoUrl
- name: OIDC_LOGOUT_URL
valueFrom:
secretKeyRef:
name: jenkins-oidc
key: logoutUrl
- name: HARBOR_ROBOT_USERNAME
valueFrom:
secretKeyRef:
name: harbor-robot-creds
key: username
- name: HARBOR_ROBOT_PASSWORD
valueFrom:
secretKeyRef:
name: harbor-robot-creds
key: password
- name: GITEA_PAT_USERNAME
valueFrom:
secretKeyRef:
name: gitea-pat
key: username
- name: GITEA_PAT_TOKEN
valueFrom:
secretKeyRef:
name: gitea-pat
key: token
customInitContainers:
- name: clean-jcasc-stale
image: alpine:3.20
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- |
set -euo pipefail
rm -f /var/jenkins_home/casc_configs/* || true
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
initScripts:
theme.groovy: |
import jenkins.model.Jenkins
import org.codefirst.SimpleThemeDecorator
def instance = Jenkins.get()
def decorators = instance.getExtensionList(SimpleThemeDecorator.class)
if (decorators?.size() > 0) {
def theme = decorators[0]
theme.setCssUrl("https://jenkins-contrib-themes.github.io/jenkins-material-theme/dist/material-ocean.css")
theme.setJsUrl("")
theme.setTheme("")
instance.save()
println("Applied simple-theme-plugin dark theme")
} else {
println("simple-theme-plugin not installed; skipping theme configuration")
}
JCasC:
defaultConfig: false
securityRealm: |
oic:
clientId: "${OIDC_CLIENT_ID}"
clientSecret: "${OIDC_CLIENT_SECRET}"
serverConfiguration:
wellKnown:
wellKnownOpenIDConfigurationUrl: "${OIDC_ISSUER}/.well-known/openid-configuration"
scopesOverride: "openid profile email"
logoutFromOpenIdProvider: true
postLogoutRedirectUrl: "https://ci.bstein.dev"
sendScopesInTokenRequest: true
rootURLFromRequest: true
userNameField: "preferred_username"
fullNameFieldName: "name"
emailFieldName: "email"
groupsFieldName: "groups"
authorizationStrategy: |
loggedInUsersCanDoAnything:
allowAnonymousRead: false
configScripts:
base.yaml: |
jenkins:
disableRememberMe: false
mode: NORMAL
numExecutors: 0
labelString: ""
projectNamingStrategy: "standard"
markupFormatter:
plainText
clouds:
- kubernetes:
containerCapStr: "10"
defaultsProviderTemplate: ""
connectTimeout: "5"
readTimeout: "15"
jenkinsUrl: "http://jenkins.jenkins.svc.cluster.local:8080"
jenkinsTunnel: "jenkins-agent.jenkins.svc.cluster.local:50000"
skipTlsVerify: false
usageRestricted: false
maxRequestsPerHostStr: "32"
retentionTimeout: "5"
waitForPodSec: "600"
name: "kubernetes"
namespace: "jenkins"
restrictedPssSecurityContext: false
serverUrl: "https://kubernetes.default"
credentialsId: ""
podLabels:
- key: "jenkins/jenkins-jenkins-agent"
value: "true"
templates:
- name: "default"
namespace: "jenkins"
id: a23c9bbcd21e360a77d51b426f05bd7b8032d8fdedd6ffb97c436883ce6c5ffa
containers:
- name: "jnlp"
alwaysPullImage: false
args: "^${computer.jnlpmac} ^${computer.name}"
envVars:
- envVar:
key: "JENKINS_URL"
value: "http://jenkins.jenkins.svc.cluster.local:8080/"
image: "jenkins/inbound-agent:3355.v388858a_47b_33-3"
privileged: "false"
resourceLimitCpu: 512m
resourceLimitMemory: 512Mi
resourceRequestCpu: 512m
resourceRequestMemory: 512Mi
ttyEnabled: false
workingDir: /home/jenkins/agent
idleMinutes: 0
instanceCap: 2147483647
label: "jenkins-jenkins-agent "
nodeUsageMode: "NORMAL"
podRetention: Never
showRawYaml: true
serviceAccount: "default"
slaveConnectTimeoutStr: "100"
yamlMergeStrategy: override
inheritYamlMergeStrategy: false
slaveAgentPort: 50000
crumbIssuer:
standard:
excludeClientIPFromCrumb: true
security:
apiToken:
creationOfLegacyTokenEnabled: false
tokenGenerationOnCreationEnabled: false
usageStatisticsEnabled: true
creds.yaml: |
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
scope: GLOBAL
id: gitea-pat
username: "${GITEA_PAT_USERNAME}"
password: "${GITEA_PAT_TOKEN}"
description: "Gitea PAT for pipelines"
- usernamePassword:
scope: GLOBAL
id: harbor-robot
username: "${HARBOR_ROBOT_USERNAME}"
password: "${HARBOR_ROBOT_PASSWORD}"
description: "Harbor robot for pipelines"
jobs.yaml: |
jobs:
- script: |
pipelineJob('harbor-arm-build') {
triggers {
scm('H/5 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/harbor-arm-build.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
}
}
}
pipelineJob('ci-demo') {
triggers {
scm('H/1 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/ci-demo.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
scriptPath('Jenkinsfile')
}
}
}
pipelineJob('bstein-dev-home') {
triggers {
scm('H/2 * * * *')
}
definition {
cpsScm {
scm {
git {
remote {
url('https://scm.bstein.dev/bstein/bstein-dev-home.git')
credentials('gitea-pat')
}
branches('*/master')
}
}
scriptPath('Jenkinsfile')
}
}
}
persistence:
enabled: true
storageClass: astreae
size: 50Gi
serviceAccount:
create: true

View File

@ -0,0 +1,26 @@
# services/jenkins/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins
namespace: jenkins
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
tls:
- secretName: jenkins-tls
hosts:
- ci.bstein.dev
rules:
- host: ci.bstein.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jenkins
port:
name: http

View File

@ -4,4 +4,10 @@ kind: Kustomization
namespace: jenkins
resources:
- namespace.yaml
- helmrelease.yaml
- pvc.yaml
- configmap-jcasc.yaml
- configmap-init-scripts.yaml
- configmap-plugins.yaml
- deployment.yaml
- service.yaml
- ingress.yaml

13
services/jenkins/pvc.yaml Normal file
View File

@ -0,0 +1,13 @@
# services/jenkins/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins
namespace: jenkins
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: astreae

View File

@ -0,0 +1,18 @@
# services/jenkins/service.yaml
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: jenkins
labels:
app: jenkins
spec:
ports:
- name: http
port: 8080
targetPort: 8080
- name: agent-listener
port: 50000
targetPort: 50000
selector:
app: jenkins