Konfiguration von Go (Golang)-Anwendungen: von Umgebungsvariablen bis zu komplexen YAML-Strukturen
Konfiguration ist einer der Schlüsselaspekte jeder Production-Anwendung. In Go (Golang) bietet das Ökosystem flexible und leistungsstarke Werkzeuge zur Verwaltung von Einstellungen, die auf verschiedene Szenarien zugeschnitten sind: von einfachen 12-Faktor-Anwendungen bis hin zu Microservices mit Dutzenden von Parametern. In diesem Artikel behandeln wir die grundlegenden Ansätze, beliebten Bibliotheken und Best Practices für die Konfiguration von Go-Anwendungen.
Warum ist eine separate Konfiguration notwendig?
Hartcodierte Parameter (Hardcode) sind ein Anti-Pattern. Konfiguration ermöglicht:
- Das Verhalten der Anwendung ohne Neukompilierung zu ändern;
- Einstellungen für verschiedene Umgebungen (Dev/Staging/Prod) zu trennen;
- Sicherheit zu gewährleisten (Geheimnisse gelangen nicht ins Repository);
- Die Bereitstellung in Containern und Cloud-Plattformen zu vereinfachen.
Grundlegender Ansatz: Umgebungsvariablen
Die Go-Standardbibliothek bietet die Funktion os.Getenv. Dies ist eine minimalistische, aber effektive Methode für kleine Projekte, die den Prinzipien der 12-Faktor-App entspricht.
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", cfg.Port)}Die Viper-Bibliothek: Das „Schweizer Taschenmesser“ der Konfiguration
Viper (von den Machern von Cobra) ist die beliebteste Bibliothek für die Konfiguration in Go. Sie unterstützt mehrere Formate, die Suche in mehreren Quellen und eine Hierarchie der Einstellungen.
Hauptfunktionen von Viper:
- Lesen aus YAML, JSON, TOML, HCL, INI, envfile, Java properties;
- Unterstützung von Umgebungsvariablen mit benutzerdefinierten Präfixen;
- Festlegen von Standardwerten;
- Dynamische Aktualisierung der Konfiguration (Live Reload);
- Integration mit entfernten Quellen (etcd, Consul, Firebase).
Beispiel für die Verwendung von Viper mit 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") // Dateiname ohne Erweiterung viper.SetConfigType("yaml") // Format viper.AddConfigPath(".") // Suchpfad viper.AddConfigPath("./config/")
// Lesen von Umgebungsvariablen mit dem Präfix 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", cfg.Server.Host, cfg.Server.Port) fmt.Printf("Database URL: %s", cfg.Database.URL)}Beispiel einer config.yaml-Datei:
server: port: 8080 host: "0.0.0.0"database: url: "postgres://user:pass@localhost:5432/app" pool: 10Die envconfig-Bibliothek: Einfachheit und Deklarativität
Wenn Sie keine komplexen Formate benötigen, sondern nur Umgebungsvariablen – Auch in der Bibliothek
Empfehlungen