

Автор статьи: Рустем Галиев
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.
