๐ข Enterprise Microservices Stack
๋๊ท๋ชจ ํธ๋ํฝ๊ณผ ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ์ํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ - Go, gRPC, Kubernetes๋ก ๊ตฌ์ถํ๋ ํ์ฅ ๊ฐ๋ฅํ ์์คํ
๐ข Enterprise Microservices Stack
๋๊ท๋ชจ ํธ๋ํฝ๊ณผ ๋ณต์กํ ๋น์ฆ๋์ค๋ฅผ ์ํ ๊ฒ์ฆ๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ
"Build for millions, designed for complexity" - ์ํฐํ๋ผ์ด์ฆ๊ธ ํ์ฅ์ฑ๊ณผ ์์ ์ฑ
๐ฏ ์ด ์คํ์ด ์ ํฉํ ๊ฒฝ์ฐ
โ ์ถ์ฒํ๋ ๊ฒฝ์ฐ
- ์ผ์ผ ์๋ฐฑ๋ง ์์ฒญ ์ฒ๋ฆฌ ํ์
- 100๋ช ์ด์์ ๊ฐ๋ฐํ์ด ๋์ ์์
- ๋ณต์กํ ๋๋ฉ์ธ ๋ก์ง๊ณผ ๋ค์ํ ์๋น์ค ์ด์
- 99.9% ์ด์์ ๊ฐ์ฉ์ฑ ์๊ตฌ์ฌํญ
- ๊ท์ ์ค์์ ๊ฐ์ฌ ์ถ์ ํ์
โ ๋ค๋ฅธ ์คํ์ ๊ณ ๋ คํด์ผ ํ ๊ฒฝ์ฐ
- 10๋ช ์ดํ ํ โ Modern SaaS Stack
- ๋จ์ํ CRUD ์ฑ โ Monolithic ์ํคํ ์ฒ
- ๋น์ฉ ๋ฏผ๊ฐํ ์คํํธ์ โ Serverless Stack
๐งฉ ํต์ฌ ๊ตฌ์ฑ์์
๋ง์ดํฌ๋ก์๋น์ค ๋ ์ด์ด
// ์๋น์ค ๊ฐ ํต์ (gRPC) type OrderService struct { userClient user.UserServiceClient productClient product.ProductServiceClient paymentClient payment.PaymentServiceClient kafkaProducer *kafka.Producer } func (s *OrderService) CreateOrder(ctx context.Context, req *OrderRequest) (*OrderResponse, error) { // ๋ถ์ฐ ํธ๋์ญ์
(Saga Pattern) saga := s.sagaOrchestrator.StartSaga(ctx, "create-order") // 1. ์ฌ์ฉ์ ๊ฒ์ฆ user, err := s.userClient.GetUser(ctx, &user.GetUserRequest{Id: req.UserId}) saga.AddCompensation(func() { /* rollback logic */ }) // 2. ์ฌ๊ณ ํ์ธ inventory, err := s.productClient.CheckInventory(ctx, req.Items) saga.AddCompensation(func() { /* restore inventory */ }) // 3. ๊ฒฐ์ ์ฒ๋ฆฌ payment, err := s.paymentClient.ProcessPayment(ctx, req.Payment) saga.AddCompensation(func() { /* refund payment */ }) // 4. ์ด๋ฒคํธ ๋ฐํ event := &OrderCreatedEvent{ OrderId: order.Id, UserId: user.Id, Amount: payment.Amount, } s.kafkaProducer.Produce("order-events", event) return &OrderResponse{Order: order}, nil } ์๋น์ค ๋ฉ์ (Istio)
# Istio ํธ๋ํฝ ๊ด๋ฆฌ apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: order-service spec: hosts: - order-service http: - match: - headers: x-version: exact: v2 route: - destination: host: order-service subset: v2 weight: 100 - route: - destination: host: order-service subset: v1 weight: 90 - destination: host: order-service subset: v2 weight: 10 # Canary ๋ฐฐํฌ timeout: 30s retries: attempts: 3 perTryTimeout: 10s ๋ฐ์ดํฐ ์ํคํ ์ฒ
# ํด๋ฆฌ๊ธ๋ ํผ์์คํด์ค databases: postgresql: - users_db # ํธ๋์ญ์
๋ฐ์ดํฐ - orders_db # ์ฃผ๋ฌธ ๊ด๋ฆฌ - billing_db # ๊ฒฐ์ ์ ๋ณด mongodb: - products_db # ์ ํ ์นดํ๋ก๊ทธ - analytics_db # ๋ถ์ ๋ฐ์ดํฐ redis: - session_cache # ์ธ์
๊ด๋ฆฌ - rate_limiter # API ์ ํ - pub_sub # ์ค์๊ฐ ๋ฉ์์ง elasticsearch: - search_index # ์ ๋ฌธ ๊ฒ์ - logs_index # ๋ก๊ทธ ์ง๊ณ ์ด๋ฒคํธ ์คํธ๋ฆฌ๋ฐ
// Kafka ์ด๋ฒคํธ ์ฒ๋ฆฌ type EventProcessor struct { consumer *kafka.Consumer handlers map[string]EventHandler } func (p *EventProcessor) ProcessEvents() { for { msg, err := p.consumer.ReadMessage(-1) if err != nil { continue } // ์ด๋ฒคํธ ํ์
๋ณ ์ฒ๋ฆฌ handler, exists := p.handlers[msg.Topic] if exists { // ๋ฉฑ๋ฑ์ฑ ๋ณด์ฅ if err := handler.Handle(msg); err != nil { // Dead Letter Queue๋ก ์ ์ก p.sendToDLQ(msg, err) } } p.consumer.CommitMessage(msg) } } ๐ฐ ์ธํ๋ผ ๋น์ฉ ๋ถ์
| ์ปดํฌ๋ํธ | ๊ฐ๋ฐ/ํ ์คํธ | ํ๋ก๋์ | ๊ณ ๊ฐ์ฉ์ฑ ๊ตฌ์ฑ | |----------|-------------|----------|--------------| | Kubernetes (EKS/GKE) | $200/์(3 nodes) | $1,000/์(10 nodes) | $3,000/์(30 nodes) | | ๋ฐ์ดํฐ๋ฒ ์ด์ค | $300/์ | $2,000/์ | $6,000/์ | | Kafka | $150/์ | $800/์ | $2,500/์ | | ๋ชจ๋ํฐ๋ง | $100/์ | $500/์ | $1,500/์ | | ๋ก๋๋ฐธ๋ฐ์/CDN | $50/์ | $300/์ | $1,000/์ | | ๋ฐฑ์ /DR | $100/์ | $500/์ | $2,000/์ | | ์ด๊ณ | ~$900/์ | ~$5,100/์ | ~$16,000/์ |
์ธ๋ ฅ ๋น์ฉ (ํ์ ๊ณ ๋ ค์ฌํญ)
- DevOps ์์ง๋์ด: 2-3๋ช
- SRE: 2-3๋ช
- ํ๋ซํผ ์์ง๋์ด: 1-2๋ช
- ์ฐ๊ฐ ์ธ๊ฑด๋น: $500K - $1M
๐ ๊ตฌํ ๋ก๋๋งต
Phase 1: ๊ธฐ๋ฐ ๊ตฌ์ถ (1-2๊ฐ์)
# Kubernetes ํด๋ฌ์คํฐ ํ๋ก๋น์ ๋ terraform init terraform plan -var-file=environments/production.tfvars terraform apply # ๊ธฐ๋ณธ ์๋น์ค ๋ฐฐํฌ kubectl apply -f namespaces/ kubectl apply -f rbac/ helm install istio istio/base helm install prometheus prometheus-community/kube-prometheus-stack Phase 2: ํต์ฌ ์๋น์ค (2-3๊ฐ์)
// ์๋น์ค ํ
ํ๋ฆฟ type BaseService struct { logger *zap.Logger metrics *prometheus.Registry tracer trace.Tracer health *health.Handler rateLimit *redis.Client } func NewService(name string) *BaseService { return &BaseService{ logger: logger.NewLogger(name), metrics: metrics.NewRegistry(name), tracer: tracing.NewTracer(name), health: health.NewHandler(), } } Phase 3: ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ (1-2๊ฐ์)
# Kafka ํ ํฝ ์ค๊ณ topics: - name: user-events partitions: 50 replication: 3 retention: 7d - name: order-events partitions: 100 replication: 3 retention: 30d - name: payment-events partitions: 50 replication: 3 retention: 90d Phase 4: ๊ด์ธก์ฑ ๊ตฌ์ถ (1๊ฐ์)
// ๋ถ์ฐ ์ถ์ func (s *OrderService) CreateOrder(ctx context.Context, req *OrderRequest) (*OrderResponse, error) { span, ctx := opentracing.StartSpanFromContext(ctx, "CreateOrder") defer span.Finish() // ๋ฉํธ๋ฆญ ์์ง timer := prometheus.NewTimer(orderCreationDuration) defer timer.ObserveDuration() // ๊ตฌ์กฐํ๋ ๋ก๊น
logger := s.logger.With( zap.String("order_id", req.OrderId), zap.String("user_id", req.UserId), zap.Float64("amount", req.Amount), ) logger.Info("Creating order") // ... ๋น์ฆ๋์ค ๋ก์ง } ๐๏ธ ์ํคํ ์ฒ ํจํด
Domain-Driven Design
services/ โโโ user-service/ โ โโโ domain/ # ๋๋ฉ์ธ ๋ชจ๋ธ โ โโโ application/ # ์ ์ค์ผ์ด์ค โ โโโ infrastructure/ # ์ธ๋ถ ์ฐ๋ โ โโโ interfaces/ # API ๋ ์ด์ด โโโ order-service/ โโโ product-service/ โโโ payment-service/ โโโ notification-service/ Circuit Breaker ํจํด
// Hystrix ์คํ์ผ Circuit Breaker type CircuitBreaker struct { failureThreshold int timeout time.Duration resetTimeout time.Duration } func (cb *CircuitBreaker) Call(fn func() error) error { if cb.state == Open { if time.Since(cb.lastFailure) > cb.resetTimeout { cb.state = HalfOpen } else { return ErrCircuitOpen } } err := fn() if err != nil { cb.recordFailure() if cb.failures > cb.failureThreshold { cb.state = Open cb.lastFailure = time.Now() } return err } cb.recordSuccess() return nil } CQRS + Event Sourcing
// Command ์ฒ๋ฆฌ type CreateOrderCommand struct { OrderID string UserID string Items []OrderItem Timestamp time.Time } // Event ์ ์ฅ type OrderCreatedEvent struct { AggregateID string Version int Payload interface{} Timestamp time.Time } // Query ๋ชจ๋ธ (Read DB) type OrderProjection struct { OrderID string UserName string TotalAmount float64 Status string UpdatedAt time.Time } ๐ ๊ธฐ์ ๋์
์ธ์ด๋ณ ๋ณํ
| ์ปดํฌ๋ํธ | Go ์คํ | Java ์คํ | Node.js ์คํ | |----------|---------|-----------|--------------| | ํ๋ ์์ํฌ | Gin/Echo | Spring Boot | Express/Fastify | | RPC | gRPC | gRPC/REST | GraphQL | | ๋ฉ์์ง | Kafka | RabbitMQ | NATS | | ๋ชจ๋ํฐ๋ง | Prometheus | Micrometer | PM2 |
ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ๋์
# AWS ๋ค์ดํฐ๋ธ aws: compute: EKS service_mesh: App Mesh api_gateway: API Gateway messaging: Kinesis/SQS database: Aurora/DynamoDB cache: ElastiCache # GCP ๋ค์ดํฐ๋ธ gcp: compute: GKE service_mesh: Anthos Service Mesh api_gateway: Apigee messaging: Pub/Sub database: Spanner/Firestore cache: Memorystore ๐ ์ค์ ์ ์ฉ ์ฌ๋ก
Uber
- ๊ท๋ชจ: ์์ฒ ๊ฐ ๋ง์ดํฌ๋ก์๋น์ค
- ํธ๋ํฝ: ์ด๋น ์๋ฐฑ๋ง ์์ฒญ
- ํน์ง: ์์ฒด RPC ํ๋ ์์ํฌ (TChannel)
Netflix
- ๊ท๋ชจ: 700+ ๋ง์ดํฌ๋ก์๋น์ค
- ํน์ง: Chaos Engineering ์ ๊ตฌ์
- ๋๊ตฌ: Eureka, Hystrix, Zuul
Airbnb
- ์ด์ : Ruby Monolith
- ํ์ฌ: ์๋น์ค ์งํฅ ์ํคํ ์ฒ
- ํน์ง: GraphQL Federation
๐ ํ์ ํ์ต ์๋ฃ
๋์
- "Building Microservices" - Sam Newman
- "Microservices Patterns" - Chris Richardson
- "Site Reliability Engineering" - Google
์จ๋ผ์ธ ๋ฆฌ์์ค
์ธ์ฆ ๋ฐ ๊ต์ก
- CKA (Certified Kubernetes Administrator)
- CKAD (Certified Kubernetes Application Developer)
- AWS/GCP/Azure Solutions Architect
๐ฌ ํ์ ๊ฐ๋ฐ์ ์กฐ์ธ
"๋ง์ดํฌ๋ก์๋น์ค๋ ์กฐ์ง ๊ตฌ์กฐ๋ฅผ ๋ฐ์ํฉ๋๋ค. Conway's Law๋ฅผ ๋ฌด์ํ๋ฉด ์คํจํฉ๋๋ค. ํ ๊ตฌ์กฐ๋ถํฐ ๋ฐ๊พธ์ธ์." - @microservices_architect
"์ฒ์๋ถํฐ ๋ชจ๋ ๊ฑธ ๋ง์ดํฌ๋ก์๋น์ค๋ก ๋ง๋ค์ง ๋ง์ธ์. Monolith First๋ก ์์ํ๊ณ ํ์ํ ๋ ๋ถ๋ฆฌํ์ธ์." - @pragmatic_dev
"๋ถ์ฐ ํธ๋ ์ด์ฑ ์์ด๋ ๋๋ฒ๊น ์ด ์ง์ฅ์ ๋๋ค. Jaeger๋ Zipkin์ ์ ํ์ด ์๋๋ผ ํ์์์." - @sre_expert
"๋ฐ์ดํฐ ์ผ๊ด์ฑ์ด ์ ์ผ ์ด๋ ค์์. Saga ํจํด ์ ๋๋ก ๊ตฌํํ๋๋ฐ 6๊ฐ์ ๊ฑธ๋ ธ์ต๋๋ค." - @distributed_systems_dev
โก ์ฑ๋ฅ ์ต์ ํ
์๋น์ค ๊ฐ ํต์ ์ต์ ํ
// Connection Pooling var grpcConnPool = sync.Map{} func getGRPCConnection(address string) (*grpc.ClientConn, error) { if conn, ok := grpcConnPool.Load(address); ok { return conn.(*grpc.ClientConn), nil } conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: 10 * time.Second, Timeout: 3 * time.Second, PermitWithoutStream: true, }), grpc.WithDefaultCallOptions( grpc.MaxCallRecvMsgSize(10 * 1024 * 1024), // 10MB ), ) grpcConnPool.Store(address, conn) return conn, err } ์บ์ฑ ์ ๋ต
// ๋ค์ธต ์บ์ฑ type CacheLayer struct { l1Cache *ristretto.Cache // In-memory l2Cache *redis.Client // Redis l3Cache *memcached.Client // Memcached } func (c *CacheLayer) Get(key string) (interface{}, error) { // L1 ์ฒดํฌ if val, found := c.l1Cache.Get(key); found { return val, nil } // L2 ์ฒดํฌ val, err := c.l2Cache.Get(key).Result() if err == nil { c.l1Cache.Set(key, val, 1) return val, nil } // L3 ์ฒดํฌ // ... ๊ตฌํ } ๐จ ์ด์ ๊ณ ๋ ค์ฌํญ
์ฅ์ ๋์
- Graceful Degradation: ํต์ฌ ๊ธฐ๋ฅ ์ฐ์ ๋ณด์ฅ
- Circuit Breaker: ์ฐ์ ์ฅ์ ๋ฐฉ์ง
- Bulkhead: ๋ฆฌ์์ค ๊ฒฉ๋ฆฌ
- Timeout/Retry: ์ ์ ํ ์ค์ ํ์
๋ณด์
- mTLS๋ก ์๋น์ค ๊ฐ ํต์ ์ํธํ
- RBAC์ผ๋ก ์ธ๋ฐํ ๊ถํ ๊ด๋ฆฌ
- Secret ๊ด๋ฆฌ (Vault, Sealed Secrets)
- Zero Trust Network ๊ตฌํ
์ปดํ๋ผ์ด์ธ์ค
- ๊ฐ์ฌ ๋ก๊ทธ ์ค์ํ
- ๋ฐ์ดํฐ ์ํธํ (์ ์ก/์ ์ฅ)
- GDPR/PCI-DSS ์ค์
- ์ ๊ธฐ ๋ณด์ ๊ฐ์ฌ
๋ง์ง๋ง ์ ๋ฐ์ดํธ: 2025-01-28
๊ธฐ์ฌํ๊ธฐ: GitHub์์ ํธ์ง