IL-0197 Palark · 2021-05-27 (게시일)kubernetes
Palark 는 내부 운영 인프라 가운데 중요도가 낮은 부분의 Kubernetes 를 1.15 에서 올리려 했다. 이 부분의 퍼시스턴트 볼륨은 Rook operator v0.9 가 만들었다. 이 오래된 operator 의 Helm 릴리스에 폐기된 API 버전의 리소스가 들어 있어 클러스터를 업그레이드할 수 없었다. 팀은 실행 중인 클러스터에서 Rook 을 올리지 않고 손으로 걷어 내기로 했고, 데이터를 Rook 이 관리하지 않는 StorageClass 로 옮기고 있었다.
kibana-production 네임스페이스의 3노드 Elasticsearch 는 다운타임 없이 옮기기로 했다. 팀은 차트의 volumeClaimTemplates 에서 StorageClass 를 rbd 에서 high-speed 로 바꾸고, StatefulSet 을 --cascade=false 로 지운 뒤 다시 배포해 레플리카를 6개로 늘렸다. 리밸런싱 한도를 올리고 cluster.routing.allocation.exclude._ip 로 옛 노드 세 개에서 샤드를 모두 빼냈다. 그다음 레플리카 2, 1, 0 의 PVC 와 파드를 하나씩 지우고 high-speed PVC 를 손으로 만들어 새 볼륨을 연결했다.
elasticsearch-2 와 elasticsearch-1 은 기존 클러스터에 다시 합류했지만 elasticsearch-0 은 합류하지 않았다. 차트의 StatefulSet 이 cluster.initial_master_nodes 를 0번 파드 하나로 고정해 두었기 때문이다. 이 값은 클러스터를 처음 만들 때 마스터 후보를 정한다. 0번 파드가 빈 볼륨으로 시작하자 클러스터 부트스트랩이 새로 시작됐고, elasticsearch-2 에 있던 기존 마스터는 무시됐다.
팀은 데이터를 쓰는 워커를 멈춰 데이터가 두 클러스터로 갈라지는 것을 막았다. 파드 라벨을 떼어 Service 에서 빼는 방법은 쓰지 않았다. Elasticsearch 는 이 Service 로 클러스터 멤버를 찾기 때문에 파드를 한 번 빼면 다시 합류시킬 수 없었다. 그 뒤 initial_master_nodes 설정을 고치고 디스커버리 Service 와 0번 PVC 를 지웠다가 다시 만드는 순서로 노드 6개를 한 클러스터로 모았다. 마스터 선출은 문제없이 끝났고 green 이 아닌 인덱스는 0개였다.
옛 노드 세 개를 하나씩 새 StorageClass 의 볼륨으로 바꾸다가 마지막으로 elasticsearch-0 파드를 지우고 다시 띄웠다. elasticsearch-0 안에서 `_cat/nodes` 를 조회하자 자기 자신 하나만 마스터로 나왔다.
같은 시각 elasticsearch-1 안에서 같은 조회를 하면 elasticsearch-1, elasticsearch-2, elasticsearch-3, elasticsearch-4, elasticsearch-5 다섯 노드가 보였고 마스터는 elasticsearch-2 였다. 클러스터가 둘로 갈라진 스플릿 브레인 상태였다. 새로 들어오는 데이터는 두 클러스터 중 아무 쪽에나 무작위로 쓰이고 있었다.
엔지니어는 먼저 설정을 다시 확인했지만 ConfigMap 에는 이상이 없었다. 원인은 Helm 차트의 es-data-statefulset.yaml 에 있었다. 차트가 StatefulSet 에 cluster.initial_master_nodes 환경 변수를 0번 파드 이름 하나로 박아 넣고 있었다.
복구 중에 디스커버리 Service 와 0번 파드의 PVC 를 지운 상태로 롤링 업데이트가 끝나자 elasticsearch-0 은 PVC 가 없어 Pending 이 됐다. 나머지 노드는 서로를 찾지 못했고, elasticsearch-1 에서 `_cat/nodes` 를 조회하면 Open Distro Security not initialized. 라는 메시지가 나왔다.
증상 ? 스플릿 브레인
마지막으로 0번 파드를 교체한 뒤 엔지니어가 elasticsearch-0 과 elasticsearch-1 안에서 각각 `_cat/nodes` 를 조회했다. 두 결과가 서로 다른 노드 목록과 다른 마스터를 보여 스플릿 브레인을 알아챘다.
운영 데이터를 옮길 때는 설정 오류, 데이터센터 장애, 네트워크 단절처럼 무언가 잘못될 수 있다는 전제로 준비해야 한다. 이번에 쓴 다운타임 없는 절차는 예상하지 못한 문제에 약했다. 팀은 ConfigMap 을 꼼꼼히 확인했지만 Helm 차트 템플릿이 StatefulSet 에 넣는 환경 변수는 보지 않았다. cluster.initial_master_nodes 가 파드 하나로 고정된 상태에서 그 파드가 빈 볼륨으로 다시 뜨면 기존 클러스터에 합류하지 않고 새 클러스터를 만든다.
같은 원인 · 공통 영구 볼륨과 스테이트풀셋