{"id":1312,"date":"2023-02-26T11:04:39","date_gmt":"2023-02-26T07:34:39","guid":{"rendered":"https:\/\/mshaeri.com\/blog\/?p=1312"},"modified":"2023-03-21T14:24:42","modified_gmt":"2023-03-21T10:54:42","slug":"local-ci-di-pipeline-with-gitblit-jenkins-kubernetes-microservice-architecture-react-spring-boot-flask-part-1","status":"publish","type":"post","link":"https:\/\/mshaeri.com\/blog\/local-ci-di-pipeline-with-gitblit-jenkins-kubernetes-microservice-architecture-react-spring-boot-flask-part-1\/","title":{"rendered":"Local CI\/CD Pipeline With Gitblit + Jenkins + Kubernetes For Microservice Architecture React + Spring Boot + Flask Part 1"},"content":{"rendered":"\n<p>A local CI\/CD pipeline offers several benefits for software development teams. It can help improve the security of your code by keeping sensitive information on companies local machine. This reduces the risk of security breaches and other issues that can arise when code is stored on a remote server. Moreover, setting up a local CI\/CD pipeline can be more cost-effective than using a remote pipeline, especially for small projects. Using open-source tools and running the pipeline on a local server or virtual machine, you can save money on hosting and infrastructure costs.<\/p>\n\n\n\n<pre class=\"wp-block-verse has-vivid-red-color has-very-light-gray-to-cyan-bluish-gray-gradient-background has-text-color has-background\">By the recent change in Docker hub account policy, if accounts do not upgrade to a paid plan before April 14, 2023, their organization's images may be deleted after 30 days. <\/pre>\n\n\n\n<p>In this article we go through following steps to setup a local CI\/CD pipeline for your small to medium sized team :<\/p>\n\n\n\n<ol><li><a href=\"#Setup-Kubernetes\" data-type=\"internal\" data-id=\"#Setup-Kubernetes\">Setup Kubernetes<\/a><\/li><li><a href=\"#Setup-local-docker-registry-on-Kubernetes\" data-type=\"internal\" data-id=\"#Setup-local-docker-registry-on-Kubernetes\">Setup local docker registry on Kubernetes<\/a><\/li><li><a href=\"#Setup-GitBlit-on-Kubernetes\" data-type=\"internal\" data-id=\"#Setup-GitBlit-on-Kubernetes\">Setup Gitblit on Kubernetes<\/a><\/li><li><a href=\"#Setup-Jenkins-In-Local-Kubernetes\" data-type=\"internal\" data-id=\"#Setup-Jenkins-In-Local-Kubernetes\">Setup Jenkins on Kubernetes<\/a><\/li><li>Configure Spring boot Backend pipeline in Jenkins<\/li><li>Configure Flask Backend pipeline in Jenkins<\/li><li>Configure React Frontend pipeline in Jenkins<\/li><li>Code in Frontend and Backend projects<\/li><li>Run Frontend and Backend pipelines<\/li><\/ol>\n\n\n\n<p>We are going to setup all the CI\/CD nodes including Source code repository (Gitblit), Automation server (Jenkins) and Docker image repository on top of Kubernetes, this figure shows the final architecture of pipeline :<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1-1024x674.jpg\" alt=\"cidi jenkins gitblit kubernetes spring boot react docker registry\" class=\"wp-image-1369\" width=\"768\" height=\"506\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1-1024x674.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1-300x197.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1-768x505.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/cidi_jenkins_gitblit_kubernetes_spring_boot_react_docker_registry_inline-1.jpg 1385w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/a><figcaption>Local CI\/DI Pipeline For Microservices Architecture<\/figcaption><\/figure><\/div>\n\n\n\n<h2 class=\"is-style-default has-large-font-size wp-block-heading\" id=\"Setup-Kubernetes\"> Setup Kubernetes  <\/h2>\n\n\n\n<p>Kubernetes works on top of Docker, it undertakes tasks including creating container , deployment and configuration. There exist several methods and tools to setup a K8s such as <a href=\"https:\/\/www.docker.com\/products\/docker-desktop\/\" data-type=\"URL\" data-id=\"https:\/\/www.docker.com\/products\/docker-desktop\/\">Docker Desktop<\/a>, <a href=\"https:\/\/minikube.sigs.k8s.io\" data-type=\"URL\" data-id=\"https:\/\/minikube.sigs.k8s.io\">minikube<\/a>, <a href=\"https:\/\/kind.sigs.k8s.io\/\" data-type=\"URL\" data-id=\"https:\/\/kind.sigs.k8s.io\/\">kind<\/a>, <a href=\"https:\/\/k3s.io\/\" data-type=\"URL\" data-id=\"https:\/\/k3s.io\/\">k3s<\/a>, <a href=\"https:\/\/kubernetes.io\/docs\/setup\/production-environment\/tools\/kubeadm\/create-cluster-kubeadm\/\" data-type=\"URL\" data-id=\"https:\/\/kubernetes.io\/docs\/setup\/production-environment\/tools\/kubeadm\/create-cluster-kubeadm\/\">kubeadm <\/a>and etc. In this article for the sake of simplicity I setup K8s using Docker Desktop.<\/p>\n\n\n\n<p>If you haven&#8217;t already installed the Docker Desktop, download and install latest version of it which is compatible to your OS from the official Docker website. Docker is originally made for Linux, but thanks to Hyper-v and WSL 2 features you can also run it in windows. So, you need to enable\/install one of them in before Docker installation.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/hyper_v_install.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/hyper_v_install.jpg\" alt=\"\" class=\"wp-image-1318\" width=\"709\" height=\"409\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/hyper_v_install.jpg 945w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/hyper_v_install-300x173.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/hyper_v_install-768x443.jpg 768w\" sizes=\"(max-width: 709px) 100vw, 709px\" \/><\/a><figcaption>Check the box for Hyper-V feature in the list<\/figcaption><\/figure><\/div>\n\n\n\n<p>In the first installation step where the installer ask you to use WSL 2 instead of Hyper-V, you can check the box if you have WSL 2 installed and click <strong>OK<\/strong>. WSL 2 works on top of Hyper-V, but it doesn&#8217;t require Hyper-V to be enabled. <\/p>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_setup_wsl2.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_setup_wsl2.jpg\" alt=\"\" class=\"wp-image-1319\" width=\"414\" height=\"110\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_setup_wsl2.jpg 552w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_setup_wsl2-300x79.jpg 300w\" sizes=\"(max-width: 414px) 100vw, 414px\" \/><\/a><figcaption>Check the box if you want to use WSL 2, but make sure you have already installed it.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Docker Desktop comes with a graphical user interface that allows configuring and installing Kubernetes. Open the Docker Desktop application and go to the settings menu, under the <em>Kubernetes<\/em> tab, check the <strong>Enable Kubernetes<\/strong> checkbox and click <strong>Apply &amp; Restart<\/strong>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1-1024x548.jpg\" alt=\"\" class=\"wp-image-1321\" width=\"768\" height=\"411\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1-1024x548.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1-300x160.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1-768x411.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/docker_desktop_k8_enable-1.jpg 1060w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/a><figcaption>To enable Kubernetes, check the <strong>Enable Kubernetes<\/strong> checkbox and click <strong>Apply &amp; Restart<\/strong> <\/figcaption><\/figure><\/div>\n\n\n\n<p>After Docker Desktop restarts, use the following command to verify that the Kubernetes cluster is running :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl cluster-info<\/code><\/pre>\n\n\n\n<p>If the installation is completed successfully, the above command should display information about your Kubernetes cluster. <\/p>\n\n\n\n<p>We need to install Kubernetes dashboard to have graphical interface in order to work with K8s and obtain an overview of the applications that are active on the cluster. It requires manual setup :<\/p>\n\n\n\n<p>First execute below command :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl apply -f https:\/\/raw.githubusercontent.com\/kubernetes\/dashboard\/v2.6.0\/aio\/deploy\/recommended.yaml<\/code><\/pre>\n\n\n\n<p>Next,  create a proxy server between the client and the Kubernetes API server. This allows the user to interact with the Kubernetes API server : <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl proxy<\/code><\/pre>\n\n\n\n<p>The Dashboard can be accessed at  <strong><code>http:\/\/localhost:8001\/api\/v1\/namespaces\/kubernetes-dashboard\/services\/https:kubernetes-dashboard:\/proxy\/<\/code><\/strong>. However, the login page needs you to provide token which you can create with following command :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl -n kubernetes-dashboard create token admin-user<\/code><\/pre>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token-1024x564.jpg\" alt=\"Kubernetes dashboard login page\" class=\"wp-image-1329\" width=\"768\" height=\"423\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token-1024x564.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token-300x165.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token-768x423.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/k8_dashboard_login_token.jpg 1112w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/a><figcaption>Kubernetes dashboard login page<\/figcaption><\/figure><\/div>\n\n\n\n<h2 class=\"has-large-font-size wp-block-heading\" id=\"Setup-local-docker-registry-on-Kubernetes\"> Setup Local Docker Registry On Kubernetes<\/h2>\n\n\n\n<p>Besides many advantages of using the <a href=\"http:\/\/hub.docker.com\" data-type=\"URL\" data-id=\"hub.docker.com\">dockerhub <\/a>, it comes with its own set of limitations. For example, anonymous users and members using free plan are limited to 100 and 200 container image pull requests per 6 hours. In such cases, private local repository would be a better choice. It enables us to have a image repository that in addition to unlimited pull and push requests, it gives us control over storage options and many other options per our needs. Let&#8217;s get to the point :<\/p>\n\n\n\n<p>First of all let&#8217;s create a namespace for our CI\/CD pipeline in Kubernetes with the following command :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl create namespace cidi-pipeline<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, with <strong>openssl <\/strong>and <strong>htpasswd <\/strong>tools we build  TLS certificate and implement user authentication for our repository.<\/p>\n\n\n\n<p>TLS certificate :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">mkdir certs\nopenssl req -x509 -newkey rsa:4096 -days 365 -nodes -sha256 -keyout certs\/tls.key -out certs\/tls.crt -subj \"\/CN=docker-registry\" -addext \"subjectAltName = DNS:docker-registry\"<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Adding authentication :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">mkdir auth\ndocker run --rm --entrypoint htpasswd registry:2.6.2 -Bbn admin 1234@5 &gt; auth\/htpasswd<\/code><\/pre>\n\n\n\n<p>Let&#8217;s look at breakdown of the each parts of the above command. In the above command with &#8220;<code><strong>--rm\"<\/strong><\/code> flag we tell Docker to automatically remove the container when it&#8217;s done running. With &#8220;<code><strong>--entrypoint htpasswd\"<\/strong><\/code> we set the entrypoint for the container to be <code><strong>htpasswd<\/strong><\/code>, which is a tool used to manage user authentication files. &#8220;<code><strong>registry:2.6.2<\/strong><\/code>&#8221;  is the name of the Docker image that our container will be based on.<strong> &#8220;<code>-Bbn ... ...<\/code>&#8220;<\/strong> is  the <strong><code>htpasswd<\/code> <\/strong>option to create an encrypted password for the user <code><strong>admin<\/strong><\/code> with the password <code><strong>1234@5<\/strong><\/code>. <strong>&#8220;-Bbn<\/strong>&#8221; consists of three flags. The <code><strong>B<\/strong><\/code> flag tells the <strong>htpasswd <\/strong>to use <strong>bcrypt<\/strong> encryption algorithm for password and <strong>b<\/strong> flag get the password from the command line instead of prompting for it, and <strong>n <\/strong>flag shows the results on standard output instead of updating a file.<strong> &#8220;<code>&gt; auth\/htpasswd<\/code>&#8220;<\/strong> redirects the output of the <strong><code>htpasswd<\/code> <\/strong>command to a file named <strong><code>htpasswd<\/code> <\/strong>in the <strong><code>auth<\/code> <\/strong>directory which we created at first line. This file will contain the username and encrypted password that were just created.<\/p>\n\n\n\n<p>Next, we generate two types of secrets &#8211; one TLS type secret and another Generic type secret &#8211; which we will use to mount our certificate and password, respectively.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl create secret tls certs-secret --cert=certs\/tls.crt --key=certs\/tls.key -n cicd-pipeline\n\nkubectl create secret generic auth-secret --from-file=auth\/htpasswd -n cicd-pipeline<\/code><\/pre>\n\n\n\n<p>Values of <code>htpasswd<\/code> file will be saved in the Generic Secret we just created in Kubernetes.<\/p>\n\n\n\n<p>The <strong>registry-volume.yaml<\/strong> config file creates the Persistent Volume for our local Docker registery:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion:&nbsp;v1\nkind:&nbsp;PersistentVolume\nmetadata:\n&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;docker-repo-pv\nspec:\n&nbsp;&nbsp;capacity:\n&nbsp;&nbsp;&nbsp;&nbsp;storage:&nbsp;1Gi\n&nbsp;&nbsp;accessModes:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;ReadWriteOnce\n&nbsp;&nbsp;hostPath:\n&nbsp;&nbsp;&nbsp;&nbsp;path:&nbsp;\/tmp\/repository\n---\napiVersion:&nbsp;v1\nkind:&nbsp;PersistentVolumeClaim\nmetadata:\n&nbsp;&nbsp;name:&nbsp;docker-repo-pvc\nspec:\n&nbsp;&nbsp;accessModes:\n&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;ReadWriteOnce\n&nbsp;&nbsp;resources:\n&nbsp;&nbsp;&nbsp;&nbsp;requests:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;storage:&nbsp;1Gi<\/code><\/pre>\n\n\n\n<p><strong><code>accessModes<\/code> <\/strong>specify the ways in which the persistent volume can be accessed by the associated pod(s). Here, <strong><code>ReadWriteOnce<\/code> <\/strong>indicates that the our persistent volume can be mounted as read-write by a single node in the cluster at a time.<\/p>\n\n\n\n<p>You can apply it to K8s cluster via Kubernetes dashboard or in command line. Here I&#8217;ll do it in command line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl create -f registry-volume.yaml -n cicd-pipeline<\/code><\/pre>\n\n\n\n<p>After creating both <strong>Persistent Volume<\/strong> and a <strong>Persistent Volume Claim<\/strong>, we can create docker registry pod utilizing this volume.<\/p>\n\n\n\n<p>Apply registry-pod-service.yaml config file to your Kubernetes cluster to create the pod and service in cicd-pipeline namespace  :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion:&nbsp;v1\nkind:&nbsp;Pod\nmetadata:\n&nbsp;&nbsp;name:&nbsp;docker-registry-pod\n&nbsp;&nbsp;labels:\n&nbsp;&nbsp;&nbsp;&nbsp;app:&nbsp;registry\nspec:\n&nbsp;&nbsp;containers:\n&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;registry\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image:&nbsp;registry:2.6.2\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;volumeMounts:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;repo-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mountPath:&nbsp;\"\/var\/lib\/registry\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;certs-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mountPath:&nbsp;\"certs\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readOnly:&nbsp;true\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;auth-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mountPath:&nbsp;\"auth\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readOnly:&nbsp;true\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;REGISTRY_AUTH\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value:&nbsp;\"htpasswd\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;REGISTRY_AUTH_HTPASSWD_REALM\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value:&nbsp;\"Registry&nbsp;Realm\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;REGISTRY_AUTH_HTPASSWD_PATH\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value:&nbsp;\"auth\/htpasswd\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;REGISTRY_HTTP_TLS_CERTIFICATE\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value:&nbsp;\"certs\/tls.crt\"\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;REGISTRY_HTTP_TLS_KEY\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value:&nbsp;\"certs\/tls.key\"\n&nbsp;&nbsp;volumes:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;repo-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;persistentVolumeClaim:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;claimName:&nbsp;docker-repo-pvc\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;certs-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secret:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secretName:&nbsp;certs-secret\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name:&nbsp;auth-vol\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secret:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secretName:&nbsp;auth-secret\n\n---\n\nkind:&nbsp;Service&nbsp;\napiVersion:&nbsp;v1&nbsp;\nmetadata:\n&nbsp;&nbsp;name:&nbsp;docker-registery\nspec:\n&nbsp;&nbsp;#&nbsp;Expose&nbsp;the&nbsp;service&nbsp;on&nbsp;a&nbsp;static&nbsp;port&nbsp;on&nbsp;each&nbsp;node\n&nbsp;&nbsp;#&nbsp;so&nbsp;that&nbsp;we&nbsp;can&nbsp;access&nbsp;the&nbsp;service&nbsp;from&nbsp;outside&nbsp;the&nbsp;cluster&nbsp;\n&nbsp;&nbsp;type:&nbsp;NodePort\n&nbsp;&nbsp;selector:\n&nbsp;&nbsp;&nbsp;&nbsp;app:&nbsp;registry&nbsp;\n&nbsp;&nbsp;ports:\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Three&nbsp;types&nbsp;of&nbsp;ports&nbsp;for&nbsp;a&nbsp;service\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;nodePort&nbsp;-&nbsp;a&nbsp;static&nbsp;port&nbsp;assigned&nbsp;on&nbsp;each&nbsp;the&nbsp;node\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;port&nbsp;-&nbsp;port&nbsp;exposed&nbsp;internally&nbsp;in&nbsp;the&nbsp;cluster\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;targetPort&nbsp;-&nbsp;the&nbsp;container&nbsp;port&nbsp;to&nbsp;send&nbsp;requests&nbsp;to\n&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;nodePort:&nbsp;30001\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port:&nbsp;5000&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;targetPort:&nbsp;5000<\/code><\/pre>\n\n\n\n<p>Use the option <strong>&#8220;-n cidi-pipeline&#8221;<\/strong> when applying the YAML config file to ensure they are created in cidi-pipeline :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl create -f docker-registry-pod.yaml -n cicd-pipeline<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>To verify that everything is working properly, use the following command to log in to the local registry from outside the cluster.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">docker login 127.0.0.1:30001 -u admin -p 123456<\/code><\/pre>\n\n\n\n<p>If everything is OK, you should get <strong>&#8220;Login Succeeded&#8221; <\/strong>message.<\/p>\n\n\n\n<h2 class=\"has-large-font-size wp-block-heading\" id=\"Setup-GitBlit-on-Kubernetes\">Setup GitBlit on Kubernetes<\/h2>\n\n\n\n<p>Gitblit is an open-source Git server that is designed to be easy to set up, configure and use. It provides a web GUI for managing repositories, which allows users to perform various Git operations, such as cloning, branching, merging, and pushing. It&#8217;s lightweight and easy to deploy, which makes it popular choice for small teams. Setting up GitBlit in Kubernetes involves a three steps:<\/p>\n\n\n\n<ol><li>Create PV and PVC for repositories storage<\/li><li>Create a Kubernetes Pod for GitBlit and attach volume <\/li><li>Create a service for the Pod<\/li><\/ol>\n\n\n\n<p>Create PV and PVC using below config :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion: v1\n kind: PersistentVolume\n metadata:\n     name: gitblit-repo-pv\n spec:\n   capacity:\n     storage: 1Gi\n   accessModes:\n      - ReadWriteOnce\n   hostPath:\n     path: \/tmp\/gitblitrepository\n apiVersion: v1\n kind: PersistentVolumeClaim\n metadata:\n   name: gitblit-repo-pvc\n spec:\n   accessModes:\n     - ReadWriteOnce\n   resources:\n     requests:\n       storage: 2Gi<\/code><\/pre>\n\n\n\n<p>Gitblit Kubernetes Pod:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion: v1\n kind: Pod\n metadata:\n   name: gitblit-pod\n   labels:\n     app: gitblit\n spec:\n   containers:\n   - name: gitblit\n     image: gitblit\/gitblit:latest \n     ports:\n     - containerPort: 8443\n     - containerPort: 8080\n     - containerPort: 9418\n     - containerPort: 29418\n     volumeMounts:\n     - name: gitblit-data\n       mountPath: \/opt\/gitblit-data\n   volumes:\n   - name: gitblit-data\n     persistentVolumeClaim:\n       claimName: gitblit-repo-pvc <\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Gitblit Kubernetes service :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion: v1\n kind: Service\n metadata:\n   name: gitblit-service\n spec:\n   selector:\n     app: gitblit\n   ports:\n     - name: https\n       protocol: TCP\n       port: 8443\n       targetPort: 8443\n       nodePort: 30443\n     - name: http\n       protocol: TCP\n       port: 8080\n       targetPort: 8080\n       nodePort: 30080\n     - name: git\n       protocol: TCP\n       port: 9418\n       targetPort: 9418\n       nodePort: 30418\n     - name: ssh\n       protocol: TCP\n       port: 29418\n       targetPort: 29418\n       nodePort: 30419\n   type: NodePort<\/code><\/pre>\n\n\n\n<p>You can either apply all of above configs using K8s dashboard or apply them in command line as yml file like this :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">\nkubectl create -f gitblit-volume.yaml -n cicd-pipeline\nkubectl create -f gitblit-pod.yaml -n cicd-pipeline\nkubectl create -f gitblit-service.yaml -n cicd-pipeline\n<\/code><\/pre>\n\n\n\n<p>If everything is OK, after creating the service, by opening <a href=\"https:\/\/127.0.0.1:30443\/\" data-type=\"URL\" data-id=\"https:\/\/127.0.0.1:30443\/\">https:\/\/your-node-ip:30443 <\/a>you should see Gitblit home page, however, due to invalid cert, you&#8217;ll get insecure connection warning  :<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"553\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service-1024x553.jpg\" alt=\"Gitblit home page running as Kubernetes service\" class=\"wp-image-1333\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service-1024x553.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service-300x162.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service-768x415.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/gitblit_kubernetes_pod_service.jpg 1295w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Gitblit home page running as Kubernetes service<\/figcaption><\/figure>\n\n\n\n<h2 class=\"has-large-font-size wp-block-heading\" id=\"Setup-Jenkins-In-Local-Kubernetes\">Setup Jenkins In Local Kubernetes<\/h2>\n\n\n\n<p>To setup Jenkins in Kubernetes apply the following config in Kubernetes either using kubectl or Kubernetes dashboard in cicd-pipeline name space :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">apiVersion: v1\n kind: PersistentVolume\n metadata:\n     name: jenkins-repo-pv\n spec:\n   capacity:\n     storage: 3Gi\n   accessModes:\n      - ReadWriteOnce\n   hostPath:\n     path: \/tmp\/jenkinsData\n apiVersion: v1\n kind: PersistentVolumeClaim\n metadata:\n   name: jenkins-repo-pvc\n spec:\n   accessModes:\n     - ReadWriteOnce\n   resources:\n     requests:\n       storage: 3Gi\n apiVersion: v1\n kind: Pod\n metadata:\n   name: jenkins\n   labels:\n     app: jenkins\n spec:\n   containers:\n     - name: jenkins\n       image: jenkins\/jenkins:lts\n       ports:\n         - containerPort: 8080\n       volumeMounts:\n         - name: jenkins-data\n           mountPath: \/var\/jenkins_home\n   volumes:\n     - name: jenkins-data\n       persistentVolumeClaim:\n         claimName: jenkins-repo-pvc\n \n apiVersion: v1\n kind: Service\n metadata:\n   name: jenkins\n   labels:\n     app: jenkins\n spec:\n   selector:\n     app: jenkins\n   type: NodePort\n   ports:\n     - port: 8080\n       targetPort: 8080\n       nodePort: 30088<\/code><\/pre>\n\n\n\n<p>As you can see, this time, instead of applying separate config files, I put all steps of creating volume, creating pod and creating service in single config file. In case you want to  apply in command line, you can execute this command :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">\nkubectl create -f jenkins.yaml -n cicd-pipeline\n<\/code><\/pre>\n\n\n\n<p>After creating the service, you can reach Jenkins web UI at http:\/\/your-node-ip:30088. At first, Jenkins ask for a initial administrator password. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password-1024x526.jpg\" alt=\"Jenkins Administrator Password\" class=\"wp-image-1338\" width=\"512\" height=\"263\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password-1024x526.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password-300x154.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password-768x395.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_first_page_password.jpg 1255w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><figcaption>Jenkins Administrator Password<\/figcaption><\/figure><\/div>\n\n\n\n<p>Use kubectl to get the password from initialAdminPassword file within the Jenkins pod :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">kubectl exec -it jenkins-pod -n cidi-pipeline -- cat \/var\/jenkins_home\/secrets\/\ninitialAdminPassword<\/code><\/pre>\n\n\n\n<p>Running above command will yield a password string, copy it and past in Jenkins and click on the button to start initial Jenkins setup. In the next step choose &#8220;Install suggested plugin&#8221;. It will install all required plugin that you need in CI\/CD process :<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2-1024x592.jpg\" alt=\"Installing suggested plugin in Jenkins\" class=\"wp-image-1339\" width=\"512\" height=\"296\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2-1024x592.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2-300x174.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2-768x444.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_suggested_2.jpg 1276w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><figcaption>Installing suggested plugin in Jenkins<\/figcaption><\/figure><\/div>\n\n\n\n<p>After completing plugin installation, you will be asked for choosing a user name password for admin. Fill the form and finish the setup:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename-1024x603.jpg\" alt=\"\" class=\"wp-image-1341\" width=\"512\" height=\"302\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename-1024x603.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename-300x177.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename-768x452.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_setup_usrename.jpg 1276w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><figcaption>Jenkins form for admin username and password<\/figcaption><\/figure><\/div>\n\n\n\n<p>The Jenkins setup is finished, now you can login in with user name and password you have chosen in previous step and start working  :<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home-1024x643.jpg\" alt=\"jenkins home page\" class=\"wp-image-1354\" width=\"512\" height=\"322\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home-1024x643.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home-300x188.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home-768x482.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/02\/jenkins_home.jpg 1192w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Now that we have installed 4 main pillars of the pipeline, we can shape and configure our pipeline to start the production. I will explain further steps in the next part of this article. Keep in touch!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A local CI\/CD pipeline offers several benefits for software development teams. It can help improve the security of your code by keeping sensitive information on &hellip; <\/p>\n","protected":false},"author":1,"featured_media":1368,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[203,1,202,124,195,69,205,204,35,41,70],"tags":[189,206,115,188,9,207,208,39,209,63,71],"_links":{"self":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1312"}],"collection":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/comments?post=1312"}],"version-history":[{"count":0,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1312\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media\/1368"}],"wp:attachment":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media?parent=1312"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/categories?post=1312"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/tags?post=1312"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}