README.md

    Дипломный практикум в Yandex.Cloud - Леонид Хорошев

    Перед началом работы над дипломным заданием изучите Инструкция по экономии облачных ресурсов.


    Цели:

    1. Подготовить облачную инфраструктуру на базе облачного провайдера Яндекс.Облако.
    2. Запустить и сконфигурировать Kubernetes кластер.
    3. Установить и настроить систему мониторинга.
    4. Настроить и автоматизировать сборку тестового приложения с использованием Docker-контейнеров.
    5. Настроить CI для автоматической сборки и тестирования.
    6. Настроить CD для автоматического развёртывания приложения.

    Этапы выполнения:

    Создание облачной инфраструктуры

    Для начала необходимо подготовить облачную инфраструктуру в ЯО при помощи Terraform.

    Особенности выполнения:

    • Бюджет купона ограничен, что следует иметь в виду при проектировании инфраструктуры и использовании ресурсов; Для облачного k8s используйте региональный мастер(неотказоустойчивый). Для self-hosted k8s минимизируйте ресурсы ВМ и долю ЦПУ. В обоих вариантах используйте прерываемые ВМ для worker nodes.

    Предварительная подготовка к установке и запуску Kubernetes кластера.

    1. Создайте сервисный аккаунт, который будет в дальнейшем использоваться Terraform для работы с инфраструктурой с необходимыми и достаточными правами. Не стоит использовать права суперпользователя
    resource "yandex_iam_service_account" "sa" {
      name = var.sa_name
    }
    

    Добавляем права storage.admin, необходимые для создания и работы с бакетом

    resource "yandex_resourcemanager_folder_iam_member" "sa-admin" {
      folder_id = var.folder_id
      role      = "storage.admin"
      member    = "serviceAccount:${yandex_iam_service_account.sa.id}"
    }
    

    Создаем ключ доступа к нашему хранилищу

    resource "yandex_iam_service_account_static_access_key" "sa-static-key" {
      service_account_id = yandex_iam_service_account.sa.id
      description        = "static access key for object storage"
    }
    

    Также, чтобы получить ключ доступа и привытный ключ, суонфигурированный вышеуказанным кодом - подготовит возможность вывода значений ключей в терминал, для чего создадим файл outputs.tf

    output "s3_access_key" {
      description = "Yandex Cloud S3 access key"
      value       = yandex_iam_service_account_static_access_key.sa-static-key.access_key
      sensitive   = true
    }
    
    output "s3_secret_key" {
      description = "Yandex Cloud S3 secret key"
      value       = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
      sensitive   = true
    
    1. Подготовьте backend для Terraform:
      а. Рекомендуемый вариант: S3 bucket в созданном ЯО аккаунте(создание бакета через TF) б. Альтернативный вариант: Terraform Cloud

    Применяем изменения и узнаем значения ключей

    terraform plan
    terraform apply
    terraform output s3_access_key
    terraform output s3_secret_key
    

    Далее прописываем их значение в конфигурации s3 backend в файле providers.tf (примечание - в момент создания бакета нижеприведенный блок кода по инициализации s3 backend закомментирован, так как нельзя настроить удаленное хранение состояния конфигурации терраформ в бакете, не создав при этом сам бакет).

     backend "s3" {
        endpoints = {
          s3 = "https://storage.yandexcloud.net"
       }
        bucket                      = "diplom-project-khoroshevlv"
        region                      = "ru-central1"
        key                         = "terraform.tfstate"
        access_key                  = <my access_key>
        secret_key                  = <my access_key>
        skip_region_validation      = true
        skip_credentials_validation = true
        skip_requesting_account_id  = true
        skip_s3_checksum            = true
      }
    }
    

    Инициализируем инфраструктуру

    terraform init
    

    Alt_text Alt_text

    1. Создайте VPC с подсетями в разных зонах доступности.
    resource "yandex_vpc_network" "my_vpc" {
      name = var.VPC_name
    }
    
    resource "yandex_vpc_subnet" "public_subnet" {
      count = length(var.public_subnet_zones)
      name  = "${var.public_subnet_name}-${var.public_subnet_zones[count.index]}"
      v4_cidr_blocks = [
        cidrsubnet(var.public_v4_cidr_blocks[0], 4, count.index)
      ]
      zone       = var.public_subnet_zones[count.index]
      network_id = yandex_vpc_network.my_vpc.id
    }
    

    К терраформ коду указываем следующие переменные:

    ### vpc vars
    
    variable "VPC_name" {
      type        = string
      default     = "my-vpc"
    }
    
    ### subnet vars
    
    variable "public_subnet_name" {
      type        = string
      default     = "public"
    }
    
    variable "public_v4_cidr_blocks" {
      type        = list(string)
      default     = ["192.168.10.0/24"]
    }
    
    variable "subnet_zone" {
      type        = string
      default     = "ru-central1"
    }
    
    variable "public_subnet_zones" {
      type    = list(string)
      default = ["ru-central1-a", "ru-central1-b",  "ru-central1-d"]
    }
    
    1. Убедитесь, что теперь вы можете выполнить команды terraform destroy и terraform apply без дополнительных ручных действий.

    При выполнении команды terraform destroy удаляются все вычислительные ресурсы, кроме нашего бакета и загруженного в него terraform.tfstate, а также созданной нами сети my-vpc (лжидаемый результат).

    Применяем изменения и проверяем инфраструктуру

    terraform apply
    terraform state list
    

    Alt_text

    1. В случае использования Terraform Cloud в качестве backend убедитесь, что применение изменений успешно проходит, используя web-интерфейс Terraform cloud.

    Ожидаемые результаты:

    1. Terraform сконфигурирован и создание инфраструктуры посредством Terraform возможно без дополнительных ручных действий.
    2. Полученная конфигурация инфраструктуры является предварительной, поэтому в ходе дальнейшего выполнения задания возможны изменения.

    Создание Kubernetes кластера

    На этом этапе необходимо создать Kubernetes кластер на базе предварительно созданной инфраструктуры. Требуется обеспечить доступ к ресурсам из Интернета.

    Это можно сделать двумя способами:

    1. Рекомендуемый вариант: самостоятельная установка Kubernetes кластера.
      а. При помощи Terraform подготовить как минимум 3 виртуальных машины Compute Cloud для создания Kubernetes-кластера. Тип виртуальной машины следует выбрать самостоятельно с учётом требовании к производительности и стоимости. Если в дальнейшем поймете, что необходимо сменить тип инстанса, используйте Terraform для внесения изменений.

    В целях экономии выделенного бюджета и с учетом ресурсоемкости создадим кластер из одной control-plane ноды и двух worker нод.

    Конфиграция control-plane ноды:

    resource "yandex_compute_instance" "control-plane" {
      name            = var.control_plane_name
      platform_id     = var.platform
      resources {
        cores         = var.control_plane_core
        memory        = var.control_plane_memory
        core_fraction = var.control_plane_core_fraction
      }
    
      boot_disk {
        initialize_params {
          image_id = var.image_id
          size     = var.control_plane_disk_size
        }
      }
    
      scheduling_policy {
        preemptible = var.scheduling_policy
      }
    
      network_interface {
        subnet_id = yandex_vpc_subnet.public_subnet[0].id
        nat       = var.nat
      }
    
      metadata = {
        user-data = "${file("/home/leo/kuber-homeworks/3.2/terraform/cloud-init.yaml")}"
     }
    }
    

    Переменные:

    ### control-plane node vars
    
    variable "control_plane_name" {
      type        = string
      default     = "control-plane"
    }
    
    variable "platform" {
      type        = string
      default     = "standard-v1"
    }
    
    variable "control_plane_core" {
      type        = number
      default     = "4"
    }
    
    variable "control_plane_memory" {
      type        = number
      default     = "8"
    }
    
    variable "control_plane_core_fraction" {
      description = "guaranteed vCPU, for yandex cloud - 20, 50 or 100 "
      type        = number
      default     = "20"
    }
    
    variable "control_plane_disk_size" {
      type        = number
      default     = "50"
    }
    
    variable "image_id" {
      type        = string
      default     = "fd893ak78u3rh37q3ekn"
    }
    
    variable "scheduling_policy" {
      type        = bool
      default     = "true"
    }
    

    Конфигурация worker нод:

    resource "yandex_compute_instance" "worker" {
      count           = var.worker_count
      name            = "worker-node-${count.index + 1}"
      platform_id     = var.worker_platform
      zone = var.public_subnet_zones[count.index]
      resources {
        cores         = var.worker_cores
        memory        = var.worker_memory
        core_fraction = var.worker_core_fraction
      }
    
      boot_disk {
        initialize_params {
          image_id = var.image_id
          size     = var.worker_disk_size
        }
      }
    
        scheduling_policy {
        preemptible = var.scheduling_policy
      }
    
      network_interface {
        subnet_id = yandex_vpc_subnet.public_subnet[count.index].id
        nat       = var.nat
      }
    
      metadata = {
        user-data = "${file("/home/leo/diplom/terraform/cloud-init.yaml")}"
     }
    }
    

    Переменные:

    ### worker nodes vars
    
    variable "worker_count" {
      type        = number
      default     = "2"
    }
    
    variable "worker_platform" {
      type        = string
      default     = "standard-v1"
    }
    
    variable "worker_cores" {
      type        = number
      default     = "4"
    }
    
    variable "worker_memory" {
      type        = number
      default     = "2"
    }
    
    variable "worker_core_fraction" {
      description = "guaranteed vCPU, for yandex cloud - 20, 50 or 100 "
      type        = number
      default     = "20"
    }
    
    variable "worker_disk_size" {
      type        = number
      default     = "50"
    }
    
    variable "nat" {
      type        = bool
      default     = "true"
    }
    

    Для всех виртуальных машин используется cloud-init.yaml следующей конфигурации:

    #cloud-config
    users:
      - name: leo
        ssh_authorized_keys:
          - ssh-rsa <public_key>
        sudo: ['ALL=(ALL) NOPASSWD:ALL']
        groups: sudo
        shell: /bin/bash
    package_update: true
    package_upgrade: true
    packages:
      - nginx
      - nano
      - software-properties-common
    runcmd:
      - mkdir -p /home/leo/.ssh
      - chown -R leo:leo /home/leo/.ssh
      - chmod 700 /home/leo/.ssh
      - sudo add-apt-repository ppa:deadsnakes/ppa -y
      - sudo apt-get update
    

    б. Подготовить ansible конфигурации, можно воспользоваться, например Kubespray

    Скачиваем репозиторий с Kubespray

    git clone https://github.com/kubernetes-sigs/kubespray
    

    Устанавливаем зависимости

    python3.10 -m pip install --upgrade pip
    pip3 install -r requirements.txt
    

    Копируем шаблон с inventory файлом

    cp -rfp inventory/sample inventory/mycluster
    

    Корректируем файл inventory.ini, где прописываем актуальные ip адреса виртуальных машин, развернутых в предвдущих пунктах, в качестве CRI будем использовать containerd, а запуск etcd будет осущуствляться на мастере.

    # ## Configure 'ip' variable to bind kubernetes services on a
    # ## different ip than the default iface
    # ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value.
    [all]
    node1 ansible_host=51.250.71.251   ansible_user=leo ansible_ssh_private_key_file=~/.ssh/id_rsa # ip=192.168.10.7  etcd_member_name=etcd1
    node2 ansible_host=84.201.174.252  ansible_user=leo ansible_ssh_private_key_file=~/.ssh/id_rsa # ip=192.168.10.10 etcd_member_name=etcd2
    node3 ansible_host=89.169.166.190  ansible_user=leo ansible_ssh_private_key_file=~/.ssh/id_rsa # ip=192.168.10.19 etcd_member_name=etcd3
    # node4 ansible_host=95.54.0.15   # ip=10.3.0.4 etcd_member_name=etcd4
    # node5 ansible_host=95.54.0.16   # ip=10.3.0.5 etcd_member_name=etcd5
    # node6 ansible_host=95.54.0.17   # ip=10.3.0.6 etcd_member_name=etcd6
    
    # ## configure a bastion host if your nodes are not directly reachable
    # [bastion]
    # bastion ansible_host=x.x.x.x ansible_user=some_user
    
    [kube_control_plane]
    node1
    # node2
    # node3
    
    [etcd]
     node1
    # node2
    # node3
    
    [kube_node]
    node2
    node3
    # node4
    # node5
    # node6
    
    [calico_rr]
    
    [k8s_cluster:children]
    kube_control_plane
    kube_node
    calico_rr
    

    в. Задеплоить Kubernetes на подготовленные ранее инстансы, в случае нехватки каких-либо ресурсов вы всегда можете создать их при помощи Terraform.

    ansible-playbook -i inventory/mycluster/inventory.ini cluster.yml -b -v
    

    Alt_text

    1. Альтернативный вариант: воспользуйтесь сервисом Yandex Managed Service for Kubernetes
      а. С помощью terraform resource для kubernetes создать региональный мастер kubernetes с размещением нод в разных 3 подсетях
      б. С помощью terraform resource для kubernetes node group

    Альтернативный вариант рассмотрен в одном из домашних заданий, где бул подготовлен соответствующий манифест, но для выполнения дипломного проекта готовый кластер Yandex Managed Service for Kubernetes дороже подготовленного самостоятельно в варианте 1.

    Ожидаемый результат:

    1. Работоспособный Kubernetes кластер. Подключаемся к control-plane ноде
    ssh leo@51.250.71.251
    

    Проверяем, что кластер состоит из одной control-plane ноды и двух worker нод

    kubectl get nodes
    

    Alt_text 3. В файле ~/.kube/config находятся данные для доступа к кластеру.

    cat ~/.kube/config
    

    Alt_text

    1. Команда kubectl get pods --all-namespaces отрабатывает без ошибок.

    Alt_text


    В качестве промежуточного итога прилагаю terraform манифест и файлы, необходимые для создания инфраструктуры, описанной выше:

    main.tf

    variables.tf

    providers.tf

    outputs.tf

    cloud-init.yaml

    inventory.ini

    Создание тестового приложения

    Для перехода к следующему этапу необходимо подготовить тестовое приложение, эмулирующее основное приложение разрабатываемое вашей компанией.

    Способ подготовки:

    1. Рекомендуемый вариант:
      а. Создайте отдельный git репозиторий с простым nginx конфигом, который будет отдавать статические данные.

    В соответствии с рекомендуемым вариантом создан репозиторий nginx-static

    Далее копируем репозиторий на виртуальную машину в одноименную директорию

    git init
    git clone https://github.com/LeonidKhoroshev/nginx-static.git
    

    Создаем внутри проекта директорию static и в ней указываем конфигурацию файла основной стартовой страницы index.html и styles.css, созданного для улучшения внешнего вида нашей веб-страницы. Также создаем директорию images для хранения там фоновой картинки.

    б. Подготовьте Dockerfile для создания образа приложения.

    Переходим в корневую директорию и создаем Dockerfile следующей конфигурации

    FROM nginx:latest
    
    COPY ./static /usr/share/nginx/html
    
    EXPOSE 80
    

    Сохраняем изменения в ветке main нашего репозитория:

    git add .
    git commit -m "first commit"
    git push https://github.com/LeonidKhoroshev/nginx-static main
    

    Далее авторизовываемся на DockerHub и в консоли и собираем docker образ

    docker login
    docker build -t leonid1984/nginx-static:latest .
    

    Alt_text

    И размещаем его в нашем хранилище на DockerHub

    docker push leonid1984/nginx-static:latest
    

    Alt_text

    Alt_text

    1. Альтернативный вариант:
      а. Используйте любой другой код, главное, чтобы был самостоятельно создан Dockerfile.

    Ожидаемый результат:

    1. Git репозиторий с тестовым приложением и Dockerfile.
    2. Регистри с собранным docker image. В качестве регистри может быть DockerHub или Yandex Container Registry, созданный также с помощью terraform.

    Подготовка cистемы мониторинга и деплой приложения

    Уже должны быть готовы конфигурации для автоматического создания облачной инфраструктуры и поднятия Kubernetes кластера.
    Теперь необходимо подготовить конфигурационные файлы для настройки нашего Kubernetes кластера.

    Цель: 1. Задеплоить в кластер prometheus, grafana, alertmanager, экспортер основных метрик Kubernetes.

    Выполним установку вышеуказанных мониторингов через helm. Сначала установим helm на control-plane ноду

    curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
    

    Создаем отдельное простанство имен для мониторинга

    kubectl create namespace monitoring
    

    Добавляем репозиторий helm c prometheus

    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    

    Устанавливаем kube-prometheus-stack (установка Prometheus, Grafana, Alertmanager, node-exporter и kube-state-metrics)

    helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring
    

    Проверяем, что все поры работают нормально

    kubectl get pods -n monitoring
    

    Alt_text

    Получаем пароль от Grafana

    kubectl get secret -n monitoring prometheus-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
    

    Настраиваем доступ к Grafana по внешнему ip адресу, для чего создаем файл values.yml

    grafana:
      service:
        type: NodePort
        nodePort: 32000 
    

    Обновляем helm чарт

    helm upgrade prometheus prometheus-community/kube-prometheus-stack -n monitoring -f values.yml
    

    Alt_text

    Далее настраиваем необходимые метрики, так для k8s кластера. Для простоты воспользуемся готовым дашбордом из того, что предлагает Grafana - ID 315

    Проверем наличие новых дашбордов

    Alt_text

    Видим, что из кластера поступают данные, но в пока мы не деплоили наше приложение, мониторинг не слишком информативен ввиду отсутствия рабочей нагрузки

    Alt_text

    1. Задеплоить тестовое приложение, например, nginx сервер отдающий статическую страницу.

    Cоздаем деплой nginx-deployment.yml, куда прописываем следующую конфигурацию

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-static
      labels:
        app: nginx-static
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx-static
      template:
        metadata:
          labels:
            app: nginx-static
        spec:
          containers:
            - name: nginx
              image: leonid1984/nginx-static:latest
              ports:
                - containerPort: 80
    

    Также нам необходим сервис nginx-service.yml

    piVersion: v1
    kind: Service
    metadata:
      name: nginx-static
      labels:
        app: nginx-static
    spec:
      type: NodePort
      ports:
        - port: 80
          targetPort: 80
          nodePort: 32001
      selector:
        app: nginx-static
    

    Применяем изменения и проверяем результат

    kubectl apply -f nginx-deployment.yml
    kubectl apply -f nginx-service.yml
    kubectl get pods -l app=nginx-static
    

    Alt_text Alt_text

    Способ выполнения: 1. Воспользоваться пакетом kube-prometheus, который уже включает в себя Kubernetes оператор для grafana, prometheus, alertmanager и node_exporter. Альтернативный вариант - использовать набор helm чартов от bitnami.

    1. Если на первом этапе вы не воспользовались Terraform Cloud, то задеплойте и настройте в кластере atlantis для отслеживания изменений инфраструктуры. Альтернативный вариант 3 задания: вместо Terraform Cloud или atlantis настройте на автоматический запуск и применение конфигурации terraform из вашего git-репозитория в выбранной вами CI-CD системе при любом комите в main ветку. Предоставьте скриншоты работы пайплайна из CI/CD системы.

    Ожидаемый результат: 1. Git репозиторий с конфигурационными файлами для настройки Kubernetes (в качестве конфигурационных файлов представлены деплой nginx-deployment.yml и сервис nginx-service.yml для развертывания нашей тестовой страницы). 2. Http доступ к web интерфейсу grafana. 3. Дашборды в grafana отображающие состояние Kubernetes кластера. 4. Http доступ к тестовому приложению.


    Установка и настройка CI/CD

    Осталось настроить ci/cd систему для автоматической сборки docker image и деплоя приложения при изменении кода.

    Цель:

    1. Автоматическая сборка docker образа при коммите в репозиторий с тестовым приложением.
    2. Автоматический деплой нового docker образа.

    Можно использовать teamcity, jenkins, GitLab CI или GitHub Actions.

    Для настройки CI/CD процессов нашего проекта выбран Jenkins как наиболее широко применяемое open-source решение. Ранее в соответствующем модуле обучения установка Jenkins была описана посредством создания двух виртуальных машин jenkins-master и jenkins-agent на базе следующего кода terraform. В целях экономии вычислительных ресурсов, а также общей архитектуры и логики работы нашей инфраструктуры в данном задании выбран вариант запуска Jenkins в k8s по следующей инструкции.

    Копируем репозиторий

    git clone https://github.com/scriptcamp/kubernetes-jenkins
    

    Создаем новое пространство имен, чтобы было проще отслеживать работу подов и сервисов

    kubectl create namespace devops-tools
    

    Создаем сервисный аккаунт для Jenkins, оставляя без изменений файл serviceAccount.yaml из скачанного репозитория

    kubectl apply -f serviceAccount.yaml
    

    Указываем в deployment.yaml тома постоянного хранения данных (настройки пользователя, пайплайны и т.д., так как наш кластер использует ради экономии прерываемые виртуальные машины).

    volumeMounts:
                - name: jenkins-data
                  mountPath: /var/jenkins_home
                - name: docker-socket
                  mountPath: /var/run/docker.sock
                - name: docker-bin
                  mountPath: /tmp/docker-bin
    
    volumes:
            - name: jenkins-data
              persistentVolumeClaim:
                claimName: jenkins-pvc
            - name: docker-socket
              hostPath:
                path: /var/run/docker.sock
            - name: docker-bin
              emptyDir: {}
    

    И соответственно создаем требуемый persistent volume

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: jenkins-pv
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Retain
      hostPath:
        path: "/var/jenkins_home"
    

    А также persistent volume claim

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: jenkins-pvc
      namespace: devops-tools
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
    

    Для корректной работы необходимо создать директорию /var/jenkins_home на всех нодах нашего кластера, для чего необходимо в deployment.yaml добавить инитконтейнер, устанавливающий docker и git

    initContainers:
            - name: install-docker-git
              image: ubuntu:22.04
              command:
              - sh
              - -c
              - |
                apt-get update && \
                apt-get install -y curl gnupg && \
                curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
                echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bullseye stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
                apt-get update && \
                apt-get install -y docker-ce-cli && \
                mkdir -p /tmp/docker-bin && \
                cp /usr/bin/docker /tmp/docker-bin/docker
                apt-get install -y git
              volumeMounts:
              - name: docker-bin
                mountPath: /tmp/docker-bin
              - name: jenkins-data
                mountPath: /var/jenkins_home
              - name: docker-socket
                mountPath: /var/run/docker.sock
    

    Применяем изменения и проверяем успешный запуск Jenkins

    kubectl apply -f pv.yaml
    kubectl apply -f pvc.yaml
    kubectl apply -f deployment.yaml
    kubectl get deployments -n devops-tools
    kubectl get pods -n devops-tools
    

    Alt_text

    Далее создаем соответствующий сервис. Незначительно корректируем дефортный файл service.yaml из скачанного репозитория, указав nodePort: 32002, так как дефолтный порт 32000 уже занят мониторингом (Grafana), а на порту 32001 работает наш сервер nginx.

    Запускаем сервис

    kubectl apply -f service.yaml
    

    Для первого входа через веб-интерфейс определяем пароль

    kubectl logs jenkins-cf789dc4d-l2v56 --namespace=devops-tools
    

    Alt_text

    Входим в графический интерфейс и устанавливаем плагины, предлагаемые Jenkins. Далее осуществляем стандартную настройку Jenkins, указывая логин, пароль и электронную почту в соответствующих пунктах меню. Далее копирую ссылку url для быстрого доступа (для упрощения задания ip адреса виртуальных машин, участвующих в проекте сделаны статическими).

    http://89.169.145.151:32002/
    

    Далее необходимо настроить pipeline. Сборка и отправка в регистр docker-image по условиям задания должна осуществляться при любом коммите в репозитории.

    Для этого переходим в репозиторий и создаем webhook в веб-интерфейсе GitHub

    Alt_text

    Также необходимо настроить Docker Credentials в веб-интерфейсе Jenkins.

    Alt_text

    Далее настраиваем агенты для сборки на основе Kubernetes pod. Для этого сначала установим плагин Kubernetes для Jenkins, далее создаем новое облако Kubernetes, в настройках прописываем пространство имен devops-tools, в котором развернут под с Jenkins и также через графический интерфейс тестируем соединение с кластером.

    Alt_text

    Убедившись в наличии подключения, добавляем шаблон пода, который будет являться нашим сборочным агентом. Задаем название jenkins-agent, указываем пространство имен и image inbound-agent

    Alt_text

    Подробная инструкция по созданию и настройке агента доступна по ссылке. Поскольку после публикации статьи в Jenkins прошел ряд обновлений, то не вся информация в ней актуальна (например названия плагинов), но в целом описанный метод является рабочим (на период сентября 2024 года).

    Также для автоматизации нашего проекта необходима организация доступа через токен к DockerHub, Получаем токен в личном кабинете на https://app.docker.com

    Alt_text

    Сохраняем токен в credentials

    Alt_text

    После того как предварительная настройка Jenkins произведена, создадим pipeline для нашего проекта

    pipeline {
        agent any  
    
        environment {
            DOCKER_HUB_REPO = 'leonid1984/nginx-static'
            DOCKER_CREDENTIALS_ID = 'docker-hub-credentials'  // ID учетных данных Docker Hub в Jenkins
            KUBECONFIG_CREDENTIALS_ID = 'kubeconfig-credentials'  // ID учетных данных для подключения к Kubernetes в Jenkins
        }
    
        stages {
            stage('Checkout') {
                steps {
                    // Получение кода из GitHub
                    git branch: 'main', url: 'https://github.com/LeonidKhoroshev/nginx-static.git'
                }
            }
            
            stage('Build Docker Image') {
                steps {
                    script {
                        // Получение текущего тега, если есть
                        def tag = env.GIT_TAG_NAME ?: 'latest'
                        // Сборка Docker-образа
                        sh "docker build -t ${DOCKER_HUB_REPO}:${tag} ."
                    }
                }
            }
            
            stage('Push to Docker Hub') {
               steps {
                 withCredentials([string(credentialsId: 'docker_hub_pat', variable: 'DOCKER_HUB_PAT')]) {
                   sh """
                   echo $DOCKER_HUB_PAT | docker login -u leonid1984 --password-stdin
                   docker push leonid1984/nginx-static:latest
                   """
                }
            }
        }
            
            stage('Deploy to Kubernetes') {
                when {
                    tag "v*" // Деплой выполняется только при создании тега
                }
                steps {
                    script {
                        withCredentials([file(credentialsId: KUBECONFIG_CREDENTIALS_ID, variable: 'KUBECONFIG')]) {
                            def tag = env.GIT_TAG_NAME ?: 'latest'
                            // Применение конфигурации деплоя в Kubernetes
                            sh """
                            kubectl set image deployment/nginx-static-deployment nginx-static=${DOCKER_HUB_REPO}:${tag}
                            kubectl rollout status deployment/nginx-static-deployment
                            """
                        }
                    }
                }
            }
        }
        
        post {
            success {
                echo 'Pipeline completed successfully!'
            }
            failure {
                echo 'Pipeline failed!'
            }
        }
    }
    

    Проверяем работу pipeline

    Alt_text

    Сборка прошла успешно

    Ожидаемый результат:

    1. Интерфейс ci/cd сервиса доступен по http.
    2. При любом коммите в репозитории с тестовым приложением происходит сборка и отправка в регистр Docker образа.
    3. При создании тега (например, v1.0.0) происходит сборка и отправка с соответствующим label в регистри, а также деплой соответствующего Docker образа в кластер Kubernetes.

    Что необходимо для сдачи задания?

    1. Репозиторий с конфигурационными файлами Terraform и готовность продемонстрировать создание всех ресурсов с нуля.
    2. Пример pull request с комментариями созданными atlantis’ом или снимки экрана из Terraform Cloud или вашего CI-CD-terraform pipeline.

    Alt_text Alt_text Alt_text Alt_text Alt_text

    1. Репозиторий с конфигурацией ansible, если был выбран способ создания Kubernetes кластера при помощи ansible.
    2. Репозиторий с Dockerfile тестового приложения и ссылка на собранный docker image.
    3. Репозиторий с конфигурацией Kubernetes кластера (в моем случае конфигурация кластера задана в репозитории с kubespray в файле inventory.ini ).
    4. Ссылка на тестовое приложение и веб интерфейс Grafana с данными доступа: логин - admin пароль - prom-operator.
    5. Все репозитории рекомендуется хранить на одном ресурсе (github, gitlab).

    Дополнительно прилагаю файлы для развертывания Jenkins в k8s

    deployment.yaml

    namespace.yaml

    pv.yaml

    pvc.yaml

    service.yaml

    serviceAccount.yaml

    Конвейеры
    0 успешных
    0 с ошибкой