![](https://habrastorage.org/getpro/habr/upload_files/79f/32a/5c0/79f32a5c09520178a2c42837027a4225.png)
Отладка в контейнерной среде – дело не самое простое, поэтому разработчики зачастую прибегают к неэффективным методам локализации ошибок на этапе развертывания. Допустим, вам надо отладить Kubernetes-оператор, потому что согласование кластера (reconciliation process) выполняется как-то не так или сам оператор работает неправильно. Чтобы понять, что происходит в продакшен-кластере, можно развернуть этот оператор на локальном Kubernetes-кластере, который крутится на вашем ноутбуке. Затем надо будет написать соответствующий код для отладки оператора, собрать новый отладочный образ, развернуть его на локальном кластере, и, наконец, отследить вывод и журналы на предмет информации, которая поможет исправить ошибку. Долго? Да, долго. Так что быстрее и красивее будет использовать отладчик на удаленном сервере, задав точки останова для поиска причин неполадок.
Именно этот, второй и красивый, вариант мы рассмотрим сегодня и покажем, как запускать отладчик на кластере Kubernetes через IDE-среду Visual Studio Code (VS Code). В качестве приложения у нас будет программа на Go, но всё изложенное вполне применимо и к другим языкам программирования и отладчикам.
Какие есть варианты отладки
Для начала разберем упомянутые выше методы отладки, чтобы понять, в чем красота второго варианта.
Отладка через вывод
Контроль вывода – это самый простой способ развертывания и отладки приложений и операторов. Вам просто нужно продумать, где в коде расставить контрольные точки и добавить в них команды вывода отладочной информации.
Проблема в том, что каждый раз, когда вы захотите добавить, убрать или изменить такой вывод, придется заново пересобирать образ – а на это требуется время. Плюс, добавьте время на то, чтобы развернуть оператор на кластере, прежде чем выполнять тестирование.
Кроме того, в циклах согласования для комплексных операторов (тысячи и более строк на цикл) трудно бывает сразу определиться, какая именно информация понадобится для отладки. В результате, вам понадобится несколько попыток на то, чтобы локализовать проблему, и лишь потом уже приступить к отладке. Кроме того, при таком способе легко упустить из виду важные (с отладочной точки зрения) места в коде и не вставить туда команды вывода отладочной информации. В общем, при таком варианте отладки можно потерять массу времени, особенно, если вы не очень хорошо разбираетесь в отлаживаемом коде.
Отладка на удаленном сервере
Суть этого метода в том, чтобы использовать классический отладчик для получения информации, помогающей быстро понять причину проблемы. Процесс, который мы рассматриваем в этой статье, выполняется на удаленном сервере в среде Kubernetes, допустим, в промежуточной (staging) среде, которая эмулирует продакшен. Более того, вы создаете образы одновременно с тем, как вносите исправления в код, раз за разом развертывая оператор, и тем самым сокращаете время получения отладочной информации.
Итак, ключевые особенности метода удаленной отладки:
Выполняется удаленно, в тестовой, промежуточно или продакшен-среде.
Используется интерактивный отладчик (в нашем случае, это Delve для программ на Go).
Автоматическая сборка и развертывание образов при запуске отладчика.
А теперь подробно рассмотрим, как организовать удаленную отладку.
Предварительные требования
Расскажем, как настроить среду и инструменты для нашего приложения-примера. Мы будем использовать кластер Kubernetes на удаленном сервере и с помощью одного расширения для VS Code будем удаленной запускать там отладчик и наше приложение. Для этого мы будем использовать следующую среду:
Ноутбук разработчика:
ОС: macOS Big Sur 11.6
VS Code IDE и расширения
На ноутбуке установлена VS Code, но без инструментов развёртывания, вроде Docker, kubectl, и т. п.
Сервер (hostname alkmini):
Linux (Fedora 5.11.8-200.fc33.x86_64)
SSH-server
Работающий Docker daemon
kubectl и oc (интерфейс командной строки для Red Hat OpenShift)
Delve (отладчик для Go)
Кластер Kubernetes (в нашем примере, с использованием minikube)
Для удаленного сервера также можно использовать промежуточный кластер, локальный кластер или какую-то другую конфигурацию. Здесь важно вот что:
На локальной машине вам нужна только среда разработки.
На удаленном сервер среда разработки не нужна.
Установка необходимых инструментов и расширений VS Code
Первым делом установим расширения VS Code, чтобы потом запускать всё удаленно.
Расширение Remote - SSH
Это расширение позволяет использовать VS Code на вашем ноутбуке для работы на удаленном сервере так, как будто вы работаете на своей локальной машине. Расширение использует SSH для подключения к удаленному серверу и выполнения команд на нем, а также для использования других расширений VS Code.
Расширение Remote - SSH устанавливается из магазина приложений Visual Studio.
Добавим в него наш сервер и откроем папку как рабочую область, чтобы начать работать удаленно. Добавим информацию о хосте и проверим, что у нас есть SSH-доступ к нему (через ранее добавленный SSH ключ). Затем переходим к папке, которую хотим добавить, как показано на Рис.1.
![Рис. 1. Расширение Remote - SSH обеспечивает доступ к файлам на удаленных системах, как будто они расположены на локальной машине. Рис. 1. Расширение Remote - SSH обеспечивает доступ к файлам на удаленных системах, как будто они расположены на локальной машине.](https://habrastorage.org/getpro/habr/upload_files/7ec/ecd/217/7ececd217fd6fb3bb65f13dc5b6b7035.png)
О том, что вы работает удаленно, информирует зеленый индикатор SSH-соединения в левом нижнем углу, как показано на Рис. 2.
![Рис. 2. В режиме удаленной работы в левом нижнем углу экрана отображается SSH-соединение. Рис. 2. В режиме удаленной работы в левом нижнем углу экрана отображается SSH-соединение.](https://habrastorage.org/getpro/habr/upload_files/5f1/e73/9ba/5f1e739ba4bef6fe7fb811e47ae7c4b5.png)
Теперь всё, что вы делаете на своем ноутбуке в редакторе VS Code, выполняется на удаленном сервере.
Продумайте, какие расширения нужны вам на удаленном сервере. Скорее всего, это лишь часть тех расширений, которые установлены на локальной системе.
Расширение Cloud Code Kubernetes
Это расширение обеспечивает непрерывную локальную обратную связь с вашим проектом в процессе редактирования, сборки, развертывания и запуска приложений локально или в облаке. Расширение взаимодействует с контейнерными инструментами командной строки Google, такими как Skaffold, minikube и kubectl. Cloud Code изначально был разработан для использования с Google Cloud Platform, но теперь работает на любых кластерах Kubernetes, в частности здесь мы используем на Kubernetes-кластере minikube.
Расширение Cloud Code устанавливается из магазина приложений Visual Studio.
Примечание. На момент написания этой статьи в последней версии Cloud Code была обнаружена ошибка, поэтому надо устанавливать предыдущую версию Cloud Code 1.14.1.
Delve
Delve (dlv) –это официальный отладчик для языка программирования Go. Он удобен как в плане вызова, так и плане использования, и предоставляет простой и вместе с тем полнофункциональный инструмент отладки для Go. Если вы используете отладчик, то, скорее всего, дела идут не очень хорошо. Поэтому Delve, насколько это возможно, постарается не мешать вам.
Delve устанавливается из репозитория GitHub следующим образом:
bash
$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/dlv
Чтобы отлаживать приложение на удаленном сервере с локальной машины, используя редактор VS Code и расширение Remote – SSH, на удаленном сервере должен быть запущен отладчик Delve.
Конфигурация
В этом разделе мы в пошагового режиме разберем удаленную отладку. А также рассмотрим репозиторий и конфигурации, необходимые для интерактивной отладки приложения в кластере Kubernetes.
Репозиторий
Загрузить пример приложения Go можно из репозитория GitHub. Содержимое этого репозитория следующее:
alknopfler:alkmini : ~/projects/src/github.com/alknopfler/go-remote-debug-delve {master}
$ tree .
.
|-- .vscode
| |-- settings.json
| |-- launch.json
├── docker
│ └── debug
│ └── Dockerfile
├── go.mod
├── k8s
│ └── deployment.yml
├── main.go
├── Makefile
├── README.md
└── skaffold.yaml
Ключевые элементы здесь следующие:
Папка docker – содержит Dockerfile для генерации образа, который мы затем отправим в реестр Quay.io.
Папка k8s – содержит манифест, описывающий развертывание приложения.
Main.go – основной файл кода приложения.
Skaffold.yaml – конфигурационный файл, используемый для сборки и развертывания приложения.
.vscode/launch.json – конфигурация для расширения Cloud Code.
Настройка Cloud Code
Откроем файл .vscode/launch.json и добавим параметры конфигурации, отмеченные стрелками на Рис. 3 (мы опишем эти параметры ниже).
![Рис.3. Добавление параметров конфигурации для расширения Cloud Code. Рис.3. Добавление параметров конфигурации для расширения Cloud Code.](https://habrastorage.org/getpro/habr/upload_files/3e8/733/105/3e873310557913138643c54db6832035.png)
Параметр Attach (Go) to k8s Pod
Мы используем эту опцию, чтобы подключаться к запущенному pod’у в кластере Kubernetes для отладки. Если образ был собран с установленным отладчиком Delve, то в коде можно задать точку останова. Отладка приложения выполняется путем подключения к его podselector’у и порту, как показано в следующей конфигурации:
yaml
{
"name": "Attach to Kubernetes Pod (Go)",
"type": "cloudcode.kubernetes",
"request": "attach",
"language": "Go",
"debugPort": 40000,
"podSelector": {
"app": "server-debug"
},
"localRoot": "${workspaceFolder}",
"remotePath": "",
"remoteRoot": "/"
}
При этом надо обязательно настроить свойства remotePath и remoteRoot. Иначе, к pod’у можно будет подключаться, но не отлаживаться, поскольку точка останова окажется «неверифицированной».
Параметр Run/Debug Kubernetes App
Конфигурация для этой опции выглядит следующим образом:
`yaml
{
"name": "Kubernetes: Run/Debug",
"type": "cloudcode.kubernetes",
"request": "launch",
"autoStop": false,
"skaffoldConfig": "${workspaceFolder}/skaffold.yaml",
"watch": true,
"cleanUp": true,
"portForward": true,
"imageRegistry": "quay.io",
"debug": [
{
"image": "quay.io/amorgant/server-debug",
"containerName": "server-debug",
"sourceFileMap": {
"${workspaceFolder}": ""
}
}
]
},
Поясним некоторые моменты:
autoStop: – если задано true, сеанс отладки автоматически прекращается при завершении работы приложения.
SkaffoldConfig: – это конфигурационный файл, с помощью которого происходит непрерывная сборка и развертывание приложения.
watch: – это самая важная опция для интерактивной отладки. Она уведомляет Skaffold об изменениях в коде, чтобы Skaffold мог выполнять пересборку и повторно развёртывание образа, когда мы отлаживаем и меняем код. Кроме того, если включена опция auto-save, этот процесс будет работать постоянно.
cleanUp: – это опция возврата сервера в чистое состоянии по завершении сеанса. Иначе говоря, уничтожения контейнеров и образов, созданных Skaffold’ом после того, как вы останавливаете отладчик.
portForward: – эта опция позволяет запускать отладчик удаленно, перенаправляя порт в локальную среду.
debug/image: – это образ, который будет собираться с уже установленным в него отладчиком Delve и нашим приложением.
Если вкратце, то для отладки работающего pod’а можно использовать расширение Cloud Code вместе с опцией Attach (Go) to k8s Pod. Когда вы что-то меняете в своем коде, Skaffold выполняет пересборку и повторное развертывание образа, чтобы вы тут же венмогли продолжить отладку. Сочетание двух этих опций и обеспечивает волшебство удаленной отладки приложений и операторов Kubernetes.
Настройка Skaffold
Skaffold – это очень интересный инструмент для создания и развертывания приложений непосредственно с помощью уже рассмотренного расширения Cloud Code. Вам достаточно лишь скопировать манифест, чтобы создать конвейера для своего приложения и запустить расширение Cloud Code, чтобы использовать этот манифеста. У Skaffold тоже есть ряд важных опций, которые мы рассмотрим чуть ниже.
На Рис. 4 показана схема работы Skaffoldпри сборке и развертывании приложений. Этот процесс не происходит мгновенно, поскольку сборка занимает некоторое ненулевое время, но тем менее он обеспечивает итеративную отладку и пересборку, не заставляя останавливать отладчик.
![Рис. 4. Схема работы Skaffold при автоматизированной сборке и развертывании. Рис. 4. Схема работы Skaffold при автоматизированной сборке и развертывании.](https://habrastorage.org/getpro/habr/upload_files/faa/0b0/66f/faa0b066ffb99b36fa5a7c6dd471a02a.png)
При этом мы используем следующий конфигурационный файл:
yaml
apiVersion: skaffold/v2beta19
kind: Config
build:
artifacts:
- image: quay.io/amorgant/server-debug
buildpacks:
builder: gcr.io/buildpacks/builder:v1
context: .
sync:
auto: true
local:
push: true
deploy:
kubectl:
manifests:
- k8s/deployment.yml
На этапе сборки Skaffold можно использовать различные builder’ы, включая такие широко распространенные инструменты, как Dockerfile, Maven и Buildpacks, а также настраиваемые пользовательские скрипты. В нашем примере используется Buildpacks, который автоматически детектирует ваш Dockerfile и использует его для сборки:
yaml
buildpacks:
builder: gcr.io/buildpacks/builder:v1
В больших проектах, где есть несколько этапов сборки со сложными Makefiles, можно использовать конфигурацию вроде такой:
yaml
buildpacks:
custom:
buildCommand: |
sudo -S skipper make update-debug-minimal
docker tag quay.io/ocpmetal/assisted-service:latest quay.io/amorgant/assisted-service:latest
docker push quay.io/amorgant/assisted-service:latest
Также сборку можно выполнить, используя свою спецификацию Dockerfile, например, так:
yaml
docker:
dockerfile: deploy/Dockerfile
Для этапе развертывания Skaffold также можно использовать разные deployer’ы: Kubernetes, Helm, Kustomize и Docker.
Конфигурация Dockerfile для отладки
Для отладки приложения, его образ надо собирать вместе с Delve, чтобы иметь возможность запускать приложение через точку входа (entry point) команды dlv:
Dockerfile
FROM golang:1.16 AS build
WORKDIR /
COPY . .
RUN CGO_ENABLED=0 go get -ldflags "-s -w -extldflags '-static'" github.com/go-delve/delve/cmd/dlv
RUN CGO_ENABLED=0 go build -gcflags "all=-N -l" -o ./app
FROM alpine
WORKDIR /
COPY . .
COPY --from=build /go/bin/dlv dlv
COPY --from=build /app app
ENTRYPOINT [ "/dlv" , "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/app"]]
Точка входа запускает приложение с помощью команды dlv, что позволяет отлаживать его на удаленном сервере.
Развертывание Kubernetes
В нашем примере используется базовое развертывание приложения Kubernetes. Это простое развертывание с одним контейнером с использованием образа, который мы только что создали:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: server-debug
spec:
selector:
matchLabels:
app: server-debug
template:
metadata:
labels:
app: server-debug
spec:
containers:
- name: server-debug
image: quay.io/amorgant/server-debug
imagePullPolicy: Always
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
ports:
- containerPort: 8080
Удаленная отладка в Kubernetes – демо
Как только вся конфигурация будет готова, откроем расширение Cloud Code и выполним следующие шаги.
Сначала проверим, что кластер готов, и мы можем обращаться к нему из VS Code и расширения Cloud Code, как показано на Рис. 5.
![Рис. 5. Развертывания и pod’ы доступны из VS Code. Рис. 5. Развертывания и pod’ы доступны из VS Code.](https://habrastorage.org/getpro/habr/upload_files/9c1/12e/b3b/9c112eb3b8dc2057244a5bdd6f66ab30.png)
Затем зададим в коде точку останова в коде, как показано на Рис. 6.
![Рис. 6. Задаем точку останова в Main.go. Рис. 6. Задаем точку останова в Main.go.](https://habrastorage.org/getpro/habr/upload_files/747/996/07f/74799607f62977dba3b0fae83accfd87.png)
А теперь щелкаем мышкой кнопку Debug on Kubernetes, чтобы запустить процесс, как показано на Рис. 7.
![Рис. 7. Кнопка Debug on Kubernetes в расширении Cloud Code. Рис. 7. Кнопка Debug on Kubernetes в расширении Cloud Code.](https://habrastorage.org/getpro/habr/upload_files/b65/a1c/697/b65a1c697ea858a40069a7dbef661009.png)
Теперь, когда наше приложение запускается, оно работает через отладчик, как показано в этом видео:
Ограничения и быстрые исправления
Чтобы воспроизвести описанный выше процесс у себя, надо скопировать проект внутрь Dockerfile, поскольку отладчику нужен доступ к стеку, чтобы знать, какая трасса является трассой для отладки. Делается это так:
WORKDIR /
COPY . .
Полезные ресурсы
Основные инструменты, упоминавшиеся в этой статье: