무언가 많이 했구나; 2024년 3분기 회고
목차
쓱 지나간 3개월 #
정말 눈 깜짝할 새에 3개월이 지나갔다. 회사에서의 업무와 집에서의 개발을 반복한 일상에서, 큰 이벤트는 없었지만, 다시 돌아보니 다사다난한 90일이었다.
올해 6월에 첫 회고를 작성하고, 난 그 때의 회고가 나의 마지막 회고가 될까봐 두려웠다. 하지만, 지금 다시 이 글을 작성하는 내 자신을 보고나니, 조금이나마 두 번째 발걸음을 내딛은 나를 칭찬한다. 이전 회고에서는 회사에서 업무를 하는 나와 집에서의 취미로 개발을 하는 나를 구별했지만, 이번에는 별다른 목차 없이 어떤 것들을 했는지 나열해보고자 한다.
아무래도 긴 글이 되겠지만, 그 내용은 흥미로울 것이라고 확신한다. 우연히라도 이 글을 읽게 된 분들은 꼭 끝까지 한 번 읽어주시면 좋을 것 같다 😁.
목차는 다음과 같다.
- 커스텀 도메인 지원
- Mermaid
- Terragrunt
- OpenSearch와 CoreDNS
- Java, AmazonLinux, Karpenter 버전 업데이트
- Github Actions Runner가 멈췄다!!
- Zsh 갈아엎기
- Kafka Session 101, 201
- 사랑해요, Cloudflare Workers
- (WIP) 요즘은 이런걸 합니다
1) 커스텀 도메인 지원 #
Vercel이나 Github 같은 곳에서는 .vecel.app
이나 .github.io
등을 대신해서, CNAME 혹은 TXT 레코드 등을 추가해서, 본인의 커스텀 도메인을 사용할 수 있게 해주는 기능을 본 적이 있을 것이다. 7월 동안은 해당 기능을 구현하고 리팩토링하는 데에 많은 시간을 쏟았다. 기존 Managed 서비스에서 맹목적으로 사용만 하고 있었던 기능을 직접 구현하는 데에서 일차적으로 많이 설렘을 느꼈고, 쿠버네티스를 활용해서 클라우드 네이티브하게 문제를 해결하는 것도 즐거웠다.
간단하게는 1) HTTP-01 Challenge를 이용하고 인증서를 발급 과정을 자동화하며, 2) 해당 domain으로의 traffic을 동적으로 라우팅하고 그에 맞는 웹페이지를 리턴하는 것이 주요 포인트였다. 단순히 작동하는 것 이외에 디테일로는 어떻게 해당 시스템을 유지/보수 가능하고, 수많은 예외 상황에 대응하는지였다. (수많은 인증서를 어떻게 관리할 것인지, 잘못된 CNAME record 설정을 처리할지, 얼마나 많은 양의 request 혹은 route들을 처리할 수 있을지도 고민이 필요했다 등)
무엇보다 인증서를 발급하는 데에 필요한 1) Cert Manager와 Let’s Encrypt를 살펴보는 것, 2) TLS Passthrough를 위해서 평소에 많이 사용하는 AWS ALB가 아닌 L4 단계의 NLB를 사용하게 된 것도 즐거웠던 경험 중 하나였다.
다이어그램과 함께 더 자세한 아키텍쳐에 대한 이야기를 나누어보면 좋겠지만, (시간이 허락하는 선에서) 보다 더 정제된 내용으로 다른 블로그 글에서 작성하는 것이 좋을 듯 하다. (보통은 시간이 허락하지 않음. 써야 할 블로그 글이 쭉 밀려있음…)
2) Mermaid #
7월 정도부터 Mermaid를 애용하기 시작했다. 지난 회고에서도 그렇고 Redis stream에 대한 내용을 다루었던 바로 지난 글에서도 Mermaid를 이용해서 많은 내용을 소개했었다. 기존에는 Excalidraw를 많이 사용했었다. 특히, https://excalidraw.jaehong21.com 과 같이 Excalidraw를 self-host에서 사용할만큼 애정이 있던 프로덕트였다. 하지만, 늘 걸렸던 점 중 하나는 프로젝트를 저장하는 방법이었다. Excalidraw는 프로젝트를 export해서 .excalidraw
파일 형식으로 저장했어야했고, .png
로 저장하면 수정이 불가능했다.
그에 반해 Mermaid는 코드처럼 단순히 텍스트 파일로 저장할 수 있다. 또한, Markdown, Notion, Github 등에서도 모두 Mermaid에 대한 Preview 기능은 기본적으로 탑재되어 있다. 그리고, 나도 Mermaid를 공부하기 전에는 몰랐지만 기본적인 Flowchart 이외에도 Class Diagram, ERD, Packet, Mindmap 등 별의 별 기능을 다 지원한다.
물론 단점으로는 유연하게 컴포넌트들을 배치하기 힘들다. Declarative하게 그래프가 그려진다는 것은 큰 장점이자 단점으로 다가오기는 한다. 그럼에도, 매우 뛰어난 호환성과 범용성 때문에 충분히 시간을 들여서 배울 가치는 있는 것 같다. (이것도 나에게 Neovim을 알려준 우리 팀원 @Claud가 사용한 것을 보고, 어깨 너머로 배웠다)
3) Terragrunt #
다른 많은 회사들처럼 내가 다니는 채널톡 또한 Terraform을 이용해서 인프라를 관리한다. 하지만, 점점 관리해야할 region이 늘어나고, 컴포넌트와 모듈이 많아지면서, 하나의 큰 terraform state로 대부분의 인프라를 커버하는 데에는 한계를 느끼고 있다 (사실 꽤 오래전부터 힘들었다고 한다).
그래서, 7월부터 지금까지 계속 진행하고 있는 것은 Terraform project(Git repo) 대규모 공사이다. 그리고 이 기회에 Terragrunt 리서치도 함께 병행하고 있다. Terragrunt는 Keep your Terraform code DRY라는 일념 하에 만들어진 툴이다. 특히 여러 리전, 계정 별로 유사한 형태의 인프라를 찍어내야하는 use-case에 특화되어 있다.
인프라를 관리하는 Terraform 같은 경우에는 더더욱이 한 번 아키텍쳐가 정해지면 바꾸기가 쉽지 않다. 그만큼 신중을 기해야한다. 이번에 Terraform v2 리서치를 진행하면서 느낀 점은 기술적으로 어려운 것보다 정책과 유지/보수에 대해서 훨씬 더 많은 고민이 필요하다는 것이었다. 사실, 이 부분은 단순히 Terragrunt 뿐만 아니라 DevOps 팀에 합류하면서 계속해서 배우는 부분인 것 같다.
+) Terraform에 CI/CD 파이프라인도 이번에 추가해야한다…. Apply after Merge vs Apply before Merge 등. 관련 정책도 잡아야한다.
4) OpenSearch 와 CoreDNS #
말하기는 조금 조심스럽지만, 이번 3분기에는 채널톡 Status 페이지에서 확인할 수 있듯이 다사다난한 하루가 많았다. 위 incidents와 직접적인 관련은 없지만, 올해부터 OpenSearch와 CoreDNS 관련 이슈가 조금 잦았다 (지금은 많이 해결됨).
하지만, 해당 이슈로부터 개인적으로 배운 것은 많았다. 특히 두 개가 공통적으로 가지는 특징은 Application Layer에서의 버그라기보다는 인프라 레벨에서의 이슈이며, 더욱이 CoreDNS는 온전히 쿠버네티스 영역의 컴포넌트라는 점이다.
기존에 ElasticSearch나 ELK 스택을 많이 활용해보지 않는 나로서는 OpenSearch는 상당히 낯설었다. 하지만 이번 기회로 OpenSearch에 대한 기본적인 개념을 다질 수 있었고, 평소에 크게 신경쓰지 않고 그저 잘 사용했던 CoreDNS에 대해서도 공부해볼 수 있었다. 특히 사두고 읽지 않았던, O’Reilly CoreDNS를 바로 완독할 수 있었다.
쿠버네티스를 사용하는 Cloud Native한 환경, 그리고 수많은 AWS 인프라들을 같이 활용하는 서비스에서 한쪽에서 이슈가 생겼을 때, 어떻게 에러가 전파되고, 그 에러의 원인을 찾아나가는 과정을 직접 체험하고, 지켜볼 수 있었다. 이 맥락에서 단순히 성능을 튜닝하고, 작동하는 아키텍쳐를 만드는 것만큼 어떻게 서비스 아키텍쳐에서의 가시성을 확보하고 빠른 시간내에 대응하는지가 중요한 것을 느끼게 해준 시간이었다.
5) Java, AmazonLinux, Karpenter 버전 업데이트 #
제목만 봐도 숨이 턱 막힌다. 한 개씩만 따로 놓고만 봐도 큰 이벤트 같지만, 놀랍게도 8월 동안 세 개 모두 변화가 있었다. Java랑 AmazonLinux 업데이트는 사실 실제 작업 시간 자체는 오래 걸리지 않았다. 하지만 무엇보다 메인 API 서버에 적용해야하는 신중한 작업이기에 만전을 기하는 시간이 오래 걸렸다. Java는 패치 버전이었지만, Linux는 AL2에서 AL2023으로 넘어가는 작업이었다. Datadog이랑 Grafana로 JVM metrics를 열심히 봐주신 @Pepper에게 너무 감사하다. Java, AmazonLinux 업데이트에서는 어떻게 downtime 없이 seamless 하게 메인 API 서버를 갈아끼울 것인지에 대한 고민이 조금 있었던 것 같다. 물론 downtime 없이 서버를 교체하는 방법이야 너무 많지만, 어떻게 최소한의 리소스로 간단히 교체하고, 문제가 있다고 판단됐을 때는 재빠르게 롤백할 수 있을지에 대한 고민이었다.
Karpenter 업데이트도 생각보다 리소스가 많이 들었다. 내가 처음 채널톡에 합류했을 때는 이미 Karpenter는 stable version이 많이 올라와서, NodePool
이라는 CRD를 사용하고 있었다. 하지만, 내가 (사다리타기로) 맡게된 다른 리전 쪽 EKS 클러스터에서는 여전히 Karpenter는 업데이트를 기다리며, Provisioner
라는 그 이전 CRD를 사용하고 있었다. 앞으로 사용할 일도 없는 CRD였지만, 문제 없는 업데이트를 위해서는 공부했어야 했다. 그리고 Karpenter는 EKS Node들에 직접적으로 영향을 끼치는 툴인 만큼 더 신중했어야 했다. 여러 크고 작은 일들이 있었지만, 늦은 밤까지 온라인으로도 도움을 준 Karpenter 선배 @Lento에게 무한한 감사를 표한다.
6) Github Action Runners가 멈췄다 #
지난 회고에서 언급한 것과 같이, 내가 채널톡에 합류하면서부터 메인으로 맡은 업무는 CircleCI에서 Github Actions로 마이그레이션하는 것이었다. 하지만, 조금 특이사항이라면 쿠버네티스 환경에서 Self-hosted runner를 이용한 Github Actions를 구축한다는 것이었다.
보통 Managed에서 Self-hosted runner로 마이그레이션하는 것은 1) 보안과 2) 비용 문제 때문이다. 덧붙이자면 가용성 문제도 있다. 예를 들어, CircleCI에만 의존한다면, CircleCI 장애가 일어났을 때는 전사 CI 파이프라인이 마비될 것이기 때문이다. 실제로 CircleCI는 생각보다 이슈가 잦기도 하다.
초반에는 GHA(Github Actions)로 이루어진 CI가 많지 않아서 큰 부담은 없었다. 하지만, 9월이 된 이제 대부분의 프로젝트들은 GHA로 마이그레이션이 완료되어서, GHA runner 자체도 이제 가용성을 신경쓰지 않을 수 없게 되었다. 특히, 예기치 못한 이슈가 2개가 있었는데 바로 1) Github Token Expired 와 2) Docker Hub Rate Limit였다.
두 이슈 모두 어떻게 보면 간단한 이슈지만, CI 파이프라인을 전체를 멈출 수 있을 정도로 치명적이다. 이러한 디테일들을 놓쳤다는 점에서 아쉬운 기분이 들었다. 그리고, 동시에 나로 인해 사내 개발자 분들이 불편을 겪을 수 있음에 책임감과 부담감을 다시 한번 상기시켜주는 이벤트였다.
이제 Docker Hub rate limit 이슈는 해결되었는데, 이에 대해서는 할 말이 조금 (아니 매우) 많다. 이는 꼭 다른 글에서 참고해주면 좋을 것 같다. (위에서 언급했던 밀려있는 다른 글감들 중 하나다). 미리 내용을 살짝만 알려주자면, 밑과 같은 내용을 다룰 예정이다.
- Docker pull through cache (w/ ECR and Harbor)
- Mirror registry
- Istio를 활용해서 Docker hub → ECR로 traffic reroute 시도…
7) Zsh 갈아엎기 #
oh-my-zsh를 사용하던 도중, 우리 팀에서 이상한 경쟁이 붙었다. 바로 shell startup time 경쟁이다.
# Start timer
zmodload zsh/datetime
zsh_start_time=$EPOCHREALTIME
# End timer and calculate duration
zsh_end_time=$EPOCHREALTIME
zsh_load_time=$(echo "($zsh_end_time - $zsh_start_time) * 1000" | bc)
echo "zsh startup time: ${zsh_load_time}ms"
.zshrc
맨 위와 밑에 해당 코드를 넣고, 얼마나 빠르게 shell이 시작되는지 보는 것이다. 나는 oh-my-zsh을 사용하면서 zsh를 시작하는데에 보통 400 ~ 500ms 정도가 소요되었고, 크게 불편한 점은 느끼지 못했다. 하지만, Neovim과 수많은 terminal 창을 활용하는 나로서 이또한 최적화해보자라는 오기가 생기기는 했다.
그렇게 oh-my-zsh가 느리다는 팀원 @Tanto의 말을 귀담아듣고, oh-my-zsh를 모두 또 다른 zsh plugin manager인 antidote + powerlevel10k로 옮겨갔다. 또한 가장 많은 시간을 잡아먹었던 NVM을 버리고, Rust로 작성된 asdf 대체재 mise로 옮겨갔다. 모든 세팅을 마쳤을 때에는 shell의 시작시간을 80ms 내외로 단축시킬 수 있었다.
하지만, 그 다음에 불편했던 점은 기존에 사용하던 autocompletions나 autojump 등을 활용하는 데에 불편함이 있었고, 이번에는 @Claud의 조언대로 많은 plugin 기능들이 built-in으로 제공되는 fish shell을 도전해보기로 했다.
뭐 결과는 만족이다. 그래서, 현재는 zsh → fish shell로 교체하고 몇몇 툴들을 함께 활용해서 shell 시작시간을 100ms 아래로 유지하고 있다.
8) Kafka Session 101, 201 #
9월에 Confluent에서 Kafka 관련 Session 101, 201을 진행해주셨다. Kafka 101에서는 Kafka에 대한 기본 개념, Kafka 201에서는 Producer ACK, Consumer Rebalancing 및 Replica Recovery에 대한 내용을 수강했다. Kafka에 대해서 따로 공부할 시간이 부족했는데, 매우 유익했다.
9) 사랑해요, Cloudflare Workers #
요즘 사이드 프로젝트가 있을 때, 주로 Cloudflare Workers(Cloudflare의 AWS Lambda)를 애용한다. 과할정도로 좋은 스펙의 홈서버가 여러대 있지만, 아무래도 안정성은 여전히 Managed 서비스(특히, 무료라는 가정 하에 서버리스)를 따라갈 수 없다고 생각한다. 밑에는 Cloudflare Workers의 Pricing이다.
그렇다. 말도 안된다. 특히 Standard는 매달 $5 Plan을 의미하는데, 이 또한 파격적이다. 심지어, 이번 분기에 애용한 Cloudflare 프로덕트 중에는 Cloudflare D1도 있는데, 이는 SQLite를 Cloudflare에서 튜닝해서 Serverless Database로 이용할 수 있게 해주는데, 이 또한 무료이며, 유료 Plan도 위 Standard Plan과 Usage를 공유한다.
D1 Pricing은 다음과 같다.
사이드 프로젝트나 간단한 프로덕트는 이 2개만 이용하면, 왠만하면 모두 무료로 뚝딱 만들 수 있다. 이외에도 Durable Objects을 이용하면 서버리스 환경에서 Websocket 채팅 서버도 구축 가능한데, 이는 이 글에서 다루지 않겠다. Kafka 101처럼 언제 한 번 Cloudflare Free-tier 101으로 찾아오겠다. 요즘 Cloudflare에 있는 기능들은 하나하나 다 써보면서, 가성비가 넘쳐 흐르는 사이드 프로젝트 개발을 즐기고 있다.
10) (WIP) 요즘은 이런걸 합니다 #
최근에 Raspberry Pi 5 2개를 추가 구매했다. 이로써 NAS를 포함하면 내가 관리하는 노드는 총 7개다. 처음 두 세개 때는 간단했는데, 요즘은 서버에 새로운 servername을 붙이는 것부터가 고민이다.
이번 10월에 개인적으로 도전하고자 하는 것은 바로 Multi-cluster다. 최근 들어, 모든 내 홈서버 클러스터의 ingress-nginx를 Istio로 전환했다. 이제는 Istio를 이용해서 Multi-cluster 환경을 한 번 구축해보고자 하고, Prometheus 또한 회사처럼 Thanos를 이용해서 HA와 안정성을 높이고자 한다.
그리고 Multi-cluster를 구축함에 있어서, 집 뿐만 아니라 클라우드와 학교 등 여러 장소를 활용하여 진정한 DR(disaster recovery)과 HA(high availability)를 구성하는 것이 목표이다.