02-07 20:37
λ°˜μ‘ν˜•
250x250
Recent Posts
Recent Comments
Link
관리 메뉴

DevOps Tasks

AWS Parameter Store, λŒ€λŸ‰μ˜ νŒŒλΌλ―Έν„° 관리 μ–΄λ–»κ²Œ ν•΄κ²°ν• κΉŒ? Go와 Cobra둜 λ§Œλ“  CLI 개발기 λ³Έλ¬Έ

GO

AWS Parameter Store, λŒ€λŸ‰μ˜ νŒŒλΌλ―Έν„° 관리 μ–΄λ–»κ²Œ ν•΄κ²°ν• κΉŒ? Go와 Cobra둜 λ§Œλ“  CLI 개발기

데λ°₯슀 2024. 12. 3. 15:34
728x90
λ°˜μ‘ν˜•

πŸ”„ 반볡 μž‘μ—…μ˜ λΉ„νš¨μœ¨μ„± ν•΄κ²°

`AWS Parameter Store` λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ„€μ • 값을 μ•ˆμ „ν•˜κ²Œ 관리할 수 μžˆλŠ” κ°•λ ₯ν•œ μ„œλΉ„μŠ€μž…λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ 관리 μž‘μ—…μ—μ„œ νš¨μœ¨μ„±μ„ μ €ν•΄ν•˜λŠ” λͺ‡ 가지 λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ CLI 도ꡬλ₯Ό κ°œλ°œν•˜μ—¬ μžλ™ν™”μ™€ μ„±λŠ₯ μ΅œμ ν™”λ₯Ό λͺ©ν‘œλ‘œ μ‚Όμ•˜μŠ΅λ‹ˆλ‹€.

반볡 μž‘μ—…

μ—¬λŸ¬ ν™˜κ²½μ— λ™μΌν•œ μ„€μ • 값을 μΆ”κ°€ν•˜κ±°λ‚˜ μ—…λ°μ΄νŠΈν•˜λŠ” μž‘μ—…μ΄ 번거둭고, 이λ₯Ό μžλ™ν™”ν•˜μ—¬ μ‹œκ°„μ„ μ ˆμ•½ν•˜κ³  μ‹€μˆ˜λ₯Ό μ€„μ΄κ³ μž ν–ˆμŠ΅λ‹ˆλ‹€.

λŒ€λŸ‰ νŒŒλΌλ―Έν„° 처리

λŒ€λŸ‰μ˜ νŒŒλΌλ―Έν„°λ₯Ό 효율적으둜 μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ `병렬 처리`λ₯Ό ν™œμš©ν•˜μ—¬ μž‘μ—… 속도λ₯Ό μ΅œμ ν™”ν–ˆμŠ΅λ‹ˆλ‹€.

Rate Limit 문제

λŒ€λŸ‰ νŒŒλΌλ―Έν„° 처리 쀑 API 호좜 μ œν•œμ΄ λ°œμƒν•˜μ—¬ 이λ₯Ό `μ§€μˆ˜ λ°±μ˜€ν”„`와 `랜덀 지터`둜 ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.

μˆ˜λ™ 처리

μ½˜μ†”μ—μ„œ νŒŒλΌλ―Έν„°λ₯Ό κ΄€λ¦¬ν•˜λŠ” κ³Όμ •μ—μ„œ λ°œμƒν•  수 μžˆλŠ” μ‹€μˆ˜λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μž‘μ—…μ„ μžλ™ν™”ν–ˆμŠ΅λ‹ˆλ‹€.


CLI 개발과 Cobra ν”„λ ˆμž„μ›Œν¬

이 λ„κ΅¬λŠ” Go μ–Έμ–΄μ™€ Cobra ν”„λ ˆμž„μ›Œν¬λ₯Ό ν™œμš©ν•˜μ—¬ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

특히 CobraλŠ” 효율적이고 직관적인 CLI κ°œλ°œμ„ κ°€λŠ₯ν•˜κ²Œ ν•˜λ©°, μ•ˆμ •μ„±κ³Ό ν™•μž₯성을 λ™μ‹œμ— μ œκ³΅ν•©λ‹ˆλ‹€. 

CobraλŠ” λͺ…λ Ήμ–΄ κΈ°λ°˜ CLI κ°œλ°œμ„ λ‹¨μˆœν™”ν•˜λ©΄μ„œλ„ κ°•λ ₯ν•œ κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” Go μ–Έμ–΄μ˜ ν‘œμ€€ ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 μ£Όμš” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€

  • λͺ…λ Ήμ–΄ ꡬ쑰화 : λͺ…λ Ήμ–΄λ₯Ό λ…λ¦½μ μœΌλ‘œ κ΅¬ν˜„ν•˜λ©°, 계측적 트리 ꡬ쑰둜 ν™•μž₯ν•  수 μžˆμ–΄ λ³΅μž‘ν•œ CLI 섀계에 μ ν•©ν•©λ‹ˆλ‹€.
  • ν”Œλž˜κ·Έ 지원 : λ‹€μ–‘ν•œ μ˜΅μ…˜ ν”Œλž˜κ·Έ(-region, -profile λ“±)λ₯Ό μΆ”κ°€ν•΄ μ‚¬μš©μž κ²½ν—˜μ„ κ°œμ„ ν•˜κ³  μœ μ—°μ„±μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
  • μžλ™ μ™„μ„± : Zsh, Bash, Fish λ“± λ‹€μ–‘ν•œ μ‰˜ ν™˜κ²½μ—μ„œ λͺ…λ Ήμ–΄ μžλ™ μ™„μ„± κΈ°λŠ₯을 μ œκ³΅ν•˜μ—¬ 개발 생산성을 λ†’μž…λ‹ˆλ‹€.


λ˜ν•œ, Go의 κ°•λ ₯ν•œ ν”Œλž«νΌ 독립성을 ν™œμš©ν•΄ λ‹€μ–‘ν•œ ν™˜κ²½μ— 맞좘 λ°”μ΄λ„ˆλ¦¬λ₯Ό μ†μ‰½κ²Œ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

# macOS Apple Silicon (M1/M2/M3)
GOOS=darwin GOARCH=arm64 go build -o ssm-silicon-mac

# macOS Intel
GOOS=darwin GOARCH=amd64 go build -o ssm-intel-mac

# Windows
GOOS=windows GOARCH=amd64 go build -o ssm-windows.exe

🎯 ν•΄κ²°ν•˜κ³ μž ν•œ μ£Όμš” 문제

 

반볡 μž‘μ—… μžλ™ν™”

for _, env := range envs {
    go func(env string) {
        params, err := ssmClient.GetParameters(fmt.Sprintf("%s", env))
        if err == nil {
            jsonutil.WriteJSON(fmt.Sprintf("%s.json", env), params, true)
        }
    }(env)
}

πŸš€ μ—¬λŸ¬ ν™˜κ²½μ— λŒ€ν•΄ νŒŒλΌλ―Έν„°λ₯Ό μ‘°νšŒν•˜κ³  μ—…λ‘œλ“œν•˜λŠ” μž‘μ—…μ„ CLI둜 κ°„μ†Œν™”.


Rate Limit 문제 μ™„ν™”

baseDelay := 200 * time.Millisecond
for attempt := 1; attempt <= maxRetries; attempt++ {
    err := ssmClient.PutParameters(params)
    if err == nil {
        break
    }
    time.Sleep(baseDelay * time.Duration(1<<attempt) + time.Duration(rand.Int63n(int64(100*time.Millisecond))))
}

πŸš€ μ§€μˆ˜ λ°±μ˜€ν”„λ‘œ μž¬μ‹œλ„ 간격을 2λ°°μ”© 늘리고, 랜덀 μ§€ν„°λ‘œ 0~100ms의 λ¬΄μž‘μœ„ 지연을 μΆ”κ°€ν•΄ 호좜 μΆ©λŒμ„ λ°©μ§€ν•©λ‹ˆλ‹€.

 


μž‘μ—… 속도와 μ•ˆμ •μ„± ν–₯상

sem := make(chan struct{}, 5)
var wg sync.WaitGroup
for _, param := range params {
    sem <- struct{}{}
    wg.Add(1)
    go func(param string) {
        defer func() { <-sem }()
        defer wg.Done()
        ssmClient.PutParameter(param)
    }(param)
}
wg.Wait()

πŸš€ κ³  루틴과 μ„Έλ§ˆν¬μ–΄λ₯Ό ν™œμš©ν•œ λ™μ‹œμ„± 처리둜 λŒ€κ·œλͺ¨ μž‘μ—…μ˜ 속도 μ΅œμ ν™”.

 


⚑ μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•œ 핡심 방법듀

λ©”λͺ¨λ¦¬ 관리와 μž¬μ‚¬μš©μœΌλ‘œ 효율 ν–₯상

Buffered I/O ν™œμš©

  • 파일 읽기와 μ“°κΈ° μž‘μ—…μ—μ„œ `bufio.NewReader`와 `bufio.NewWriter`λ₯Ό μ‚¬μš©ν•΄ λ©”λͺ¨λ¦¬λ₯Ό 효율적으둜 μ‚¬μš©ν•˜κ³  μž‘μ—… 속도λ₯Ό λ†’μ˜€μŠ΅λ‹ˆλ‹€.
reader := bufio.NewReader(file)
decoder := json.NewDecoder(reader)

writer := bufio.NewWriter(file)
encoder := json.NewEncoder(writer)
encoder.SetIndent("", "  ")
encoder.Encode(data)
writer.Flush()

πŸš€ ν° 파일 처리 μ‹œ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 쀄이고, λ””μŠ€ν¬ I/O μž‘μ—… μ‹œκ°„μ„ 단좕.


ꡬ쑰체와 ν¬μΈν„°μ˜ ν™œμš©

  • AWS SDK ν΄λΌμ΄μ–ΈνŠΈλ₯Ό `SSMClient` ꡬ쑰체에 μΊ‘μŠν™”ν•˜μ—¬ λ©”λͺ¨λ¦¬ μž¬μ‚¬μš©μ„±μ„ 높이고, μž‘μ—… 쀑 반볡 μ΄ˆκΈ°ν™”λ₯Ό 방지.
type SSMClient struct {
    client *ssm.Client
    sts    *sts.Client
}

func NewSSMClient(profile, region string) *SSMClient {
    cfg, _ := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion(region),
        config.WithSharedConfigProfile(profile),
    )
    return &SSMClient{
        client: ssm.NewFromConfig(cfg),
        sts:    sts.NewFromConfig(cfg),
    }
}

πŸš€ AWS ν΄λΌμ΄μ–ΈνŠΈλ₯Ό 반볡 μƒμ„±ν•˜λŠ” λŒ€μ‹  μž¬μ‚¬μš©ν•˜μ—¬ μ΄ˆκΈ°ν™” λΉ„μš© κ°μ†Œ.


λ™μ‹œμ„± 처리둜 μž‘μ—… 속도 ν–₯상

κ³  루틴과 WaitGroup

  • `AWS SSM API` ν˜ΈμΆœμ„ κ³  λ£¨ν‹΄μœΌλ‘œ λ³‘λ ¬ν™”ν•˜μ—¬ μž‘μ—… 속도λ₯Ό μ΅œμ ν™”.
  • `sync.WaitGroup`을 ν™œμš©ν•΄ 병렬 μž‘μ—…μ˜ μ™„λ£Œλ₯Ό 동기화.
var wg sync.WaitGroup

for _, param := range params {
    wg.Add(1)
    go func(param string) {
        defer wg.Done()
    }(param)
}
wg.Wait()

πŸš€ μ—¬λŸ¬ ν™˜κ²½μ˜ νŒŒλΌλ―Έν„°λ₯Ό λ™μ‹œμ— μ‘°νšŒν•˜κ±°λ‚˜ μ—…λ‘œλ“œν•˜μ—¬ 처리 속도 증가.


μ„Έλ§ˆν¬μ–΄λ₯Ό ν™œμš©ν•œ λ™μ‹œ μž‘μ—… μ œν•œ

  • 채널(`chan struct{}`)을 μ„Έλ§ˆν¬μ–΄λ‘œ μ‚¬μš©ν•΄ 병렬 μž‘μ—… 수λ₯Ό μ œμ–΄.
sem := make(chan struct{}, 5) // λ™μ‹œ μž‘μ—… 5개 μ œν•œ
for _, param := range params {
    sem <- struct{}{}
    go func(param string) {
        defer func() { <-sem }()
        // μž‘μ—… μˆ˜ν–‰
    }(param)
}

πŸš€ AWS Rate Limit 초과λ₯Ό λ°©μ§€ν•˜λ©΄μ„œλ„ μ΅œλŒ€ 병렬 μž‘μ—… 처리 κ°€λŠ₯.


μ§€μˆ˜ λ°±μ˜€ν”„μ™€ 랜덀 μ§€ν„°λ‘œ μ•ˆμ •μ„± 확보

  • AWS Rate Limit에 도달할 경우, μ§€μˆ˜ λ°±μ˜€ν”„μ™€ 랜덀 지터λ₯Ό μ μš©ν•œ μž¬μ‹œλ„ 둜직으둜 μ•ˆμ •μ„±μ„ λ†’μž„.
baseDelay := 200 * time.Millisecond
attempt := 0

for {
    attempt++
    err := ssmClient.PutParameters(map[string]interface{}{paramPath: value}, "")
    if err == nil {
        break
    }

    backoff := baseDelay * time.Duration(1<<uint(attempt))
    jitter := time.Duration(rand.Int63n(int64(100 * time.Millisecond)))
    time.Sleep(backoff + jitter)
}

πŸš€ AWS API 호좜 μ‹€νŒ¨ μ‹œ μ§€μˆ˜ λ°±μ˜€ν”„μ™€ 랜덀 지터λ₯Ό ν™œμš©ν•΄ 호좜 μΆ©λŒμ„ λ°©μ§€ν•˜λ©° μž‘μ—… 성곡λ₯ μ„ λ†’μ΄λŠ” λ°©μ‹μœΌλ‘œ ν•΄κ²°.

 


JSON 데이터 μ΅œμ ν™” 처리

  • μ€‘μ²©λœ JSON ꡬ쑰 평탄화 및 μž¬κ΅¬μ„±:
    • AWS SSM의 νŒŒλΌλ―Έν„° ν˜•μ‹μ— 맞게 μ€‘μ²©λœ JSON 데이터λ₯Ό ν‰νƒ„ν™”ν•˜κ±°λ‚˜ λ‹€μ‹œ μ€‘μ²©λœ ꡬ쑰둜 λ³€ν™˜.
    func flattenParameters(prefix string, params interface{}, flatParams map[string]interface{}) {
        switch paramsTyped := params.(type) {
        case map[string]interface{}:
            for key, value := range paramsTyped {
                fullKey := filepath.Join(prefix, key)
                flattenParameters(fullKey, value, flatParams)
            }
        default:
            flatParams[prefix] = params
        }
    }
    

πŸš€ JSON λ°μ΄ν„°μ˜ λ³€ν™˜ 과정을 μžλ™ν™”ν•˜μ—¬ 개발자의 μˆ˜μž‘μ—… λΆ€λ‹΄ κ°μ†Œ.


CLI 섀계와 μ‚¬μš©μž κ²½ν—˜ κ°•ν™”

  • λͺ…λ Ήμ–΄ 섀계와 ν”Œλž˜κ·Έ 관리
    • Cobra ν”„λ ˆμž„μ›Œν¬λ₯Ό ν™œμš©ν•΄ 직관적인 CLI λͺ…λ Ήμ–΄ 섀계.
    var rootCmd = &cobra.Command{
        Use:   "ssm",
        Short: "AWS SSM Parameter Store 관리 CLI",
    }
    
    func init() {
        rootCmd.AddCommand(getCmd, putCmd, completionCmd, versionCmd)
    }
    

πŸš€ μ‚¬μš©μž μΉœν™”μ  λͺ…령어와 ν”Œλž˜κ·Έλ₯Ό 톡해 CLI μ ‘κ·Όμ„± ν–₯상.


πŸ–±οΈ AWS μ½˜μ†”? κ·Έκ±° 아직도 μ¨μš”?

이 CLI 도ꡬλ₯Ό 톡해 λ‹€μŒκ³Ό 같은 κ²°κ³Όλ₯Ό 얻을 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€

 

⏱️ μ‹œκ°„ μ ˆμ•½: 반볡 μž‘μ—… μžλ™ν™”λ‘œ μˆ˜μž‘μ—… μ‹œκ°„μ„ λŒ€ν­ μ ˆκ°ν–ˆμŠ΅λ‹ˆλ‹€.

πŸš€ 속도 μ—…κ·Έλ ˆμ΄λ“œ: 병렬 μ²˜λ¦¬μ™€ λ™μ‹œμ„± μ΅œμ ν™”λ‘œ μž‘μ—… νš¨μœ¨μ„ 크게 ν–₯μƒμ‹œμΌ°μŠ΅λ‹ˆλ‹€.

πŸ”’ μ•ˆμ •μ„± κ°•ν™”: Rate Limit 문제λ₯Ό μ§€μˆ˜ λ°±μ˜€ν”„μ™€ 랜덀 μ§€ν„°λ‘œ ν•΄κ²°ν•˜λ©° μž‘μ—…μ˜ μ•ˆμ •μ„±μ„ ν™•λ³΄ν–ˆμŠ΅λ‹ˆλ‹€.

 

이제 반볡 μž‘μ—…μ˜ λΆ€λ‹΄μ—μ„œ λ²—μ–΄λ‚˜, 더 μ€‘μš”ν•œ λ¬Έμ œμ— 집쀑할 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

그리고 무엇보닀, 맀번 Console을 열지 μ•Šμ•„λ„ λœλ‹€λŠ” 점은 말할 ν•„μš”λ„ μ—†κ² μ£ ? 😊

 

728x90
λ°˜μ‘ν˜•