Конфигурация приложений на Go: лучшие практики и инструменты

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Конфигурация приложений на Go (Golang): от переменных окружения до сложных YAML-структур

Конфигурация — один из ключевых аспектов любого production-приложения. В Go (Golang) экосистема предлагает гибкие и мощные инструменты для управления настройками, адаптированные под разные сценарии: от простых 12-factor приложений до микросервисов с десятками параметров. В этой статье мы разберём основные подходы, популярные библиотеки и лучшие практики конфигурирования Go-приложений.

Зачем нужна отдельная конфигурация?

Жёсткое кодирование параметров (hardcode) — антипаттерн. Конфигурация позволяет:

  • Изменять поведение приложения без перекомпиляции;
  • Разделять настройки для разных окружений (dev/staging/prod);
  • Обеспечивать безопасность (секреты не попадают в репозиторий);
  • Упрощать развёртывание в контейнерах и облачных платформах.

Базовый подход: переменные окружения

Стандартная библиотека Go предоставляет функцию os.Getenv. Это минималистичный, но эффективный способ для небольших проектов, соответствующий принципам 12-factor app.

package main

import (
    "fmt"
    "os"
)

type Config struct {
    Port string
    DBHost string
    DBUser string
}

func loadConfig() Config {
    return Config{
        Port:   getEnv("APP_PORT", "8080"),
        DBHost: getEnv("DB_HOST", "localhost"),
        DBUser: getEnv("DB_USER", "admin"),
    }
}

func getEnv(key, fallback string) string {
    if value, ok := os.LookupEnv(key); ok {
        return value
    }
    return fallback
}

func main() {
    cfg := loadConfig()
    fmt.Printf("Server starting on port %s\n", cfg.Port)
}

Библиотека Viper: «швейцарский нож» конфигурации

Viper (от создателей Cobra) — самая популярная библиотека для конфигурации в Go. Она поддерживает множество форматов, поиск в нескольких источниках и hierarchy (иерархию) настроек.

Основные возможности Viper:

  • Чтение из YAML, JSON, TOML, HCL, INI, envfile, Java properties;
  • Поддержка переменных окружения с кастомными префиксами;
  • Установка значений по умолчанию;
  • Динамическое обновление конфигурации (live reload);
  • Интеграция с удалёнными источниками (etcd, Consul, Firebase).

Пример использования Viper с YAML:

package main

import (
    "fmt"
    "github.com/spf13/viper"
)

type AppConfig struct {
    Server   ServerConfig   `mapstructure:"server"`
    Database DatabaseConfig `mapstructure:"database"`
}

type ServerConfig struct {
    Port int    `mapstructure:"port"`
    Host string `mapstructure:"host"`
}

type DatabaseConfig struct {
    URL  string `mapstructure:"url"`
    Pool int    `mapstructure:"pool"`
}

func main() {
    viper.SetConfigName("config")    // имя файла без расширения
    viper.SetConfigType("yaml")      // формат
    viper.AddConfigPath(".")         // путь поиска
    viper.AddConfigPath("./config/")

    // Чтение переменных окружения с префиксом APP_
    viper.AutomaticEnv()
    viper.SetEnvPrefix("APP")

    if err := viper.ReadInConfig(); err != nil {
        panic(fmt.Errorf("fatal error config file: %s", err))
    }

    var cfg AppConfig
    if err := viper.Unmarshal(&cfg); err != nil {
        panic(fmt.Errorf("unable to decode into struct: %s", err))
    }

    fmt.Printf("Server: %s:%d\n", cfg.Server.Host, cfg.Server.Port)
    fmt.Printf("Database URL: %s\n", cfg.Database.URL)
}

Пример файла config.yaml:

server:
  port: 8080
  host: "0.0.0.0"
database:
  url: "postgres://user:pass@localhost:5432/app"
  pool: 10

Библиотека envconfig: простота и декларативность

Если вам не нужны сложные форматы, а только переменные окружения — envconfig (github.com/kelseyhightower/envconfig) идеальное решение. Она автоматически маппит переменные окружения на поля структуры с помощью тегов.

package main

import (
    "fmt"
    "github.com/kelseyhightower/envconfig"
)

type Config struct {
    Port    int    `envconfig:"PORT" default:"8080"`
    DBUrl   string `envconfig:"DATABASE_URL" required:"true"`
    Debug   bool   `envconfig:"DEBUG" default:"false"`
    Timeout int    `split_words:"true" default:"30"` // превратится в TIMEOUT
}

func main() {
    var cfg Config                    

Рекомендаций