В современном DevOps для эффективного, надежного и последовательного развертывания программного обеспечения важна автоматизация. Весь процесс разработки и выпуска ПО оптимизируется при помощи мощного инструмента GitLab CI/CD. Развернем с ним приложение Java в мультикластере Kubernetes, управляемом на AWS и настраиваемом в eksctl. Рассмотрим процесс от создания кластера Kubernetes до развертывания контейнеризованного приложения Java, попутно интегрируя инструменты DevOps Trivy и SonarQube.

Рабочий процесс пошагово

Обзор проекта

  1. Регистрация экземпляра AWS в качестве GitLab Runner
  2. Создание конвейера GitLab CI/CD
  3. Модульное тестирование с Maven
  4. Сканирование зависимостей с Trivy
  5. Анализ качества кода с SonarQube
  6. Сборка и контейнеризация приложения
  7. Сканирование образа контейнера с Trivy
  8. Отправка образа контейнера в реестр контейнеров GitLab
  9. Развертывание приложения в Kubernetes
  10. Настройка кластера AWS EKS с помощью eksctl
  11. Подключение кластера EKS к GitLab
  12. Запуск конвейера

Шаг 1. Создание проекта в Gitlab

Шаг 2. Настройка экземпляра AWS как агента Gitlab

  1. Заходим на консоль управления AWS.
  2. Запускаем экземпляр EC2 с такими характеристиками:
  • AMI  —  образ машины Amazon: выбираем Ubuntu или Amazon Linux.
  • Тип экземпляра: выбираем его под требования задания, например t2.micro для тестирования.
  • Группа безопасности: разрешаем экземпляру SSH-доступ.

Установка GitLab Runner на экземпляр EC2

  1. Подключаемся по SSH к экземпляру EC2.

2. Устанавливаем GitLab Runner:

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
sudo chmod +x /usr/local/bin/gitlab-runner
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

Регистрация GitLab Runner в проекте

  • Чтобы зарегистрировать этот экземпляр в качестве GitLab Runner проекта, нужен токен аутентификации.
  • Переходим в Project > settings > CI/CD > Runners.
  • Указываем соответствующее название тега:
  • Регистрируем экземпляр такой командой:
sudo gitlab-runner run

Следуем инструкциям:

  • Вводим URL-адрес экземпляра GitLab: https://gitlab.com/.
  • Указываем исполнителя: ради простоты выбираем shell, хотя можно воспользоваться Docker или другими исполнителями.
  • Проверяем в файле /home/ubuntu/.gitlab-runner/config.toml.

Шаг 3. Создание конвейера GitLab CI/CD

Подготовив инфраструктуру, приступим к определению конвейера GitLab CI/CD в файле .gitlab-ci.yml. Этапы конвейера: тестирование, сканирование, сборка, развертывание приложения Java и другие.

  1. Клонируем репозиторий в локальной среде:

2. Добавляем код и отправляем в репозиторий Github:

Шаг 4. Настройка конвейера: .gitlab-ci.yml

1. Этапы

  • Сначала задаем в файле этапы:

2. Необходимые инструменты

  • Прежде чем переходить к проекту, устанавливаем инструменты:
- sudo apt install -y openjdk-17-jre-headless  
- sudo apt install -y maven
- sudo apt-get install -y wget apt-transport-https gnupg lsb-release
- wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
- echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
- sudo apt -y install docker.io && chmod 666 /var/run/docker.sock
- sudo apt-get update && sudo apt-get install -y trivy
- sudo snap install kubectl --classic
  • Для этого предоставим пользователю gitlab-runner права суперпользователя, иначе эти команды не выполнятся.
  • Переходим в папку /etc/sudoers.d и редактируем файл ниже:

3. Модульное тестирование с Maven

Это первый этап, которым обеспечивается надежность приложения. Для запуска модульных тестов привлекается Maven:

mvn test
  • Этой командой компилируется код, запускаются определенные в проекте тесты. В выводе выявляются сбои или проблемы, которые необходимо устранить.

4. Сканирование зависимостей с Trivy

  • Trivy  —  это сканер уязвимостей для образов контейнеров, файловых систем, гит-репозиториев. Просканируем им зависимости приложения Java:
trivy fs --format table -o fs.html .
  • Этой командой в текущем каталоге сканируются уязвимости применяемых в приложении зависимостей, вывод сохраняется в файле fs.html.
  • Это важный этап для обеспечения безопасности проекта. Чтобы получить вывод, после мы «артефактируем» файл fs.html.
  • Запуская его, получаем примерно такой вывод:

5. Анализ качества кода с SonarQube

  • SonarQube  —  инструмент анализа исходного кода на предмет выявления багов, уязвимостей и кода с запашком.
  • Интегрируя SonarQube в конвейер сборки, вы поддерживаете высокое качество кода.
  • SonarQube состоит из двух частей: сканерной и серверной. Сначала настроим сервер вручную с помощью Docker:
  • Имя пользователя и пароль по умолчанию  —  admin.
  • Чтобы получить персональный токен, переходим в Project > Settings > Access Tokens и нажимаем Add Token:
  • Копируем код и вставляем в SonarQube:
  • Сохраняем настройку.
  • Вводим персональный токен и сохраняем.
  • Нажимаем Set up.
  • Выбираем other и копируем код:
  • Переходим в репозиторий Gitlab, создаем файл sonar-project.properties и вставляем в него этот код:
  • Сохраняем файл и нажимаем continue на портале SonarQube:
  • Затем переходим к следующему этапу Add environment variables:
  • Согласно второму этапу, создадим две переменные:
  • По завершении нажимаем continue на 2-м этапе SonarQube.
  • И получаем целиком задание, которое нужно скопировать и вставить в файл .gitlab-ci.yml:
  • Добавляем конвейер и сохраняем:
  • Запускаем конвейер и благодаря SonarQube видим на сервере примерно такой результат:

6. Сборка, контейнеризация и сканирование приложения

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

7. Отправка образа контейнера в реестр контейнеров GitLab

Созданный образ Docker отправляем на хранение в реестр контейнеров GitLab, где он будет доступен для развертывания:

  • Запуская это задание, находим добавленный в реестр контейнеров образ в Deploy > Container Registry:

8. Развертывание приложения в Kubernetes

Наконец, развертываем контейнеризованное приложение в кластере Kubernetes.

  • В этом задании декодируем детали конфигурации, закодированные ранее в целях безопасности:
  • Здесь создается переменная KUBE_CONTEXT project-path:k8s-agent и используется образ bitnami для kubectl.
  • В манифесте включено несколько переменных Gitlab, поэтому ключевым словом envsubst заменяем их на исходные значения.

deployment-service.yaml.template:

apiVersion: apps/v1
kind: Deployment # Создаваемый Kubernetes вид ресурса
metadata:
name: java-app
spec:
selector:
matchLabels:
app: java-app
replicas: 2 # Количество реплик, создаемых для этого развертывания
template:
metadata:
labels:
app: java-app
spec:
imagePullSecrets:
- name: registry-credentials # Для аутентификации в реестре контейнеров
containers:
- name: java-app-container
image: $CI_REGISTRY/harsh005/java-app/java-app:$CI_PIPELINE_ID # Образ, используемый для контейнеров в кластере
imagePullPolicy: Always
ports:
- containerPort: 8080 # Порт, на котором запускается контейнер в кластере


---

apiVersion: v1 # Версия API Kubernetes
kind: Service # Создаваемый Kubernetes вид ресурса
metadata: # Метаданные создаваемого вида ресурса
name: java-app-svc
spec:
selector:
app: java-app
ports:
- protocol: "TCP"
port: 80
targetPort: 8080
type: LoadBalancer # тип службы.

Этап 5. Настройка кластера AWS EKS с помощью eksctl

eksctl  —  простой инструмент CLI для создания кластеров и управления ими в EKS, его предпочитают за простоту и удобство. Вот как с помощью eksctl создается кластер EKS.

1. Установка eksctl

  • Нажимаем ссылку.
  • Устанавливаем версию для Windows.
  • Извлекаем папку на локальном компьютере:

2. Настройка интерфейса командной строки AWS

  • Загружаем инструмент интерфейса командной строки AWS на локальный компьютер:
  • Загружаем отсюда интерфейс командной строки AWS и настраиваем его для учетной записи.
  • Для этого в учетке создаем пользователя IAM.
  • В Security credentials нажимаем Create access key и создаем ключ доступа и секретный ключ, оба копируем:
  • Теперь в локальной командной строке вводим aws configure и вставляем туда эти учетные данные:
  • Вставляем ключ доступа в Access Key ID, а секретный ключ  —  в Secret Access Key ID. Указываем также регион по умолчанию.

Настроив интерфейс командной строки AWS, установим и инструмент клиента для Kubernetes, то есть Kubectl, с помощью которого приложения будут развертываться в кластере.

3. Создание кластера EKS

  • Создаем yaml-файл настройки кластера EKS:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
name: my-test-cluster
region: us-east-1

nodeGroups:
- name: small-nodegroup
instanceType: t2.micro
desiredCapacity: 2

- name: medium-nodegroup
instanceType: t2.small
desiredCapacity: 2
  • Теперь весь кластер запустится в EKS одной командой:
eksctl create cluster -f cluster-setup.yml

Этап 6. Подключение кластера EKS к GitLab

Создав кластер EKS, переходим к следующему этапу  —  подключению кластера к GitLab для управления развертываниями напрямую из конвейера сборки.

  • Создаем в репозитории конфигурационный файл агента .gitlab/agents/eks-k8s/config.yaml:
user_access:
access_as:
agent: {}
projects:
- id: <your-project-id>
  • Переходим в Operate («Управление») > Kubernetes Cluster («Кластер Kubernetes») > Connect a cluster («Подключить кластер»).
  • Выбираем агента из выпадающего списка и зарегистрируем:
  • После этого этапа копируем команды настройки токена и агента.
  • Чтобы развернуть GitLab-агент, запускаем эти команды в кластере:
helm repo add gitlab https://charts.gitlab.io
helm repo update
helm upgrade --install eks-k8s gitlab/gitlab-agent \
--namespace gitlab-agent-eks-k8s \
--create-namespace \
--set image.tag=v17.3.0-rc7 \
--set config.token= <your-token> \
--set config.kasAddress= <your-kasAddress>
  • После того как агент развернут, статус подключения в Gitlab становится Connected:
  • Теперь, чтобы извлечь образ при создании развертывания, добавим учетные данные реестра в манифесте Kubernetes.
  • Для этого создаем секрет registry-credentials.
  • Так что для этого этапа понадобится токен аутентификации в реестре.
  • Переходим в Settings («Настройки») > Repository («Репозиторий») > Display tokens («Показать токены») > Add Token («Добавить токен»):
  • Создаем токен развертывания и копируем учетные данные как имя пользователя и пароль для реестра контейнеров:
  • Переходим в кластер и создаем секрет реестра Docker registry-credentials:
kubectl create secret docker-registry registry-credentials --docker-server=registry.gitlab.com --docker-username=<1st-token> --docker-password=<2nd-token> --dry-run=client -o yaml > registry-credentials.yml

Этап 7. Запуск конвейера

Все настроено, пора запустить конвейер.

1. Коммитим и добавляем код

  • Коммитим в репозиторий GitLab файл .gitlab-ci.yml, исходный код Java и манифесты развертывания Kubernetes, Dockerfile:

.gitlab-ci.yml:

stages:
- pre-requisite
- unit_test
- trivy_scan
- sonar_test
- build_and_scan
- image_push
- deploy_to_eks

install_tools:
stage: pre-requisite
script:
- sudo apt install -y openjdk-17-jre-headless
- sudo apt install -y maven
- sudo apt-get install -y wget apt-transport-https gnupg lsb-release
- wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
- echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
- sudo apt-get update && sudo apt-get install -y trivy
- sudo apt -y install docker.io && sudo chmod 666 /var/run/docker.sock
- sudo snap install kubectl --classic
tags:
- dedicated-runner
only:
- main

unit_testing:
stage: unit_test
script:
- mvn test
tags:
- dedicated-runner
only:
- main

trivy_fs_scan:
stage: trivy_scan
script:
- trivy fs --format table -o fs.html .
tags:
- dedicated-runner
artifacts:
paths:
- fs.html
only:
- main

sonarqube-check:
stage: sonar_test
image:
name: sonarsource/sonar-scanner-cli:latest
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Определяется местоположение кеша задачи анализа.
GIT_DEPTH: "0" # В git дается указание извлечь все ветки проекта, требуемые задаче анализа
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner
allow_failure: true
only:
- main


image_build_&_scan:
stage: build_and_scan
variables:
Image_tag: $CI_REGISTRY/harsh005/java-app/java-app:$CI_PIPELINE_ID
script:
- mvn clean package
- docker build -t $Image_tag .
- trivy image $Image_tag --format table -o image.html
tags:
- dedicated-runner
artifacts:
paths:
- image.html
only:
- main

image_push:
stage: image_push
variables:
Image_tag: $CI_REGISTRY/harsh005/java-app/java-app:$CI_PIPELINE_ID
before_script:
- docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
script:
- docker push $Image_tag
tags:
- dedicated-runner
only:
- main

k8s-deploy:
stage: deploy_to_eks
variables:
KUBE_CONTEXT: harsh005/java-app:eks-k8s
image:
name: bitnami/kubectl:latest
entrypoint: ['']
before_script:
- kubectl config use-context "$KUBE_CONTEXT"
script:
- envsubst < deployment-service.yaml.template > deployment-service.yaml
- kubectl apply -f deployment-service.yaml
only:
- main
when: manual
  • Чтобы все задания выполнялись в ветке main, здесь также поместили элементы управления заданиями. И если все задания до этапа deploy_to_eks выполнены успешно, вручную запускается задание k8s-deploy, которым код развернется в самоуправляемом Kubernetes.

2. Активируем конвейер

  • После того как код добавлен, в GitLab файл .gitlab-ci.yml автоматически обнаруживается, и конвейер активируется:

3. Отслеживаем конвейер

  • Переходим в CI/CD («Непрерывная интеграция и непрерывное развертывание») > Pipelines («Конвейеры») проекта GitLab и отслеживаем ход выполнения.
  • В ветке test видим последовательное выполнение каждого этапа  —  установки, тестирования, сканирования, сборки и развертывания:
  • С результатами сканирования Trivy или тестирования SonarQube можно ознакомиться.
  • Завершив все тестирование и сборку, вручную активируем задание k8s-deploy для финального развертывания:

4. Просматриваем результаты конвейера

  • Логи выходных данных просматриваются для каждого задания. Если все настроено корректно, конвейер успешно завершается, и приложение Java развертывается в кластере Kubernetes:

5. Вывод

  • Копируем внешний ip-адрес  —  URL-адрес балансировщика нагрузки  —  и вставляем в браузер:

Заключение

С помощью GitLab CI/CD мы настроили и развернули приложение Java в самоуправляемом Kubernetes-мультикластере проекта. Провели при этом модульное тестирование, сканирование безопасности, анализ качества кода, сборку и контейнеризацию.

Этим конвейером автоматизируется процесс разработки и выпуска программного обеспечения, безопасно и эффективно осуществляется последовательное развертывание высококачественного кода. Запустив конвейер, мы увидели, как с GitLab CI/CD упрощаются сложные развертывания, а инструменты и процессы интегрируются в единый автоматизированный рабочий поток.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи @Harsh: Deploying Secure Java Applications on AWS EKS Using GitLab CI/CD, Maven, Trivy and SonarQube

Предыдущая статьяОзнакомление с Work Manager в Android