Jenkins CI/CD Pipeline (Kubernetes)

Arif KIZILTEPE
4 min readOct 5, 2022

--

Bu yazı aslında bir önceki yazımın devamı gibi olacak okumaya başlamadan şuna göz atmanızı öneririm.

Bir önceki yazıda jenkins ile gitteki kodu docker build ile image oluşturup nexus’a göndermiştik. Bu yazıda;

  • Kubernetes ortamlarının private image repository olarak Nexus’a bağlayacağız.
  • Dinamik kubernetes manifest dosyası oluşturacağız.
  • Jenkins pipeline’a yeni bir stage ekleyerek deployment işlemini yapacağız. (Parametreler ile)

1. Kubernetes Private Image Repository

Kubernetes eski sürümlerinde docker bağımlılığı vardı yeni sürümlerinde containerd ile de çalışabiliyor.

Kubernetes’da bir deployment yaptığınızda docker/containerd, manifest içerisinde belirtilen image’ı suncusuya çeker ve default olan docker official repodur.

Bizim kendi nexus ortamımızı eklemek için öncelikle her kubernetes sunucusunda bazı ayarlar yapmamız gerekiyor. Docker ve containerd için ayrı ayrı anlatacağım.

Docker; (Tüm kubernetes sunucularında yapılacak)

/etc/docker/daemon.json içerisine private repo adresleri eklenir.

{
"insecure-registries": [
"nexus.xxx.com:8082",
"nexus.xxx.com:8083"
]
}

Daha sonra docker login komutu ile Nexus’a register olunur.

docker login -u USER -p PASS http://nexus.xxx.com:8083docker login -u USER -p PASS http://nexus.xxx.com:8082

Daha sonra docker servisleri restart edilir.

Containerd (Tüm kubernetes sunucularında yapılacak)

/etc/containerd/config.toml içerisine private repo adresleri eklenir.

version = 2[plugins."io.containerd.grpc.v1.cri".registry]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."nexus.i01.paytr.com:8083"]
endpoint = ["http://nexus.xxx.com:8083"]
[plugins."io.containerd.grpc.v1.cri".registry.configs]

Daha sonra containerd servisi restart edilir. Containerd kullanıyorsanız credentials bilgilerini secret oluşturarak verebiliyoruz. Bu işlem içinde aşağıdaki komut çalıştırılır.

kubectl create secret generic nexus --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson

Kubernetes private repo ayaları tamamlandı.

2. Dinamik Kubernetes Manifest

Burada hedeflediğimiz konu image versiyonları, namespace, url gibi tüm değişkenleri git üzerinden alıp oluşturduğumuz Jenkins pipeline’ı tamamen dinamik oluşturmak.

Bir önceki yazımda image versiyon değerini ben jenkins load ile gitlab’daki env dosyamdan okuyordum. Burada da aynı şekilde bir işlem yapacağım.

İşlemlere geçmeden önce proje içerisindeki pipeline için gerekli dosyaları kısana belirteyim;

.envvars : Değişkenleri yönettiğim dosya.

kubernetes.yml : Dinamik manifest dosyam. Deployment, service ve Ingress işlemleri bu dosya içerisinde yazıyor.

Jenkinsfile : Pipeline groovy secreti yazdığımız dosya. Jenkins pipeline’da ben Pipeline script from SCM yönetmini kullanıyorum bu yüzden scripti gitlab üzerinde tutuyorum.

Aşağıda gördüğünüz gibi yml içerisindeki birçok değer aslında kendi belirlediğim yöntem ile dinamik hale getirilmiş.

#kubernetes.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: _APP_NAME_
namespace: _APP_NAMESPACE_
labels:
app: _APP_NAME_
spec:
replicas: _APP_REPLICA_
revisionHistoryLimit: 3
selector:
matchLabels:
app: _APP_NAME_
template:
metadata:
labels:
app: _APP_NAME_
spec:
containers:
- name: _APP_NAME_
image: _DOCKER_REP_URL_/_IMAGE_NAME_:_VERSION_
imagePullPolicy: Always
resources:
limits:
memory: 6144Mi
requests:
memory: 2048Mi
ports:
- containerPort: 80
imagePullSecrets:
- name: paytrnexus
---
apiVersion: v1
kind: Service
metadata:
name: _APP_NAME_
namespace: _APP_NAMESPACE_
labels:
app: _APP_NAME_
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
selector:
app: _APP_NAME_
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: _APP_NAME_
namespace: _APP_NAMESPACE_
spec:
ingressClassName: kong
rules:
- http:
paths:
- path: /api-testpipeline
pathType: Prefix
backend:
service:
name: _APP_NAME_
port:
number: 443

Bu değişkenleri yukarıdaki anlattığım gibi .envvars dosyasından dolduracağım. Sizde bu yöntem ile istediğiniz gibi değerlerin yönetimini parametrik hale getirebilirsiniz.

_APP_NAME_="hellonode"
_APP_NAMESPACE_="default"
_APP_REPLICA_="2"
_DOCKER_REP_URL_="nexus.xxx.com:8083"
_IMAGE_NAME_="hellonode"
_VERSION_="3.3"
_APP_URL_="hellonode.xxx.com"

Son olarak işlemleri yaptığım Jenkinsfile dosyası

  • Pipeline kısmında gördüğünüz gibi 2 tane node tanımı var. 1. node kısmında ben Gitten projeyi al → Docker image oluştur → Nexus’a gönder işlemlerinin yapıldı kısım. Bu işlemleri ben Jenkins sunucusunda yapıyorum node içerisinde belirttiğim label ise Jenkins sunucusunun label bilgileri.
  • Enviroment bilgilerini yukarıda anlattığım gibi load “.envvars” ile yapıyoruz. Bu işlem sonrasında değişkenleri kendimize almış oluyoruz.
  • 2. Node kısmında artık deployment işlemini yapacağımız kısım. Burada ben kubernetes sunucusuna bağlanıp işlemi yapıyorum.
  • Sed ifadeleri aslında buradaki püf nokta. Benim gitten aldığı manifest aslında projem için yazılmamıştı. Ben .envvars’dan okuduğum değerler ile manifest dosyasını yeniden düzenleyip kubernetes ortamına deploy ediyoruz.
node('DefaultJenkinsMachine') {
def app
stage('Clone repository') {
/* Let's make sure we have the repository cloned to our workspace */
checkout scm
load ".envvars"
echo " Version ${_VERSION_}"
}stage('Build image') {
/* This builds the actual image; synonymous to
* docker build on the command line */
app = docker.build("nexus.i01.paytr.com:8083/hellonode:${_VERSION_}")
}
stage('Test image') {
/* Ideally, we would run a test framework against our image.
* For this example, we're using a Volkswagen-type approach ;-) */
echo "NoN-Test"
}stage('Push image') {
sh "docker push nexus.xxx.com:8083/hellonode:${_VERSION_}"

}

}
node('VTGENK8SMAS01') {
stage('Publish image on kubernetes') {
sh "hostname"
checkout scm
sh "sed -i 's/_APP_NAME_/${_APP_NAME_}/g' kubernetes.yml"
sh "sed -i 's/_APP_NAMESPACE_/${_APP_NAMESPACE_}/g' kubernetes.yml"
sh "sed -i 's/_APP_REPLICA_/${_APP_REPLICA_}/g' kubernetes.yml"
sh "sed -i 's/_DOCKER_REP_URL_/${_DOCKER_REP_URL_}/g' kubernetes.yml"
sh "sed -i 's/_IMAGE_NAME_/${_IMAGE_NAME_}/g' kubernetes.yml"
sh "sed -i 's/_VERSION_/${_VERSION_}/g' kubernetes.yml"
sh "sed -i 's/_APP_URL_/${_APP_URL_}/g' kubernetes.yml"
sh "/usr/bin/kubectl apply -f kubernetes.yml"
sh "/usr/bin/kubectl get pod --all-namespaces"

}
}

Jenkins üzerindeki Build pipeline’ı çalıştırarak deneme yapalım. Ben imageversion olarak 3.3 değerini yazdım.

Pipeline başarılı şekilde çalıştı. Tek tek kontrol edelim.

Nexus;

Kubernetes;

Web UI;

Her şey başarılı görünüyor.

--

--

Responses (1)