golang gomaxprocs in k8s

golang garbage collector(GC) 는 데이터 무결성을 위해 stop-the-world(일시정지)가 필요하다.

linux 스케줄러 Completely Fair Scheduler(CFS) 에서 프로세스를 cpu(core) 시간에 할당한다.
golang 은 container(linux cfs 기반)의 cpu (시간)제한을 인지하지 못해 일시정지가 발생할 수 있다.

GOMAXPROCS 를 k8s cpu limit 와 일치 시키면 gc 로 인한 일시정지를 줄 일 수 있다.
GOMAXPROCS 를 k8s cpu limit 와 일치시키기
ubuer automaxprocs 를 golang main 에 import 하면 되지만 GOMEMLIMIT 는 지원하지 않는다.

대신 환경변수로 GOMAXPROCS, GOMEMLIMIT 로 pod > resource > limits 를 설정하면 된다.

golang main() 에 다음을 추가해 프로그램 시작시 GOMAXPROCS, GOMEMLIMIT 값을 찍어보자.
기본적으로 최대 core 개수가 찍힌다.
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
fmt.Printf("GOMEMLIMIT: %d\n", debug.SetMemoryLimit(-1))

이제 deployment 에 다음과 같이 GOMAXPROCS, GOMEMLIMIT 환경변수를 적용하면
pod(container)가 새로 시작되고 golang 프로그램의 GOMAXPROCS, GOMEMLIMIT 에 반영된다.
spec:
  template:
    spec:
      containers:
      - name: ysoftman-app
        resources:
          requests:
            cpu: "2000m"
            memory: "2048Mi"
          limits:
            cpu: "2000m"
            memory: "2048Mi"
        env:
        - name: GOMEMLIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
        - name: GOMAXPROCS
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu

argocd sync k8tz replicaset

argocd k8tz 가 하루에 한번씩 rolling update(new replicaset, new pod) 되는 현상을 발견했다.

argocd sync policy > automated 로 되어 자동으로 싱크를 맞춘다.(디폴트 3분 마다)
repo url: https://k8tz.github.io/k8tz
chart: k8tz:0.16.0

자동/수동으로 모두 변화가 없다가 하루 중 특정 시간만 되면 checksum/config 를 변경해 rolling update 가 된다.

k8tz pod 의 로그를 확인하고 싶은데, 하루마다 재시작돼 이전 로그를 확인할 수 없는 문제가 있다.

k8tz replicaset 이 10개가 쌓여 있었고 매일 비슷한 시간에 생성되었다.
k8s event 를 보면 argocd sync 로 k8tz replicaset 가 생긴 기록이 있다.
(kube api server --event-ttl duration Default: 1h0m0s 로 1시간내 이벤트만 보임)

# 다음으로 replicaset 의 checksum/config 값이 매번 변했다는 것을 알 수 있다.
for r in $(k get rs -n k8tz | sed 1d | awk '{print $1}'); do k get rs $r -n k8tz -o json | jq '.spec.template.metadata.annotations'; done

k8tz helm chart 를 보면
admission-webhook 에서는 genSelfSignedCert 함수로 helm 실행시 마다 selfsigned 값이 새로 생성된다.

deployment 는 위 admission-webhook.yaml 을 checksum/config sha256 값으로 사용한다.
https://github.com/k8tz/k8tz/blob/a07bfbc76c4fcea972b46da4ebc26408c2dfc1a1/charts/k8tz/templates/controller.yaml#L21

# 설치시 생성되는 인증서값으로 매번 새로운 checksum 이 생성된다.
helm install k8tz k8tz/k8tz --dry-run | rg -i checksum

이렇게 deployment checksum/config annotation 는 secret 나 configmap 변경이 발생하면 pod 를 새로 시작해서 반영한다.
참고로 https://github.com/stakater/Reloader 라는 방법도 있다.

argocd k8tz 특정 버전을 sync 하지만 변경사항이 없어서 deployment 변화(checksum/config)가 없어야 하는데 하루에 한번 변경이 발생된다는 것인데...

k8tz 앱 > self heal 을 비활성화 해두고 하루 지나서 sync 가 발생되면 바로 적용하지 않아 diff 로 차이점을 확인할 수 있었다.
차이는 아래 3개의 리소스에서 발생했다.
Secret

MutatingWebhookConfiguration

Deployoment

secret 이 변경이 됐고, 이로 인해 deployment checuksum/config 도 변경되었다.

24시간(1day)값과 관련된 argocd 내용중 repo cache 시간이 기본 24시로 되어있는것 발견했다.
디폴트로 24시간동안 repo를 캐싱하기 때문에 sync를 하더라도 변경이 없게 된다.
24시 이후에는 repo 캐시가 변경되고 이때 싱크하면 변경사항이 발생하게 된다.

argocd-repo-server Deployment 에 다음과 같이 repo 캐싱 시간을 짧게 줄 수 있다.
spec:
  template:
    spec:
      containers:
      - args:
        - /usr/local/bin/argocd-repo-server
        - --port=8081
        - --metrics-port=8084
        - --repo-cache-expiration=30s

k8tz hard refresh 를 한번 실행(이렇게하면 강제 재시작됨), 이후 부터 3분 마다 argocd 가 k8tz sync 하면 pod 재시작한다.

정리
- k8tz 는 helm 에서 설치시 셀프 인증서를 생성하고, k8tz deployment > checksum/config 을 변경해서 pod 를 재시작함
- argocd repo 캐시로 24시간(디폴트값) 동안 싱크해도 변화 없음(hard refresh로는 변경됨)

해결
- k8tz 재시작 방지를 위해선 자동싱크(sync policy > automated) 를 비활성화 한다.

2024년의 10월의 첫날

2024년의 10월의 첫날, 가을이다.
살랑거리는 빨간 단풍이 맘을 녹이는 나른해지는 늦은 오후 공원 벤치에 앉아 지는 석양과
길게 늘어진 그림자가 쓸쓸해 보이기도 한 계절이다.
그렇게 놔주지 않던 덥고 습한 짜증을 붙여주던 여름은 이제야 사라졌다.
매년 좋아하는 가을이 되면 행복보다는 어딘지 모르게 차분해지고 좀 심하면 우울하지만 내심 이 기분이 싫지도 않다.
가을은 40대의 나를 더욱 애틋하게 만든다.
시간은 그렇게 빨리 사라져 간다.
계획은 모두 실패로 돌아가고 하루하루 큰 사고 없이 평탄하게 보내고 있지만
갑자기 하늘에서 운석이 떨어지고 전쟁이나고 이런 세상이 멸망하는 생각에 빠지곤 한다.
무난한 날이 계속되고 있어도 목적지 없이 배회하는 내 맘은 불안감을 잔뜩 품고 있다.
아니 오히려 화가 난다고나 할까.
바랬던 일들, 포기하고 싶지 않았던 꿈꿨던 미래 들이 시간의 모래속으로 허무하게 빨려들어간다.
그냥 하염없이 멍하게 나의 희망들이 괴물같은 모래에 잡아 먹히는걸 보고 있으면
겉으론 태연한척 하지만 속은 건물 기둥이 하나둘 뽀개져 버리고 결국 와장창 무너져 내린다.
뭐 어때 그냥 괜찮다고 넘기는 하루도 한달이 되고 1년이 지나면 사람인지라 힘들고 무서워 도망치고 싶기도 하다.
몇일 아픈 허리로 방바닥 붙어있으니 더 두려워지는것 같다.
이대로 아무것도 하지 못하고 쓰러져 버리는게 아닐까하고
아픈 과거를 뒤로하고 밝은 앞날만 생각하자고 했었는데, 시간은 나의 늙음은 나를 더욱더 큰 짐으로 누르고 있다.
발버둥 쳐봐야 힘만 빠지고 결국 숨막히는 늪에서 허우적거리는 불쌍한 사슴처럼
애처로이 주위를 둘러보지만 아무도 없고 순간 덮어오는 공포에 눈물이 찔금.
나는 왜 이렇게 됐을까? 여기까지 흘러들어온게 나의 잘못된 생각과 행동 뭐 이런 것들의 결과일까?
힘내지 않고, 노력하지 않고, 내 선천적인 무능함때문일까?
나는 나름대로 힘을 냈고 노력도 했으며 무능함을 기꺼이 받아들이고 달리고 있었다.
하지만 그 모습은 이 세상이 바라는 눈높이에선 그냥 별로인 인간으로 평가되는것 같다.
왜 나는 이렇게 살아야 하는걸까? 선악의 인과율로 내가 잘못한 것때문에 벌을 받고 있나?
아니면 너무 바보처럼 헤헤 거리면 살아서 그결과가 이런것일까?
나름 행복했다. 사랑하는 사람을 만나고 설레고 앞날을 생각하기만 하면 기분 좋은 그런 날들도 있었다.
지금 생각하면 너무 그리운 달콤한 시간들이다.
그런데 왜 오래가지 못할까? 내 팔자가 이런걸까? 태어나면서 부터 넌 이렇게 잠깐 행복하다 나중에 힘든 인생을 살게될거야라고 누군가 속삭이는것 같다.
과거의 후회가 밀려들어 오는 가을이다.
누워서 아무것도 하고 있지 않으면 후회스러운 일들이 재판을 받는것 처럼 조목조목 나를 괴롭힌다.
현재의 난 다음 세상을, 천국을, 신을 믿지 않는다.
그래서 현재의 나를 더욱 아끼고 소중하게 살고 싶다.
그런데 내 유전자에 프로그래밍된 로직은 과감함 대신 소심함, 좋게 말하면 신중함을 우선하고 있다.
이게 생존에 더 유리하니 나서지 말고 무난하게 가늘고 길게 가라고 가이드 한다.
훗, 웃고싶다. 나를 즐겁게 해줄 많은 일들이 있었으면 좋겠다.
아직 작은 일에는 의욕이 있고 즐겁다. 이 불씨가 꺼지지 않고 활활 타올라 항상 얼굴에 미소를 머금었으면 좋겠다.