2023-03-15-TIL
2023-03-15-TIL
Today I Learned
How to See the Memory Usage in Linux
시스템 전체는 free -h로 전체, 사용 중, 버퍼/캐시, 사용 가능 메모리를 본다. 리눅스는 남는 메모리를 파일 캐시로 적극 쓰기 때문에 free 값이 작아도 available이 넉넉하면 문제가 아니다. 프로세스별로는 top을 쓰는데, 참고한 글의 정리처럼 메모리 열은 세 가지다.
VIRT: 프로세스에 할당된 가상 메모리 전체RES: 실제로 물리 메모리에 올라와 쓰이는 양. 메모리 사용량을 볼 때 핵심 지표다.SHR: glibc 같은 공유 라이브러리처럼 다른 프로세스와 공유하는 메모리
top 실행 중 Shift+M을 누르면 메모리 사용률 순으로 정렬된다. EC2에서는 한 가지가 더 있다. SentinelOne 글에 따르면 CloudWatch는 기본적으로 CPU, 네트워크 같은 지표만 수집하고 메모리 사용량은 수집하지 않으므로, 인스턴스에 CloudWatch 에이전트를 설치하고 권한(IAM 역할)을 주어야 메모리 지표를 볼 수 있다.
- https://zzsza.github.io/development/2018/07/18/linux-top/
- https://sabarada.tistory.com/146
- https://www.runit.cloud/2020/11/linux-process-memory-usage.html
- https://www.sentinelone.com/blog/ec2-memory-usage/
- https://blog.krishnachaitanya.ch/2016/03/monitor-ec2-memory-usage-using-aws.html
- https://lepczynski.it/en/aws_en/how-to-monitor-memory-usage-on-aws-ec2/
- https://blog.mingoon.com/10
Docker Hub Pull Limit
도커 허브에서 pull 받을 수 있는 횟수는 제한이 있다. 회사 규모가 커지면 직접 pull 받아서 빌드 및 배포하는 방식은 위험할 수 있다. 도커 허브에 의존하지 않도록 별도의 저장소에 이미지를 저장해두고 사용하는 것이 안전하다.
참고한 글(2021년 1월 기준)에 따르면 로그인하지 않은 익명 사용자는 IP 기준으로 6시간에 100회, 무료 계정으로 로그인한 사용자는 계정 기준으로 6시간에 200회까지 pull할 수 있고, 유료 계정은 이 제한이 없다. 남은 횟수는 레지스트리 응답의 RateLimit-Limit, RateLimit-Remaining 헤더로 확인할 수 있다. CI 서버나 쿠버네티스 노드 여러 대가 같은 공인 IP(NAT)로 나가면 익명 한도를 금방 공유해 소진한다. 해결책으로는 유료 계정, Harbor 같은 사내 레지스트리를 미러나 캐시로 두는 방법이 소개된다. 정책은 이후 바뀔 수 있으므로 적용 전에 Docker 공식 문서를 다시 확인해야 한다.
- https://subicura.com/k8s/2021/01/02/docker-hub-pull-limit/
Message Queue Callback for Acknowledge
Kafka 프로듀서의 send()는 비동기로 동작하고, 결과는 Callback으로 받는다. Javadoc에 따르면 onCompletion(RecordMetadata metadata, Exception exception)은 보낸 레코드가 서버에서 확인(acknowledge)되었을 때 호출되고, 실패하면 exception이 채워진다. 이 콜백은 보통 백그라운드 I/O 스레드에서 실행되므로 빨리 끝나야 한다. 무거운 후처리는 별도 실행기로 넘긴다. 얼마나 확실하게 확인받을지는 프로듀서의 acks 설정으로 정한다.
1
2
3
4
5
6
7
producer.send(record, (metadata, exception) -> {
if (exception != null) {
log.error("send failed", exception);
} else {
log.info("sent to {}-{}@{}", metadata.topic(), metadata.partition(), metadata.offset());
}
});
함께 저장한 Stack Overflow 질문은 메시지 큐와 태스크 큐의 차이를 묻는다. 답변의 요지는 RabbitMQ 같은 메시지 큐는 메시지를 받아 전달할 뿐이고, Celery 같은 태스크 큐는 작업과 데이터를 받아 실행하고 결과까지 전달한다는 것이다.
- https://www.javatpoint.com/kafka-producer-callbacks
- https://kafka.apache.org/26/javadoc/org/apache/kafka/clients/producer/Callback.html
- https://stackoverflow.com/questions/10075817/message-queue-vs-task-queue-difference
Java Agent
자바 에이전트는 애플리케이션의 main보다 먼저 실행되어 클래스 바이트코드를 조작할 수 있는 특별한 JAR다. java.lang.instrument 패키지 문서에 따르면 에이전트 JAR의 매니페스트에 Premain-Class 속성으로 에이전트 클래스를 지정하고, 그 클래스는 public static void premain(String agentArgs, Instrumentation inst) 메서드를 구현한다. JVM이 초기화된 뒤 지정된 순서대로 각 에이전트의 premain을 호출하고, 그다음에 실제 main을 호출한다. 넘겨받은 Instrumentation으로 클래스가 로드될 때 바이트코드를 바꿀 수 있다. Datadog, Pinpoint 같은 APM 도구가 코드 수정 없이 -javaagent 옵션만으로 추적을 붙이는 원리다.
- https://catsbi.oopy.io/6136946a-9139-4541-b2af-2af93bb634a5
상계
상계(相計)는 민법 용어로, 위키백과의 정의를 빌리면 채무자가 채권자에게 같은 종류의 채권을 가지고 있을 때 서로의 채권과 채무를 대등한 금액만큼 소멸시키는 의사표시다. A가 B에게 100만 원을 갚아야 하고 B도 A에게 70만 원을 갚아야 한다면, 상계로 70만 원씩 지우고 A가 30만 원만 갚으면 된다. 상계를 주장하는 쪽의 채권을 자동채권, 상대방의 채권을 수동채권이라고 한다. 정산, 포인트, 환불처럼 주고받을 금액이 얽히는 도메인을 모델링할 때 나오는 개념이다.
- https://namu.wiki/w/%EC%83%81%EA%B3%84
- https://ko.wikipedia.org/wiki/%EC%83%81%EA%B3%84_(%EB%B2%95%EB%A5%A0)
무한댓글
댓글에 답글, 답글에 또 답글이 달리는 계층형 댓글을 DB에 저장하는 방법이다. Aleksandra의 글은 단계별로 설명한다. 한 단계 답글만 허용한다면 comments 테이블에 자기 자신을 참조하는 parent_id 컬럼 하나면 충분하다. 깊이 제한 없이 중첩된다면 조회가 문제인데, 재귀 CTE(WITH RECURSIVE)로 루트부터 내려가며 /5/6/7 같은 경로를 만들어 정렬하거나, PostgreSQL이라면 계층 경로를 저장하는 ltree 타입을 쓰는 방법을 소개한다.
1
2
3
4
5
6
7
CREATE TABLE comments (
id BIGINT PRIMARY KEY,
post_id BIGINT NOT NULL,
parent_id BIGINT NULL REFERENCES comments (id),
content TEXT NOT NULL,
created_at TIMESTAMP NOT NULL
);
경로 문자열을 컬럼에 미리 저장해 두는 방식(materialized path)은 조회가 빠르지만 댓글을 옮길 때 하위 경로를 모두 고쳐야 하는 등, 방식마다 조회와 수정 비용이 다르다. 실제 서비스는 깊이를 2단계 정도로 제한하는 경우가 많다.
- https://velog.io/@yoho98/%EA%B2%8C%EC%8B%9C%EA%B8%80-%EB%8C%93%EA%B8%80-%EB%8C%80%EB%8C%93%EA%B8%80%EB%AC%B4%ED%95%9C%EB%8C%93%EA%B8%80-%EB%A1%9C%EC%A7%81
- https://xerar.tistory.com/44
- https://zi2eon.tistory.com/20
- https://bibidi0.tistory.com/99
- https://stackoverflow.com/questions/10466500/comments-with-subcomments
- https://www.aleksandra.codes/comments-db-model
- https://medium.com/@kuldeepluvani/nested-comment-database-model-92078f6a2b46
- https://laracasts.com/discuss/channels/general-discussion/table-structure-for-one-level-deep-nested-comment
- https://docs.airdev.co/canvas-pages-and-modules/modules/nested-comments-thread-with-like-and-reply
- https://stackoverflow.com/questions/597882/how-do-i-implement-threaded-comments
- https://velog.io/@dleunji/%ED%8C%8C%EC%9D%BC-%EC%B2%A8%EB%B6%80%EB%90%98%EB%8A%94-%EB%8C%93%EA%B8%80%EC%9D%84-%EC%9C%84%ED%95%9C-DB-Design
- https://drawsql.app/templates/adonis-acl
Go panic()
Go 블로그의 “Defer, Panic, and Recover”에 따르면 panic은 정상적인 실행 흐름을 멈추는 내장 함수다. 함수 F에서 panic이 호출되면 F의 실행이 멈추고, F의 defer 함수들이 실행된 뒤 F가 호출자에게 돌아간다. 호출자 입장에서는 F가 panic을 호출한 것처럼 보이고, 이 과정이 현재 고루틴의 모든 함수가 끝날 때까지 스택을 따라 올라가면 프로그램이 종료된다. recover는 defer 함수 안에서만 의미가 있고, 패닉 중인 고루틴의 패닉 값을 잡아 정상 실행으로 되돌린다. Go에서 일반적인 오류는 error 반환값으로 다루고, panic은 프로그램이 더 진행할 수 없는 경우에만 쓴다.
- https://www.educative.io/answers/what-is-panic-in-golang
댓글
아직 댓글이 없습니다