- Home »

Как использовать пакет Cobra в Go
Если вы админите сервера и наконец-то решили разобраться с Go, то наверняка сталкивались с задачей создания CLI-утилит. Хочется что-то простое, но мощное — чтобы можно было быстро накидать пару команд для управления конфигами, запуска скриптов или мониторинга. Пакет Cobra — это именно то, что нужно. Он используется в kubectl, docker, etcd и куче других инструментов, которые мы используем каждый день. Сегодня разберём, как с ним работать, чтобы вы могли создавать свои CLI-утилиты для автоматизации рутинных задач.
Что такое Cobra и зачем оно нужно
Cobra — это библиотека для создания CLI-приложений в Go. Она решает три основные задачи:
- Парсинг аргументов командной строки
- Создание иерархии команд (как у git: git commit, git push)
- Автоматическая генерация справки и автодополнения
Основные принципы работы:
- Команды — основные действия вашей утилиты
- Аргументы — позиционные параметры
- Флаги — именованные параметры (-v, –verbose)
Быстрый старт с Cobra
Начнём с простого примера — создадим утилиту для управления сервером:
mkdir server-cli
cd server-cli
go mod init server-cli
go get github.com/spf13/cobra@latest
Создаём основной файл main.go:
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "server-cli",
Short: "Утилита для управления сервером",
Long: "CLI-утилита для управления конфигурацией и мониторинга сервера",
}
var statusCmd = &cobra.Command{
Use: "status",
Short: "Показать статус сервера",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Сервер работает нормально")
},
}
var restartCmd = &cobra.Command{
Use: "restart [service]",
Short: "Перезапустить сервис",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
service := args[0]
fmt.Printf("Перезапускаем сервис: %s\n", service)
},
}
func init() {
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(restartCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Теперь можно собрать и протестировать:
go build -o server-cli
./server-cli --help
./server-cli status
./server-cli restart nginx
Работа с флагами
Флаги — это сердце любой CLI-утилиты. Cobra поддерживает persistent флаги (наследуются всеми подкомандами) и локальные флаги:
var verbose bool
var configFile string
func init() {
// Persistent флаг - доступен для всех команд
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "подробный вывод")
rootCmd.PersistentFlags().StringVar(&configFile, "config", "", "путь к конфигу")
// Локальный флаг только для restart
var force bool
restartCmd.Flags().BoolVarP(&force, "force", "f", false, "принудительный перезапуск")
// Обязательный флаг
restartCmd.MarkFlagRequired("force")
}
Обновим команду restart:
var restartCmd = &cobra.Command{
Use: "restart [service]",
Short: "Перезапустить сервис",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
service := args[0]
force, _ := cmd.Flags().GetBool("force")
if verbose {
fmt.Printf("Подробный режим включён\n")
}
if force {
fmt.Printf("Принудительно перезапускаем сервис: %s\n", service)
} else {
fmt.Printf("Мягко перезапускаем сервис: %s\n", service)
}
},
}
Практический пример: утилита для мониторинга
Создадим более сложную утилиту для мониторинга сервера. Она будет проверять состояние сервисов, использование ресурсов и отправлять уведомления:
package main
import (
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/spf13/cobra"
)
var (
configFile string
verbose bool
interval int
)
var rootCmd = &cobra.Command{
Use: "monitor",
Short: "Утилита мониторинга сервера",
}
var checkCmd = &cobra.Command{
Use: "check",
Short: "Проверить состояние системы",
}
var serviceCmd = &cobra.Command{
Use: "service [name]",
Short: "Проверить состояние сервиса",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
serviceName := args[0]
checkService(serviceName)
},
}
var diskCmd = &cobra.Command{
Use: "disk",
Short: "Проверить использование диска",
Run: func(cmd *cobra.Command, args []string) {
checkDisk()
},
}
var watchCmd = &cobra.Command{
Use: "watch",
Short: "Непрерывный мониторинг",
Run: func(cmd *cobra.Command, args []string) {
watchSystem()
},
}
func checkService(name string) {
cmd := exec.Command("systemctl", "is-active", name)
output, err := cmd.Output()
status := strings.TrimSpace(string(output))
if err != nil {
fmt.Printf("❌ Сервис %s: недоступен\n", name)
return
}
if status == "active" {
fmt.Printf("✅ Сервис %s: работает\n", name)
} else {
fmt.Printf("⚠️ Сервис %s: %s\n", name, status)
}
}
func checkDisk() {
cmd := exec.Command("df", "-h", "/")
output, err := cmd.Output()
if err != nil {
fmt.Printf("❌ Не удалось проверить диск: %v\n", err)
return
}
lines := strings.Split(string(output), "\n")
if len(lines) >= 2 {
fields := strings.Fields(lines[1])
if len(fields) >= 5 {
fmt.Printf("💾 Диск: использовано %s из %s (%s)\n",
fields[2], fields[1], fields[4])
}
}
}
func watchSystem() {
fmt.Printf("🔍 Запуск мониторинга (интервал: %d секунд)\n", interval)
for {
fmt.Printf("\n--- %s ---\n", time.Now().Format("15:04:05"))
// Проверяем основные сервисы
services := []string{"nginx", "ssh", "systemd-resolved"}
for _, service := range services {
checkService(service)
}
checkDisk()
time.Sleep(time.Duration(interval) * time.Second)
}
}
func init() {
rootCmd.PersistentFlags().StringVar(&configFile, "config", "", "путь к конфигу")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "подробный вывод")
watchCmd.Flags().IntVarP(&interval, "interval", "i", 10, "интервал проверки в секундах")
checkCmd.AddCommand(serviceCmd)
checkCmd.AddCommand(diskCmd)
rootCmd.AddCommand(checkCmd)
rootCmd.AddCommand(watchCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Теперь утилита поддерживает команды:
./monitor check service nginx
./monitor check disk
./monitor watch --interval 5
Продвинутые возможности
Cobra предоставляет множество дополнительных возможностей:
Валидация аргументов
var deployCmd = &cobra.Command{
Use: "deploy [environment]",
Short: "Развернуть приложение",
Args: cobra.MatchAll(
cobra.ExactArgs(1),
cobra.OnlyValidArgs,
),
ValidArgs: []string{"dev", "staging", "prod"},
Run: func(cmd *cobra.Command, args []string) {
env := args[0]
fmt.Printf("Развёртывание на %s\n", env)
},
}
Кастомная валидация
var backupCmd = &cobra.Command{
Use: "backup [path]",
Short: "Создать резервную копию",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("требуется путь к директории")
}
if _, err := os.Stat(args[0]); os.IsNotExist(err) {
return fmt.Errorf("директория не существует: %s", args[0])
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
path := args[0]
fmt.Printf("Создаём бэкап: %s\n", path)
},
}
Подкоманды с группировкой
var nginxCmd = &cobra.Command{
Use: "nginx",
Short: "Управление Nginx",
}
var nginxStartCmd = &cobra.Command{
Use: "start",
Short: "Запустить Nginx",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Запускаем Nginx...")
},
}
var nginxStopCmd = &cobra.Command{
Use: "stop",
Short: "Остановить Nginx",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Останавливаем Nginx...")
},
}
var nginxConfigCmd = &cobra.Command{
Use: "config",
Short: "Проверить конфигурацию",
Run: func(cmd *cobra.Command, args []string) {
cmd := exec.Command("nginx", "-t")
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("❌ Ошибка конфигурации:\n%s\n", output)
} else {
fmt.Println("✅ Конфигурация корректна")
}
},
}
func init() {
nginxCmd.AddCommand(nginxStartCmd)
nginxCmd.AddCommand(nginxStopCmd)
nginxCmd.AddCommand(nginxConfigCmd)
rootCmd.AddCommand(nginxCmd)
}
Интеграция с другими пакетами
Cobra отлично работает с другими популярными пакетами:
Viper для конфигурации
go get github.com/spf13/viper
import "github.com/spf13/viper"
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(".")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Используется конфиг:", viper.ConfigFileUsed())
}
}
Logrus для логирования
go get github.com/sirupsen/logrus
import "github.com/sirupsen/logrus"
var logger = logrus.New()
func init() {
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "подробный вывод")
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
if verbose {
logger.SetLevel(logrus.DebugLevel)
}
}
}
Сравнение с альтернативами
Библиотека | Размер | Сложность | Возможности | Производительность |
---|---|---|---|---|
Cobra | Средний | Средняя | Очень богатые | Хорошая |
flag (стандартная) | Маленький | Простая | Базовые | Отличная |
urfave/cli | Средний | Простая | Хорошие | Хорошая |
kingpin | Средний | Средняя | Хорошие | Хорошая |
Cobra выигрывает в случаях, когда нужна сложная иерархия команд, автодополнение и богатые возможности валидации.
Генерация автодополнения
Одна из крутых фич Cobra — автогенерация скриптов автодополнения для разных shell’ов:
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Генерация скрипта автодополнения",
Long: `Генерация скрипта автодополнения для вашего shell.
Для bash:
source <(yourprogram completion bash)
Для zsh:
source <(yourprogram completion zsh)`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletion(os.Stdout)
}
},
}
func init() {
rootCmd.AddCommand(completionCmd)
}
Кастомное автодополнение
Можно создать интеллектуальное автодополнение:
var serviceCmd = &cobra.Command{
Use: "service [name]",
Short: "Управление сервисом",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Получаем список активных сервисов
out, err := exec.Command("systemctl", "list-units", "--type=service", "--state=active", "--no-legend").Output()
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var services []string
lines := strings.Split(string(out), "\n")
for _, line := range lines {
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) > 0 {
serviceName := strings.TrimSuffix(parts[0], ".service")
services = append(services, serviceName)
}
}
return services, cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
// логика команды
},
}
Деплой утилиты
Для развёртывания CLI-утилиты на сервере удобно использовать простой Makefile:
BINARY_NAME=server-cli
VERSION=1.0.0
build:
go build -ldflags="-X main.Version=${VERSION}" -o ${BINARY_NAME}
build-linux:
GOOS=linux GOARCH=amd64 go build -ldflags="-X main.Version=${VERSION}" -o ${BINARY_NAME}-linux
install: build
sudo cp ${BINARY_NAME} /usr/local/bin/
sudo chmod +x /usr/local/bin/${BINARY_NAME}
clean:
rm -f ${BINARY_NAME}
rm -f ${BINARY_NAME}-linux
.PHONY: build build-linux install clean
И не забудьте про версионирование:
var Version = "dev"
var versionCmd = &cobra.Command{
Use: "version",
Short: "Показать версию",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("server-cli version %s\n", Version)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
Использование в production
Несколько советов для боевого использования:
- Логирование: Всегда логируйте важные операции
- Конфигурация: Используйте Viper для гибкой настройки
- Ошибки: Обрабатывайте ошибки правильно и информативно
- Тестирование: Покрывайте команды unit-тестами
func TestStatusCommand(t *testing.T) {
cmd := &cobra.Command{
Use: "status",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("OK")
},
}
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.Execute()
out, err := ioutil.ReadAll(b)
if err != nil {
t.Fatal(err)
}
if string(out) != "OK\n" {
t.Fatalf("expected OK, got %s", string(out))
}
}
Интеграция с CI/CD
Пример GitHub Actions для автоматической сборки:
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Build
run: |
GOOS=linux GOARCH=amd64 go build -o server-cli-linux
GOOS=darwin GOARCH=amd64 go build -o server-cli-darwin
GOOS=windows GOARCH=amd64 go build -o server-cli-windows.exe
- name: Release
uses: softprops/action-gh-release@v1
with:
files: |
server-cli-linux
server-cli-darwin
server-cli-windows.exe
Нестандартные применения
Cobra можно использовать не только для CLI, но и для:
- Интерактивные меню: Комбинация с promptui
- Webhook-обработчики: CLI как фронтенд к API
- Конфигурационные утилиты: Генерация конфигов для других сервисов
- Мониторинг и алертинг: Интеграция с Prometheus, Grafana
Пример интерактивного меню:
go get github.com/manifoldco/promptui
import "github.com/manifoldco/promptui"
var interactiveCmd = &cobra.Command{
Use: "interactive",
Short: "Интерактивный режим",
Run: func(cmd *cobra.Command, args []string) {
prompt := promptui.Select{
Label: "Выберите действие",
Items: []string{"Статус сервера", "Перезагрузить сервис", "Показать логи"},
}
_, result, err := prompt.Run()
if err != nil {
fmt.Printf("Ошибка: %v\n", err)
return
}
switch result {
case "Статус сервера":
fmt.Println("Сервер работает нормально")
case "Перезагрузить сервис":
// дополнительный промпт для выбора сервиса
case "Показать логи":
// показать логи
}
},
}
Автоматизация и скрипты
Cobra открывает новые возможности для автоматизации:
- Цепочки команд: Выполнение последовательности операций
- Условная логика: Выполнение команд в зависимости от состояния
- Интеграция с cron: Регулярные задачи через CLI
- Удалённое выполнение: SSH-команды через CLI
Пример cron-задачи:
# /etc/cron.d/server-monitor
*/5 * * * * root /usr/local/bin/server-cli check disk && /usr/local/bin/server-cli check service nginx
Если планируете развернуть такие утилиты в production, рекомендую обратить внимание на качественный VPS или выделенный сервер — стабильность инфраструктуры критична для автоматизации.
Заключение и рекомендации
Cobra — это мощный инструмент для создания CLI-утилит в Go. Он идеально подходит для:
- Системных администраторов: Создание утилит для управления серверами
- DevOps-инженеров: Автоматизация развёртывания и мониторинга
- Разработчиков: Создание инструментов для разработки
Когда использовать Cobra:
- Нужна сложная иерархия команд
- Требуется богатая валидация аргументов
- Важно автодополнение
- Планируется активное развитие утилиты
Когда лучше выбрать альтернативы:
- Простая утилита с 1-2 командами — используйте стандартный flag
- Критична производительность — рассмотрите более лёгкие варианты
- Нужна максимальная простота — попробуйте urfave/cli
Cobra используется в kubectl, docker, hugo, etcd и множестве других проектов. Это проверенное решение, которое поможет быстро создать профессиональную CLI-утилиту. Начните с простых команд, постепенно добавляйте функциональность, и вскоре у вас будет мощный инструмент для автоматизации повседневных задач.
Полезные ссылки:
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.