쿠버네티스에서 JSON 데이터 처리를 위한 JSONPath 사용법
수십, 수백 개의 노드와 리소스가 돌아가는 상용 환경에서 내게 필요한 정보만 골라내어 확인할 수 있는 방법이 있다. 바로 JSONPath를 이용하는 것이다. 이번 글에서는 JSONPath의 기본 문법과 사용법을 알아보고, 이를 쿠버네티스 환경에서 활용하는 방법을 소개한다.
JSONPath란?
JSONPath는 JSON 포맷의 데이터 구조를 손쉽게 처리할 수 있도록 고안된 표현식이다. 독일의 Stefan Gössner 교수가 2007년에 최초로 제안했고, 이때 제안된 문법이 큰 변화 없이 지금도 쓰이고 있다.
당시에는 자바스크립트와 PHP, C#로 구현되었으며, 현재는 Java, PHP, Golang 등의 언어로 된 구현 프로젝트가 깃허브에 올라와 있다.
JSONPath 표현식을 직접 다뤄볼 수 있는 웹사이트도 있다. 아래의 본문 내용을 참고하면서 직접 써보면 도움이 될 것이다.
JSONPath 사용법
기본 문법 요소
JSON의 데이터는 키-값 쌍으로 이루어진 요소들을 객체(object; {}
로 표현) 또는 배열(array; []
로 표현)로 묶어놓은 형태를 가진다. 데이터 구조가 간단하다 보니 이를 처리하는 JSONPath의 기본 문법 요소도 비교적 간단하게 구성되어 있다. 자주 쓰이는 문법 요소들은 다음과 같다.
$
: 루트 노드. JSONPath의 모든 표현식은 이것으로 시작된다.@
: 현재 노드. 아래에서 소개할 조건부 필터 표현식에서 사용된다..
: 하위 노드..
: 중첩된 전체 하위 요소들(nested descendants)[]
: 배열 인덱스*
: 모든 요소와 매칭되는 와일드 카드?(boolean expression)
: 조건부 필터 표현식
아래에서부터는 위의 문법 요소들을 어떻게 사용하는지 안내할 것이다. 이 포스팅에 쓰인 모든 JSON 데이터 예제들은 Stefan Gössner의 JSONPath 소개글에서 가져왔다.
객체(object)를 다루는 문법
{
"bicycle": {
"color": "red",
"price": 19.95
}
}
JSON 객체에서 현재 노드와 하위 노드는 .
로 구분하고, 최상단에 위치한 가상의 노드로서 $
기호를 경로에 함께 표현해줘야 한다. 이에 유의하여 각 표현식별 결과를 살펴보면 다음과 같다.
표현식 | 출력 |
---|---|
$.bicycle |
[{"color": "red", "price": 19.95}] |
$.bicycle.color |
["red"] |
주의할 점이 있다. JSONPath에서 모든 Query의 결과물은 반드시 배열 형태로 리턴된다. 즉, $.bicycle.color
를 호출한 결과값은 "red"
가 아니라 ["red"]
가 된다.
배열(array)을 다루는 문법
JSON 데이터에서 배열에 포함된 n
개의 원소들은 기재된 순서에 따라 0
에서 최대 n-1
까지의 인덱스 값을 갖는다. 여기서 각 원소를 호출하는 방법은 다음과 같다.
- 여러 원소들을 함께 지정하려면
[0,3]
처럼 쉼표(,
)를 이용한다. - 모든 원소들을 지정하려면 와일드카드 문자(
*
)를 쓴다. [시작값:끝값+1]
형태로 범위를 지정할 수 있다.[0:3]
으로 입력하면0
에서2
까지의 인덱스값을 가진 원소들이 선택된다.[시작값:끝값+1:스텝]
형태로 범위 및 스텝(건너뜀) 지정도 가능하다.
역순으로도 원소를 선택할 수 있다. 이때에는 -1
부터 -n
까지의 번호를 사용 가능하다. 단, 역순 번호 만으로 호출할 경우 특정 환경에서는 동작하지 않을 수 있으므로, 가급적 '범위' 포맷으로 지정해 주어야 한다.
- 배열의 마지막 원소를 지정할 땐
[-1:0]
또는[-1:]
와 같이 입력한다. - 마지막 3개의 원소는
[-3:]
와 같이 지정하면 된다.
위의 내용을 참고하여 아래의 예제에 대한 각 표현식별 결과를 살펴보자.
{
"book": [
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
]
}
표현식 | 출력 |
---|---|
$.book[0:1].isbn |
["0-553-21311-3"] |
$.book[-1:].title |
["The Lord of the Rings"] |
$.book[*].category |
["fiction", "fiction"] |
조건부 필터 다루기
JSONPath의 핵심 기능이 바로 여기서 소개할 조건부 필터 표현식이다. TRUE
또는 FALSE
의 결과만을 갖는 boolean
표현식으로 원하는 조건에 맞는 데이터만 빠르게 골라낼 수 있다.
JSONPath에서 조건부 필터는 ?(boolean expression)
형태로 표현된다. 이 조건문에는 기본적인 논리 연산자(==
, !=
, >
, <
, >=
, <=
) 사용이 가능하다. 환경에 따라 in
, nin
등의 기타 연산자가 추가로 구현된 경우도 있을 수 있다.
이제 아래의 예제에 대한 각 표현식별 결과를 살펴보자.
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
표현식 | 출력 |
---|---|
$..book[?(@.price < 10)].title |
["Sayings of the Century", "Moby Dick"] |
$.store..[?(@.category == "reference")].author |
["Nigel Rees"] |
$.store.[?(@.category == "reference")][title,author] |
["Sayings of the Century", "Nigel Rees"] |
$.store.[?(@.color == "red")].price |
[19.95] |
쿠버네티스에서의 JSONPath 사용법
쿠버네티스에서는 클러스터 컨픽 내용이나 노드, 리소스 정보를 조회할 때 JSONPath 템플릿을 사용할 수 있다. kubectl get
기본 명령 만으로는 접근할 수 없는 정보를 알아볼 수도 있고, 필요에 따라 출력 결과물을 원하는 조건에 따라 정렬시킬 수도 있다. 수십 개의 노드를 동시에 관리해야 하는 상용 환경에서도 내게 꼭 필요한 정보만 걸러내어 확인할 수 있게 해주는 것이 쿠버네티스에서의 JSONPath 템플릿이다.
아래의 내용은 Mumshad Mannambeth의 Certified Kubernetes Administrator (CKA) with Practice Tests 강의에서 소개된 사용법을 간략하게 요약한 것임을 밝힌다.
기본 문법
우선 kubectl get <리소스> -o json
명령의 출력 결과물에 익숙해져야 한다. 조회 명령의 JSON 포맷 데이터 구조를 알아야 JSONPath를 제대로 사용할 수 있다.
쿠버네티스의 get
명령에 JSONPath를 붙일 때엔 -o jsonpath='{표현식}'
형태로 사용한다. 예를 들어 각 파드들을 구성한 이미지 정보만 조회하고 싶다면, 다음과 같이 입력하면 된다.
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'
- 하나의 JSONPath 표현식은 중괄호(
{}
) 안에, 전체 표현식들의 조합은 홑따옴표(''
)나 겹따옴표(""
) 안에 묶여 있어야 한다. - 표현식에서
root
에 해당하는$
문자는 생략 가능하다. 생략할 경우에는.
로 시작한다.
여러 개의 표현식을 조합해서 여러 항목의 정보를 한 번에 조회할 수도 있다. 아래 예시는 각 노드의 이름과 할당된 CPU 수를 조회하는 명령어다.
kubectl get nodes -o jsonpath='{.items[*].metadata.name}{"\n"}{.items[*].status.capacity.cpu}'
# 위 명령어의 출력 결과(예시)는 아래와 같다.
# master01 worker01
# 4 4
- 각 표현식의 구분은
{}
로 한다. {"\n"}
로 줄 바꿈을,{"\t"}
로 탭 추가를 할 수 있다.
반복 목록 만들기
쿠버네티스에서는 JSONPath 표현식에 range
와 end
오퍼레이터를 결합하여 반복되는 목록을 생성시킬 수 있다.
위에서 살펴봤던 노드별 이름 및 CPU 수 조회 명령을 반복 목록의 형태로 구현해보자. 아래는 의사코드와 이를 JSONPath와 range
오퍼레이터로 구현한 예시다.
# 의사코드
FOR EACH NODE
PRINT NODE NAME \t PRINT CPU COUNT\n
END FOR
# JSONPath 표현식
'{range .items[*]}
{.metadata.name}{"\t"}{.status.capacity.cpu}{"\n"}
{end}'
이렇게 얻어낸 표현식을 kubectl get -o jsonpath
와 결합해보자.
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.capacity.cpu}{"\n"}{end}'
# 위 명령어의 출력 결과(예시)는 아래와 같다.
# master01 4
# worker01 4
출력 결과에 칼럼별 이름 붙이기
만약 각 컬럼에 이름을 붙이고 싶다면? 아래와 같이 -o jsonpath
대신 -o custom-columns
를 붙이면 된다. 여러 개의 컬럼은 쉼표(,
)로 구분하며, 중괄호({}
)는 불필요하다. 기본 문법은 다음과 같다.
kubectl get <리소스> -o custom-columns=<NAME1>:<PATH>,<NAME2>:<PATH>,...
위 문법을 따라서, 앞서 살펴봤던 노드 이름 및 CPU 수 조회용 예시에 칼럼별 이름(NAME
, CPU
)을 붙여보자.
kubectl get nodes -o custom-columns=NODE:.metadata.name,CPU:.status.capacity.cpu
# 위 명령어의 출력 결과(예시)는 아래와 같다.
# NODE CPU
# master01 4
# worker01 4
위의 예시 명령문을 잘 살펴보면 JSONPath 표현식 부분에 .items[*]
가 생략되어 있음을 알 수 있다. kubectl get
명령 자체가 .items[*]
경로의 데이터 조회를 전제하고 있기 때문이다. 때문에 -o custom-columns
를 쓸 때에는 .items[*]
이후의 경로만 적어주면 된다.
출력 결과 정렬하기
JSONPath는 조회 결과의 정렬에도 쓰일 수 있다. --sort-by=<PATH>
형식을 따르며, 역시 중괄호({}
)는 불필요하다. 이때 정렬은 오름차순(Ascending)으로 이루어진다. 아래 예시들은 kubectl 치트시트에 소개된 것들이다.
# 서비스 목록을 이름 순으로 정렬하여 출력
kubectl get services --sort-by=.metadata.name
# 파드 목록을 재시작 횟수로 정렬하여 출력
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
# PV 목록을 용량에 따라 정렬하여 출력
kubectl get pv --sort-by=.spec.capacity.storage
위의 예시들에서도 JSONPath 표현식 부분에 .items[*]
가 생략되어 있다. --sort-by
를 쓸 때에도 표현식에서 .items[*]
이후의 경로부터 적어줘야 한다는 점에 주의하자.