레이블이 timezone인 게시물을 표시합니다. 모든 게시물 표시
레이블이 timezone인 게시물을 표시합니다. 모든 게시물 표시

kafka invalid timestamp error by pidstat

k8s 환경에서 프로세스등의 로그를 수집(collector pod) > json stdout(/var/log/pods/.../0.log) > filebeat > kafka cluster 로 흐름이 구축되어 있다.
그런데 자정(0시)에 가끔 collector CPU 90% 이 넘어가고 해소가 되지 않는다.

kafka(4.0.2) server.log 를 보면 다음과 같이 invalid timestamp 로 거부가 됐다.
kafka 디폴트로 메시지 1시간까지 허용하고 넘어가면 거부된다.
org.apache.kafka.common.errors.InvalidTimestampException: One or more records have been rejected due to invalid timestamp

collector 는 pidstat 커맨드로 수집을 하는데 pidstat 는 Time=HH:MM:SS 으로 날짜가 출력되지 않는다.
# -h : 한 줄 가로 출력, 평균 행 없음 (파싱용)
# -u : CPU
# -r : 메모리
# -d : 디스크 I/O
# -l : Command 에 전체 커맨드라인
# -I : CPU 사용률을 전체 코어 수로 나눔 (SMP 정규화)
# -T : TASK + CHILD 통계 모두
# -p : 대상 PID (discover 가 찾은 프로세스들)
pidstat -hurdl -I -T ALL -p <PIDs> <interval> <count>

# Time        UID       PID    %usr %system  %guest   %wait    %CPU   CPU  minflt/s  majflt/s     VSZ     RSS   %MEM   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
05:04:08        0       630    0.00    0.00    0.00    0.00    0.00     9      0.00      0.00    2228    1024   0.01      0.00      0.00      0.00       0  sleep 300

이 Time을 collector 에서 파싱하고 now() 의 날짜부분과 합쳐서 @timestamp 필드값을 설정한다.

# 파싱 과정
# local_to_utc() python 로직
dt = datetime.strptime(local_timestamp, "%H:%M:%S")
return datetime.now().replace(hour=dt.hour, minute=dt.minute, second=dt.second).astimezone(tz=timezone.utc)

# timestamp 필드 설정
obj["@timestamp"] = local_to_utc(obj.pop("Time")).strftime("%Y-%m-%dT%H:%M:%SZ")

# 문제가 되는 상황 예시
pidstat 출력 23:59:59.6 (8/15) -> Time="23:59:59"
파서 처리 시점 00:00:00.2 (8/16) -> now().date = 8/16 날짜 붙여서 -> timestamp = 2026-0816T23:59:59Z
실제 측정 시각 8/15 23:59:59 대비 +24h이 지난 상태의 timestamp 가 된다.
+24h 메시지는 kafka 에서 거부하는데, collector 는 계속 재시도를 해 cpu 리소스가 높게 유지 된 것이다.

# 해결 시도
# pidstat 와 1초 정도밖에 차이 안나서 이렇게 파싱시 now 를 timestamp 로 사용할 수 있겠지만
# pidstat 이 한 주기에 프로세스 n개를 측정하고 있어 원래는 n개의 라인이 전부 Time 이 같아야 한다. 매번 now 로 조금씩 달라지면 배치 동일성이 깨진다.
obj["@timestamp"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

# 해결 방법1
# +24(12시간 차이 정도로 하자)시간이 지난 경우 -1day 한다.
now = datetime.now()
ts = now.replace(hour=dt.hour, minute=dt.minute, second=dt.second, microsecond=0)
if ts - now > timedelta(hours=12):
    ts -= timedelta(days=1)
    logger.info(f"midnight rollover: Time={local_timestamp} -> {ts}")
return ts.astimezone(tz=timezone.utc)

# 해결 방법2
# pidstat 사용 안 하고 k8s 환경이면 이미 cAdvisor(ContainerAdvisor, kubelet 내장, 실행 중인 컨테이너들의 리소스 사용량과 성능 메트릭을 수집,처리,노출하는 도구)로 prometheus 에 메트릭이 있으니 이 값을 조회해서 사용한다.
# 메트릭들은 이미 같은 timestamp 로 저장되어 있다.
# 예시
%CPU
sum by (namespace, pod, container) (
 rate(container_cpu_usage_seconds_total{container!="",container!="POD",namespace=~"ysoftman.*"}[5m])
) * 100

%usr
sum by (namespace, pod, container) (
 rate(container_cpu_user_seconds_total{container!="",container!="POD",namespace=~"ysoftman.*"}[5m])
) * 100

%system
sum by (namespace, pod, container) (
 rate(container_cpu_system_seconds_total{container!="",container!="POD",namespace=~"ysoftman.*"}[5m])
) * 100

RSS - pidstat RSS 단위는 KB, cAdvisor는 byte
sum by (namespace, pod, container) (
 container_memory_rss{container!="",container!="POD",namespace=~"ysoftman.*"}
) / 1024

# 현재시각을 고정하고 이 값으로 prometheus 메트릭을 조회 하고 그대로 @timestamp 로 설정한다.
at = datetime.now(timezone.utc).timestamp()
ts = datetime.fromtimestamp(at, timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

# 다음과 같이 만들수 있다.
{"@timestamp": "2026-08-16T05:23:44Z", "%CPU": "0.88", "%usr": "35.59", "%system": "6.81", "RSS": "571528", "container": "ysoftman1-container", "pod": "ysoftman1-aaa"}

# 어쩌다 발생해서 신경쓰였는데 원인 파악 돼서 개비스콘 짤로 남긴다.ㅎ

change grafana pod localtime

# grafana timezone 이 UTC 로 되어 있다.
kubectl exec monitoring-grafana-aaa -it -- date
Wed Mar  6 07:35:45 UTC 2024

# 그래서 로그가 UTC 로 기록된다.
kubectl logs --tail=3 monitoring-grafana-aaa | rg -i "t="
... t=2024-03-06T07:45:00.515393518Z ...

# 이를 KST 로 변경하기 위해 deployment 에서
env > TZ 의 값을 Asia/Seoul 로 변경하면된다.

# 또는 아래와 같이 노드의 timezone 을 container 의 /etc/localtime 을 마운트되도록 설정한다.
kubectl edit deploy monitoring-grafana

spec > template > spec > containers > env > volumeMounts
volumeMounts:
- mountPath: /etc/localtime
  name: localtime-volume

spec > template > spec > containers > env > volumes
volumes:
- hostPath:
    path: /usr/share/zoneinfo/Asia/Seoul
  name: localtime-volume

# pod 가 다시 시작하고 나면 KST 로 변경되어 있다.
kubectl exec monitoring-grafana-aaa -it -- date
Wed Mar  6 16:45:55 KST 2024

# 이제 로그도 KST 로 기록된다.
kubectl logs --tail=3 monitoring-grafana-aaa | rg -i "t="
... t=2024-03-06T16:54:49.939479809+09:00 ...

# k8tz 을 사용하면 pod 에 편한게 적용할 수 있다.
# 배포되면 기본 k8tz 네임스페이스에 service,deployment,pod 등이 뜬다.
# install k8tz
helm repo add k8tz https://k8tz.github.io/k8tz/
helm install k8tz k8tz/k8tz --set timezone=Asia/Seoul

# deploy 등을 재시작해보자.
# 새로 뜨는 파드는 k8tz container 를 사이드카로 해서 locatime 이 반영된다. 

# k8tz 명령어를 사용하는 경우
# install k8tz cli command 
wget -c https://github.com/k8tz/k8tz/releases/download/v0.16.0/k8tz_0.16.0_darwin_amd64.tar.gz -O - | tar zx
./k8tz version

# 수동으로 현재 네임스페이스의 모든 deployment 에 반영하기
kubectl get deploy -oyaml | k8tz inject --timezone=Asia/Seoul -| kubectl apply -f -

# 참고로 grafana dashboard 디폴트 타임존은 다음 값으로 설정하면 된다.
grafana:
  defaultDashboardsTimezone: Asia/Seoul

#####

# argocd 로 등록하기
# --parameter namespace= 를 명시하지 않으면 k8tz 네임스페이스에 pod 가 뜬다.
argocd app create ysoftman-k8tz \
--dest-server https://kubernetes.default.svc \
--sync-option CreateNamespace=true \
--sync-policy automated \
--project ysoftman \
--repo https://k8tz.github.io/k8tz \
--helm-chart k8tz \
--revision "0.16.0" \
--dest-namespace ysoftman-k8tz \
--parameter namespace=ysoftman-k8tz \
--parameter timezone=Asia/Seoul

add timezone in alpine

# golang 에서 타임존이 변경된 time 사용을 위해 time.LoadLocation 를 사용했다.
loc, _ := time.LoadLocation("Asia/Seoul")
t = t.In(loc)

# 로컬에서는 동작했지만,
# 배포된 도커에서는 loc 를 찾을 수 없어 panic 이 발생하고 있었다.
[PANIC RECOVER] time: missing Location in call to Time.

# 컨테이너 사이즈를 줄이기 위해 alpine 이미지를 사용하고 있었는데,
# LoadLocation 가 참조하는 zoneinfo 데이터가 존재하지 않아 발생한 문제였다.

# dockerfile 에 tzdata 를 설치를 추가하자.
RUN apk add tzdata

# 이제 다음과 같은 위치에 timezone 데이터 값이 추가되고,
# time.LoadLocation 도 사용할 수 있다.
/usr/share/zoneinfo/Asia/Seoul

centos7 docker 이미지 locale 설정 오류

# centos7 기본 도커 이미지를 받아 실행한다.
# .bashrc 파일에 다음과 같이 언어 설정을 하고 접속을 하게 되면
export LANG=ko_KR.utf8
export LC_ALL=ko_KR.utf8

# 다음과 같이 locale 을 설정할 수 없다는 에러가 발생하는 경우가 있다.
bash: warning: setlocale: LC_ALL: cannot change locale (ko_KR.utf8): No such file or directory

# 사용 가능한 모든 지역 언어(locale -a)설정 리스트를 확인해보면
# 지원되는 언어 3개 밖에 없다.
locale -a
C
POSIX
en_US.utf8

# 언어 패키지가 설치 안된것 같아 다음과 같이 설치해보았지만 설치자체가 안된다.
sudo yum groupinstall korean-support

# 이와 같은 이슈를 고민하는 사람들이 있다.
# https://serverfault.com/questions/616790/how-to-add-language-support-on-centos-7-on-docker

# 도커 centos7 최근 이미지에서 영어, c, posix 를 제외한 로케일을 일부러 삭제한것으로 보인다.
# 우선 glibc 패키지를 재설치하라고 했지만 해결되지 않는다.
sudo yum reinstall -y glibc-common glibc

# 해결 방법
# (centos8의 경우) glibc-locale-source 설치
sudo yum install glibc-locale-source

# 이렇게 하면 /usr/lib/locale/locale-archive (locale 이 참고하는 파일)에 추가된다고 한다.
# -f UTF-8 :charmap 지정, /usr/share/i18n/charmaps/UTF-8.gz 심벌, 인코딩 정보
# -i ko_KR : sourcefile 지정, /usr/share/i18n/locales/ko_KR 언어 정의
# ko_KR.utf : 아카이브에 추가할 이름
sudo localedef -f UTF-8 -i ko_KR ko_KR.utf8

# 추가로  UTC 로 설정(링크)되어 있는 링크를 한국시간으로 변경해보자.
# https://yoonbh2714.blogspot.kr/2017/11/centos-localtime.html
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# 참고
# centos7 기본이라 패키지가 설치된게 거의 없다.
# dockerfile 로 다음 패키지들은 설치하도록 한다.
# yum 으로 패키지 설치후 locale 이 변경될 수 있어 localedef 는 뒤에 실행 하자.
RUN yum install -y sudo tar git rsync gcc gcc-c++ net-tools which iproute man-pages man setuptool ncurses-devel python-devel libevent-expat-devel bzip2-devel
RUN yum groupinstall -y 'development tools'
RUN localedef -f UTF-8 -i ko_KR ko_KR.utf8 && ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# 추가로 
# ubuntu 에서 타임존 설정
RUN apt-get update && \
apt-get install -y rsync apt-utils && \
rm -f /etc/localtime && \
ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# alpine 에서 타임존 설정
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone

Prev