Docker : Jenkins with Helm
![Helm_Icon.png](/DevOps/Docker/images/Docker-Helm-Jenkins/Helm_Icon.png)
![Jenkins_Icon.png](/DevOps/Docker/images/Docker-Helm-Jenkins/Jenkins_Icon.png)
Note: Not complete ---------
$ kubectl create namespace jenkins namespace/jenkins created $ kubectl get ns NAME STATUS AGE default Active 9d jenkins Active 6s kube-node-lease Active 9d kube-public Active 9d kube-system Active 9d kubernetes-dashboard Active 9d
A typical Jenkins deployment consists of a controller node and one or more agents. To simplify the deployment of Jenkins, we'll use Helm to deploy Jenkins.
Let's add the Jenkins repo as follows:
$ helm repo add jenkinsci https://charts.jenkins.io "jenkinsci" has been added to your repositories $ helm repo update ...Successfully got an update from the "jenkinsci" chart repository
The helm charts in the Jenkins repo can be listed with the following command:
$ helm search repo jenkinsci NAME CHART VERSION APP VERSION DESCRIPTION jenkinsci/jenkins 3.3.17 2.277.4 Jenkins - Build great things at any scale! The ...
We want to create a persistent volume for our Jenkins controller pod. This will prevent us from losing our whole configuration of the Jenkins controller and our jobs when we reboot our minikube. We can use the /data directory. This directory will contain our Jenkins controller configuration.
Let's create a volume (jenkins-pv) using the following yaml, jenkins-volume.yaml:
apiVersion: v1 kind: PersistentVolume metadata: name: jenkins-pv namespace: jenkins spec: storageClassName: jenkins-pv accessModes: - ReadWriteOnce capacity: storage: 5Gi persistentVolumeReclaimPolicy: Retain hostPath: path: /data/jenkins-volume/
Note that, in the above spec, hostPath uses the /data/jenkins-volume/ of our node to emulate network-attached storage. This approach is only suited for development and testing purposes. For production, we should provide a network resource like a Google Compute Engine persistent disk, or an Amazon Elastic Block Store volume.
$ kubectl apply -f jenkins-volume.yaml persistentvolume/jenkins-pv created
A service account provides an identity for processes that run in a Pod.
When we create a pod, if we do not specify a service account, it is automatically assigned the default service account in the same namespace.
When we access the cluster using kubectl, we are authenticated by the apiserver as a particular User Account (currently this is usually admin, unless our cluster administrator has customized our cluster).
Processes in containers inside pods can also contact the apiserver. When they do, they are authenticated as a particular Service Account (for example, default).
If we want to define a role cluster-wide, we can use a ClusterRole; if we want to define a role within a namespace, we want to use a Role.
A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted.
Because we want to bind a ClusterRole to all the namespaces in our cluster, we should use a ClusterRoleBinding.
jenkins-sa.yaml:
--- apiVersion: v1 kind: ServiceAccount metadata: name: jenkins namespace: jenkins --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: font color="maroon">annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: jenkins rules: - apiGroups: - '*' resources: - statefulsets - services - replicationcontrollers - replicasets - podtemplates - podsecuritypolicies - pods - pods/log - pods/exec - podpreset - poddisruptionbudget - persistentvolumes - persistentvolumeclaims - jobs - endpoints - deployments - deployments/scale - daemonsets - cronjobs - configmaps - namespaces - events - secrets verbs: - create - get - watch - delete - list - patch - update - apiGroups: - "" resources: - nodes verbs: - get - list - watch - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: jenkins roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jenkins subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:serviceaccounts:jenkins
$ kubectl apply -f jenkins-sa.yaml serviceaccount/jenkins created clusterrole.rbac.authorization.k8s.io/jenkins created clusterrolebinding.rbac.authorization.k8s.io/jenkins created $ kubectl get sa -n jenkins NAME SECRETS AGE default 1 3h jenkins 1 2m7s
We will deploy Jenkins including the Jenkins Kubernetes plugin.
jenkins-values.yaml(https://raw.githubusercontent.com/jenkinsci/helm-charts/main/charts/jenkins/values.yaml):
# Default values for jenkins. # This is a YAML-formatted file. # Declare name/value pairs to be passed into your templates. # name: value ## Overrides for generated resource names # See templates/_helpers.tpl # nameOverride: # fullnameOverride: # namespaceOverride: # For FQDN resolving of the controller service. Change this value to match your existing configuration. # ref: https://github.com/kubernetes/dns/blob/master/docs/specification.md clusterZone: "cluster.local" renderHelmLabels: true controller: # Used for label app.kubernetes.io/component componentName: "jenkins-controller" image: "jenkins/jenkins" tag: "2.277.4-jdk11" imagePullPolicy: "Always" imagePullSecretName: # Optionally configure lifetime for controller-container lifecycle: # postStart: # exec: # command: # - "uname" # - "-a" disableRememberMe: false numExecutors: 0 # configures the executor mode of the Jenkins node. Possible values are: NORMAL or EXCLUSIVE executorMode: "NORMAL" # This is ignored if enableRawHtmlMarkupFormatter is true markupFormatter: plainText customJenkinsLabels: [] # The default configuration uses this secret to configure an admin user # If you don't need that user or use a different security realm then you can disable it adminSecret: true hostNetworking: false # When enabling LDAP or another non-Jenkins identity source, the built-in admin account will no longer exist. # If you disable the non-Jenkins identity store and instead use the Jenkins internal one, # you should revert controller.adminUser to your preferred admin user: adminUser: "admin" # adminPassword: <defaults to random> admin: existingSecret: "" userKey: jenkins-admin-user passwordKey: jenkins-admin-password # This values should not be changed unless you use your custom image of jenkins or any devired from. If you want to use # Cloudbees Jenkins Distribution docker, you should set jenkinsHome: "/var/cloudbees-jenkins-distribution" jenkinsHome: "/var/jenkins_home" # This values should not be changed unless you use your custom image of jenkins or any devired from. If you want to use # Cloudbees Jenkins Distribution docker, you should set jenkinsRef: "/usr/share/cloudbees-jenkins-distribution/ref" jenkinsRef: "/usr/share/jenkins/ref" # Path to the jenkins war file which is used by jenkins-plugin-cli. jenkinsWar: "/usr/share/jenkins/jenkins.war" resources: requests: cpu: "50m" memory: "256Mi" limits: cpu: "2000m" memory: "4096Mi" # Environment variables that get added to the init container (useful for e.g. http_proxy) # initContainerEnv: # - name: http_proxy # value: "http://192.168.64.1:3128" # containerEnv: # - name: http_proxy # value: "http://192.168.64.1:3128" # Set min/max heap here if needed with: # javaOpts: "-Xms512m -Xmx512m" # jenkinsOpts: "" # If you are using the ingress definitions provided by this chart via the `controller.ingress` block the configured hostname will be the ingress hostname starting with `https://` or `http://` depending on the `tls` configuration. # The Protocol can be overwritten by specifying `controller.jenkinsUrlProtocol`. # jenkinsUrlProtocol: "https" # If you are not using the provided ingress you can specify `controller.jenkinsUrl` to change the url definition. # jenkinsUrl: "" # If you set this prefix and use ingress controller then you might want to set the ingress path below # jenkinsUriPrefix: "/jenkins" # Enable pod security context (must be `true` if podSecurityContextOverride, runAsUser or fsGroup are set) usePodSecurityContext: true # Note that `runAsUser`, `fsGroup`, and `securityContextCapabilities` are # being deprecated and replaced by `podSecurityContextOverride`. # Set runAsUser to 1000 to let Jenkins run as non-root user 'jenkins' which exists in 'jenkins/jenkins' docker image. # When setting runAsUser to a different value than 0 also set fsGroup to the same value: runAsUser: 1000 fsGroup: 1000 # If you have PodSecurityPolicies that require dropping of capabilities as suggested by CIS K8s benchmark, put them here securityContextCapabilities: {} # drop: # - NET_RAW # Completely overwrites the contents of the `securityContext`, ignoring the # values provided for the deprecated fields: `runAsUser`, `fsGroup`, and # `securityContextCapabilities`. In the case of mounting an ext4 filesystem, # it might be desirable to use `supplementalGroups` instead of `fsGroup` in # the `securityContext` block: https://github.com/kubernetes/kubernetes/issues/67014#issuecomment-589915496 # podSecurityContextOverride: # runAsUser: 1000 # runAsNonRoot: true # supplementalGroups: [1000] # # capabilities: {} servicePort: 8080 targetPort: 8080 # For minikube, set this to NodePort, elsewhere use LoadBalancer # Use ClusterIP if your setup includes ingress controller serviceType: NodePort # Jenkins controller service annotations serviceAnnotations: {} # Jenkins controller custom labels statefulSetLabels: {} # foo: bar # bar: foo # Jenkins controller service labels serviceLabels: {} # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https # Put labels on Jenkins controller pod podLabels: {} # Used to create Ingress record (should used with ServiceType: ClusterIP) # nodePort: <to set explicitly, choose port between 30000-32767 # Enable Kubernetes Liveness and Readiness Probes # if Startup Probe is supported, enable it too # ~ 2 minutes to allow Jenkins to restart when upgrading plugins. Set ReadinessTimeout to be shorter than LivenessTimeout. healthProbes: true probes: startupProbe: httpGet: path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' port: http periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 12 livenessProbe: failureThreshold: 5 httpGet: path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' port: http periodSeconds: 10 timeoutSeconds: 5 # If Startup Probe is not supported on your Kubernetes cluster, you might want to use "initialDelaySeconds" instead. # It delays the initial liveness probe while Jenkins is starting # initialDelaySeconds: 60 readinessProbe: failureThreshold: 3 httpGet: path: '{{ default "" .Values.controller.jenkinsUriPrefix }}/login' port: http periodSeconds: 10 timeoutSeconds: 5 # If Startup Probe is not supported on your Kubernetes cluster, you might want to use "initialDelaySeconds" instead. # It delays the initial readyness probe while Jenkins is starting # initialDelaySeconds: 60 agentListenerEnabled: true agentListenerPort: 50000 agentListenerHostPort: agentListenerNodePort: disabledAgentProtocols: - JNLP-connect - JNLP2-connect csrf: defaultCrumbIssuer: enabled: true proxyCompatability: true # Kubernetes service type for the JNLP agent service # agentListenerServiceType is the Kubernetes Service type for the JNLP agent service, # either 'LoadBalancer', 'NodePort', or 'ClusterIP' # Note if you set this to 'LoadBalancer', you *must* define annotations to secure it. By default # this will be an external load balancer and allowing inbound 0.0.0.0/0, a HUGE # security risk: https://github.com/kubernetes/charts/issues/1341 agentListenerServiceType: "ClusterIP" # Optionally assign an IP to the LoadBalancer agentListenerService LoadBalancer # GKE users: only regional static IPs will work for Service Load balancer. agentListenerLoadBalancerIP: agentListenerServiceAnnotations: {} # Example of 'LoadBalancer' type of agent listener with annotations securing it # agentListenerServiceType: LoadBalancer # agentListenerServiceAnnotations: # service.beta.kubernetes.io/aws-load-balancer-internal: "True" # service.beta.kubernetes.io/load-balancer-source-ranges: "172.0.0.0/8, 10.0.0.0/8" # LoadBalancerSourcesRange is a list of allowed CIDR values, which are combined with ServicePort to # set allowed inbound rules on the security group assigned to the controller load balancer loadBalancerSourceRanges: - 0.0.0.0/0 # Optionally assign a known public LB IP # loadBalancerIP: 1.2.3.4 # Optionally configure a JMX port # requires additional javaOpts, ie # javaOpts: > # -Dcom.sun.management.jmxremote.port=4000 # -Dcom.sun.management.jmxremote.authenticate=false # -Dcom.sun.management.jmxremote.ssl=false # jmxPort: 4000 # Optionally configure other ports to expose in the controller container extraPorts: [] # - name: BuildInfoProxy # port: 9000 # List of plugins to be install during Jenkins controller start installPlugins: - kubernetes:1.29.4 - workflow-aggregator:2.6 - git:4.7.1 - configuration-as-code:1.51 # Set to false to download the minimum required version of all dependencies. installLatestPlugins: false # List of plugins to install in addition to those listed in controller.installPlugins additionalPlugins: [] # Enable to initialize the Jenkins controller only once on initial installation. # Without this, whenever the controller gets restarted (Evicted, etc.) it will fetch plugin updates which has the potential to cause breakage. # Note that for this to work, `persistence.enabled` needs to be set to `true` initializeOnce: false # Enable to always override the installed plugins with the values of 'controller.installPlugins' on upgrade or redeployment. # overwritePlugins: true # Configures if plugins bundled with `controller.image` should be overwritten with the values of 'controller.installPlugins' on upgrade or redeployment. overwritePluginsFromImage: true # Enable HTML parsing using OWASP Markup Formatter Plugin (antisamy-markup-formatter), useful with ghprb plugin. # The plugin is not installed by default, please update controller.installPlugins. enableRawHtmlMarkupFormatter: false # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval scriptApproval: [] # - "method groovy.json.JsonSlurperClassic parseText java.lang.String" # - "new groovy.json.JsonSlurperClassic" # List of groovy init scripts to be executed during Jenkins controller start initScripts: [] # - | # print 'adding global pipeline libraries, register properties, bootstrap jobs...' # 'name' is a name of an existing secret in same namespace as jenkins, # 'keyName' is the name of one of the keys inside current secret. # the 'name' and 'keyName' are concatenated with a '-' in between, so for example: # an existing secret "secret-credentials" and a key inside it named "github-password" should be used in Jcasc as ${secret-credentials-github-password} # 'name' and 'keyName' must be lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', # and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc') additionalExistingSecrets: [] # - name: secret-name-1 # keyName: username # - name: secret-name-1 # keyName: password additionalSecrets: [] # - name: nameOfSecret # value: secretText # Generate SecretClaim resources in order to create Kubernetes secrets from HashiCorp Vault using kube-vault-controller. # 'name' is name of the secret that will be created in Kubernetes. The Jenkins fullname is prepended to this value. # 'path' is the fully qualified path to the secret in Vault # 'type' is an optional Kubernetes secret type. Defaults to 'Opaque' # 'renew' is an optional secret renewal time in seconds secretClaims: [] # - name: secretName # required # path: testPath # required # type: kubernetes.io/tls # optional # renew: 60 # optional # Below is the implementation of Jenkins Configuration as Code. Add a key under configScripts for each configuration area, # where each corresponds to a plugin or section of the UI. Each key (prior to | character) is just a label, and can be any value. # Keys are only used to give the section a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label # characters: lowercase letters, numbers, and hyphens. The keys become the name of a configuration yaml file on the controller in # /var/jenkins_home/casc_configs (by default) and will be processed by the Configuration as Code Plugin. The lines after each | # become the content of the configuration yaml file. The first line after this is a JCasC root element, eg jenkins, credentials, # etc. Best reference is https://<jenkins_url>/configuration-as-code/reference. The example below creates a welcome message: JCasC: defaultConfig: true configScripts: {} # welcome-message: | # jenkins: # systemMessage: Welcome to our CI\CD server. This Jenkins is configured and managed 'as code'. # Ignored if securityRealm is defined in controller.JCasC.configScripts and # ignored if controller.enableXmlConfig=true as controller.securityRealm takes precedence securityRealm: |- local: allowsSignup: false enableCaptcha: false users: - id: "${chart-admin-username}" name: "Jenkins Admin" password: "${chart-admin-password}" # Ignored if authorizationStrategy is defined in controller.JCasC.configScripts authorizationStrategy: |- loggedInUsersCanDoAnything: allowAnonymousRead: false # Optionally specify additional init-containers customInitContainers: [] # - name: custom-init # image: "alpine:3.7" # imagePullPolicy: Always # command: [ "uname", "-a" ] sidecars: configAutoReload: # If enabled: true, Jenkins Configuration as Code will be reloaded on-the-fly without a reboot. If false or not-specified, # jcasc changes will cause a reboot and will only be applied at the subsequent start-up. Auto-reload uses the # http://<jenkins_url>/reload-configuration-as-code endpoint to reapply config when changes to the configScripts are detected. enabled: true image: kiwigrid/k8s-sidecar:0.1.275 imagePullPolicy: IfNotPresent resources: {} # limits: # cpu: 100m # memory: 100Mi # requests: # cpu: 50m # memory: 50Mi # How many connection-related errors to retry on reqRetryConnect: 10 # env: # - name: REQ_TIMEOUT # value: "30" # SSH port value can be set to any unused TCP port. The default, 1044, is a non-standard SSH port that has been chosen at random. # Is only used to reload jcasc config from the sidecar container running in the Jenkins controller pod. # This TCP port will not be open in the pod (unless you specifically configure this), so Jenkins will not be # accessible via SSH from outside of the pod. Note if you use non-root pod privileges (runAsUser & fsGroup), # this must be > 1024: sshTcpPort: 1044 # folder in the pod that should hold the collected dashboards: folder: "/var/jenkins_home/casc_configs" # If specified, the sidecar will search for JCasC config-maps inside this namespace. # Otherwise the namespace in which the sidecar is running will be used. # It's also possible to specify ALL to search in all namespaces: # searchNamespace: # Allows you to inject additional/other sidecars other: [] ## The example below runs the client for https://smee.io as sidecar container next to Jenkins, ## that allows to trigger build behind a secure firewall. ## https://jenkins.io/blog/2019/01/07/webhook-firewalls/#triggering-builds-with-webhooks-behind-a-secure-firewall ## ## Note: To use it you should go to https://smee.io/new and update the url to the generete one. # - name: smee # image: docker.io/twalter/smee-client:1.0.2 # args: ["--port", "{{ .Values.controller.servicePort }}", "--path", "/github-webhook/", "--url", "https://smee.io/new"] # resources: # limits: # cpu: 50m # memory: 128Mi # requests: # cpu: 10m # memory: 32Mi # Name of the Kubernetes scheduler to use schedulerName: "" # Node labels and tolerations for pod assignment # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature nodeSelector: {} terminationGracePeriodSeconds: tolerations: [] affinity: {} # Leverage a priorityClass to ensure your pods survive resource shortages # ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ priorityClassName: podAnnotations: {} # Add StatefulSet annotations statefulSetAnnotations: {} # StatefulSet updateStrategy # ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies updateStrategy: {} ingress: enabled: false # Override for the default paths that map requests to the backend paths: [] # - backend: # serviceName: ssl-redirect # servicePort: use-annotation # - backend: # serviceName: >- # {{ template "jenkins.fullname" . }} # # Don't use string here, use only integer value! # servicePort: 8080 # For Kubernetes v1.14+, use 'networking.k8s.io/v1beta1' # For Kubernetes v1.19+, use 'networking.k8s.io/v1' apiVersion: "extensions/v1beta1" labels: {} annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" # Set this path to jenkinsUriPrefix above or use annotations to rewrite path # path: "/jenkins" # configures the hostname e.g. jenkins.example.com hostName: tls: # - secretName: jenkins.cluster.local # hosts: # - jenkins.cluster.local # often you want to have your controller all locked down and private # but you still want to get webhooks from your SCM # A secondary ingress will let you expose different urls # with a differnt configuration secondaryingress: enabled: false # paths you want forwarded to the backend # ex /github-webhook paths: [] # For Kubernetes v1.14+, use 'networking.k8s.io/v1beta1' # For Kubernetes v1.19+, use 'networking.k8s.io/v1' apiVersion: "extensions/v1beta1" labels: {} annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" # configures the hostname e.g. jenkins-external.example.com hostName: tls: # - secretName: jenkins-external.example.com # hosts: # - jenkins-external.example.com # If you're running on GKE and need to configure a backendconfig # to finish ingress setup, use the following values. # Docs: https://cloud.google.com/kubernetes-engine/docs/concepts/backendconfig backendconfig: enabled: false apiVersion: "extensions/v1beta1" name: labels: {} annotations: {} spec: {} # Openshift route route: enabled: false labels: {} annotations: {} # path: "/jenkins" # controller.hostAliases allows for adding entries to Pod /etc/hosts: # https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ hostAliases: [] # - ip: 192.168.50.50 # hostnames: # - something.local # - ip: 10.0.50.50 # hostnames: # - other.local # Expose Prometheus metrics prometheus: # If enabled, add the prometheus plugin to the list of plugins to install # https://plugins.jenkins.io/prometheus enabled: false # Additional labels to add to the ServiceMonitor object serviceMonitorAdditionalLabels: {} # Set a custom namespace where to deploy ServiceMonitor resource # serviceMonitorNamespace: monitoring scrapeInterval: 60s # This is the default endpoint used by the prometheus plugin scrapeEndpoint: /prometheus # Additional labels to add to the PrometheusRule object alertingRulesAdditionalLabels: {} # An array of prometheus alerting rules # See here: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ # The `groups` root object is added by default, simply add the rule entries alertingrules: [] # Set a custom namespace where to deploy PrometheusRule resource prometheusRuleNamespace: "" # Can be used to disable rendering controller test resources when using helm template testEnabled: true httpsKeyStore: jenkinsHttpsJksSecretName: '' enable: false httpPort: 8081 path: "/var/jenkins_keystore" fileName: "keystore.jks" password: "password" # Convert keystore.jks files content to base64 ( cat keystore.jks | base64 ) and put the output here jenkinsKeyStoreBase64Encoded: | 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 agent: enabled: true defaultsProviderTemplate: "" # URL for connecting to the Jenkins contoller jenkinsUrl: # connect to the specified host and port, instead of connecting directly to the Jenkins controller jenkinsTunnel: kubernetesConnectTimeout: 5 kubernetesReadTimeout: 15 maxRequestsPerHostStr: "32" namespace: image: "jenkins/inbound-agent" tag: "4.6-1" workingDir: "/home/jenkins" customJenkinsLabels: [] # name of the secret to be used for image pulling imagePullSecretName: componentName: "jenkins-agent" websocket: false privileged: false runAsUser: runAsGroup: resources: requests: cpu: "512m" memory: "512Mi" limits: cpu: "512m" memory: "512Mi" # You may want to change this to true while testing a new image alwaysPullImage: false # Controls how agent pods are retained after the Jenkins build completes # Possible values: Always, Never, OnFailure podRetention: "Never" # You can define the volumes that you want to mount for this container # Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, PVC, Secret # Configure the attributes as they appear in the corresponding Java class for that type # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes volumes: [] # - type: ConfigMap # configMapName: myconfigmap # mountPath: /var/myapp/myconfigmap # - type: EmptyDir # mountPath: /var/myapp/myemptydir # memory: false # - type: HostPath # hostPath: /var/lib/containers # mountPath: /var/myapp/myhostpath # - type: Nfs # mountPath: /var/myapp/mynfs # readOnly: false # serverAddress: "192.0.2.0" # serverPath: /var/lib/containers # - type: PVC # claimName: mypvc # mountPath: /var/myapp/mypvc # readOnly: false # - type: Secret # defaultMode: "600" # mountPath: /var/myapp/mysecret # secretName: mysecret # Pod-wide environment, these vars are visible to any container in the agent pod # You can define the workspaceVolume that you want to mount for this container # Allowed types are: DynamicPVC, EmptyDir, HostPath, Nfs, PVC # Configure the attributes as they appear in the corresponding Java class for that type # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes/workspace workspaceVolume: {} # - type: DynamicPVC # configMapName: myconfigmap # - type: EmptyDir # memory: false # - type: HostPath # hostPath: /var/lib/containers # - type: Nfs # readOnly: false # serverAddress: "192.0.2.0" # serverPath: /var/lib/containers # - type: PVC # claimName: mypvc # readOnly: false # Pod-wide environment, these vars are visible to any container in the agent pod envVars: [] # - name: PATH # value: /usr/local/bin nodeSelector: {} # Key Value selectors. Ex: # jenkins-agent: v1 # Executed command when side container gets started command: args: "${computer.jnlpmac} ${computer.name}" # Side container name sideContainerName: "jnlp" # Doesn't allocate pseudo TTY by default TTYEnabled: false # Max number of spawned agent containerCap: 10 # Pod name podName: "default" # Allows the Pod to remain active for reuse until the configured number of # minutes has passed since the last step was executed on it. idleMinutes: 0 # Raw yaml template for the Pod. For example this allows usage of toleration for agent pods. # https://github.com/jenkinsci/kubernetes-plugin#using-yaml-to-define-pod-templates # https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ yamlTemplate: "" # yamlTemplate: |- # apiVersion: v1 # kind: Pod # spec: # tolerations: # - key: "key" # operator: "Equal" # value: "value" # Defines how the raw yaml field gets merged with yaml definitions from inherited pod templates: merge or override yamlMergeStrategy: "override" # Timeout in seconds for an agent to be online connectTimeout: 100 # Annotations to apply to the pod. annotations: {} # Below is the implementation of custom pod templates for the default configured kubernetes cloud. # Add a key under podTemplates for each pod template. Each key (prior to | character) is just a label, and can be any value. # Keys are only used to give the pod template a meaningful name. The only restriction is they may only contain RFC 1123 \ DNS label # characters: lowercase letters, numbers, and hyphens. Each pod template can contain multiple containers. # For this pod templates configuration to be loaded the following values must be set: # controller.JCasC.defaultConfig: true # Best reference is https://<jenkins_url>/configuration-as-code/reference#Cloud-kubernetes. The example below creates a python pod template. podTemplates: {} # python: | # - name: python # label: jenkins-python # serviceAccount: jenkins # containers: # - name: python # image: python:3 # command: "/bin/sh -c" # args: "cat" # ttyEnabled: true # privileged: true # resourceRequestCpu: "400m" # resourceRequestMemory: "512Mi" # resourceLimitCpu: "1" # resourceLimitMemory: "1024Mi" # Here you can add additional agents # They inherit all values from `agent` so you only need to specify values which differ additionalAgents: {} # maven: # podName: maven # customJenkinsLabels: maven # # An example of overriding the jnlp container # # sideContainerName: jnlp # image: jenkins/jnlp-agent-maven # tag: latest # python: # podName: python # customJenkinsLabels: python # sideContainerName: python # image: python # tag: "3" # command: "/bin/sh -c" # args: "cat" # TTYEnabled: true persistence: enabled: true ## A manually managed Persistent Volume and Claim ## Requires persistence.enabled: true ## If defined, PVC must be created manually before volume will be bound existingClaim: ## jenkins data Persistent Volume Storage Class ## If defined, storageClassName: <storageClass> ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## storageClass: jenkins-pv annotations: {} accessMode: "ReadWriteOnce" size: "8Gi" volumes: # - name: nothing # emptyDir: {} mounts: # - mountPath: /var/nothing # name: nothing # readOnly: true networkPolicy: # Enable creation of NetworkPolicy resources. enabled: false # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1' # For Kubernetes v1.7, use 'networking.k8s.io/v1' apiVersion: networking.k8s.io/v1 # You can allow agents to connect from both within the cluster (from within specific/all namespaces) AND/OR from a given external IP range internalAgents: allowed: true podLabels: {} namespaceLabels: {} # project: myproject externalAgents: {} # ipCIDR: 172.17.0.0/16 # except: # - 172.17.1.0/24 ## Install Default RBAC roles and bindings rbac: create: true readSecrets: false serviceAccount: create: false # The name of the service account is autogenerated by default name: jenkins annotations: {} imagePullSecretName: serviceAccountAgent: # Specifies whether a ServiceAccount should be created create: false # The name of the ServiceAccount to use. # If not set and create is true, a name is generated using the fullname template name: annotations: {} imagePullSecretName: ## Backup cronjob configuration ## Ref: https://github.com/maorfr/kube-tasks backup: # Backup must use RBAC # So by enabling backup you are enabling RBAC specific for backup enabled: false # Used for label app.kubernetes.io/component componentName: "backup" # Schedule to run jobs. Must be in cron time format # Ref: https://crontab.guru/ schedule: "0 2 * * *" labels: {} annotations: {} # Example for authorization to AWS S3 using kube2iam or IRSA # Can also be done using environment variables # iam.amazonaws.com/role: "jenkins" # "eks.amazonaws.com/role-arn": "arn:aws:iam::123456789012:role/jenkins-backup" # Set this to terminate the job that is running/failing continously and set the job status to "Failed" activeDeadlineSeconds: "" image: repository: "maorfr/kube-tasks" tag: "0.2.0" # Additional arguments for kube-tasks # Ref: https://github.com/maorfr/kube-tasks#simple-backup extraArgs: [] # Add existingSecret for AWS credentials existingSecret: {} ## Example for using an existing secret # jenkinsaws: ## Use this key for AWS access key ID # awsaccesskey: jenkins_aws_access_key ## Use this key for AWS secret access key # awssecretkey: jenkins_aws_secret_key # Add additional environment variables # jenkinsgcp: ## Use this key for GCP credentials # gcpcredentials: credentials.json env: [] # Example environment variable required for AWS credentials chain # - name: "AWS_REGION" # value: "us-east-1" resources: requests: memory: 1Gi cpu: 1 limits: memory: 1Gi cpu: 1 # Destination to store the backup artifacts # Supported cloud storage services: AWS S3, Minio S3, Azure Blob Storage, Google Cloud Storage # Additional support can added. Visit this repository for details # Ref: https://github.com/maorfr/skbn destination: "s3://jenkins-data/backup" # By enabling only the jenkins_home/jobs folder gets backed up, not the whole jenkins instance onlyJobs: false # Enable backup pod security context (must be `true` if runAsUser or fsGroup are set) usePodSecurityContext: true # When setting runAsUser to a different value than 0 also set fsGroup to the same value: runAsUser: 1000 fsGroup: 1000 securityContextCapabilities: {} # drop: # - NET_RAW checkDeprecation: true
Note that we modified the file as shown below:
servicePort: 8080 targetPort: 8080 # For minikube, set this to NodePort, elsewhere use LoadBalancer # Use ClusterIP if your setup includes ingress controller # For minikube, set this to NodePort, elsewhere use LoadBalancer # Use ClusterIP if your setup includes ingress controller serviceType: NodePort ... persistence: enabled: true ## A manually managed Persistent Volume and Claim ## Requires persistence.enabled: true ## If defined, PVC must be created manually before volume will be bound existingClaim: ## jenkins data Persistent Volume Storage Class ## If defined, storageClassName: <storageClass> ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## storageClass: jenkins-pv annotations: {} accessMode: "ReadWriteOnce" size: "8Gi" volumes: # - name: nothing # emptyDir: {} mounts: # - mountPath: /var/nothing # name: nothing # readOnly: true serviceAccount: create: false # The name of the service account is autogenerated by default name: jenkins annotations: {} imagePullSecretName:
Now we can install Jenkins by running the helm install
command and passing it the following arguments:
- The name of the release jenkins
- The -f flag with the YAML file with overrides jenkins-values.yaml
- The name of the chart jenkinsci/jenkins
- The -n flag with the name of your namespace jenkins
$ helm install jenkins -n jenkins -f jenkins-values.yaml jenkinsci/jenkins NAME: jenkins LAST DEPLOYED: Thu May 20 13:07:43 2021 NAMESPACE: jenkins STATUS: deployed REVISION: 1 NOTES: 1. Get your 'admin' user password by running: kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo 2. Get the Jenkins URL to visit by running these commands in the same shell: echo http://127.0.0.1:8080 kubectl --namespace jenkins port-forward svc/jenkins 8080:8080 3. Login with the password from step 1 and the username: admin 4. Configure security realm and authorization strategy 5. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos
To get 'admin' user password by running:
$ jsonpath="{.data.jenkins-admin-password}" $ secret=$(kubectl get secret -n jenkins jenkins -o jsonpath=$jsonpath) $ echo $(echo $secret | base64 --decode) clthUgXTLYmxRMd6oC47fP
Get the Jenkins URL to visit by running these commands in the same shell:
$ jsonpath="{.spec.ports[0].nodePort}" $ NODE_PORT=$(kubectl get -n jenkins -o jsonpath=$jsonpath services jenkins) $ jsonpath="{.items[0].status.addresses[0].address}" $ NODE_IP=$(kubectl get nodes -n jenkins -o jsonpath=$jsonpath) $ echo http://$NODE_IP:$NODE_PORT/login http://172.17.0.3:32767/login
Login with the password (clthUgXTLYmxRMd6oC47fP) and the username: admin.
Ref:
- https://www.jenkins.io/doc/book/installing/kubernetes/
- https://github.com/jenkinsci/helm-charts/tree/main/charts/jenkins
- Docker install on Amazon Linux AMI
- Docker install on EC2 Ubuntu 14.04
- Docker container vs Virtual Machine
- Docker install on Ubuntu 14.04
- Docker Hello World Application
- Nginx image - share/copy files, Dockerfile
- Working with Docker images : brief introduction
- Docker image and container via docker commands (search, pull, run, ps, restart, attach, and rm)
- More on docker run command (docker run -it, docker run --rm, etc.)
- Docker Networks - Bridge Driver Network
- Docker Persistent Storage
- File sharing between host and container (docker run -d -p -v)
- Linking containers and volume for datastore
- Dockerfile - Build Docker images automatically I - FROM, MAINTAINER, and build context
- Dockerfile - Build Docker images automatically II - revisiting FROM, MAINTAINER, build context, and caching
- Dockerfile - Build Docker images automatically III - RUN
- Dockerfile - Build Docker images automatically IV - CMD
- Dockerfile - Build Docker images automatically V - WORKDIR, ENV, ADD, and ENTRYPOINT
- Docker - Apache Tomcat
- Docker - NodeJS
- Docker - NodeJS with hostname
- Docker Compose - NodeJS with MongoDB
- Docker - Prometheus and Grafana with Docker-compose
- Docker - StatsD/Graphite/Grafana
- Docker - Deploying a Java EE JBoss/WildFly Application on AWS Elastic Beanstalk Using Docker Containers
- Docker : NodeJS with GCP Kubernetes Engine
- Docker : Jenkins Multibranch Pipeline with Jenkinsfile and Github
- Docker : Jenkins Master and Slave
- Docker - ELK : ElasticSearch, Logstash, and Kibana
- Docker - ELK 7.6 : Elasticsearch on Centos 7
- Docker - ELK 7.6 : Filebeat on Centos 7
- Docker - ELK 7.6 : Logstash on Centos 7
- Docker - ELK 7.6 : Kibana on Centos 7
- Docker - ELK 7.6 : Elastic Stack with Docker Compose
- Docker - Deploy Elastic Cloud on Kubernetes (ECK) via Elasticsearch operator on minikube
- Docker - Deploy Elastic Stack via Helm on minikube
- Docker Compose - A gentle introduction with WordPress
- Docker Compose - MySQL
- MEAN Stack app on Docker containers : micro services
- MEAN Stack app on Docker containers : micro services via docker-compose
- Docker Compose - Hashicorp's Vault and Consul Part A (install vault, unsealing, static secrets, and policies)
- Docker Compose - Hashicorp's Vault and Consul Part B (EaaS, dynamic secrets, leases, and revocation)
- Docker Compose - Hashicorp's Vault and Consul Part C (Consul)
- Docker Compose with two containers - Flask REST API service container and an Apache server container
- Docker compose : Nginx reverse proxy with multiple containers
- Docker & Kubernetes : Envoy - Getting started
- Docker & Kubernetes : Envoy - Front Proxy
- Docker & Kubernetes : Ambassador - Envoy API Gateway on Kubernetes
- Docker Packer
- Docker Cheat Sheet
- Docker Q & A #1
- Kubernetes Q & A - Part I
- Kubernetes Q & A - Part II
- Docker - Run a React app in a docker
- Docker - Run a React app in a docker II (snapshot app with nginx)
- Docker - NodeJS and MySQL app with React in a docker
- Docker - Step by Step NodeJS and MySQL app with React - I
- Installing LAMP via puppet on Docker
- Docker install via Puppet
- Nginx Docker install via Ansible
- Apache Hadoop CDH 5.8 Install with QuickStarts Docker
- Docker - Deploying Flask app to ECS
- Docker Compose - Deploying WordPress to AWS
- Docker - WordPress Deploy to ECS with Docker-Compose (ECS-CLI EC2 type)
- Docker - WordPress Deploy to ECS with Docker-Compose (ECS-CLI Fargate type)
- Docker - ECS Fargate
- Docker - AWS ECS service discovery with Flask and Redis
- Docker & Kubernetes : minikube
- Docker & Kubernetes 2 : minikube Django with Postgres - persistent volume
- Docker & Kubernetes 3 : minikube Django with Redis and Celery
- Docker & Kubernetes 4 : Django with RDS via AWS Kops
- Docker & Kubernetes : Kops on AWS
- Docker & Kubernetes : Ingress controller on AWS with Kops
- Docker & Kubernetes : HashiCorp's Vault and Consul on minikube
- Docker & Kubernetes : HashiCorp's Vault and Consul - Auto-unseal using Transit Secrets Engine
- Docker & Kubernetes : Persistent Volumes & Persistent Volumes Claims - hostPath and annotations
- Docker & Kubernetes : Persistent Volumes - Dynamic volume provisioning
- Docker & Kubernetes : DaemonSet
- Docker & Kubernetes : Secrets
- Docker & Kubernetes : kubectl command
- Docker & Kubernetes : Assign a Kubernetes Pod to a particular node in a Kubernetes cluster
- Docker & Kubernetes : Configure a Pod to Use a ConfigMap
- AWS : EKS (Elastic Container Service for Kubernetes)
- Docker & Kubernetes : Run a React app in a minikube
- Docker & Kubernetes : Minikube install on AWS EC2
- Docker & Kubernetes : Cassandra with a StatefulSet
- Docker & Kubernetes : Terraform and AWS EKS
- Docker & Kubernetes : Pods and Service definitions
- Docker & Kubernetes : Service IP and the Service Type
- Docker & Kubernetes : Kubernetes DNS with Pods and Services
- Docker & Kubernetes : Headless service and discovering pods
- Docker & Kubernetes : Scaling and Updating application
- Docker & Kubernetes : Horizontal pod autoscaler on minikubes
- Docker & Kubernetes : From a monolithic app to micro services on GCP Kubernetes
- Docker & Kubernetes : Rolling updates
- Docker & Kubernetes : Deployments to GKE (Rolling update, Canary and Blue-green deployments)
- Docker & Kubernetes : Slack Chat Bot with NodeJS on GCP Kubernetes
- Docker & Kubernetes : Continuous Delivery with Jenkins Multibranch Pipeline for Dev, Canary, and Production Environments on GCP Kubernetes
- Docker & Kubernetes : NodePort vs LoadBalancer vs Ingress
- Docker & Kubernetes : MongoDB / MongoExpress on Minikube
- Docker & Kubernetes : Load Testing with Locust on GCP Kubernetes
- Docker & Kubernetes : MongoDB with StatefulSets on GCP Kubernetes Engine
- Docker & Kubernetes : Nginx Ingress Controller on Minikube
- Docker & Kubernetes : Setting up Ingress with NGINX Controller on Minikube (Mac)
- Docker & Kubernetes : Nginx Ingress Controller for Dashboard service on Minikube
- Docker & Kubernetes : Nginx Ingress Controller on GCP Kubernetes
- Docker & Kubernetes : Kubernetes Ingress with AWS ALB Ingress Controller in EKS
- Docker & Kubernetes : Setting up a private cluster on GCP Kubernetes
- Docker & Kubernetes : Kubernetes Namespaces (default, kube-public, kube-system) and switching namespaces (kubens)
- Docker & Kubernetes : StatefulSets on minikube
- Docker & Kubernetes : RBAC
- Docker & Kubernetes Service Account, RBAC, and IAM
- Docker & Kubernetes - Kubernetes Service Account, RBAC, IAM with EKS ALB, Part 1
- Docker & Kubernetes : Helm Chart
- Docker & Kubernetes : My first Helm deploy
- Docker & Kubernetes : Readiness and Liveness Probes
- Docker & Kubernetes : Helm chart repository with Github pages
- Docker & Kubernetes : Deploying WordPress and MariaDB with Ingress to Minikube using Helm Chart
- Docker & Kubernetes : Deploying WordPress and MariaDB to AWS using Helm 2 Chart
- Docker & Kubernetes : Deploying WordPress and MariaDB to AWS using Helm 3 Chart
- Docker & Kubernetes : Helm Chart for Node/Express and MySQL with Ingress
- Docker & Kubernetes : Deploy Prometheus and Grafana using Helm and Prometheus Operator - Monitoring Kubernetes node resources out of the box
- Docker & Kubernetes : Deploy Prometheus and Grafana using kube-prometheus-stack Helm Chart
- Docker & Kubernetes : Istio (service mesh) sidecar proxy on GCP Kubernetes
- Docker & Kubernetes : Istio on EKS
- Docker & Kubernetes : Istio on Minikube with AWS EC2 for Bookinfo Application
- Docker & Kubernetes : Deploying .NET Core app to Kubernetes Engine and configuring its traffic managed by Istio (Part I)
- Docker & Kubernetes : Deploying .NET Core app to Kubernetes Engine and configuring its traffic managed by Istio (Part II - Prometheus, Grafana, pin a service, split traffic, and inject faults)
- Docker & Kubernetes : Helm Package Manager with MySQL on GCP Kubernetes Engine
- Docker & Kubernetes : Deploying Memcached on Kubernetes Engine
- Docker & Kubernetes : EKS Control Plane (API server) Metrics with Prometheus
- Docker & Kubernetes : Spinnaker on EKS with Halyard
- Docker & Kubernetes : Continuous Delivery Pipelines with Spinnaker and Kubernetes Engine
- Docker & Kubernetes : Multi-node Local Kubernetes cluster : Kubeadm-dind (docker-in-docker)
- Docker & Kubernetes : Multi-node Local Kubernetes cluster : Kubeadm-kind (k8s-in-docker)
- Docker & Kubernetes : nodeSelector, nodeAffinity, taints/tolerations, pod affinity and anti-affinity - Assigning Pods to Nodes
- Docker & Kubernetes : Jenkins-X on EKS
- Docker & Kubernetes : ArgoCD App of Apps with Heml on Kubernetes
- Docker & Kubernetes : ArgoCD on Kubernetes cluster
- Docker & Kubernetes : GitOps with ArgoCD for Continuous Delivery to Kubernetes clusters (minikube) - guestbook
Docker & K8s
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization