[컴] elasticsearch 에서 JVM heap size 설정

 

elastic search heap size 결정 / 힙 사이즈 / 어느정도 크기로 / 메모리 확장 /

elasticsearch 에서 JVM heap size 설정

  • 총 메모리의 1/2 까지만
  • 그래도 최대치는 26GB ~ 30GB 이하면 된다.

Set the JVM heap size

기본적으로 Elasticsearch는 노드의 role(master, data 등) 및 총 메모리를 기반으로 JVM heap 크기를 자동으로 설정한다. 대부분의 프로덕션 환경에서는 기본 크기를 사용하는 것을 권장한다.

기본 heap 크기를 재정의하려면 최소 및 최대 힙 크기 설정(Xms 및 Xmx)을 설정하면 된다. 최소값(Xms)과 최대값(Xmx)은 같아야 한다.

heap 크기는 사용 가능한 RAM을 기준으로 해야 한다.

  • Xms 및 Xmx를 전체 메모리의 50% 이하로 설정한다. Elasticsearch에는 JVM heap 이외의 목적으로 메모리가 필요하다. 예를 들어 Elasticsearch는 효율적인 네트워크 통신을 위해 off-heap buffers를 사용하고 파일에 대한 효율적인 액세스를 위해 운영 체제의 파일 시스템 캐시에 의존한다. JVM 자체도 약간의 메모리가 필요하다. Elasticsearch는 Xmx 설정으로 구성된 제한보다 많은 메모리를 사용하는 것이 일반적이다.

    • off-heap buffers
    • 파일 시스템 캐시
    • JVM 자체가 사용하는 memory

    NOTE: Docker와 같은 컨테이너에서 실행되는 경우 총 메모리는 호스트의 “총 시스템 메모리”가 아니라 “컨테이너에 표시되는 메모리 양”으로 정의된다.

  • Xms 및 Xmx를 압축된 일반 개체 포인터(ordinary object pointers, oops)의 임계값(threshold) 이하로 설정한다. 정확한 임계값은 다양하지만 26GB는 대부분의 시스템에서 안전하며 일부 시스템에서는 30GB까지 정도 될 수 있다. 임계값 미만인지 확인하려면 Elastic search 로그에서 다음과 같은 항목을 확인하자.

heap size [1.9gb], compressed ordinary object pointers [true]

또는 노드 정보 API를 사용하여 노드에 대한 jvm.using_compressed_ordinary_object_pointers 값을 확인하자.

curl -X GET "localhost:9200/_nodes/_all/jvm?pretty"

Elasticsearch에 사용할 수 있는 heap이 많을수록 내부 캐시(internal cache)에 더 많은 메모리를 사용할 수 있다. 이렇게 하면 운영 체제가 파일 시스템 캐시(filesystem cache)에 사용할 메모리가 줄어듭니다. heap이 클수록 garbabe collection pause 들이 길어질 수도 있다.

heap size를 설정하려면 확장자가 .options인 사용자 지정 JVM 옵션 파일에 XmsXmx JVM 인수를 추가하고 jvm.options.d/ 디렉토리에 저장한다. 예를 들어 max heap size를 2GB로 설정하려면 Xms 및 Xmx를 모두 2g으로 설정한다.

-Xms2g
-Xmx2g

테스트를 할 때는, ES_JAVA_OPTS 환경 변수를 사용하여 heap size를 설정할 수도 있다.

ES_JAVA_OPTS="-Xms2g -Xmx2g" ./bin/elasticsearch

ES_JAVA_OPTS 변수는 다른 모든 JVM 옵션을 재정의(override) 한다. 운영에서는 ES_JAVA_OPTS 를 사용하지 않는 것이 좋다.

Heap: Sizing and Swapping

ref.2 의 내용은 오래된 버전의 내용이지만 알아둬야 할 내용들이 있다.

Give (less than) Half Your Memory to Lucene

일반적인 문제는 heap을 너무 크게 구성하는 것이다. 64GB의 컴퓨터를 사용하고 있다면, 놀랍게도 Elasticsearch에 64GB의 모든 메모리를 제공하려고 한다. 많을수록 좋다!

heap은 Elasticsearch에서 확실히 중요하다. 빠른 동작을 제공하기 위해 많은 in-memory data structure들에서 사용된다. 하지만 같은 맥락으로, memory의 또 다른 주요 사용자인 Lucene이 있다.

Lucene은 in-memory data sturcture를 caching하기 위해 OS를 활용하도록 설계되었다. Lucene segment는 개별 파일에 저장된다. segment는 immutable하므로 이러한 파일은 변경되지 않는다. 이것은 cache 를 사용하는데 아주 적합한 구조다. OS는 더 빠른 액세스를 위해 hot segement를 메모리에 상주시킨다. 이러한 segment들에는 inverted index(fulltext search 를 위한)과 doc values(aggregations의 경우)이 모두 포함된다.

Lucene의 성능은 OS와의 이 상호 작용에 달려있다. 하지만 Elasticsearch의 heap에 사용 가능한 모든 메모리를 준다면 Lucene에게 남은 메모리가 없게 된다. 이는 성능에 심각한 영향을 미칠 수 있다.

표준 권장 사항은 사용 가능한 메모리의 50%를 Elasticsearch heap에 할당하고 나머지 50%는 사용 가능한 상태로 두는 것이다. Lucene 이 남은 메모리를 사용할 것이다.

분석된 문자열 필드를 aggregate하지 않는 경우(예: 필드 데이터가 필요하지 않음) heap을 더 낮추는 것을 고려할 수 있다. heap을 작게 만들수록 Elasticsearch(더 빠른 GC)와 Lucene(캐싱을 위한 더 많은 메모리) 모두에서 더 나은 성능을 기대할 수 있다.

Don’t Cross 32 GB!

Elastic search에 엄청난 heap을 할당하지 않는 또 다른 이유가 있다. HotSpot JVM은 heap이 약 32GB 미만일 때 객체 포인터(object pointer)를 압축하는 트릭을 사용한다.

자바에서 모든 object들은 heap에 할당되고 포인터에 의해 참조된다. 일반 객체 포인터(Ordinary object pointers, OOP)는 이러한 object를 가리키며, 전통적으로 프로세서의 cpu의 native word size 이다.(32비트 또는 64비트, cpu에 따라 다르다). 포인터는 값의 정확한 byte location를 reference 한다.

32비트 시스템의 경우 최대 heap 크기는 4GB입니다. 64비트 시스템의 경우 heap 크기가 훨씬 커질 수 있지만 64비트 포인터의 오버헤드는 포인터가 더 크기 때문에 낭비되는 공간이 더 많다는 것을 의미한다. 그리고 낭비되는 공간보다 더 나쁜 것은, 큰 포인터들이 메인 메모리와 다양한 캐시들(LLC, L1 등) 사이에서 값을 이동할 때 더 많은 대역폭을 소비한다는 것이다.

자바는 이 문제를 피하기 위해 compressed oops 라고 불리는 트릭을 사용한다. 포인터들은 메모리에서 정확한 바이트 위치를 가리키는 대신 object offset 들을 참조한다. 이것은 32비트 포인터가 메모리에 있는 exact byte location 40억 바이트가 아닌 40억 개의 객체를 참조할 수 있다는 것을 의미한다. 궁극적으로, 이는 heap이 한계치가 4GB 인 32비트 포인터를 사용하면서도 메모리가 32GB로 증가할 수 있음을 의미한다.

마법의 ~32GB 경계를 넘으면 포인터가 다시 일반 개체 포인터(ordinary object pointers)로 전환된다. 각 포인터의 크기가 커지고 더 많은 CPU 메모리 대역폭이 사용되며 사실상 메모리가 손실된다. 실제로 할당된 heap의 유효 메모리가 약 40-50GB가 되어야 compressed oops을 사용하여 32GB 미만의 heap을 사용할 수 있다.

이 이야기의 교훈은 다음과 같다. 메모리가 남아있을 때에도 32GB heap 경계를 넘지 않도록 해야한다. 그것은 메모리를 낭비하고 CPU 성능을 떨어뜨리며 큰 heap 로 인해 GC 를 오래걸리게 한다.

See Also

  1. Compressed OOPs in the JVM | Baeldung

Reference

  1. Advanced configuration | Elasticsearch Guide [master] | Elastic
  2. Heap: Sizing and Swapping | Elasticsearch: The Definitive Guide [2.x] | Elastic

[컴] ch.qos.logback 에서 RollingFileAppender 사용법

rolling log file appender / file로 logging / 로깅 / 로그 / 코드 사용법/ 코딩 / log rotate

ch.qos.logback 에서 RollingFileAppender 사용법

자세한 내용은 ref. 1을 참고하자.

아래 code에 있는 pattern 에 대해 간략한 설명:

// %-12 는 12개의 character 자리를 유지, %msg는 메시지, %n 은 new line
logEncoder.pattern = "%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n"

TimeBasedRollingPolicy 에서 fileNamePattern:

  • TimeBasedRollingPolicy 에서 fileNamePattern의 값을 보고 rollover period 를 정한다.
// `yyyy-MM-dd_HH` 이기에 1시간에 한번씩 rollover 된다.
// `yyyy-MM-dd` 라면 1일에 한번씩 rollover 된다.
// maxHistory 는 log 를 몇개까지 보관하느냐로 보면 된다.
val logFilePolicy = TimeBasedRollingPolicy<ILoggingEvent>();
logFilePolicy.fileNamePattern = "${logDir}/archived/logfile-%d{yyyy-MM-dd_HH}.log.zip";
logFilePolicy.maxHistory = 7;
// @ref: https://logback.qos.ch/manual/layouts.html
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
val root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
root.level = if(System.getProperty("log.level") == "debug") Level.DEBUG else Level.INFO

val logEncoder = PatternLayoutEncoder()
logEncoder.context = loggerContext
logEncoder.pattern = "%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n"
logEncoder.start()

val logDir = "logs"
val logFileAppender = RollingFileAppender<ILoggingEvent>();
logFileAppender.context = loggerContext;
logFileAppender.name = "logfile";
logFileAppender.encoder = logEncoder as Encoder<ILoggingEvent>;
logFileAppender.isAppend = true;
logFileAppender.file = "${logDir}/logfile.log"; // 현재 directory 기준이다. directory 가 없으면 자동으로 생성한다.

val logFilePolicy = TimeBasedRollingPolicy<ILoggingEvent>();
logFilePolicy.context = loggerContext;
logFilePolicy.setParent(logFileAppender);
logFilePolicy.fileNamePattern = "${logDir}/archived/logfile-%d{yyyy-MM-dd_HH}.log.zip";
logFilePolicy.maxHistory = 7;
logFilePolicy.start();

logFileAppender.setRollingPolicy(logFilePolicy);
logFileAppender.start();

root.addAppender(logFileAppender)

Reference

  1. Chapter 6: Layouts | Logback Project
  2. TimeBasedRollingPolicy | Chapter 4: Appenders

[컴][웹] http reqeust timeouts 기본값은?

 

http timeout default value

Http reqeust timeouts 기본값은?

결론적으로 기본값은 없다. client 에서 값을 set 하는 것이라서 사용하는 client library 의 기본값은 있겠지만 protocol 상에서 정해놓은 값은 없다.

다음은 ref.1 의 내용이다.

HTTP 는 client-driven protocol(클라이언트 기반 프로토콜) 이다. 클라이언트가 그것을 요청할 때 서버는 단순히 클라이언트에서 정보를 제공할 수 있다.

서버들이 클라이언트들에게 낮은 laytency 의 알림들을 제공하기 위해 long-polling 의 방식을 이용하는 것이 일반적이다.

long-polling 은 client 가 서버에 대해 "열려있는 request(open request)" 를 유지하는 것이 필요하다. 서버는 관련있는 event 가 발생할때까지 이 request 에 대한 응답(response) 를 보류한다.

open request 는 서버가 이벤트가 발생하자마자 메시지를 보낼 수 있게 해준다. notification을 가지고 있는 response 를 받은 후, 클라이언트는 즉시 다른 open request 를 연다.

long-polling 은 많은 HTTP request 들과 많이 다르다. http requst 들은 request 와 response 사이에 큰 delay 를 가지고 있지 않다. long-polling 요청은 처리가 이뤄지는 동안에 connection 을 독점한다. 이런 이유로 서버 또는 중간자(intermediary)는 long-polling reqeust 를 식별하는 것이 유용하다. long-polling request 를 실벽하는 것은 long-polling reqeust 들에 대한 특별한 resource 관리가 가능하다. 예를 들면, 분리된 연결 할당과 관리, 더 긴 timeout 시간, 그리고 long-poll 최적화 등이 가능하다.

long-polling 에 사용되는 연결도 특정 기간 idle 상태인 것으로 나타납니다. idle connection의 관리는 long-polling 의 작동을 방해할 수 있다. 많은 중개자들은 “클라이언트가 outgoing connection들에 대한 사용을 독점하는 것”을 피하기 위해 적합하게 timeout들을 사용한다. 중개인이 요청에 대해 timeout 이 되어버리면, 서버는 응답을 보낼 수 없다.

reponse을 제공하는 데 가능한 시간을 알면 timeout 문제를 방지할 수 있다. 현재 구현에서는 30초에서 120초 사이의 시간, 즉 경험적으로 안전하다고 판단된 시간을 선택한다. 서버는 이 시간 전에 null response을 제공하여 중개인에게 연결이 active 상태로 나타나도록 한다. 각 null response 및 후속 요청이 트래픽을 생성하므로 timeout 의 값이 낮으면 낭비가 많이 생길 가능성이 있다. 그 response 가 서버가 통지에 사용할 채널을 제거하기 때문에 null reponse 는 또한 notification 들의 latency 를 증가 시킬 수 있다.

Section 3. 에 정의된 Request-Timeout 헤더는 특정 요청에 대한 응답을 제공하는 데 서버가 사용할 수 있는 최대 시간을 갖고 있다.

client library 마다 timeout 값은 다르다

기본적으로 client 에서 timeout 값을 정하는 것이기에 그것은 library 가 정하기 나름이다. 다음 2가지 경우만 봐도 http request 의 timeout 값은 library 마다 다르다는 것을 알 수 있다.

Reference

  1. Hypertext Transfer Protocol (HTTP) Timeouts

[컴] systemd 등록 방법

systemctl 등록/ register systemd / 부팅때마다 실행 / 자동 실행 / booting 시 실행 / init /

systemd 등록 방법

systemd 에 의해 실행되려면 service unit file 이 있어야 한다. 같은 이름이 이미 존재하면 안된다.

다음 명령으로 현재 존재하는 service 이름들을 확인하자.

sudo systemctl list-unit-files --type-service

sudo vi /etc/systemd/system/myapp.service
sudo chmod 640 /etc/systemd/system/myapp.service

systemctl edit myservice

systemctl edit myservice 로 생성하는 것을 권장한다.(참고로, edit 를 하면, override 되는 값만 넣어야 한다. ExecStart 가 들어가면 ExecStart= 값이 두번있다고 에러가 뜬다.) 만약 존재하지 않는 service 인 경우 systemctl edit --force --full myservice.service 를 사용해서 create 하라고 나온다.

service unit file

/home/myhome/myserver-start.sh 를 실행하도록 하는 service unit file 이다. $MAINPID값은 실행된 service 에 대한 PID 값이다. 그래서 아래처럼 ExecStop 을 하면, 실행된 myserver-start.sh 의 pid 에 대해서 kill 하게 된다.

multi-user.target 은 run level2 로 보면 된다.(참고1, 참고2)

[Unit]
Description=MyApp Service
Wants=network.target mongod.service

[Service]
Environment="SECRET=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN"
Environment="ANOTHER_SECRET=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"
Type=simple
User=ec2-user
Group=ec2-user

WorkingDirectory=/home/myhome
ExecStartPre=
ExecStart=/home/myhome/myserver-start.sh
ExecStartPost=
ExecStop=/bin/kill -TERM $MAINPID
ExecReload=

[Install]
WantedBy=multi-user.target

Type=simple or Type=forking

Restart 를 사용할 때 주의할 점

Restart를 넣었더니, 일정시간(RestartSec=1) 이후에 restart 를 시도해 버려서 제대로 실행되지 못했다. (holdoff time over error 가 계속 발생했다.)

확인 테스트

sudo systemctl daemon-reload
sudo systemctl start myapp

booting 때마다 실행하려면

sudo systemctl enable myapp

실행하려는 app 이 shell script 인 경우

shell script 시작부분에 #!/bin/bash 를 넣어주면 된다.

#!/bin/bash

user mode

  • systemctl edit --user --force --full pms-fish
  • systemctl start --user pms-fish
  • systemctl status --user pms-fish
  • journalctl --user -u pms-fish.service -r

User, Group 을 지우고 사용하자. 안그러면, operation not permission error 가 발생한다.

[Unit]
Description=MyApp Service
Wants=network.target mongod.service

[Service]

# User=ec2-user
# Group=ec2-user

...

[Install]
...

See Also

  1. Run a Java Application as a Service on Linux | Baeldung on Linux : java application 에 대한 service 등록
  2. Chapter 1. Working with systemd unit files Red Hat Enterprise Linux 9 | Red Hat Customer Portal
  3. Deploying Spring Boot Applications: Spring Boot 을 systemd 로 실행할때

References

  1. How to Run a Linux Program at Startup with systemd
  2. How To Add Your Own Services to systemd For Easier Management

[컴][db] centos8 에서 mongodb v5, rpm 설치

yum install / rpm install / dnf install / mongodb install /

centos8 에서 mongodb v5 rpm 설치

$ dnf config-manager --add-repo https://repo.mongodb.org/yum/redhat/8/mongodb-org/5.0/x86_64/

/etc/yum.repos.d/repo.mongodb.org_yum_redhat_8_mongodb-org_5.0_x86_64_.repo가 생성된다. 이제 여기에 gpg key 를 추가하자.

[mongodb-org-5.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/5.0/x86_64/
enabled=1
gpgcheck=1
gpgkey=https://www.mongodb.org/static/pgp/server-5.0.asc

설치

$ dnf install -y mongodb-org

특정버전으로 설치하고 싶다면, 각 package 별로 version 을 적어주면 된다. 다음 링크를 확인하자.

  • https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/#install-the-mongodb-packages

See Also

  1. 쿠…sal: [컴][db] macos 에서 mongodb 설치
  2. 쿠…sal: [컴][DB] Windows7 64 bit 에서 MongoDB 설치하기

Reference

  1. Install MongoDB Community Edition on Red Hat or CentOS — MongoDB Manual

[컴] Nuxtjs 에서 client 에서 request 를 보낼때 referrer 를 보내지 않는 방법

nuxt 에서 referrer / nuxt client 에서 referer / nuxtjs referrer / no referer / no referrer

Nuxtjs 에서 client 에서 request 를 보낼때 referrer 를 보내지 않는 방법

서버쪽으로 보내는 모든 request 에 referrer 가 기본적으로 붙어서 간다.

  1. ajax call 에 의해 request 를 보내는 경우
  2. tag 에 의해 request 를 보내야 하는 경우(<script>, <img> 등)

ajax call 에서 referrer 삭제

ajax call 에 의해 request 를 보내는 경우는 간단하다. 만약 fetch 함수를 쓴다고 하면 아래처럼 option 에 referrer 에 대한 설정을 주면 된다.

const res = await fetch(url, {
  method: "GET", // POST, PUT, DELETE, etc.
  headers: {
    "Content-Type": "text/plain;charset=UTF-8",
    ...
  },
  referrer: "about:client", // or "" to send no Referer header,
  // or an url from the current origin
  referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin...
  ...
});

tag 에 의한 request 에서 referrer 를 삭제

이건 몇번의 테스트를 해봤다. nuxtjs 2.15.7 버전에서 테스트했다.

결과적으로 nuxt.config.js 에 meta tag 를 삽입해서 referrer 를 전송하지 않도록 했다.

nuxt.config.js 에 meta tag 를 삽입

이 방법으로 해결했다. 이 방법의 문제는 전 page 에서 보내지 않는다는 것이다. 그것만 빼면 큰 문제는 없어 보인다.

다른 실패한 방법들은 아래에 적었다.

// nuxt.config.js
export default {
  ...
  head: {
    ... 
    meta: [
      { charset: 'utf-8' },
      {
          name: 'referrer',
          content: 'no-referrer'
      }
    ],
    link: [
      { rel: 'stylesheet', href: '/css/daterangepicker.css' },
      ...
    ],
    script: [
      { src: '/js/jquery-3.5.1.slim.min.js' },
      ...
    ],
  },
  ...
}

실패한 방법 2가지

.vue component 에 넣는 방법 :

다음 처럼 개별 nuxt page 에 meta tag 를 추가할 수 있다. meta tag 는 삽입되긴 하지만, 실제로 referrer 는 계속 붙어서 날아갔다. 이때도

export default {
  ...
  /*
  ** Headers of the page
  ** See https://nuxtjs.org/api/configuration-head
  */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
        ...
      {
                    name: 'referrer',
                    content: 'no-referrer'
             }
    ],
    ...
  },
  ...
}

template 안에 tag 에 넣는 방법:

이 방법은 안됐다. referrerpolicy attribute 가 동작하지 않았다. 계속 referrer 가 header 에 같이 붙어서 갔다.

<template>
    ...
    <img :src="screenImageUrl" referrerpolicy="no-referrer">
  ...
</template>

<script>
    ...

    export default {
        head: {
            meta: [
                {
                    name: 'referrer',
                    content: 'no-referrer'
                }
            ],
        },
        ...
  
</script>
<style>
    ...
</style>

[컴][[db] mongodb 의 계정관련 보안

password restriction / restric password / 암호 제한 / limit / mongodb 에서 패스워드에 제한을 걸 수 있나?

mongodb 의 계정과 보안

패스워드 제한

결론적으로 가능하지 않다.

ref. 1 에서 아래와 같은 사항에 대한 mongodb 기능이 있는지 물어본다. ref. 1 에서는 LDAP 를 이용하는 대안을 이야기해 준다. 하지만 이 대안도 Enterprise 버전에서만 가능하다고 한다.

현재(2022-05-31) SERVER-7363 처럼 요청사항은 존재하지만, 아직 unresolved 이다.

  1. 최소길이 제한
  2. 특정 password 문자열 제한
  3. 최소한 포함해야 하는 문자
  4. 들어가면 안되는 단어들
  5. 이전에 사용했던 암호

로그인 횟수 제한

일정 로그인 시도가 실패하면 계정을 lock 하는 방법도 제공하지 않는다.(ref. 3)

session time out

접속시점에 일정시간이 지나면 timeout 하게 하는 option 을 넣을 수 있다. 하지만 이것은 mongo server 에서 제공하는 기능이 아니라, mongo driver 단에서 제공하는 기능이다. 그래서 client 에서 접속시 idletime 을 set 하는 것 만 가능하다.

audit 기능

Enterprise version 에서만 지원하는 듯 하다. 아래 링크를 보면 community version 에 audit 기능을 넣은 것도 있긴 한듯 하다.

audit log 를 file, syslog, console 로 출력할 수 있다. 그리고 format 도 정할 수 있다. JSONBSON 이 가능하다.

# mongod.conf
auditLog:
   destination: file
   format: BSON
   path: /var/lib/mongodb/audit_mongodb.bson

기타 사용할만한 log

  1. clientConnections

mongosh client 가 db 에 접속할 때마다 clientConnections collection 에 doc 를 만들어서 접속시간 로그를 남길 수 있다.

<your-home-directory>/.mongoshrc.js

db.clientConnections.insertOne( { connectTime: ISODate() } )

를 추가

  1. db.setProfilingLevel(2)

level 2 인 경우 profiler 가 모든 operation들의 데이터를 모으게 된다.

See Also

  1. 쿠…sal: [컴][db] mongodb 를 production 에서 배포시 확인할 사항

References

  1. Configure strong password user - Ops and Admin - MongoDB Developer Community Forums
  2. [SERVER-7363] Allow users to set specify a password validation policy - MongoDB Jira
  3. Limit failed logins from MongoDB shell - Ops and Admin / Installation & Upgrades - MongoDB Developer Community Forums, 2021-06