Blog

2017.03.03 - 번역 - Memory Issues We'll Remember ...

drscg 2019. 1. 7. 11:01

Elastic Cloud is on the tail end of eliminating a mix of memory issues that has caused problems for a lot of low-memory nodes, and in some rare cases even large nodes. Following the memory problems, we experienced connectivity issues on a handful of servers in eu-west-1 that affected any cluster with at least one node on these impacted servers.

Elastic Cloud는 많은 memory가 작은 node와 일부 드문 경우에 큰 node에서도 문제를 야기한 memory 문제를 제거하는 작업의 막바지에 있다. memory 문제에 이어 eu-west-1의 소수 server에서 연결 문제가 발생하여, 영향을 받는 server에서 하나 이상의 node가있는 cluster에 영향을 미쳤다.

After investigation, we found that there were a number of small problems that, when combined, created larger issues.  This post attempts to summarise the breadth of things that all contributed to the problems, and the extent of testing we’re ramping up to avoid repeating our mistakes. You might learn a thing or two about Linux, Docker, memory accounting, glibc, JVM-settings, Netty, and Elasticsearch as well. We sure have.

조사한 결과, 더 큰 문제를 만들었던 여러 가지 작은 문제가 있음을 발견했다. 이 게시물은 문제의 원인 모두를 요약하고, 우리가 실수를 되풀이하지 않도록 테스트 범위를 요약하려고 한다. Linux, Docker, memory 통계, glibc, JVM 설정, Netty 및 Elasticsearch에 대해서도 상당한 지식을 배울 수 있을 것이다. 우리는 확실히 가지고있다.

Scope of the memory problem

Elastic Cloud runs tens of thousands of Elasticsearch nodes. These nodes run on servers with memory ranging from 30-244 GiB and 4-32 cores, which we call our “allocator pool”. A single server can host a lot of Elasticsearch nodes, each running in containers getting a reserved slice of memory and a (boostable) slice of the CPU. The servers run a small number of different kernel and Docker versions, and we’ll get back to why.

Elastic cloud는 수만개의 Elasticsearch node를 실행한다. 이들 node는 30 - 244 GiB memory 및 4 - 32 core를 가진 server에서 실행되며, 이를 "allocator pool" 이라고 한다. 단일 server는 많은 Elasticsearch node를 hosting할 수 있으며, 각 node는 예약된 memory 조각과 CPU(boostable)를 가진 container에서 실행된다. server는 소수의 다른 kernel과 Docker 버전을 실행하고 있는데, 그 이유를 다시 살펴보겠다.

The memory issues can be categorised as:

memory 문제는 다음과 같이 분류할 수 있는데

  • High memory pressure from Elasticsearch causing increased GC-load and latencies, and eventually the JVM running out of heap space. There were several things that could lead to this.
    Elasticsearch로 인한 높은 memory 부담으로 인해, GC-load 및 대기 시간이 증가하고, 결국 JVM의 heap 공간이 부족해진다. 이 문제를 일으키는 몇 가지 사항이 있다.
  • Growth in the JVM’s non-heap memory, eating away memory intended for page cache and possibly causing kernel-level OOM-reaping.
    JVM의 hepa이 아닌 memory가 증가하여, page cache용 memory를 소모하여, kernel 수준의 OOM이 발생할 수 있다.
  • Because we used long-running containers, kernel memory was being incorrectly accounted, causing the kernel to kill processes in a container almost as soon as the node started.
    오랜 시간 동안 실행되는 container를 사용했기 때문에, kernel memory가 잘못 계산되어, node가 시작하자마자 kernel이 container의 process를 만든다.

All of these could affect a single node, but just one would be sufficient to make the node unreliable.

이 모든 것이 단일 node에 영향을 미칠 수 있지만, node를 신뢰할 수 없게 만드는 데 충분하다.

Whenever a process is restarted due to memory issues, we log the event and notify the account owner and operational contacts (rate limiting to max one email per 6 hours). While Elasticsearch keeps getting more careful against running out of memory, it’s not uncommon for an overloaded cluster to run out of memory.

memory 문제로 인해 process가 다시 시작될 때마다, event를 기록하고 계정 소유자와 운영 담당자에게 알린다(6시간당 email을 최대 1개까지 전송하도록 제한). Elasticsearch가 memory 부족한 상태에서 계속 주의를 기울이지만, 과부하 상태의 cluster에서는 memory가 부족한 것은 드문 일이 아니다.

Thus, we know that in the period we had the most issues, approximately 1% of the running clusters were affected. That’s a lot of clusters, but as most mitigations affect every cluster and some required restarting Elasticsearch and/or upgrading, we needed to proceed carefully to not cause any problems to the majority of nodes not having any issues.

이와 같이, 가장 많은 문제가 발생했던 시기에, 실행 중인 cluster의 약 1%가 영향을 받았다는 것을 알고 있다.이것은 많은 cluster이지만, 대부분의 migration이 모든 cluster에 영향을 미치고, 일부는 Elasticsearch를 재시작하거나 upgrade를 해야 하기 때문에, 문제가 없는 대부분의 node에 어떠한 문제도 발생하지 않도록 신중하게 진행해야 했다.

On environment variety

Elastic Cloud is based on the acquisition of Found, which launched a hosted Elasticsearch service in 2012. Having managed lots of containers since before Docker even existed and container schedulers were buzzwords, we have a lot of experience in how container-features can cause Linux to crash, or sometimes worse, cause nodes to slow down to a crawl. Even the 4.4 kernel series in Ubuntu LTS recently had OOM-issues.

Elastic Cloud는, 2012년에  Elasticsearch hosting service를 시작한, Found의 인수에 기반을 두고 있다. Docker가 존재하기도 전부터 많은 container를 관리해 왔고, container scheduler가 유행(buzzword)이었기 때문에,  container 기능으로 인해 Linux가 crash되거나 때로는 더 나빠지고, 또는 node가 느려지는 경우가 많았다. Ubuntu LTS의 4.4 kernel series조차도 최근에 OOM 문제가 있었다.

With the exception of security patches, we’re typically very slow when it comes to upgrading Linux and Docker: issues with these components can severely hurt our reliability or create significant ops workload  to clean up containers that are not being created or destroyed correctly. Docker is a fast-moving technology, and generally only the most recent versions receive security patches. This makes it important to keep up, and we were gradually increasing the numbers of servers running more recent versions as we gained confidence in it.

보안 patch를 제외하면, Linux 와 Docker룰 upgrade할 경우 일반적으로 매우 느리다. 이러한 구성 요소의 문제로 인해, 우리의 신뢰성이 심각하게 훼손되거나, 올바르게 생성되지 않거나 제대로 삭제되지 않는 container를 정리하기위한 중요한 작업 부하가 발생할 수 있다. Docker는 빠른 속도로 움직이는 기술이며, 일반적으로 최신 버전에게만 보안 patch가 제공된다. 계속 유지하는 것이 중요한 것은 이 때문이다. 최신 버전을 실행하는 서버의 수를 점차 늘려 가면서 자신감을 얻었다.

Our server fleet is also composed of servers of varying size. Smaller servers limit the blast radius if there’s an issue, while larger servers are necessary to host the beefier clusters. Depending on available capacity during provisioning, a small 1GB node can end up on a massive server. A node will be allotted the same CPU time regardless of the numbers of cores available, so performance differences are small between servers. There are settings that rely  on the core count, however, and we didn’t properly cover all the bases of settings that look at cores. This could pose problems for a small node landing on a large server.

우리 server들은 또한 다양한 크기의 server로 구성되어 있다. 문제가 발생하면, server 수가 작을수록 영향이  제한되고, 많을수록 더 큰 cluster의 hosting이 필요하다. 구축 중에, 사용 가능한 용량에 따라, 1 GB 의 작은 node가 거대한 server에 배치될 수 있다. 사용 가능한 core 수에 관계 없이 node에 동일한 CPU time이 할당되므로, server 간의 성능 차이가 적다. core 수에 따라 달라지는 설정이 있지만, core를 보는 모든 설정의 기반을 제대로 다루지 않았다. 이는 큰 server에 있는 작은 node에 문제가 될 수 있었다.

Having run the 3.19-series of the kernel for a long time without issues, it took some time before we suspected it could be the issue. This will be described more later, but we’ve found that  Docker ≥1.12 has problems on Linux < 4.4.

오랜 시간 동안, 문제 없이 kernel 3.19 series를 실행해 왔기 때문에, 문제가 될 수 있다고 생각하기까지는 다소 시간이 걸렸다. 나중에 더 자세히 설명하겠지만, Docker 1.12 이상은 Linux 4.4 이하에서 문제가 있다.

To start with, we turned every stone related to Elasticsearch and the JVM.

우선 Elasticsearch 및 JVM과 관련된 모든 stone을 조정했다.

Elasticsearch, Lucene, and the JVM

Elasticsearch needs both heap space and page cache to perform well. A cluster with 1 GB memory on Cloud gets a little less than half the memory for heap space, to leave memory for page cache and non-heap JVM usage.

Elasticsearch가 잘 동작하려면 heap 공간과 page cache가 모두 필요하다. Cloud에 1GB memory를 가진 cluster는 heap 공간에 절반도 안 되는 memory를 사용하고, page cache와 heap이 아닌 JVM 사용을 위해  memory를 남겨둔다.

There were a few issues in the early 5.0s that could cause a small node to quickly OOM as segments grew large enough to consume the available buffer space the S3 snapshotter could use, which was changed to 100 MB. This would be about 20% of a 1GB nodes available space, quickly leading to issues. That was quickly identified and remedied and every cluster was upgraded to apply the fix.

5.0 초기에는, S3 snapshotter가 사용할 수있는 사용 가능한 buffer 공간을 소비 할 수있을만큼 segment가 커지면서, 작은 node가 빠르게 OOM 될 수 있는 몇 가지 문제가 있었다(100 MB로 변경됨). 이것은 1GB node에서 사용 가능한 공간의 약 20 %가 될 것이므로, 급한 문제가 되었다. 이를 신속하게 확인하고 개선했으며, 모든 cluster는 수정 사항을 적용하여 upgrade했다.

However, we still saw Elasticsearch 5.x use a lot more non-heap memory than 2.x, which we eventually attributed to Elasticsearch 5 upgrading to Netty 4. Disabling Netty’s pooled allocator and recycler further reduced non-heap memory.

그러나 우리는 여전히 Elasticsearch 5.x가 2.x보다 훨씬 더 많은 heap이 아닌 memory를 사용하는 것을 알게 되었다. 결국 Elasticsearch 5는 Netty 4로 upgrade 되었다. Netty의 pooled allocator 및 recycler를 비활성화하면 heap이 아닌 memory가 더 많이 감소한다.

That still wasn’t enough, some nodes kept on OOM-ing – but now by the kernel’s OOM-reaper, which triggers if a process in a container with limited memory exceeds its memory. Increased non-heap usage would normally result in performance reductions, and not processes getting killed by the kernel OOM-reaper. So we knew we still had an issue.

여전히 충분하지 않았고, 일부 node는 OOM이 계속 발생하였는데,이제는 kernel의 OOM-reaper에 의해서. 제한된 memory를 가진 container의 process가 memory를 초과 할 경우 발생한다. 증가된 non-heap 사용은 일반적으로 성능 저하를 가져오고, process는 kernel OOM-reaper에 의해 종료되지 않는다. 그래서 여전히 문제가 있다는 것을 알게 되었다.

We found more tweaks that improved the memory usage issues:

memory 사용 문제를 개선하는 추가적인 해결 방법을 발견했다.

  • Upgrading to JVM 8 turned tiered compilation on by default, something not really necessary for Elasticsearch. This would eat up 48MB memory for additional code caches.
    JVM 8로 upgrade하면 기본적으로 계층화된 compile이 활성화되어 있지만, 실제로 Elasticsearch에는 필요하지 않다. 이렇게 하면 추가 code cache에 48 MB의 memory를 소모된다.
  • glibc’s memory allocator could waste a lot of memory on servers with many cores. A colleague coming in from Prelert has described the interactions of the JVM and virtual memory on Linux as they relate to that change, which could waste a lot of memory for a small node running on a large server.
    glibc의 memory allocator는 많은 core를 가진 server에서 많은 memory를 낭비할 수 있다. Prelert 출신의 동료가 Linux에서 JVM과 virtual memory의 상호작용에 대해서 설명해 줬는데, 이들 문제가 변경사항과 연관되어 있으므로, 큰 server에서 실행되는 작은 node는 많은 메모리를 낭비 할 수 있다.
  • There were a number of small fixes in Elasticsearch between 5.0.0 and 5.2.2 that helped with memory usage.   For example, not closing a stream could leak memory.
    5.0.0 과 5.2.2 사이의 Elasticsearch에는 memory 사용에 도움이되는 몇 가지 작은 수정 사항이 있었다. 예를 들어, stream을 닫지 않으면 memory가 누수될 수 있다.
  • We reduced the number of JVM allocated GC threads.
    JVM의 할당 GC thread 수를 줄였다.

We are also expanding our test suites to include tests that specifically address long running containers under load, and measure memory usage.

또한 우리는 부하가 걸린 상태에서, 장기간 가동되는 container를 구체적으로 다루고, memory 사용량을 측정하는 테스트를 포함하도록 테스트를 확장하고 있다.

Kernel and Docker bugs

After much debugging, we found that specific combinations of kernel version and Docker version create a major problem with memory accounting.  In our case, combining kernel version 3.19 with Docker version 1.12 exposed this bug.  We had been running the 3.19 kernel for a long time, and it wasn’t immediately obvious that the kernel was a contributing factor to memory issues.

많은 debugging 후에, kernel 버전과 Docker 버전의 특정 조합이 메모리 통계에 큰 문제를 일으키는 것을 발견했다. 우리의 경우, kernel 3.19와 Docker 1.12를 조합하면 이 bug가 발생한다. 우리는 오랜 시간 동안 3.19 kernel을 운영하고 있었고, kernel이 memory 문제의 원인이라는 것이 당장은 명백하지 않았다.

The core of the issue is that Docker 1.12 turns on kmem accounting.  In 3.x versions of the Linux kernel, this causes problems because of a slab shrinker issue.  In a nutshell, this causes the kernel to think that there is more kernel memory used than there actually is, and it starts killing processes to reclaim memory.  Eventually it kills the JVM, which obviously hurts the cluster.  There is a fix for the slab shrinker issue in kernel versions >= 4.0.  Our testing led us to combine kernel version 4.4 with Docker 1.12.  This combination solved the kmem accounting problems.

이 문제의 핵심은 Docker 1.12가 kmem accounting을 활성화하는 것이다. Linux kernel 3.x 에서, slab shrinker issue로 인해 문제가 발생한다. 간단히 말해서, 이것은 kernel이 실제로 사용된 것 보다 더 많은 사용된 kernel memory가 사용되었다고 생각하게 하고, memory를 회수하기 위해 process를 종료하기 시작한다. 결국 그것은 JVM을 종료시키고 명백하게 cluster를 망친다. kernel 4.0 이상에는 slab shrinker issue에 대한 fix가 있다. 테스트에서, 우리는 kernel 4.4와 Docker 1.12를 조합했다. 이 조합은 kmem accounting 문제를 해결했다.

Looking forward

As you can see, there were a number of issues that combined to create a kind of “perfect storm” of memory issues.  We are now at a point where we’re convinced we’ve identified all of the major issues and are a long way toward addressing them throughout our fleet.

보다시피, memory 문제는 일종의 "최악의 상황" 이 되기 위해, 결합된 여러 가지 문제가 있었다. 우리는 이제 우리는 주요 문제 모두를 파악했으며, 이들 문제를 해결할 수 있다고 확신하는 시점에 있다.

The total number of affected clusters in our SaaS environment was around 1%.  While this seems like a small number, we’re committed to reaching out to affected customers and offering explanations and help.  Although this issue affected clusters of all sizes, smaller clusters were the fastest to be affected due to the already limited amount of memory.  Since trial customers tend to run smaller clusters, we’ll be contacting trial customers who were active during the affected time period and offering new or extended trials.

SaaS 환경에서 영향을 받는 cluster의 총 수는 약 1%이다. 이 수치는 적을지 몰라도, 영향을받는 고객에게 다가 가고 설명과 도움을 제공하기 위해 최선을 다하고 있다. 이 문제는 모든 규모의 클러스터에 영향을 주었지만, 이미 제한된 메모리로 인해, 더 작은 cluster가 가장 빨리 영향을 받았다. 평가판 고객은 소규모 cluster를 실행하는 경향이 있으므로, 영향을 받는 기간 동안 활동 중이며, 평가판을 새로 시작하거나 연장하는 고객에게 연락할 것이다.

원문 : Memory Issues We'll Remember