Selamat siang
Sudah ada beberapa artikel tentang Habré tentang jenkins, ci / cd dan kubernetes, tetapi dalam hal ini saya tidak ingin berkonsentrasi menganalisis kemampuan teknologi ini, tetapi pada konfigurasi mereka yang paling sederhana untuk membangun pipa ci / cd.
Maksud saya, pembaca memiliki pemahaman dasar tentang buruh pelabuhan, dan saya tidak akan membahas topik pemasangan dan konfigurasi kubernet. Semua contoh akan ditampilkan di minikube, tetapi juga dapat diterapkan pada EKS, GKE, atau sejenisnya tanpa perubahan signifikan.

Lingkungan
Saya sarankan menggunakan lingkungan berikut:
- test - untuk penyebaran manual dan pengujian cabang
- pementasan - lingkungan di mana semua perubahan yang jatuh ke master secara otomatis digunakan
- produksi - lingkungan yang digunakan oleh pengguna nyata, di mana perubahan hanya akan terjadi setelah mengkonfirmasikan operabilitas mereka pada pementasan
Lingkungan akan diatur menggunakan kubernetes namespaces dalam satu cluster. Pendekatan ini sesederhana dan secepat mungkin di awal, tetapi juga memiliki kelemahan: ruang nama tidak sepenuhnya terisolasi satu sama lain dalam kubernet.
Dalam contoh ini, setiap namespace akan memiliki set ConfigMaps yang sama dengan konfigurasi lingkungan ini:
apiVersion: v1 kind: Namespace metadata: name: production --- apiVersion: v1 kind: ConfigMap metadata: name: environment.properties namespace: production data: environment.properties: | env=production
Helm
Helm adalah aplikasi yang membantu mengelola sumber daya yang diinstal di kubernetes.
Instruksi pemasangan dapat ditemukan di sini .
Untuk memulai, Anda harus menginisialisasi pod anakan untuk menggunakan helm dengan kluster:
helm init
Jenkins
Saya akan menggunakan Jenkins karena ini adalah platform yang cukup sederhana, fleksibel dan populer untuk membangun proyek. Ini akan dipasang di namespace terpisah untuk mengisolasi dirinya dari lingkungan lain. Karena saya berencana untuk menggunakan helm di masa depan, saya dapat menyederhanakan instalasi Jenkins menggunakan grafik open source yang ada :
helm install --name jenkins --namespace jenkins -f jenkins/demo-values.yaml stable/jenkins
demo-values.yaml berisi versi Jenkins, satu set plugin yang sudah diinstal, nama domain, dan konfigurasi lainnya
demo-values.yaml Master: Name: jenkins-master Image: "jenkins/jenkins" ImageTag: "2.163-slim" OverwriteConfig: true AdminUser: admin AdminPassword: admin InstallPlugins: - kubernetes:1.14.3 - workflow-aggregator:2.6 - workflow-job:2.31 - credentials-binding:1.17 - git:3.9.3 - greenballs:1.15 - google-login:1.4 - role-strategy:2.9.0 - locale:1.4 ServicePort: 8080 ServiceType: NodePort HostName: jenkins.192.168.99.100.nip.io Ingress: Path: / Agent: Enabled: true Image: "jenkins/jnlp-slave" ImageTag: "3.27-1" #autoadjust agent resources limits resources: requests: cpu: null memory: null limits: cpu: null memory: null #to allow jenkins create slave pods rbac: install: true
Konfigurasi ini menggunakan admin / admin sebagai nama pengguna dan kata sandi untuk login, dan dapat dikonfigurasi ulang nanti. Salah satu opsi yang mungkin adalah google SSO (plugin google-login diperlukan untuk ini, pengaturannya terletak di Jenkins> Kelola Jenkins> Konfigurasikan Keamanan Global> Kontrol Akses> Realm Keamanan> Login dengan Google).
Jenkins akan segera dikonfigurasi untuk secara otomatis membuat budak satu kali untuk setiap build. Berkat ini, tim tidak akan lagi mengharapkan agen gratis untuk perakitan, dan bisnis akan dapat menghemat jumlah server yang diperlukan.

Juga di luar kotak, PersistenceVolume dikonfigurasikan untuk menyimpan saluran pipa saat memulai ulang atau memperbarui.
Agar skrip penerapan otomatis berfungsi dengan benar, Anda perlu memberikan izin cluster-admin untuk Jenkins untuk mendapatkan daftar sumber daya di kubernet dan memanipulasi mereka.
kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=jenkins:default
Di masa mendatang, Anda dapat memperbarui Jenkins menggunakan helm jika ada versi baru plugin atau perubahan konfigurasi.
helm upgrade jenkins stable/jenkins -f jenkins/demo-values.yaml
Ini dapat dilakukan melalui antarmuka Jenkins itu sendiri, tetapi dengan helm Anda akan memiliki kesempatan untuk memutar kembali ke revisi sebelumnya menggunakan:
helm history jenkins helm rollback jenkins ${revision}
Pembuatan aplikasi
Sebagai contoh, saya akan membangun dan menggunakan aplikasi boot spring paling sederhana. Demikian pula dengan Jenkins saya akan menggunakan helm.
Majelis akan berlangsung dalam urutan berikut:
- checkout
- kompilasi
- unit test
- uji integrasi
- perakitan artefak
- penyebaran artefak dalam register buruh pelabuhan
- menggunakan artefak untuk pementasan (hanya untuk cabang master)
Untuk ini saya menggunakan file Jenkins . Menurut pendapat saya, ini adalah cara yang sangat fleksibel (tapi, sayangnya, bukan yang termudah) untuk mengkonfigurasi perakitan proyek. Salah satu kelebihannya adalah kemampuan untuk menjaga konfigurasi perakitan proyek dalam repositori dengan proyek itu sendiri.
checkout

Dalam kasus bitbucket atau organisasi github, Anda dapat mengonfigurasi Jenkins untuk secara berkala memindai seluruh akun untuk keberadaan repositori dengan Jenkinsfile dan secara otomatis membuat rakitan untuk mereka. Jenkins akan mengumpulkan master dan cabang. Permintaan tarik akan ditampilkan di tab terpisah. Ada opsi yang lebih sederhana - tambahkan repositori git terpisah, di mana pun ia di-host. Dalam contoh ini, saya akan melakukan hal itu. Semua yang diperlukan ada di menu Jenkins> Item baru> Multibranch Pipeline, pilih nama assembly dan ikat repositori git.
Kompilasi
Karena Jenkins membuat pod baru untuk setiap rakitan, dalam hal menggunakan pakar atau kolektor serupa, dependensi akan diunduh lagi setiap kali. Untuk menghindari ini, Anda dapat mengalokasikan PersistenceVolume untuk .m2 atau cache serupa dan memasangnya di pod yang membangun proyek.
apiVersion: "v1" kind: "PersistentVolumeClaim" metadata: name: "repository" namespace: "jenkins" spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi
Dalam kasus saya, ini memungkinkan untuk mempercepat saluran pipa dari sekitar 4 hingga menit ke-1.
Versi
Agar CI / CD berfungsi dengan benar, setiap versi memerlukan versi yang unik.
Pilihan yang sangat bagus adalah menggunakan versi semantik . Ini akan memungkinkan Anda untuk melacak perubahan yang kompatibel dan tidak kompatibel ke belakang, tetapi versi seperti itu lebih sulit untuk diotomatisasi.
Dalam contoh ini, saya akan membuat versi dari id dan tanggal komit, serta nama cabang, jika bukan master. Misalnya 56e0fbdc-201802231623 atau b3d3c143-201802231548-PR-18 .
Keuntungan dari pendekatan ini:
- kemudahan otomatisasi
- mudah untuk mendapatkan kode sumber dan waktu pembuatannya dari versi
- secara visual, Anda dapat membedakan versi rilis kandidat (dari wisaya) atau eksperimental (dari cabang)
tapi: - versi ini lebih sulit digunakan dalam komunikasi lisan
- tidak jelas apakah ada perubahan yang tidak kompatibel.
Karena gambar buruh pelabuhan dapat memiliki beberapa tag pada saat yang sama, pendekatan dapat digabungkan: semua rilis menggunakan versi yang dihasilkan, dan orang-orang yang jatuh pada produksi juga (secara manual) ditandai dengan versi semantik. Ini, pada gilirannya, dikaitkan dengan kompleksitas implementasi yang lebih besar dan ambiguitas versi mana yang harus ditampilkan oleh aplikasi.
Artefak
Hasil majelis akan:
- gambar buruh pelabuhan dengan aplikasi yang akan disimpan dan dimuat dari register buruh pelabuhan. Contoh ini akan menggunakan registri bawaan dari minikube, yang dapat diganti dengan hub docker atau registri pribadi dari amazon (ecr) atau google (jangan lupa untuk memberikan kredensial kepada mereka menggunakan withCredentials construct).
- grafik helm dengan deskripsi penyebaran aplikasi (penyebaran, layanan, dll) di direktori helm. Idealnya, mereka harus disimpan di repositori artefak yang terpisah, tetapi, untuk penyederhanaan, mereka dapat digunakan dengan memeriksa komit yang diperlukan dari git.
Jenkinsfile
Akibatnya, aplikasi akan dibangun menggunakan Jenkinsfile berikut:
Jenkinsfile def branch def revision def registryIp pipeline { agent { kubernetes { label 'build-service-pod' defaultContainer 'jnlp' yaml """ apiVersion: v1 kind: Pod metadata: labels: job: build-service spec: containers: - name: maven image: maven:3.6.0-jdk-11-slim command: ["cat"] tty: true volumeMounts: - name: repository mountPath: /root/.m2/repository - name: docker image: docker:18.09.2 command: ["cat"] tty: true volumeMounts: - name: docker-sock mountPath: /var/run/docker.sock volumes: - name: repository persistentVolumeClaim: claimName: repository - name: docker-sock hostPath: path: /var/run/docker.sock """ } } options { skipDefaultCheckout true } stages { stage ('checkout') { steps { script { def repo = checkout scm revision = sh(script: 'git log -1 --format=\'%h.%ad\' --date=format:%Y%m%d-%H%M | cat', returnStdout: true).trim() branch = repo.GIT_BRANCH.take(20).replaceAll('/', '_') if (branch != 'master') { revision += "-${branch}" } sh "echo 'Building revision: ${revision}'" } } } stage ('compile') { steps { container('maven') { sh 'mvn clean compile test-compile' } } } stage ('unit test') { steps { container('maven') { sh 'mvn test' } } } stage ('integration test') { steps { container ('maven') { sh 'mvn verify' } } } stage ('build artifact') { steps { container('maven') { sh "mvn package -Dmaven.test.skip -Drevision=${revision}" } container('docker') { script { registryIp = sh(script: 'getent hosts registry.kube-system | awk \'{ print $1 ; exit }\'', returnStdout: true).trim() sh "docker build . -t ${registryIp}/demo/app:${revision} --build-arg REVISION=${revision}" } } } } stage ('publish artifact') { when { expression { branch == 'master' } } steps { container('docker') { sh "docker push ${registryIp}/demo/app:${revision}" } } } } }
Pipa Jenkins tambahan untuk manajemen siklus hidup aplikasi
Asumsikan repositori diatur sehingga:
- berisi aplikasi terpisah dalam bentuk gambar buruh pelabuhan
- dapat digunakan menggunakan file helm yang terletak di direktori helm
- versi menggunakan pendekatan yang sama dan memiliki file helm / setVersion.sh untuk mengatur revisi dalam grafik helm
Kemudian kita dapat membangun beberapa jaringan pipa Jenkinsfile untuk mengelola siklus hidup aplikasi, yaitu:
Di Jenkinsfile dari setiap proyek, Anda bisa menambahkan panggilan pipa penyebaran yang akan dieksekusi setiap kali cabang master berhasil dikompilasi atau ketika cabang penyebaran secara eksplisit meminta lingkungan pengujian.
File Jenkins menyebarkan panggilan pipa ... stage ('deploy to env') { when { expression { branch == 'master' || params.DEPLOY_BRANCH_TO_TST } } steps { build job: './../Deploy', parameters: [ [$class: 'StringParameterValue', name: 'GIT_REPO', value: 'habr-demo-app'], [$class: 'StringParameterValue', name: 'VERSION', value: revision], [$class: 'StringParameterValue', name: 'ENV', value: branch == 'master' ? 'staging' : 'test'] ], wait: false } } ...
Di sini Anda dapat menemukan Jenkinsfile dengan semua langkahnya.
Dengan demikian, adalah mungkin untuk membangun penyebaran berkelanjutan pada lingkungan uji atau pertempuran yang dipilih, juga menggunakan jenkins atau pemberitahuan email / slack / etc, memiliki audit aplikasi mana, versi mana, oleh siapa, kapan dan di mana ia diinstal.
Kesimpulan
Menggunakan Jenkinsfile dan helm, Anda cukup membangun ci / cd untuk aplikasi Anda. Metode ini mungkin paling relevan untuk tim kecil yang baru-baru ini mulai menggunakan kubernet dan tidak dapat (terlepas dari alasan apa pun) untuk menggunakan layanan yang dapat menyediakan fungsionalitas seperti itu di luar kotak.
Anda dapat menemukan contoh konfigurasi untuk lingkungan, Jenkins dan pipeline untuk mengelola siklus hidup aplikasi di sini dan contoh aplikasi dengan Jenkinsfile di sini .