Автор статьи: Рустем Галиев

Senior DevOps Engineer & Integration Architect at IBM

Привет Хабр! На связи Рустем.

Недавно я был на одном интересном воркшопе от компании iTechArt и хотел бы сегодня поделится тем, что мы там делали, а точнее писали CD Pipeline с интеграцией Docker, Kubernetes и Jenkins в Google Cloud (GCE/GKE).

В этой статье мы узнаем, как развернуть динамическое веб-приложение на GKE (Google Kubernetes Engine) с использованием Jenkins.
Тема достаточно интересная, так что давайте начнем.

Наши реквизиты: Jenkins, Kubernetes(GKE), репозиторий Github и реестр образов Dockerhub.

Прежде чем двигаться дальше, давайте получим общее представление о терминологии, используемой в этом проекте:

  • GCP: Google Cloud Platform — поставщик общедоступного облака, который предлагает своим пользователям вычислительные услуги через Интернет.

  • Docker: Docker — это программа с открытым исходным кодом, которая позволяет нам создавать, развертывать и управлять виртуализированными приложениями. Это позволяет нам отделить наше приложение от нашей инфраструктуры, поскольку мы можем создавать и запускать наше приложение в контейнерной среде.

  • Jenkins: это сервер автоматизации с открытым исходным кодом, который позволяет разработчикам надежно создавать, тестировать и развертывать свое программное обеспечение. Он организует цепочку действий (сборка, тестирование, развертывание) для достижения процесса непрерывной интеграции и непрерывной доставки автоматизированным способом.

  • Kubernetes: Kubernetes или K8s — это портативная, расширяемая платформа или программное обеспечение с открытым исходным кодом, которое обеспечивает возможности настройки, автоматизации и управления контейнерами, т.е. Решает вопросы оркестрации

    Давайте начнем с подготовки инфраструктуры.

Шаг 1: Создадим и настроим машины

1. Создайте машину разработчика:

Откройте консоль Google Cloud Platform > Compute Engine > Create New Instance

2. Создадим новый экземпляр для сервера Jenkins и Docker Engine:


Установим Docker:

sudo apt-get update
sudo apt install docker.io


Установим Jenkins:

1) Сперва установим JDK:

sudo apt-get update
sudo apt install openjdk-11-jre-headless


2) Добавим ключ

wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -

 3) Apt repo

sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'

4) Обновим пакеты и установим Jenkins

sudo apt-get update
sudo apt-get install jenkins

Также не забудьте дать Jenkins’у привелегии администратора

Откроем файл /etc/sudoers и добавим следующую строчку:

jenkins ALL=(ALL) NOPASSWD: ALL

3. Создадим кластер Kubernetes в GKE

Open GCP console > GKE (Kubernetes engine) > Create

Шаг 2. Настроим Jenkins


1. Откройте дэшборд Jenkins через public IP вашего инстанса по порту 8080

http://<server_publicIP>:8080

По указанному пути найдем пароль администратора, скопируем и вставим

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Теперь выберем Install suggested plugins

После установки Jenkins попросит создать аккаунт для администратора, что мы и сделаем

2. Установим следующие плагины: Git, github, Docker pipeline, Google Kubernetes Engine

Jenkins Dashboard -> Plugin Manager -> Available -> Search bar

3. Добавим реквизиты для аутентификации в GKE и Dockerhub

Загрузите ключ сервисного аккаунта Google:

Open GCP console > IAM and Admin > service account > open default one or create new one > add key > create new key (json format) and save it

Загрузим ключ в Jenkins

Jenkins Dashboard > manage Jenkins > manage credentials > Jenkins > global credentials > add credentials



Для Docker тоже добавим (где Secret - это пароль нашего аккаунта)

3. Создадим Pipeline в Jenkins:

Dashboard -> New item

Дадим любое имя, выбираем pipeline

Definition = Pipeline script from SCM

SCM = git

Repository Url: url вашего репозитория
Script Path: Jenkinsfile

Шаг 3. Настроим наш проект для сборки


Для примера давайте соберем это приложение и форкнем или склоним его: https://github.com/komarserjio/notejam

1. Напишем Dockerfile:

FROM python:2.7
RUN mkdir app
COPY django/notejam app/
COPY django/requirements.txt ./
RUN pip install -r requirements.txt
RUN pip install psycopg2
WORKDIR app/
EXPOSE 5000

2. Напишем deployment.yaml для Kubernetes
В моем случае он содержит манифест развертывания веб-приложения и его манифест его службы, а также манифест для persistent volume claim, secret, configMap, database deployment и service.

#data-db-persistentvolumeclaim.yam
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
	service: data-db
  name: data-db
spec:
  accessModes:
	- ReadWriteOnce
  resources:
	requests:
  	storage: 2Gi
 	 
---
#db-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
	service: db
  name: db-config
data:
  db-name: "nj"

#db-secret
apiVersion: v1
kind: Secret
metadata:
  name: notejam-credentials
type: Opaque
data:
  user: YWRtaW4=
  password: YWRtaW5AMTIz

#db-deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
service: db
  name: db
spec:
  replicas: 1
  selector:
matchLabels:
  	service: db
  template:
metadata:
  	labels:
    	service: db
    	tier: backend
spec:
  	containers:
    	- image: postgres
      	name: database
      	env:
        	- name: POSTGRES_USER
          	valueFrom:
            	secretKeyRef:
              	name: notejam-credentials
              	key: user
        	- name: POSTGRES_PASSWORD
          	valueFrom:
            	secretKeyRef:
              	name: notejam-credentials
              	key: password
        	- name: POSTGRES_DB
          	valueFrom:
            	configMapKeyRef:
              	name: db-config
              	key: db-name
      	ports:
        	- containerPort: 5432
      	resources:
        	requests:
          	cpu: 100m
          	memory: 128Mi
        	limits:
          	cpu: 250m
          	memory: 256Mi
      	volumeMounts:
        	- mountPath: /var/lib/mysql/data
          	name: data-db
  	restartPolicy: Always
  	volumes:
    	- name: data-db
      	persistentVolumeClaim:
        	claimName: data-db

#db-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
service: db
  name: db
spec:
  ports:
- name: "5432"
  	protocol: TCP
  	port: 5432
  	targetPort: 5432
  selector:
service: db

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
service: web
  name: web
spec:
  replicas: 1
  selector:
matchLabels:
  	service: web
  template:
metadata:
  	labels:
    	service: web
spec:
  	containers:
    	- args:
        	- bash
        	- -c
        	- python manage.py syncdb --noinput && python manage.py migrate && python manage.py runserver 0.0.0.0:5000
      	image: arshad1914/pipeline:latest
      	name: notejam
      	ports:
        	- containerPort: 5000
      	resources:
        	requests:
          	cpu: 100m
          	memory: 128Mi
        	limits:
          	cpu: 250m
          	memory: 250Mi
  	restartPolicy: Always

apiVersion: v1
kind: Service
metadata:
  labels:
service: web
  name: web
spec:
  type: LoadBalancer
  ports:
- name: "5000"
  	port: 5000
  	targetPort: 5000
  selector:
service: web


3. Создадим Jenkinsfile, где опишем процесс сборки и доставки

pipeline {
agent any
    environment {
   	 PROJECT_ID = 'docker'
            	CLUSTER_NAME = 'jenkins'
            	LOCATION = 'us-central-1a'
            	CREDENTIALS_ID = 'kubernetes'
    }
    
stages {
    	stage('Checkout') {
   	 	steps {
   		 	checkout scm
   	 	}
    	}
    	stage('Build image') {
   	 	steps {
   		 	script {
   			 	app = docker.build("zetzo/pipeline:{env.BUILD_ID}")
   		  	}
   						 	 
   	 	}
    	}
   
    	stage('Deploy to K8s') {
   	 	steps{
   		 	echo "Deployment started ..."
   		 	sh 'ls -ltr'
   		 	sh 'pwd'
   		 	sh "sed -i 's/pipeline:latest/pipeline:class: 'KubernetesEngineBuilder', 
   			   projectId: env.PROJECT_ID, 
   			   clusterName: env.CLUSTER_NAME, 
   			   location: env.LOCATION, 
   			   manifestPattern: 'deployment.yaml', 
   			   credentialsId: env.CREDENTIALS_ID, 
   			   verifyDeployments: true])
   			 }
   		 }
   	 }    
}


Шаг 4. Протестируем наш пайплайн (спойлер: успешно)

Задеплоили, Вы восхитительны!

P.s.

Если хотите чтобы деплой был полностью автоматизированным (т.е. Перейти от Continuous Delivery к Continuous Deployment), то рекомендую настроить вебхук в вашем репозитории

settings > webhooks > add webhooks > url Вашего Jenkins instance

В Вашем Jenkins pipeline, заходим в конфигурацию и ставим галочку в поле здесь

На этом все. Также хочу порекомендовать всем бесплатный урок от OTUS на котором вы сможете изучить предпосылки к возникновению контейнеризации и познакомиться с устройством самого популярного "контейнеризатора" - docker.