๐Ÿ“ˆ ์ดˆ๋‹น 100๋งŒ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Go ๋ฐฑ์—”๋“œ๋ฅผ ํ™•์žฅํ•œ ๊ณผ์ •

sangjinsuยท2025๋…„ 3์›” 29์ผ

How I Scaled a Go Backend to Handle 1 Million Requests per Second

์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํ•œ API์˜€์Šต๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ€๋ฒผ์šด Go ์„œ๋น„์Šค์˜€๊ณ ,
ํ•˜๋ฃจ์— ๋ช‡ ์ฒœ ๊ฑด ์ •๋„์˜ ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋˜์—ˆ๊ธฐ์— ๋ฌธ์ œ์—†์ด ์ž˜ ๋Œ์•„๊ฐ”์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํŠธ๋ž˜ํ”ฝ์ด ์ ์  ๋Š˜์–ด๋‚˜๋ฉด์„œ,
ํ•œ๋•Œ ๋น ๋ฆฟํ•˜๋˜ ๋ฐฑ์—”๋“œ๋Š” ์ ์  ๋А๋ ค์ง€๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ง€์—ฐ ์‹œ๊ฐ„์€ ๊ธ‰๊ฒฉํžˆ ๋Š˜์—ˆ๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋Š” ๋ณ‘๋ชฉ์ด ๋˜์—ˆ์œผ๋ฉฐ,
์„œ๋ฒ„๋Š” ๋†’์€ ๋ถ€ํ•˜๋ฅผ ๊ฒฌ๋””์ง€ ๋ชปํ•˜๊ณ  ์ ์  ์ง€์ณ๊ฐ”์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋˜ ์–ด๋А ๋‚ ,
์ €ํฌ ์„œ๋น„์Šค๊ฐ€ ํ•œ ์œ ๋ช… ๋‰ด์Šค ์‚ฌ์ดํŠธ์— ์†Œ๊ฐœ๋˜๋ฉด์„œ ์ƒํ™ฉ์ด ๊ธ‰๋ณ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
๋‹จ ๋ช‡ ๋ถ„ ๋งŒ์— ํŠธ๋ž˜ํ”ฝ์ด 10๋ฐฐ ์ด์ƒ ํญ์ฆํ–ˆ๊ณ ,
Go ๋ฐฑ์—”๋“œ๋Š” ๊ฐ„์‹ ํžˆ ๋ฒ„ํ‹ฐ๊ณ  ์žˆ์—ˆ์œผ๋ฉฐ, ์ธํ”„๋ผ ํŒ€์€ ์™ธ์ณค์Šต๋‹ˆ๋‹ค.

โ€œ์„œ๋ฒ„๋ฅผ ๋” ๋Š˜๋ ค์•ผ ํ•ด์š”!โ€

๊ทธ๋•Œ ์ €๋Š” ๊ฒฐ์‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค.
์ง€๊ธˆ์ด์•ผ๋ง๋กœ ์ด ์‹œ์Šคํ…œ์„ ์ง„์งœ๋กœ ์ตœ์ ํ™”ํ•  ๋•Œ๋‹ค.

๊ฒฐ๊ตญ, ์ €๋Š” ์ด ๋ฐฑ์—”๋“œ๋ฅผ
์ดˆ๋‹น 100๋งŒ ์š”์ฒญ(RPS)์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€๊นŒ์ง€ ํ™•์žฅ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ,
๊ทธ ๊ณผ์ •์„ ์—ฌ๋Ÿฌ๋ถ„๊ป˜ ๊ณต์œ ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค.


โš™๏ธ 1. ์ง„์งœ ๋ณ‘๋ ฌ์„ฑ์„ ์œ„ํ•œ ๊ณ ๋ฃจํ‹ด ์ตœ์ ํ™”

์ฒ˜์Œ์—๋Š” Go์˜ ๊ณ ๋ฃจํ‹ด์ด ๋งˆ๋ฒ• ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
โ€œ๊ฐ€๋ณ๋‹ค! ๋ฌดํ•œํžˆ ์Šค์ผ€์ผ๋œ๋‹ค!โ€ โ€” ๊ทธ๋ ‡๊ฒŒ ๋ฏฟ์—ˆ์ฃ .

ํ•˜์ง€๋งŒ ๊ณง ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.
๋ฌด์ œํ•œ์œผ๋กœ ๊ณ ๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๋ฉด ์˜คํžˆ๋ ค CPU ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด ๊ณผ๋„ํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ๋„ ๊ธˆ๋ฐฉ ๊ณ ๊ฐˆ๋œ๋‹ค๋Š” ์‚ฌ์‹ค์„์š”.

๊ณ ๋ฃจํ‹ด ๊ณผ๋ถ€ํ•˜ ํ•ด๊ฒฐ: ์›Œ์ปค ํ’€ ๋„์ž…
๋ฌด์ž‘์ • ๊ณ ๋ฃจํ‹ด์„ ๋„์šฐ๋Š” ๋Œ€์‹ ,
๊ณ ์ •๋œ ์ˆ˜์˜ ์›Œ์ปค๊ฐ€ ํ์— ์Œ“์ธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์›Œ์ปค ํ’€(worker pool)์„ ์ง์ ‘ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ ์„ ์คฌ์Šต๋‹ˆ๋‹ค:
์š”์ฒญ ์ˆ˜๊ฐ€ ๋งŽ์•„๋„ ๊ณ ๋ฃจํ‹ด ์ˆ˜๊ฐ€ ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
CPU ์ž์›์„ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์•ˆ์ •์ ์ธ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๋„ ๊ธ‰๊ฒฉํžˆ ๋Š˜์–ด๋‚˜์ง€ ์•Š์•„, GC์— ์˜ํ•œ ์ง€์—ฐ๋„ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

package main

import (
    "fmt"
    "sync"
    "time"
)

const maxWorkers = 100 // Controls concurrency level
const numJobs = 1000000
type Job struct {
    ID int
}

func worker(id int, jobs <-chan Job, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        // Simulating CPU-intensive processing
        time.Sleep(1 * time.Millisecond)
        fmt.Printf("Worker %d processed job %d\n", id, job.ID)
    }
}

func main() {
    jobs := make(chan Job, numJobs)
    var wg sync.WaitGroup
    for w := 1; w <= maxWorkers; w++ {
        wg.Add(1)
        go worker(w, jobs, &wg)
    }
    for j := 1; j <= numJobs; j++ {
        jobs <- Job{ID: j}
    }
    close(jobs)
    wg.Wait()
}

โœ… ์™œ ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์„๊นŒ?
์ˆ˜๋ฐฑ๋งŒ ๊ฐœ์˜ ๊ณ ๋ฃจํ‹ด์„ ๋ฌด์ž‘์ • ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹ ,
๋™์‹œ์„ฑ ์ˆ˜์ค€์„ maxWorkers๋กœ ์ œํ•œํ•จ์œผ๋กœ์จ
๐Ÿ‘‰ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์„ ์ค„์ด๊ณ , CPU ํšจ์œจ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿš€ 2. REST์—์„œ gRPC๋กœ์˜ ์ „ํ™˜: ๋ฒˆ๊ฐœ์ฒ˜๋Ÿผ ๋น ๋ฅธ API๋ฅผ ์œ„ํ•˜์—ฌ

์ฒ˜์Œ์—๋Š” HTTP ๊ธฐ๋ฐ˜์˜ REST API๋กœ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํŠธ๋ž˜ํ”ฝ์ด ๋งŽ์•„์ง€์ž ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋“ค์ด ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค:

  • JSON ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ์˜ค๋ฒ„ํ—ค๋“œ
  • HTTP ์š”์ฒญ๋‹น ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๊ณผ ์‘๋‹ต
  • ๋‹ค์ˆ˜์˜ ๋ผ์šด๋“œ ํŠธ๋ฆฝ์œผ๋กœ ์ธํ•œ ์ง€์—ฐ

โœ… ํ•ด๊ฒฐ์ฑ…: gRPC๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
gRPC๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์œผ๋กœ ์„ฑ๋Šฅ์„ ํš๊ธฐ์ ์œผ๋กœ ๊ฐœ์„ ํ•ด์ค๋‹ˆ๋‹ค
๋ฐ”์ด๋„ˆ๋ฆฌ ์ง๋ ฌํ™” ๋ฐฉ์‹(Protobuf) ์‚ฌ์šฉ โ†’ JSON๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฆ„
HTTP/2 ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ โ†’ ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๋กœ ์—ฌ๋Ÿฌ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
์ŠคํŠธ๋ฆฌ๋ฐ ์ง€์› โ†’ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ ์— ์œ ๋ฆฌ

๐Ÿงช gRPC ์ ์šฉ ์˜ˆ์‹œ: ๊ณ ์† Go API
๐Ÿ“„ payment.proto (protobuf ์ •์˜)

syntax = "proto3";
package pb;

service PaymentService {
    rpc ProcessPayment (PaymentRequest) returns (PaymentResponse);
}

message PaymentRequest {
    string user_id = 1;
    double amount = 2;
}

message PaymentResponse {
    bool success = 1;
    string transaction_id = 2;
}

๐Ÿง  Go ์„œ๋ฒ„ ๊ตฌํ˜„

package main

import (
    "context"
    "fmt"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "path/to/protobuf" // ์ปดํŒŒ์ผ๋œ ํ”„๋กœํ† ๋ฒ„ํผ ํŒจํ‚ค์ง€ import
)

// ์‹ค์ œ ์„œ๋น„์Šค ๊ตฌํ˜„์ฒด
type paymentServer struct {
    pb.UnimplementedPaymentServiceServer
}

// ๊ฒฐ์ œ ์š”์ฒญ ์ฒ˜๋ฆฌ ๋กœ์ง
func (s *paymentServer) ProcessPayment(ctx context.Context, req *pb.PaymentRequest) (*pb.PaymentResponse, error) {
    // ์ด๊ณณ์— DB ์—ฐ๋™, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋“ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    return &pb.PaymentResponse{
        Success:       true,
        TransactionId: "txn_123456",
    }, nil
}

func main() {
    // TCP ๋ฆฌ์Šค๋„ˆ ์ƒ์„ฑ
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // gRPC ์„œ๋ฒ„ ์ƒ์„ฑ ๋ฐ ์„œ๋น„์Šค ๋“ฑ๋ก
    s := grpc.NewServer()
    pb.RegisterPaymentServiceServer(s, &paymentServer{})

    fmt.Println("Payment Service running on port 50051...")

    // ์„œ๋ฒ„ ์‹คํ–‰
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

๐Ÿงพ ์™œ ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์„๊นŒ?
gRPC๋Š” Protobuf๋ฅผ ์‚ฌ์šฉํ•ด JSON๋ณด๋‹ค ์•ฝ 10๋ฐฐ ๋น ๋ฅธ ์ง๋ ฌํ™” ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
๋˜ํ•œ HTTP/2 ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ์„ ํ†ตํ•ด ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๋กœ ๋‹ค์ˆ˜์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ๋„คํŠธ์›Œํฌ ์˜ค๋ฒ„ํ—ค๋“œ์™€ ์ง€์—ฐ์„ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


โš–๏ธ 3. ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ & Redis ์บ์‹ฑ์œผ๋กœ ์‘๋‹ต ์†๋„ ๊ฐœ์„ ํ•˜๊ธฐ

API ์„ฑ๋Šฅ์„ ๊ณ ๋ฃจํ‹ด, gRPC๋ฅผ ํ†ตํ•ด ์ตœ์ ํ™”ํ•œ ํ›„,
์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋กœ ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ค๊ธฐ ์œ„ํ•ด NGINX + ๋ผ์šด๋“œ๋กœ๋นˆ(Round Robin) ๋ฐฉ์‹์˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ๋‚จ์•„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
ํŠธ๋ž˜ํ”ฝ์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ๋™์ผํ•œ DB ์ฟผ๋ฆฌ๊ฐ€ ๋ฐ˜๋ณต๋˜๋ฉฐ ์ „์ฒด ์„ฑ๋Šฅ์„ ๋–จ์–ด๋œจ๋ฆฌ๊ณ  ์žˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

โœ… ํ•ด๊ฒฐ์ฑ…: Redis๋ฅผ ํ™œ์šฉํ•œ ์บ์‹ฑ ๋„์ž…
์ž์ฃผ ์กฐํšŒ๋˜๋Š” ๋ฐ์ดํ„ฐ(์˜ˆ: ์ตœ๊ทผ ๊ฒฐ์ œ ๋‚ด์—ญ)๋ฅผ Redis์— ์บ์‹ฑํ•˜์—ฌ,
๋งค ์š”์ฒญ๋งˆ๋‹ค DB๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์˜ˆ์ œ ์ฝ”๋“œ (Go + Redis)

package main

import (
    "context"
    "fmt"

    "github.com/go-redis/redis/v8"
)

// Redis ๋ช…๋ น ์‹คํ–‰ ์‹œ ์‚ฌ์šฉํ•  Context
var ctx = context.Background()

func main() {
    // Redis ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379", // Redis ์„œ๋ฒ„ ์ฃผ์†Œ
        // Password, DB ๋“ฑ ์ถ”๊ฐ€ ์„ค์ • ๊ฐ€๋Šฅ
    })

    // Redis์— ๋ฐ์ดํ„ฐ ์ €์žฅ (key: lastTransaction, value: txn_123456)
    err := rdb.Set(ctx, "lastTransaction", "txn_123456", 0).Err()
    if err != nil {
        panic(err) // ์‹ค๋ฌด์—์„œ๋Š” ๋กœ๊ทธ ์ฒ˜๋ฆฌ ๊ถŒ์žฅ
    }

    // Redis์—์„œ ๋ฐ์ดํ„ฐ ์กฐํšŒ
    val, err := rdb.Get(ctx, "lastTransaction").Result()
    if err != nil {
        panic(err)
    }

    fmt.Println("Cached Transaction:", val)
}

๐Ÿ’ก ์™œ ํšจ๊ณผ์ ์ด์—ˆ์„๊นŒ?
Redis์— ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•จ์œผ๋กœ์จ DB ์š”์ฒญ ์ˆ˜๋ฅผ ์•ฝ 80% ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๊ณ ,
์‘๋‹ต ์‹œ๊ฐ„์€ ํ‰๊ท  50ms โ†’ 5ms ์ดํ•˜๋กœ ๊ฐ์†Œํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿง  ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” Redis ์บ์‹ฑ ์ „๋žต

์ „๋žต์„ค๋ช…์˜ˆ์‹œ
์ฝ๊ธฐ ์ค‘์‹ฌ ๋ฐ์ดํ„ฐ ์บ์‹ฑ์กฐํšŒ ๋นˆ๋„๊ฐ€ ๋†’์€ ๋ฐ์ดํ„ฐ๋ฅผ Redis์— ์ €์žฅํ•˜์—ฌ DB ๋ถ€ํ•˜ ๊ฐ์†Œ์‚ฌ์šฉ์ž ์ •๋ณด, ์ตœ๊ทผ ๊ฒฐ์ œ, ์ธ๊ธฐ ์ƒํ’ˆ
TTL(Time To Live) ์„ค์ •์ผ์ • ์‹œ๊ฐ„ ํ›„ ์บ์‹œ๋ฅผ ์ž๋™ ๋งŒ๋ฃŒ์‹œ์ผœ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€60์ดˆ TTL ์„ค์ •
Lazy-loading (Cache-aside)์บ์‹œ์— ์—†์„ ๊ฒฝ์šฐ DB์—์„œ ์กฐํšŒํ•˜๊ณ  Redis์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์„ฑ๋Šฅ๊ณผ ์ผ๊ด€์„ฑ ๊ท ํ˜•
์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐ ์žฅ์•  ๋Œ€๋น„Redis ์žฅ์•  ์‹œ DB๋กœ ๋Œ€์ฒด ์กฐํšŒ ์ฒ˜๋ฆฌ (fallback)if err != redis.Nil ๋“ฑ ์ฒดํฌ

๐Ÿš€ ์บ์‹ฑ ๋„์ž… ์‹œ ์‹ค๋ฌด ํŒ
Redis๋Š” ๋„คํŠธ์›Œํฌ ๋น„์šฉ์ด ๊ฑฐ์˜ ์—†๊ณ  ์†๋„๊ฐ€ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์—,
DB ์š”์ฒญ ์ˆ˜๋ฅผ ์ค„์ด๊ณ  ์ „์ฒด ์‘๋‹ต ์ง€์—ฐ(latency)์„ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ด๋Š” ๋ฐ ๋งค์šฐ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

๋‹จ, ์บ์‹ฑ์— ์˜์กดํ•œ ๋กœ์ง์€ TTL ์„ค์ •, ๋™๊ธฐํ™” ์ „๋žต, ์บ์‹œ ๋ฌดํšจํ™” ์ •์ฑ…์„ ๋ช…ํ™•ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“ˆ 4. Kubernetes & ์˜คํ† ์Šค์ผ€์ผ๋ง์œผ๋กœ 100๋งŒ RPS๊นŒ์ง€ ํ™•์žฅํ•˜๊ธฐ

์ตœ์ ํ™”๋œ API๋ฅผ ์™„์ „ํžˆ Docker๋กœ ์ปจํ…Œ์ด๋„ˆํ™”ํ•œ ํ›„,
Kubernetes ํด๋Ÿฌ์Šคํ„ฐ์— ๋ฐฐํฌํ•˜๊ณ  ์ž๋™ ํ™•์žฅ(Auto-Scaling)์„ ํ™œ์„ฑํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋‹จ๊ณ„์—์„œ ์šฐ๋ฆฌ๋Š” ์ˆ˜ํ‰ ํ™•์žฅ(Horizontal Scaling) ์„ ํ†ตํ•ด
ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ์ƒํ™ฉ์—์„œ๋„ ์„œ๋น„์Šค๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก ํƒ„๋ ฅ์ ์ธ ์ธํ”„๋ผ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿงพ Kubernetes Deployment ๊ตฌ์„ฑ ์˜ˆ์‹œ

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service  # ๋ฆฌ์†Œ์Šค ์ด๋ฆ„
spec:
  replicas: 10  # ์ดˆ๊ธฐ ๋ณต์ œ๋ณธ ์ˆ˜ (HPA์— ์˜ํ•ด ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)
  selector:
    matchLabels:
      app: payment-service  # ํŒŒ๋“œ๋ฅผ ์„ ํƒํ•  ๊ธฐ์ค€
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
        - name: payment-service
          image: myregistry/payment-service:v1  # ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€
          ports:
            - containerPort: 50051  # gRPC ํฌํŠธ

๐Ÿ”„ ์˜คํ† ์Šค์ผ€์ผ๋ง์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ตฌ์„ฑ (HPA)
Deployment๋งŒ์œผ๋กœ๋Š” ์ž๋™ ํ™•์žฅ์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—,
HorizontalPodAutoscaler(HPA) ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ„๋„๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: payment-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: payment-service
  minReplicas: 5
  maxReplicas: 100
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60

โœ… ์™œ ํšจ๊ณผ์ ์ด์—ˆ์„๊นŒ?
Kubernetes๋Š” ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€ ์‹œ ์ž๋™์œผ๋กœ ํŒŒ๋“œ ์ˆ˜๋ฅผ ํ™•์žฅํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—,
ํŠธ๋ž˜ํ”ฝ์ด ๊ฐ‘์ž๊ธฐ ๋ชฐ๋ ค๋„ ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ํƒ„๋ ฅ์ ์œผ๋กœ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ ์ตœ์ข… ๊ฒฐ๊ณผ: ์ดˆ๋‹น 100๋งŒ ์š”์ฒญ, ๋‹ค์šดํƒ€์ž„ 0
์ด ๋ชจ๋“  ์ตœ์ ํ™” ๊ณผ์ •์„ ๊ฑฐ์นœ ๊ฒฐ๊ณผ,
์šฐ๋ฆฌ์˜ ๋ฐฑ์—”๋“œ๋Š” ์ดˆ๋‹น 1,000,000 ์š”์ฒญ(RPS) ์„ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ ,
์ง€์—ฐ(latency)์€ ๋‚ฎ๊ฒŒ ์œ ์ง€๋˜์—ˆ์œผ๋ฉฐ, ๋‹ค์šดํƒ€์ž„์€ ๋‹จ ํ•œ ๋ฒˆ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

โœ… ๊ณ ๋ฃจํ‹ด ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด CPU ๋ถ€ํ•˜๋ฅผ ํฌ๊ฒŒ ์ค„์˜€๊ณ 
โœ… gRPC ์ „ํ™˜์œผ๋กœ REST ๋Œ€๋น„ API ์„ฑ๋Šฅ์„ ๋น„์•ฝ์ ์œผ๋กœ ํ–ฅ์ƒ์‹œ์ผฐ์œผ๋ฉฐ
โœ… Redis ์บ์‹ฑ์œผ๋กœ DB ์ฟผ๋ฆฌ ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์˜€๊ณ 
โœ… Kubernetes ์˜คํ† ์Šค์ผ€์ผ๋ง์œผ๋กœ ํ”ผํฌ ํŠธ๋ž˜ํ”ฝ์—๋„ ๋ฌด์ค‘๋‹จ ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ๋ฐฐ์šด ์ 

์Šค์ผ€์ผ๋ง์€ ๋‹จ์ˆœํžˆ ์„œ๋ฒ„ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.
์ง„์ •ํ•œ ํ™•์žฅ์€ ์‹œ์Šคํ…œ์˜ ๋ชจ๋“  ๊ณ„์ธต์„ ์ •๋ฐ€ํ•˜๊ฒŒ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์—์„œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

profile
๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š”, ์ด์Šˆ๋ฅผ ์ค„์ด๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€