• 블록스토리지와 파일스토리지는 OS에서 동작
  • 오브젝트 스토리지는 어플리케이션에서 동작


스토리지 유형에 따른 서로 다른 특징을 비교



파일스토리지

  • 폴더(Directory)와 파일(File)로 이루어지는 논리적 계층구조를 갖는 스토리지 → 가장 일반적인 저장소 유형
  • 각 파일(File)은 폴더(Directory)에 종속되며 폴더(Directory)도 다른 폴더(Directory)에 종속 가능
  • 파일스토리지에서 파일은 폴더(Directory)별로 저장되어, 언제든지 꺼내어 수정 및 변경 가능
  • 파일스토리지에서 파일을 찾으려면 어느 경로에 있는지 알고 있어야함
  • 파일스토리지의 규모가 작은 경우 파일을 인덱싱하는데 어렵지 않지만, 규모가 큰 경우 파일을 인덱싱하는데 많은 시간과 노력이 소모
  • NAS, DAS에서 사용 → 가장 오래되고 널리 사용되는 데이터 스토리지 시스템
  • 파일 스토리지를 케비넷으로 생각하면 이해하기 쉬움
    1. 각각의 파일철들이 케비넷별로 저장되어있고 언제든 꺼내어 수정하거나 변경 가능
    2. 파일철을 찾으려면 어느 케비넷이 있는지 알고 있어야함
    3. 케비넷에 파일철이 많지 않다면 분류하고 정리하는데 큰 문제가없지만, 파일철들이 계속해서 늘어나면 늘어날수록 점점 분류하고 정리하는 데 많은 노력이 필요
  • 파일 스토리지를 주차 타워로 표현하여도 이해하기 쉬움
    1. 주차 타워는 내 차를 타기 위해서는 주차타워가 한번 돌아가야함
    2. 주차가 많아지면 많아질수록 주차 타워가 깊어짐 → 차를 찾기가 힘듦
  • 파일스토리지의 문제
    1. 파일스토리지에 파일을 저장하면 생성 날짜, 수정 날짜 및 파일 크기 등 첨부되는 메타데이터가 제한적
    2. 파일스토리지의 조직 스키마는 데이터 양이 늘어나면서 문제 발생 가능
    3. 파일과 폴더를 계속 추적하기 위해 파일 시스템에 대한 자원 요구가 증가해 성능이 떨어짐
    4. 파일스토리지의 구조적 문제는 단순히 파일 시스템에서 사용할 수 있는 저장 공간을 늘리는 것으로 해결 불가능
    5. 파일스토리지는 규모의 측면에서 잠재적인 문제가 있음에도, 업무 현장 및 중대형 기업에서 사용하는 개인용 컴퓨터와 서버에서 잘 사용
    6. 파일 스토리지는 일반적으로 하드 드라이브 및 네트워크 연결 스토리지(NAS) 시스템에 사용


블록스토리지

  • 블록스토리지는 정해진 블록 안에 데이터를 저장
  • 블록 스토리지는 데이터를 고정된 크기의 '덩어리' 또는 '블록'을 시퀀스로 처리 → 각각의 파일이나 오브젝트를 여러 블록에 분산 가능(cf. 파일 스토리는 파일을 하나의 데이터 단위)
  • 블록스토리지는 블록들을 연속적으로 저장할 필요 X
    1. 사용자가 데이터를 요청할 때마다, 기본 스토리지 시스템에 데이터 블록을 병합하여 사용자의 요청을 처리
    2. 가장 편리한 곳에 블록을 저장 가능하기에 효율성이 매우 높음
    3. 데이터 블록은 고유 식별자를 부여
    4. 일부 데이터는 Linux 환경에 저장하고, 일부는 Windows 장치에 저장 가능
    5. 계층 구조가 필요하지 않아 블록은 서로 독립적으로 존재 가능
  • 블록 스토리지는 읽어야 할 데이터의 경로가 하나만 있는 것이 아니기 때문에 데이터를 매우 빠르게 검색 가능 → 같은 파일의 데이터를 여러 디스크에서 읽을 수 있는 디스크 배열과 유사
  • 블록 스토리지는 저장 영역 네트워크(SAN) 저장소에 사용
  • 블록 스토리지는 파일 스토리지 시스템이 구축되는 기반으로 사용
  • 대부분의 애플리케이션에서 오브젝트스토리 또는 파일스토리지에서 사용되고, 오브젝트스토리지와 파일스토리지를 구축하는 상위 계층으로 블록스토리지를 사용
  • SQL에서 테이블을 만들때 각 칼럼별로 저장할 데이터를 설정 → 데이터 삽입도 설정한 값 범위안에서 저장
  • 블록스토리지가 파일스토리지와 다른점은 파일스토리지는 1개의 경로만 갖는데 반하여 블록스토리지는 여러개의 경로를 가질 수 있음
  • 낮은 IO(Input/Output) latency로 RDB와 같은 데이터베이스에 사용하기 적합
  • BlockStorage를 주차장으로 표현하면 이해하기 쉬움
    1. 주차장이 꽉차면 더 이상 주차 불가능
    2. 더 이상 주차할 공간이 없으면 필요한만큼 주차장을 늘려놓아야합니다.
  • 블록스토리지의 동작 원리 예시
    1. 블록스토리지는 컴퓨터의 C드라이브, D드라이브와 유사 → 맨처음 파티션을 나누어주고 그 공간안에서 사용 가능
    2. C드라이브와 D드라이브를 네트워크를 통해서 공유하고 여러 사용자가 해당 드라이브를 참조하는것과 유사 → 공유는 할 수 있지만 OS와의 연결은 한번에 1개만 가능
    3. 블록 스토리지는 데이터를 사용자의 환경에서 분리해 이를 쉽게 활용 가능 → 다양한 환경 전반에 분산하도록 설정
    4. 데이터 요청되면 기본 스토리지 소프트웨어가 데이터 블록을 다시 조합해 사용자에게 제공
  • 블록 스토리지의 장점
    1. 단일 데이터 경로에 의존하지 않으므로 신속하게 검색 가능
    2. 각 블록은 독립적으로 존재하며 파티션으로 분할 가능 → 서로 다른 운영 체제에 액세스 가능
    3. 사용자는 자유롭게 데이터를 설정 가능 → 운영체제에 영향 X
    4. 데이터를 효율적이고 안정적으로 저장 가능 → 사용과 관리도 간편
    5. 대규모 트랜잭션을 수행하는 기업과 대용량 데이터베이스를 배포하는 기업에서도 원활하게 동작
    6. 더 많은 데이터를 저장해야 할수록 블록 스토리지를 사용하는 것이 더 유리
  • 블록스토리지의 한계
    1. 블록 스토리지는 비용이 많이 발생
    2. 메타데이터를 처리하는 기능이 제한적
    3. 애플리케이션 또는 데이터베이스 수준으로 메타데이터를 다루어야함 → 비효율적
    4. 개발자나 시스템 관리자의 업무 부담이 늘어남


오브젝트 스토리지

  • 오브젝트 스토리지는 '오브젝트(Object)'로 불리는 각각의 데이터 단위가 개별 단위로 저장되는 데이터 저장소 유형 → 오브젝트 스토리지 볼륨은 모듈 단위로 동작
  • 오브젝트 스토리지는 물리적 제약이 없기 때문에 원하는 만큼 공간을 확장 가능
  • 오브젝트 스토리지의 단일 리포지토리는 각각 독립적인 리포지토리로 아래 요소를 보유
    1. 데이터
      • 데이터는 오브젝트라 불리는 개별 단위로 나뉨
      • 서버의 블록이나 폴더에 파일을 보관하는 대신 단일 리포지토리에 보관
    2. 오브젝트가 분산 시스템에 존재하도록 허용하는 고유 식별자
      • 오브젝트 이름이 색인 테이블에서 '키' 역할 가능
      • 오브젝트 스토리지 시스템은 찾고 있는 오브젝트의 키(이름)만 알고 있으면 색인 테이블을 사용하여 빠르고 쉬운 검색이 가능
    3. 데이터를 설명하는 메타데이터를 보유
      • 메타데이터는 사용 기간, 개인 정보/보안 및 액세스 비상 대책 등 중요한 내용이 포함
      • 메타데이터 내용은 매우 상세 기록 가능 (영상 촬영 위치, 사용된 카메라 기종, 각 프레임에 출연한 배우 이름 등의 정보를 저장 가능)
  • 오브젝트 스토리지는 논리적인 스토리지
    1. 오브젝트 스토리지는 S3나 Cloud Sotrage에서 폴더(Directory)를 만들거나 다른 버킷(bucket)으로 파일을 옮긴다고 하여 물리적인 변화가 있는 것이 아님 → 사용자에게 옮긴 것처럼 보여줌
    2. 오브젝트 스토리지는 키 값(Key Value)과 데이터(Data)만 저장
    3. 오브젝트는 PDF, 비디오, 오디오, 텍스트, 웹사이트 데이터나 기타 다른 파일 유형 등 모든 데이터 유형을 사용 가능
    4. 파일 스토리지와 달리, 오브젝트는 폴더(Directory) 계층 구조 없이 단일한 평면(flat) 구조로 저장
  • 오브젝트 스토리지는 RESTFul Protocol(HTTP)를 이용 가능 → Get과 Post로 요청을 하면 원하는 파일을 받을 수 있음 (모든 언어로 HTTP API 사용 가능)
  • 파일에 대해 가지고 있는 정보가 적기 때문에 파일이 아무리 많아져도 블록스토리지나 파일스토리지에 비해서 빠르게 작동
  • Object Storage를 발렛으로 알아서 주차하는 것으로 표현하면 이해하기 쉬움
    1. 강남이나 판교에서 발렛 맡기면, 주차장의 공간을 효율적으로 사용하여 주차해줌
    2. 주차하는 기술도 일반적인 경우와 다르지만, 공간의 낭비가 하나도 없도록 주차를 해줌
  • 오브젝트 스토리지 예시
    1. 발렛을 맡길때 자동차(데이터) 세워 두고 번호표만 전달해 줌
    2. 어디에 주차되어있는지 알 필요 X
  • 오브젝트 스토리지 장점
    1. 평면 주소 지정 체계는 개별 오브젝트에 대한 접근이 빠르고 쉽다는 것을 의미
    2. 오브젝트는 정적 데이터에 적합한 스토리지 시스템 → 민첩성과 평면적 속성으로 인해 초대용량의 데이터로 확장 가능
    3. 비정형 데이터를 저장하기에도 좋음
    4. 오브젝트 스토리지는 클라우드 기반 저장소에서 일반적 → 확장하기도 쉬우므로 퍼블릭 클라우드 스토리지에 매우 적합
    5. 매우 높은 확장성과 신뢰성을 바탕으로 콘텐츠의 관리, 처리 및 배포에 사용
    6. 공간을 효율적으로 사용하기에 비용적으로 저렴함 → 사용만큼 비용을 지불하면 되므로 비용 효율적
    7. 사용만큼 비용을 지불하면 되므로 비용 효율적
  • 오브젝트 스토리지 단점
    1. 파일 수정이 불가능 → 한번에 오브젝트 작성을 완료해야함
    2. 오브젝트 스토리지는 전통적인 데이터베이스와 잘 연동 X
    3. 파일이 수정될때 트랜잭션을 통해 일관성을 유지하기가 힘들기 때문에 덮어쓰는 방법을 이용
    4. 이미지나 동영상같이 수정이 잘 일어나지 않는 정적인 데이터를 호스팅할때 사용
    5. 내구성이 블록스토리지에 비해 떨어지기때문에 내구성이 중요한 데이터는 처리하기 힘듦
    6. 오브젝트 작성에 노력이 필요함
    7. 오브젝트 스토리지 API를 사용하여 애플리케이션 작성에 노력이 필요함

'IT 학습 용어' 카테고리의 다른 글

GitOps 설명  (0) 2022.07.08
CI/CD 설명  (0) 2022.07.07
페이로드(payload)  (0) 2022.06.27
M3U8 파일  (0) 2022.06.27
LRU (Least Recently Used)  (0) 2022.06.27
  • 한국 시간 5월 30일 오후 7시 48분(5월 30일 오전 10시 48분 GMT)이후 Comodo의 AddTrust External CA Root가 만료
  • 2017년 10월 프랜시스코 파트너스(Francisco Partners)가 코모도 CA(Comodo CA)를 인수 → 2018년 11월 코모도(Comodo CA)를 섹티고(Sectigo)로 리브랜딩
  • 기존 코모도 사의 루트 인증서 AddTrust External CA Root를 USERTrust RSA Certification Authority로 변경 → 기존 인증서는 2020년 5월 30일에 만료
  • CA 인증서 만료가 다양한 서비스 및 시스템에 영향을 줌
  • 과거 Root CA 만료 자료로 참고용 → HTTPS를 사용하는 고객에 이슈가 발생 가능하기에 사전 정리


모던 브라우저와 레거시 브라우저의 인증서 체인 차이(출처: Sectigo 공식 문서)

  • AddTrust 루트 인증서가 만료 되더라도 대체 루트/체인과 교차서명(Cross-Sigining)을 통해서 95% 이상 대부분의 접속에는 문제 X
  • "오래된 구형 디바이스 또는 해당 SW 공급사에서 제공된 최신 업데이트를 적용하지 않은" PC/Mobile/프레임워크 등 에서 접속 문제 발생 가능
  • USERTRust 루트 인증서뿐만 아니라 AddTrust 인증서까지 인증서 체인을 검증하는 레거시 시스템의 경우 문제가 발생 가능

Comodo의 AddTrust 인증서까지 확인하는 다양한 서비스에 영향이 발생

Comodo의 AddTrust 인증서 대체하는 AAA 루트/체인 인증서

고객 조치 사항

1. 서버 인증서 설정시, 루트를 AddTrust 로 적용한 경우

  • 구형 안드로이드등의 모바일 접속 문제가 발생 : 웹서버에서 AAA 루트/체인 인증서로 교체 적용 필요
  • 서버간 SSL 통신시 접속 문제 발생
    1. 웹서버 → 루트/체인 인증서 교체 필요 (인증서 설치된 방화벽등 기타 장비도 교체 필요)
    2. 클라이언트 서버 → 신규 루트인증서 추가 설치 필요
    3. Windows PC 에서 접속 문제가 발생 → 각 PC 자체적으로 루트인증서 설치 조치 필요

2. 서버인증서 발급시, AAA 루트로 포함(적용)되어 있는 경우

  • 서버에서 조치할 작업 X
  • Windows PC 에서 접속 문제가 발생 → 각 PC 자체적으로 루트인증서 설치 조치 필요


※ 참고

  • WHOIS는 도메인 이름이나 IP 주소의 레지스트리가 관리하는 등록과 할당 정보를 인터넷에 공개하여 이용자가 참고할 수 있도록 해주는 서비스
  • ICANN에서는 WHOIS를 "도메인이나 IP 주소의 관리 책임자는 누구인가를 질문하는 시스템"으로 정의 → 누구나 쉽게 도메인 등록 정보를 검색하여 이용할 수 있는 서비스를 제공
  • 어느 Registrar나 Registry 사이트를 방문하더라도 손쉽게 도메인 정보 검색 페이지를 확인할 수 있어야함
  • WHOIS는 도메인을 확인하고 도메인과 관련된 사람과 인터넷의 자원을 찾아보기 위한 프로토콜로써 만들어짐
  • WHOIS는 전세계적으로 모든 국가에는 ISO3166-1에 지정한 2문자로 된 국가코드가 부여
    1. 한국의 경우, ISO3166-1 국가코드는 KR
    2. 북한은 KP이지만, 아직 APNIC에 가입되어 IP를 할당 받은 것은 없음
  • 도메인 등록 사업을 운영하고자 하는 자는 ICANN으로부터 그 자격을 인증 받아야 함
  • RIR(Regional Internet Registry, 대륙별 인터넷주소자원 관리기관)에 가입되어 있는 국가들 중에서도 WHOIS 서버를 공식적으로 제공하지 않는 국가들도 많음
  • WHOIS 서버에 쿼리할 때는 서버의 포트를 지정 → WHOIS 서비스는 일반적으로 43번 포트를 사용


WHOIS 서비스를 공식 서비스하는 국가별 WHOIS 서버의 인터넷 주소들

담당지역 Whios 서버
전체 whois.internic.net
유럽 www.ripe.net
아시아 태평양 지역
아시아 태평양 지역 www.arin.net
호주 whois.aunic.net
프랑스 whois.nic.fr
일본 whois.nic.ad.jp
영국 whois.nic.uk
한국 whois.krnic.net
해커들을 위한 whois www.geektools.com


WHOIS 서버를 통해서 얻을 수 있는 정보 → 관리자 이름, 연락처등등은 사회 공학적 방법으로 사용 가능

  • 도메인 등록 정보를 제공하는 곳은 등록자의 데이터를 받아 보관하고 관리하는 Registry와 Registrar 두 곳
  • ccTLD와 gTLD의 정책 결정 기구가 다르기 때문에 도메인마다 공개되는 등록 데이터에는 차이가 있음
  • WHOIS가 제공하고 있는 주요한 등록 정보를 확인
  • 도메인 등록인 혹은 홈페이지의 운영자와 관련된 연락 정보는 아래 정보 리스트에서 붉은 색으로 표시
  • 전세계 수십억의 인터넷 사용자가 언제든지 확인 가능한 개인의 전화번호와 이메일 등록 필요 → 메인 등록 정보 제공이 개인정보를 침해한다는 문제가 계속해서 제기
  • 정보 리스트
    1. 등록 , 관리 기관
    2. 도메인 이름, 도메인 관련 인터넷 자원 정보
    3. 목표 사이트 네트워크주소와 ip주소
    4. 등록자, 관리자, 기술 관리자의 이름 및 연락처, 이메일 계정
    5. 레코드 생성 시기와 갱신 시기
    6. 주 DNS서버와 보조 DNS서버
    7. IP 주소의 할당 지역 위치

WHOIS 의 사용 세가지 목적

  • 기술적인 문제가 발생했을 때 당사자의 연락처 정보 확보
  • 도메인 이름의 등록 상태 확인
  • 보안 사고나 도메인 이름과 상표의 관계 등 비기술적인 문제 해결


※ 참고

numad 정의

  • NUMA는 Non-Uniformed Memory Access(불균일 기억장치 접근)의 약자
  • CPU 소켓이 하나가 있는 데스크탑이 아니고, 여러개의 CPU 소켓을 가지고 있는 서버에서 메모리에 효과적으로 접근하기 위해서 사용되는 기술
  • 여러개의 CPU 소켓이있는 환경에서, 하나의 프로세서가 전체 메모리에 접근하게 될 경우 메모리 버스를 점유하게 되고 다른 프로세스들은 대기하여야 하는 상황이 발생
  • 위와 같이 메모리 버스를 독점하는 문제를 해결하기 위해서, 각 CPU 마다 지역 전용적으로 사용하는 지역 메모리(Local Memory)으로 할당하고, 지역 메모리를 사용하는 CPU 코어들을 묶어서 하나의 NUMA 노드로 할당
  • numad는 자동 NUMA 친화도 관리 데몬
  • numad는 백그라운드 데몬과 같은 형태로 시스템에 상주
  • 사전 배치 조언 서비스를 통해 여러 작업 관리 시스템 쿼리에 대해 적절한 CPU와 메모리 리소스의 초기 바인딩을 제공 가능
  • numad는 /proc 파일 시스템에서 주기적으로 정보에 액세스하여 각 노드에서 사용 가능한 시스템 리소스를 모니터링
  • 서버의 실제 하드웨어 사진 → 파랑색 : CPU, 빨강색 : CPU와 가까운 메모리 영역

numad의 장점

  • 리눅스에서는 numad를 통해 NUMA 메모리 할당 정책을 직접 설정하지 않고도 메모리 지역성을 높일 수 있는 방법을 제공
  • 시스템의 NUMA 토폴로지 및 리소스 사용량을 모니터링하여 동적으로 NUMA 리소스 할당 및 관리 (즉 시스템 성능)를 향상
  • 시스템 작업량에 따라 numad는 성능 기준의 최대 50% 까지 성능을 개선 가능
  • 일정 수준의 지정된 리소스 사용량을 유지하며 필요에 따라 NUMA 노드 간 프로세스를 이동하여 리소스 할당을 재조정
  • numad는 시스템의 NUMA 노드의 하위 집합에서 중요한 프로세스를 배치 및 격리하여 최적의 NUMA 성능을 구현
  • numad는 주로 상당한 리소스 양을 소비하고 전체 시스템 리소스의 하위 집합에 있는 프로세스에서 장기간 실행 중인 프로세스가 있는 시스템에 이점을 제공
  • 프로세스 A와 B 두개가 동작하고 있는 시스템 가정
    1. default 정책에 따라 메모리 지역성을 높인 상태로 운영 가능
    2. 각 프로세스가 필요로 하는 메모리가 여러 노드에 걸쳐서 존재하는 것 가능
    3. 프로세스가 필요로 하는 메모리가 노드 하나의 메모리 크기보다 메모리의 크기보다 작은 경우 노드 하나의 메모리 크기보다 작기 때문에 충분히 메모리 지역성을 높일 수 있음
    4. 다수의 프로세스를 관리해야 하는 경우 numactl 등을 사용해 수작업으로 실행하기 어려움
    5. numad → 하나의 프로세스가 필요로 하는 메모리를 하나의 노드에서만 할당 받을 수 있도록 설정할 수 있기에 메모리의 지역성을 높이고 성능을 최적화 가능

numad의 단점

  • 여러 NUMA 노드의 리소스를 소비하는 애플리케이션에 유용하지만 시스템 리소스 소비율이 증가하면 numad에서 얻을 수 있는 효과는 감소
  • 프로세스의 실행 시간이 몇 분이거나 많은 리소스를 소비하지 않는 경우 numad는 성능을 개선하지 않을 가능성이 높음
  • 대형 메모리 데이터베이스와 같은 지속적으로 예상치 못한 메모리 액세스 패턴을 갖는 시스템도 numad의 사용으로 혜택을 얻을 가능성이 낮음
  • numad가 대체적으로 좋은 성능을 낼 수 있도록 도와주지만 문제점이 발생할 수 있는 상황 존재
    1. 프로세스 A는 interleaved 정책으로 실행되어 각각의 노드에서 메모리를 순차적으로 할당
    2. 이때 프로세스 B가 실행되고, 프로세스 B는 메모리 요청이 노드 하나의 크기보다 작아서 numad에 의해 한쪽 노드에 바인딩되고 해당 노드로부터 메모리를 할당
    3. 문제는 프로세스 B가 지역성을 높이기 위해 Node 1에서 메모리 할당을 너무 많이 받아서 더 이상 프로세스 A에 할당해 줄 메모리가 없을 때 발생
    4. 프로세스 A는 워크로드에 따라 interleave로 실행되었지만 numad가 지역성을 너무 높인 탓에 메모리 불균형이 발생
    5. numad는 사용자가 신경 쓰지 않아도 메모리 할당 문제를 해결해 주긴 하지만 경우에 따라서는 오히려 성능에 좋지 않은 영향을 미칠 수 있음
    6. 현재 시스템의 워크로드에 따라 numad를 켜는 것이 더 좋을지 아닐지 판단이 필요

NUMA 아키텍처(NUMA Architecture)

1. NUMA

  • Non-Uniform Memory Access의 약자 → 불균형 메모리 접근이라는 뜻
  • 멀티 프로세서 환경에서 적용하는 메모리 접근 방식
  • 로컬 메모리로의 접근이 동시에 이뤄질 수 있음
  • 0번 CPU가 자신의 로컬 메모리에 접근하는 동안 1번 CPU도 자신의 메모리에 접근할 수 있어서 성능 향상
  • 로컬 메모리의 양이 모자라면 다른 CPU에 붙어있는 메모리에 접근이 필요하게 되고, 이때 메모리 접근에 시간이 소요되어 예상치 못한 성능 저하를 경험하게 됨
  • 로컬 메모리에서 얼마나 많이 메모리 접근이 일어나느냐가 성능 향상의 가장 중요한 포인트
  • NUMA 아키텍처에서의 메모리 접근

2. UMA

  • Uniform Memory Access의 약자 → 균형 메모리 접근이라는 뜻
  • NUMA와 반대되는 개념으로 초창기 아키텍처
  • 모든 프로세서가 공용 BUS를 이용해서 메모리에 접근
  • UMA의 문제점은 BUS를 동시에 사용할 수 없는것
  • 0번 소켓에 있는 CPU가 메모리에 접근하는 동안 1번 소켓에 있는 CPU는 메모리에 접근 X
  • UMA 아키텍처에서의 메모리 접근

리눅스에서 NUMA 확인

  • 리눅스에는 NUMA를 활용하기 위한 코드를 구현해 놓았고 명령어를 통해서 현재 시스템의 NUMA 상태를 확인

1. numactl 명령어

  • NUMA와 관련된 정책을 확인하거나 설정할 때 사용

  • --show 명령으로 NUMA 정책을 확인

    # numactl 명령어가 없다면 패키지 install 필요
    $ yum install numactl -y
    
    $ numactl --show
    
    # 기본 정책이 default
    # default는 현재 사용 중인 프로세스가 포함된 노드에서 메모리를 먼저 가져다가 사용하는 방식
    policy: default
    preferred node: current
    physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
    25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 
    51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    cpubind: 0 1
    nodebind: 0 1
    membind: 0 1  
  • NUMA와 관련된 메모리 할당 정책
    1. default 정책
      • 별도의 설정을 하지 않는 한 모든 프로세스에 적용
      • 현재 실행되고 있는 프로세스가 포함된 노드에서 먼저 메모리를 할당 받아 사용
    2. bind 정책
      • 특정 프로세스를 특정 노드에 바인딩시키는 방식
      • 0번 노드에 할당하면 0번 노드에서만 메모리를 활용 가능
      • 메모리의 지역성이 좋아지기 때문에 메모리 접근 속도가 빨라서 성능이 좋아짐
      • bind에 설정한 노드의 메모리가 부족하면 성능이 급격히 나빠질 수 있음
    3. preferred 정책
      • bind와 비슷하지만 선호하는 노드를 설정
      • bind가 반드시 설정한 노드에서 메모리를 활용 → preferred는 가능한 한 설정한 노드로부터 메모리를 할당 받음
    4. interleaved 정책
      • 다수의 노드에서 거의 동일한 비율로 메모리를 할당
      • Round-Robin 정책에 따라 다수의 노드로부터 한 번씩 돌아가면서 메모리를 할당 받음
  • numactl -H 명령어 설명

    1. available

      • NUMA 노드가 2개로 구성되어 있음
    2. node 숫자 cpus

      • 각각의 노드 0, 1에 해당하는 CPU의 번호와 각 노드에 할당된 메모리의 크기
      • CPU 번호는 시스템마다 조금씩 다를 수 있음
    3. node distances

      • 각 노드의 메모리에 접근하는 데 걸리는 시간

      • 각각의 로컬 메모리에 접근할 때 소요되는 시간이 10

      • 리모트 메모리, 즉 0번 노드에서 1번 노드에 있는 메모리에 접근하거나, 1번 노드에서 0번 노드에 있는 메모리에 접근할 때 소요되는 시간이 21

      • node distances는 절대적인 시간이 아니라 상대적인 값

      • 리모트 메모리에 접근하는 시간이 로컬 메모리에 접근하는 데 필요한 시간의 2.1배

        $ numactl -H
        # NUMA 노드가 2개로 구성
        available: 2 nodes (0-1)
        
        # node 0의 속성
        node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 36 37 38 39 40 
        41 42 43 44 45 46 47 48 49 50 51 52 53
        node 0 size: 128192 MB
        node 0 free: 126040 MB
        
        # node 1의 속성
        node 1 cpus: 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 54 55 
        56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
        node 1 size: 128993 MB
        node 1 free: 126084 MB
        
        # 각 노드의 메모리에 접근하는 데 걸리는 시간
        node distances:
        node   0   1
        0:  10  21
        1:  21  10



2. numastat 명령어

  • NUMA 환경에서 현재 시스템에 할당된 메모리의 상태를 확인
  • NUMA 아키텍처에서 메모리 불균형 상태를 확인
  • 어느 한쪽 노드의 메모리 사용률이 높으면 메모리 할당 정책에 따라 swap을 사용하는 경우 발생
  • 전체 메모리에는 free 영역이 많이 있는데도 불구하고 메모리 할당 정책에 따라 한쪽 노드에서 메모리 할당이 과하게 일어나면 swap이 사용 → numastat 명령어로 확인 가능
  • numastat -cm 명령어 출력

3. 프로세스가 어떤 메모리 할당 정책으로 실행되었는지 확인

  • /proc//numa_maps은 현재 동작 중인 프로세스의 메모리 할당 정책과 관련된 정보가 기록
    $ cat 21631/numa_maps
    55ca074c5000 default file=/usr/libexec/postfix/pickup mapped=47 N0=47 kernelpagesize_kB=4
    55ca07706000 default file=/usr/libexec/postfix/pickup anon=2 dirty=2 N1=2 kernelpagesize_kB=4
    55ca07708000 default file=/usr/libexec/postfix/pickup anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    55ca07709000 default anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    55ca080ae000 default heap anon=18 dirty=18 N1=18 kernelpagesize_kB=4
    7fdae8bb5000 default file=/usr/lib64/libnss_sss.so.2 mapped=7 mapmax=10 N1=7 kernelpagesize_kB=4
    7fdae8bbd000 default file=/usr/lib64/libnss_sss.so.2
    7fdae8dbc000 default file=/usr/lib64/libnss_sss.so.2 anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    7fdae8dbd000 default file=/usr/lib64/libnss_sss.so.2 anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    7fdae8dbe000 default file=/usr/lib64/libnss_files-2.17.so mapped=9 mapmax=47 N1=9 kernelpagesize_kB=4
    7fdae8dca000 default file=/usr/lib64/libnss_files-2.17.so
    7fdae8fc9000 default file=/usr/lib64/libnss_files-2.17.so anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    7fdae8fca000 default file=/usr/lib64/libnss_files-2.17.so anon=1 dirty=1 N1=1 kernelpagesize_kB=4
    7fdae8fcb000 default

※ 로컬 엑세스(Local Access)

  • 각각의 CPU마다 별도의 메모리가 있는데 이와 같이 메모리에 접근하는 방식

※ 노드(Node)

  • CPU와 메모리를 합쳐서 노드(Node)라고 함

※ 리모트 엑세스(Remote Access)

  • NUMA에서는 자신의 메모리가 아닌 다른 노드에 있는 메모리에도 접근하는 방식
  • VIRT, RES, SHR는 프로세스에서 중요한 요소로, 현재 프로세스가 사용하고 있는 메모리와 관련된 값


VIRT → task가 사용하는 virtual memory의 전체 용량을 의미

  • 프로세스에 할당된 가상 메모리 전체의 크기
  • VIRT는 프로그램이 현재 얼마나 많은 메모리를 접근할 수 있는지를 출력
  • VIRT는 물리 메모리의 사용한 공간을 의미하지는 않음
  • VIRT = SWAP + RES
  • VIRT는 실제로는 할당되지 않은 가상의 공간(swap)이기 때문에 VIRT의 해당 값이 크다고 해도 문제되지는 않음

1. VIRT의 Memory Commit

  • 프로세스가 커널로부터 사용을 예약 받은 메모리 → 프로세스는 malloc()과 같은 시스템 콜로 자신이 필요로 하는 메모리의 영역을 할당해 줄것을 요청
  • 프로세스가 요청한 내용은 커널이 가용할 수 있는 공간이 있다면 성공 메시지와 함께 해당 프로세스가 사용할 수 있도록 가상의 메모리 주소를 전달
  • malloc을 통해 할당 받은 가상 메모리는 물리 메모리에 해당 영역이 할당된 상태는 아님
  • Memory Commit의 참고 URL : memory commit과 vm.overcommit 커널 파라미터

2. VIRT의 malloc 테스트 코드

  • malloc_test.c 파일을 생성

    $ vi malloc_test.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    # define MEGABYTE 1024*1024
    
    int main(){
       void *myblock = NULL;
       int count = 0;
    
       while(1){
           myblock = (void *) malloc(MEGABYTE);
    
           if(!myblock){
               printf("Error!");
               break;
           }
    
           printf("Currently allocating %d MB\n", (++count)*MEGABYTE);
           sleep(1);
       }
       exit(0);
    }
    
    # malloc_test 파일을 malloc_test 실행 파일로 만듦
    $ gcc -o malloc_test malloc_test.c
    $ ls -al
    -rwxr-xr-x  1 root root 8520 Oct 11 17:53 malloc_test
    -rw-r--r--  1 root root  395 Oct 11 17:47 malloc_test.c

3. VIRT의 malloc 테스트 실행 결과

  • malloc() 으로 메모리 영역을 요청한 후에 아무것도 하지 않음

  • top 명령으로 해당 프로세스의 변화 확인 → 시간이 지나면서 VIRT는 계속해서 높아지지만, RES는 늘어나지 않음

    # 위에서 생성한 malloc_test 실행파일 실행
    $ ./malloc_test
    Currently allocating 1 MB
    Currently allocating 2 MB
    Currently allocating 3 MB
    Currently allocating 4 MB
    Currently allocating 5 MB
    # [...생략...]
    
    # 다른 터미널로 해당 내용 확인
    $ top -b -n 1 | grep -i malloc
    65246 root      20   0   33000    348    276 S   0.0  0.0   0:00.00 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    65246 root      20   0   40196    608    400 S   0.0  0.0   0:00.00 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    65246 root      20   0   47392    608    400 S   0.0  0.0   0:00.00 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    65246 root      20   0   55616    608    400 S   0.0  0.0   0:00.00 malloc_test



RES → task가 사용하고 있는 physical memory의 양을 의미

  • RES는 VIRT가 사용하고 있는 실제 물리 메모리의 크기
  • 일반적으로 RES는 VIRT보다 작은 값을 출력
  • RES는 실제로 메모리를 쓰고 있음
  • RES는 SWAP 값 제외
  • RES는 메모리 점유율이 높은 프로세스를 찾기 위해서는 RES 영역이 높은 프로세스를 찾아야함

1. 프로세스가 VIRT 할당 받은 메모리를 RES에 올려서 사용

  • 프로세스가 할당받은 메모리 영역에 실제로 쓰기 작업을 하면 Page fault가 발생
  • Page fault가 발생하면 커널은 실제 물리 메모리에 프로세스의 가상 메모리 공간을 매핑 → Page Table이라고 불리는 커널의 전역 변수로 관리
  • 물리 메모리에 바인딩된영역이 RES로 계산

2. RES의 malloc 테스트

  • 이전의 malloc_test.c 파일을 수정

    $ vi malloc_test.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    # define MEGABYTE 1024*1024
    
    int main(){
       void *myblock = NULL;
       int count = 0;
    
       while(1){
           myblock = (void *) malloc(MEGABYTE);
    
           if(!myblock){
               printf("Error!");
               break;
           }
    
           printf("Currently allocating %d MB\n", (++count)*MEGABYTE);
           memset(myblock, 1, MEGABYTE);
           sleep(1);
      }
      exit(0);
    }
    
    # malloc_test 파일을 malloc_test 실행 파일로 만듦
    $ gcc -o malloc_test malloc_test.c
    $ ls -al
    -rwxr-xr-x  1 root root 8568 Oct 11 18:02 malloc_test
    -rw-r--r--  1 root root  433 Oct 11 18:01 malloc_test.c

3. RES의 malloc 테스트 실행 결과

  • malloc()을 단독으로 요청하는 것이 아니라 할당 받은 메모리에 쓰기함 → RES 영역이 VIRT 영역 늘어나는 비율과 비슷하게 늘어남

  • 즉 해당 파일은 메모리 사용과 관련해서 VIRT 뿐만 아니라 실제 메모리를 쓰는 RES를 확인할 수 있음

  • top 명령으로 해당 프로세스의 변화 확인 → 시간이 지나면서 VIRT과 함께 RES가 계속해서 높아짐

    # 위에서 생성한 malloc_test 실행파일 실행
    $ ./malloc_test
    Currently allocating 1 MB
    Currently allocating 2 MB
    Currently allocating 3 MB
    Currently allocating 4 MB
    Currently allocating 5 MB
    # [...생략...]
    
    # 다른 터미널로 해당 내용 확인
    $ top -b -n 1 | grep -i malloc
    66652 root      20   0   21692  17772    400 S   0.0  0.0   0:00.01 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    66652 root      20   0   35056  31236    400 S   0.0  0.0   0:00.04 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    66652 root      20   0   45336  41532    400 S   0.0  0.0   0:00.05 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    66652 root      20   0   57672  53940    400 S   0.0  0.0   0:00.07 malloc_test
    
    $ top -b -n 1 | grep -i malloc
    66652 root      20   0   62812  58956    400 S   0.0  0.0   0:00.08 malloc_test

※ Page Faults

  • 프로그램이 자신의 주소 공간에는 존재하지만 시스템의 RAM에는 현재 없는 데이터나 코드에 접근 시도하였을 경우 발생하는 현상
  • Page Faults 발생하면 운영 체제는 데이터를 메모리로 가져와서 Page Faults가 전혀 발생하지 않은 것처럼 프로그램이 계속적으로 작동하게 해줌

※ Page Table

  • 가상 주소와 물리 메모리 주소의 매핑 테이블
  • 프로세스마다 하나씩 존재하게 되며, 메인 메모리 (RAM)에 상주
  • 많은 프로세스가 구동될 수록, 페이지 테이블로 인한 메인 메모리 사용이 커짐을 의미


SHR → 다른 프로세스와 공유하고 있는 shared memory의 양을 의미

  • SHR는 공유하고 있는 메모리 크기
  • SHR 영역에 대표적으로 등록되는 내용은 라이브러리(library) → 대부분의 리눅스 프로세스는 glibc라는 라이브러리를 참고하기에 해당 라이브러리를 공유 메모리에 올려서 사용
  • 공유메모리에 올려놓고 사용하지 않고, 사용하는 프로세스마다 glibc의 내용을 메모리에 올려서 사용하는 것은 공간 낭비
  • 커널은 공유메모리라는 개념을 도입했고, 다수의 프로세스가 함께 사용하는 라이브러리는 공유메모리 영역에 올려서 함께 사용하도록 구현
  • VIRT는 실제는 할당되지 않는 가상공간이기 때문에 해당 값이 크다고 해도 문제가 되지 않음
  • 실제 사용하고 있는 메모리는 RES 영역이기 때문에 메모리 점유율이 높은 프로세스를 찾기 위해서는 RES 영역이 높은 프로세스를 찾아야함
  • 매핑된 라이브러리 전체는 VIRT와 SHR에 포함되어도, 실제 사용 중인 함수가 있는 일부 메모리만 RES에 포함
  • 커널은 메모리 낭비를 막기위해 공유 메모리라는 개념을 도입했고, 다수의 프로세스가 함께 사용하는 라이브러리는 공유 메모리 영역에 올려서 함께 사용하도록 구현

'OS(운영체제)' 카테고리의 다른 글

NUMAD (메모리 할당 관리)  (0) 2022.06.29
OOM Killer와 OOM Scoring  (0) 2022.06.29
  • OOM Killer는 희생시킬 프로세스를 고르기 위해서 각 프로세스에 점수를 매기는 과정을 진행

  • OOM Killer는 메모리가 부족해졌을 때 프로세스 중 oom_score가 가장 높은 점수를 받은 순서대로 프로세스를 죽임

  • oom_score 점수는 기본적으로 프로세스에게 할당된 메모리의 양으로 점수가 메겨짐 → 해당 프로세스로부터 fork()된 자식 프로세스들의 메모리도 추가됨

  • 오래 돌고 있는 프로세스의 경우 oom_score 점수를 낮춰주고, 프로세스 순위가 낮은 프로세스는 oom_score 점수를 높여서 사용할 필요가 있음

  • 중요하게 취급되어지는 프로세스들(superuser에 의해서 실행되거나, 하드웨어와 직접적인 관련이 있는)은 값을 크게 낮춰줘서 OOM Killer를 통해서 kill되지 않도록 해야함

  • 각 서비스의 oom_score를 확인하는 방식(PID → 프로세스 ID)

    # 기본 형식
    $ cat /proc/<pid>/oom_score
    oom_socre 출력
    
    # 19862 프로세스의 oom_score 확인 
    $ cat /proc/19862/oom_score
    12

※ nice
프로세스의 우선 순위를 낮춰주는 점수를 의미

oom_score_adj, oom_score, oom_adj 커널 파라미터

1. oom_score_adj

  • 앱이 포그라운드(foreground)에 있는지, 백그라운드(background)에 있는지로 결정
  • 정확한 값은 앱이 백그라운드 서비스를 가지고 있는지, 얼마나 오랫동안 포그라운드에 빠져 있는지에 따라 달라짐
  • oom_score_adj 는 -1000 ~ 1000 의 값을 가지며, 낮은 값 일수록 우선순위에서 밀려남
  • 프로세스가 OOM Killer 에 의해 죽지 않길 원한다면 oom_score 를 조정하는게 아니라 oom_score_adj 값을 변경해야함

2. oom_score

  • 기본적으로 프로세스에게 할당된 메모리의 양으로 점수가 메겨짐

3. oom_adj

  • -17 ~ 15 의 값을 가지며, 낮은 값 일수록 우선순위에서 밀려남
  • -17이면 OOM을 완전히 비활성화시킴
    /proc/<pid>/oom_adj
    /proc/<pid>/oom_score
    /proc/<pid>/oom_score_adj


OOM Killer의 대상에서 벗어나기

  • OOM Killer를 끄는 것은 불가능

  • 서비스의 안정성을 위해서 꺼지면 안 될 프로세스도 존재 → 특정한 작업을 하고 있던 worker 프로세스

  • OOM Killer를 직접적으로 끌 수는 없지만, OOM Killer의 scoring를 통해 OOM Killer가 멈추는 대상은 벗어날수 있음

  • /proc//oom_adj 값을 -17로 설정 → -17 값은 OOM_DISABLE의 상수 값

    $ echo -17 > /proc/<pid>/oom_adj
  • /proc/oom_scoring_adj 값을 -1000으로 설정

  • -1000 값으로 지정하는 것은 OOM Scoring을 비활성화한다기보다는 충분히 낮은 값을 주어서 OOM Killer에 벗어남

    $ echo -1000 > /proc/<pid>/oom_adj



메모리 관련해서 리눅스에서 실행(kill) 하는 순서

  1. alloc_pages
  2. out_of_memory
  3. select_bad_process
  4. oom_evaluate_task
  5. oom_badness


OOM Killer 관련 함수

1. select_bad_process 함수

  • bad 프로세스를 선택

  • select_bad_process 함수 내용

    static void select_bad_process(struct oom_control *oc)
    {
    if (is_memcg_oom(oc))
        mem_cgroup_scan_tasks(oc->memcg, oom_evaluate_task, oc);
    else {
        struct task_struct *p;
    
          // RCU: Read-Copy Update
        rcu_read_lock();
    
        for_each_process(p)
            if (oom_evaluate_task(p, oc))
                break;
        rcu_read_unlock();
    }
    
    oc->chosen_points = oc->chosen_points * 1000 / oc->totalpages;
    }

2. oom_evaluate_task 함수

  • oom 선정된 프로세스가 OOM Killer를 통해 멈춰되 되는지 검증

  • oom_evaluate_task 함수 내용

    static int oom_evaluate_task(struct task_struct *task, void *arg)
    {
      struct oom_control *oc = arg;
      unsigned long points;
    
      if (oom_unkillable_task(task, NULL, oc->nodemask))
          goto next;
      /*
       * This task already has access to memory reserves and is being killed.
       * Don't allow any other task to have access to the reserves unless
       * the task has MMF_OOM_SKIP because chances that it would release
       * any memory is quite low.
       */
      if (!is_sysrq_oom(oc) && tsk_is_oom_victim(task)) {
          if (test_bit(MMF_OOM_SKIP, &task->signal->oom_mm->flags))
              goto next;
          goto abort;
      }
    
      /*
       * If task is allocating a lot of memory and has been marked to be
       * killed first if it triggers an oom, then select it.
       */
      if (oom_task_origin(task)) {
          points = ULONG_MAX;
          goto select;
      }
    
      points = oom_badness(task, NULL, oc->nodemask, oc->totalpages);
      if (!points || points < oc->chosen_points)
          goto next;
    
      /* Prefer thread group leaders for display purposes */
      if (points == oc->chosen_points && thread_group_leader(oc->chosen))
          goto next;
    select:
      if (oc->chosen)
          put_task_struct(oc->chosen);
      get_task_struct(task);
      oc->chosen = task;
      oc->chosen_points = points;
    next:
      return 0;
    abort:
      if (oc->chosen)
          put_task_struct(oc->chosen);
      oc->chosen = (void *)-1UL;
      return 1;
    }

3. oom_badness 함수

  • kill 했을 때 가장 높은 메모리를 확보할 수 있는 task 인 경우, 높은 점수를 return 한다는 개념

  • oom_badness() 함수가 리턴해주는 점수가 가장 높은 task가 bad_process로 선정 되어 죽게 됨

  • oom_score_adj 라는 값을 oom_badness 에서 LONG_MIN 을 리턴하기 위해 사용 → LONG_MIN 을 리턴한다는 개념은 낮은 점수를 줘서 kill 하지 못하게 하겠다는 의미

  • RSS (프로세스가 사용하고 있는 물리 메모리) + 프로세스의 스왑 메모리 + (프로세스의 pagetable / page_size) 의 값이 프로세스 (task) 의 점수 (point) 가 됨

  • 해당 코드만 보면 프로세스가 점유하고 있는 메모리가 클 경우 score가 높아진다고 이해 가능

  • oom_badness 함수 내용

    /**
    * oom_badness - heuristic function to determine which candidate task to kill
    * @p: task struct of which task we should calculate
    * @totalpages: total present RAM allowed for page allocation
    *
    * The heuristic for determining which task to kill is made to be as simple and
    * predictable as possible.  The goal is to return the highest value for the
    * task consuming the most memory to avoid subsequent oom failures.
    */
    long oom_badness(struct task_struct *p, unsigned long totalpages)
    {
      long points;
      long adj;
    
      if (oom_unkillable_task(p))
          return LONG_MIN;
    
      p = find_lock_task_mm(p);
      if (!p)
          return LONG_MIN;
    
      /*
       * Do not even consider tasks which are explicitly marked oom
       * unkillable or have been already oom reaped or the are in
       * the middle of vfork
       */
      adj = (long)p->signal->oom_score_adj;
      if (adj == OOM_SCORE_ADJ_MIN ||
              test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
              in_vfork(p)) {
          task_unlock(p);
          return LONG_MIN;
      }
    
      /*
       * The baseline for the badness score is the proportion of RAM that each
       * task's rss, pagetable and swap space use.
       */
      points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +
          mm_pgtables_bytes(p->mm) / PAGE_SIZE;
      task_unlock(p);
    
      /* Normalize to oom_score_adj units */
      adj *= totalpages / 1000;
      points += adj;
    
      return points;
    }

4. get_mm_rss 함수

  • get_mm_rss() 함수를 사용하여 여유 메모리(free memory)가 얼마나 되는지 추정
    static inline unsigned long get_mm_rss(struct mm_struct *mm)
    {
      return get_mm_counter(mm, MM_FILEPAGES) +
          get_mm_counter(mm, MM_ANONPAGES) +
          get_mm_counter(mm, MM_SHMEMPAGES);
    }

5. get_mm_counter 함수

  • get_mm_counter 함수를 통해 swap 메모리(swap memory)가 얼마나 되는지 추정

    static inline unsigned long get_mm_counter(struct mm_struct *mm, int member)
    {
      long val = atomic_long_read(&mm->rss_stat.count[member]);
    
      #ifdef SPLIT_RSS_COUNTING
      /*
       * counter is updated in asynchronous manner and may go to minus.
       * But it's never be expected number for users.
       */
      if (val < 0)
          val = 0;
      #endif
      return (unsigned long)val;
    }

  • 파이썬 WHOIS 모듈을 활용하여 도메인 등록기관(Registry)에서 관리하는 도메인의 만료 기간을 확인
  • 지속적으로 관리하지 않으면, 방심하는 사이에 도메인의 만료기간이 넘어서 서비스의 장애 발생
  • python3의 whois 모듈과 시간 모듈을 다운로드

    pip3 install python-whois
    pip3 install python-dateutil

  • WHOIS 모듈을 사용한 파이썬 코드 → 만료기간을 SLACK에 알려주는 기능 추가

    $ vi /root/whois_check.py
    import whois
    import datetime
    from dateutil.relativedelta import relativedelta
    import requests
    
    domain_lists = ['naver.com','kakao.co.kr']
    
    for domain in domain_lists:
        result = whois.whois(domain)
        expiration = result.expiration_date
        domain_txt = domain + "도메인의 만료기간이 " + str(expiration) + " 입니다."
        data = {'payload': '{"channel": "#hippo", "username": "alarm", "text": \"'+ domain_txt + '\"}'}
        response = requests.post('https://hooks.slack.com/services/[...생략...]',data=data)

  • crontab 설정

    $ cat /etc/crontab
    # 매일 아침 10시에 도메인 whois 확인
    00 10 * * * root python3 /root/whois_check.py > /dev/null 2>&1

※ 참고 URL

  • 특정 URL의 컨텐츠를 호출하는 파이썬 스크립트
  • 도메인 제외되고 URL의 Path만 있는 파일을 읽어 컨텐츠 호출
  • python3 requests get 호출 스크립트

    $ vi request_get.py
    #! /bin/python3
    import sys
    import requests
    import time
    import logging
    
    logging.basicConfig(filename='./preload.log', level=logging.INFO)
    
    def make_server_list(file):
        serverList = []
        with open(file, mode='r') as serverfile:
            for server in serverfile:
                server = server.strip('\n')
                if not server:
                    break
                serverList.append(server)
        return serverList
    
    def curl_request(server, domain, path):
        headers = {
            "host": domain,
        }
        response = requests.get("http://"+server+path, headers=headers, timeout=10)
        #response = requests.head("http://"+server+path, headers=headers, timeout=10)
        return response
    
    if __name__ == '__main__':
        domain = sys.argv[1]
        urlArgs = sys.argv[2]
        serverArgs = sys.argv[3]
    
        if len(sys.argv) != 4:
            print("Insufficient arguments")
            sys.exit()
    
        with open(urlArgs, mode='r' ) as urlfile:
            serverList = make_server_list(serverArgs)
    
            for fileLine in urlfile:
                fileLine = fileLine.strip('\n')
                #scheme=fileLine.split('://',maxsplit=1)[0]
                #domain=fileLine.split('/',maxsplit=3)[2]
                path=fileLine
                for server in serverList:
                    start = time.perf_counter()
                    response = curl_request(server, domain, path)
                    request_time = time.perf_counter() - start
                    if response.status_code == 200:
                        print("server:" + server + "\t\tpreload url : " + domain + path + "\ttime : " + str(round(request_time,3))+"s")
                        logging.info("success preload : " + server + "\tpreload url : " + domain + path + "\ttime : " + str(round(request_time,3))+"s")
                    else:
                        print("error preload : " + server + "\tpreload url : " + domain + path)
                        logging.info("error preload : " + server + "\tpreload url : " + domain + path)
                #time.sleep(1)

  • url 중에 도메인 제외한 리스트

    $ cat urllist.txt
    /test.txt

  • 서버 리스트

    $ cat serverlist.txt 
    8.8.8.8

  • 실행 예제 (실행 형식만 설명하지 실제 동작 X)

    $ python3 request_get.py test.co.kr urllist.txt serverlist.txt
    server:8.8.8.8            preload url : test.co.kr/test.txt       time : 3.037s

+ Recent posts