# Production

Production-ready patterns for Goa services - observability, security, and common deployment patterns.

Source: https://goa.design/fr/docs/1-goa/production/

Relative links resolve against the source URL above.


Ce guide couvre les modèles essentiels pour l'exécution des services Goa en production, y compris l'observabilité, la sécurité et les modèles de déploiement courants.

## Observabilité

Les systèmes distribués modernes nécessitent une observabilité complète. Goa recommande [Clue](https://github.com/goadesign/clue), construit sur OpenTelemetry, pour l'observabilité. Cette section couvre les modèles communs ; pour une documentation complète de l'API, voir [Clue Documentation](../3-ecosystem/clue/).

### Les trois piliers

1. **Traçage distribué** : Suivre les demandes à travers votre système
2. **Mesures** : Mesurer le comportement et les performances du système
3. **Journaux** : Enregistrer des événements et des erreurs spécifiques

### Configuration de Clue

```go
import (
    "goa.design/clue/clue"
    "goa.design/clue/log"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
)

func main() {
    // 1. Create logger
    format := log.FormatJSON
    if log.IsTerminal() {
        format = log.FormatTerminal
    }
    ctx := log.Context(context.Background(),
        log.WithFormat(format),
        log.WithFunc(log.Span))

    // 2. Configure OpenTelemetry exporters
    spanExporter, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint(*collectorAddr),
        otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf(ctx, err, "failed to initialize tracing")
    }
    
    metricExporter, err := otlpmetricgrpc.New(ctx,
        otlpmetricgrpc.WithEndpoint(*collectorAddr),
        otlpmetricgrpc.WithTLSCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf(ctx, err, "failed to initialize metrics")
    }

    // 3. Initialize Clue
    cfg, err := clue.NewConfig(ctx,
        genservice.ServiceName,
        genservice.APIVersion,
        metricExporter,
        spanExporter)
    clue.ConfigureOpenTelemetry(ctx, cfg)
}
```

### Traçage distribué

```go
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
)

func (s *Service) CreateOrder(ctx context.Context, order *Order) error {
    // Start a span
    ctx, span := otel.Tracer("service").Start(ctx, "create_order")
    defer span.End()

    // Add attributes
    span.SetAttributes(
        attribute.String("order.id", order.ID),
        attribute.Float64("order.amount", order.Amount))

    if err := s.processOrder(ctx, order); err != nil {
        span.RecordError(err)
        return err
    }

    return nil
}
```

### Métriques

```go
import (
    "go.opentelemetry.io/otel/metric"
)

type Service struct {
    orderCounter metric.Int64Counter
    orderLatency metric.Float64Histogram
}

func NewService(meter metric.Meter) *Service {
    counter, _ := meter.Int64Counter("orders_total",
        metric.WithDescription("Total number of orders"))
    latency, _ := meter.Float64Histogram("order_latency_seconds",
        metric.WithDescription("Order processing latency"))
    
    return &Service{
        orderCounter: counter,
        orderLatency: latency,
    }
}

func (s *Service) CreateOrder(ctx context.Context, order *Order) error {
    start := time.Now()
    defer func() {
        s.orderLatency.Record(ctx, time.Since(start).Seconds())
    }()
    
    s.orderCounter.Add(ctx, 1, attribute.String("type", order.Type))
    // ...
}
```

### Journalisation

```go
import "goa.design/clue/log"

func (s *Service) CreateOrder(ctx context.Context, order *Order) error {
    log.Info(ctx, "processing order",
        log.KV{"order_id", order.ID},
        log.KV{"amount", order.Amount})

    if err := s.processOrder(ctx, order); err != nil {
        log.Error(ctx, err, "failed to process order",
            log.KV{"order_id", order.ID})
        return err
    }

    return nil
}
```

### Contrôles de santé

```go
import "goa.design/clue/health"

func main() {
    // Create health checker
    checker := health.NewChecker(
        health.NewPinger("database", dbHealthAddr),
        health.NewPinger("cache", cacheHealthAddr),
    )
    
    // Mount health endpoint
    http.Handle("/healthz", log.HTTP(ctx)(health.Handler(checker)))
}
```

### Service complet observable

```go
func main() {
    ctx := log.Context(context.Background(), log.WithFormat(log.FormatJSON))

    // Initialize OpenTelemetry
    cfg, _ := clue.NewConfig(ctx, serviceName, version, metricExporter, spanExporter)
    clue.ConfigureOpenTelemetry(ctx, cfg)

    // Create service with middleware
    svc := NewService()
    endpoints := genservice.NewEndpoints(svc)
    endpoints.Use(debug.LogPayloads())
    endpoints.Use(log.Endpoint)

    // Set up HTTP with observability middleware
    mux := goahttp.NewMuxer()
    mux.Use(otelhttp.NewMiddleware(serviceName))
    mux.Use(debug.HTTP())
    mux.Use(log.HTTP(ctx))

    // Mount debug endpoints
    debug.MountDebugLogEnabler(debug.Adapt(mux))
    debug.MountPprofHandlers(debug.Adapt(mux))

    // Mount health checks
    http.Handle("/healthz", health.Handler(health.NewChecker(...)))

    // Start server
    server := &http.Server{Addr: ":8080", Handler: mux}
    server.ListenAndServe()
}
```

---

## Sécurité

Goa offre de solides dispositifs de sécurité par le biais de sa DSL.

### Schémas de sécurité

#### Authentification de base

```go
var BasicAuth = BasicAuthSecurity("basic", func() {
    Description("Basic authentication using username and password")
})
```

#### Authentification par clé API

```go
var APIKeyAuth = APIKeySecurity("api_key", func() {
    Description("Secures endpoint by requiring an API key")
})
```

#### Authentification par jeton bearer

```go
var BearerAuth = BearerSecurity("bearer", func() {
    Description("Secures endpoint by requiring a bearer token")
    Scope("api:read", "Read access to API")
    Scope("api:write", "Write access to API")
})
```

#### Authentification par JWT

```go
var JWTAuth = JWTSecurity("jwt", func() {
    Description("Secures endpoint by requiring a valid JWT token")
    Scope("api:read", "Read access to API")
    Scope("api:write", "Write access to API")
})
```

`BearerSecurity` et `JWTSecurity` utilisent tous deux par défaut le format de
transport standard `Authorization: Bearer <token>`. Utilisez `BearerSecurity`
pour les jetons bearer génériques ou opaques, et `JWTSecurity` lorsque l'API
générée doit être explicitement orientée JWT. Utilisez `BearerFormat` uniquement
lorsque la documentation OpenAPI v3 doit annoncer un format précis de jeton
bearer; `JWTSecurity` émet `bearerFormat: JWT` par défaut.

#### Authentification OAuth2

```go
var OAuth2 = OAuth2Security("oauth2", func() {
    Description("OAuth2 authentication")
    AuthorizationCodeFlow("/authorize", "/token", "/refresh")
    Scope("api:write", "Write access")
    Scope("api:read", "Read access")
})
```

### Application de la sécurité

La sécurité peut être appliquée au niveau de l'API, du service ou de la méthode :

```go
// API level - default for all endpoints
var _ = API("myapi", func() {
    Security(BasicAuth)
})

// Service level - override API default
var _ = Service("users", func() {
    Security(APIKeyAuth)
    
    Method("list", func() {
        // Uses service-level APIKeyAuth
        Payload(func() {
            APIKey("api_key", "key", String)
            Required("key")
        })
    })
    
    Method("admin", func() {
        // Override with bearer token auth for this method
        Security(BearerAuth)
        Payload(func() {
            BearerToken("token", String)
            Required("token")
        })
    })
    
    Method("public", func() {
        // No security for this method
        NoSecurity()
    })
})
```

### Meilleures pratiques en matière de sécurité

1. **Toujours utiliser HTTPS en production**
2. **Définir la sécurité au niveau de l'API** pour des valeurs par défaut cohérentes
3. **Utiliser `NoSecurity()` explicitement** pour les points d'extrémité publics
4. **Mettre en place une limitation de débit** pour l'authentification par clé d'API
5. **Utiliser une expiration de jeton appropriée** pour les jetons bearer et JWT
6. **Effectuer une rotation régulière des secrets et des clés**
7. **Enregistrer et surveiller les échecs d'authentification**
8. **Valider toutes les entrées**, même pour les demandes authentifiées

---

## Modèles communs

### Arrêt en douceur

```go
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    
    // Create server
    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
    
    // Start server in goroutine
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            log.Errorf(ctx, err, "server error")
        }
    }()
    
    // Wait for interrupt signal
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
    <-sigChan
    
    // Graceful shutdown with timeout
    shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer shutdownCancel()
    
    if err := server.Shutdown(shutdownCtx); err != nil {
        log.Errorf(ctx, err, "shutdown error")
    }
    
    cancel()
    wg.Wait()
}
```

### Gestion de la configuration

```go
type Config struct {
    HTTPAddr     string        `env:"HTTP_ADDR" default:":8080"`
    GRPCAddr     string        `env:"GRPC_ADDR" default:":8081"`
    DatabaseURL  string        `env:"DATABASE_URL" required:"true"`
    LogLevel     string        `env:"LOG_LEVEL" default:"info"`
    ReadTimeout  time.Duration `env:"READ_TIMEOUT" default:"10s"`
    WriteTimeout time.Duration `env:"WRITE_TIMEOUT" default:"30s"`
}

func main() {
    var cfg Config
    if err := envconfig.Process("", &cfg); err != nil {
        log.Fatal(err)
    }
    
    server := &http.Server{
        Addr:         cfg.HTTPAddr,
        Handler:      mux,
        ReadTimeout:  cfg.ReadTimeout,
        WriteTimeout: cfg.WriteTimeout,
    }
}
```

### Délais d'attente du serveur

```go
server := &http.Server{
    Addr:              ":8080",
    Handler:           mux,
    ReadHeaderTimeout: 10 * time.Second,
    ReadTimeout:       30 * time.Second,
    WriteTimeout:      60 * time.Second,
    IdleTimeout:       120 * time.Second,
    MaxHeaderBytes:    1 << 20, // 1MB
}
```

---

## Résumé

Les services Goa prêts à la production devraient inclure :

1. **Observabilité** : Traçage, métriques, journalisation et contrôles de santé
2. **Sécurité Authentification et autorisation appropriées
3. **Résilience** : Arrêt progressif, délais d'attente et gestion des erreurs
4. **Configuration** : Gestion de la configuration basée sur l'environnement
5. **Surveillance** : Points d'extrémité de débogage et capacités de profilage

Ces modèles garantissent la fiabilité, la sécurité et la maintenance de vos services dans les environnements de production.

---

## Voir aussi

- [Clue Documentation](../3-ecosystem/clue/) - Boîte à outils d'observabilité complète avec référence API détaillée
- [DSL Reference : Security](dsl-reference/#sécurité) - Définitions des schémas de sécurité
- [Guide de gestion des erreurs](error-handling/) - Modèles de gestion des erreurs et meilleures pratiques
- [Intercepteurs](interceptors/) - Modèles d'intergiciels et d'intercepteurs

