DevOps/Kubernetes

[k8s] kubernetes는 YAML 친화적입니다. `yq`를 사용해서 데이터 파싱, Solve Error "mapping values are not allowed in this context"

JSON(JavaScript Object Notation) 데이터 포맷은 가장 널리 사용되고, 아주 강력합니다. 하지만 kubernetes에서 JSON보다는 YAML을 사용해 구성파일을 작성하거나, describe 명령의 출력물 자체도 YAML 포맷을 띄고 있습니다.


YAML에서는 jq가 아닌 yq

JSON 포맷을 지원하는 보통에서는 jq 커맨드라인 유틸리티를 많이 사용합니다. JSON 데이터에서 필요한 정보를 추출하거나 변형하기 위해서 프로그래밍 언어에서는 데이터 파싱 + 조작의 번거로운 과정을 거치지만, jq에서는 간단하게 커맨드라인을 통해 작업이 가능합니다.

많이 사용되는 jq 문법의 기초를 다질 수 있는 부분은 다음 글을 참조하면 좋습니다. 👉44bits - 커맨드라인 JSON 프로세스 jq

하지만, Kubernets에서 JSON 포맷을 다룰 수 있는부분은 (현재 글쓴이가 테스트했던 범위내에서) kubectl get ... -o json 정도 밖에 그치지 않는다. Kubernets의 공식문서에서 구성 모범 사례에서 살펴볼 수 있듯이 JSON보다는 YAML을 통해 보다 더 사용자 친화적인 성향으로 구성합니다.

이러한 YAMl 포맷의 데이터의 정보를 추출하거나 변형하기 위해서는 yq를 이용합니다. 현재 jq를 100% 대체할만큼 개발되지 않았지만, 가장 많이 사용되는 operations과 functions에 대해서는 지원하고 있고, 추후에 추가될 예정이라고합니다. 👉yq 공식 site

yq를 설치는 다양한 환경에서 지원되는데, 글쓴이의 환경은 MacOS에서 homebrew 환경을 통해 패키지를 관리하기 때문에 homebrew를 통해 설치합니다.

$ brew install yq

혹은 일반적으로 wget 명령어로 다운을 받고 tar로 압축해제해서 사용하는 방법도 있습니다.

$ wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - |\
  tar xz && mv ${BINARY} /usr/bin/yq

가장 간단한 사용법으로는 YAML 포맷에서 필요한 데이터 추출을 예시로 다음과 같이 간단한 형태의 YAML이 있을시에

ab.yml

a:
  b: hello world

2 depth의 b 값을 얻기위해서는 다음과 같은 expression을 사용하면됩니다.

$ yq e '.a.b' ab.yml
hello world

혹은 cat이나 어떤 명령어의 표준입력을 받기위해서는 - 와 함께 사용합니다.

$ cat ab.yml | yq e '.a.b' -
hello world



kubectl describe 명령 출력에 yq 사용시 겪었던 에러


kubectl describe을 통해 자원에 대해서 간단히 파싱을 하려면 다음과 같은 형태로 생각해 볼 수 있습니다. (app=nginx라는 레이블을 가진 간단한 nginx Pod를 생성 후 테스트합니다.)

$ kubectl describe pod/nginx-label | yq e -
Error: yaml: line 20: mapping values are not allowed in this context
Error: yaml: line 20: mapping values are not allowed in this context

직역하자면, 이 context에서 값을 매핑하는게 허락되지 않는다고 합니다. 어떤 의미일까요? 분명 YAML 형식의 표준출력을 예상하고 데이터 파싱을 하고자 했던 것인데.

해당 문제가 처음에는 em-dash로 인해 생가는문제(구글링 첫번째 kubernetes github issue)인줄 알았으나, 정확한 이유는 모르겠으나 날짜를 파싱하는 부분에서 문제가 있었나 봅니다.

Containers:
  nginx:
    Container ID:   docker://...
    Image:          nginx:1.14.2
    Image ID:       docker-pullable://nginx@sha256:...
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 02 Apr 2021 11:02:12 +0900 ◀ ***20번째 라인은 이부분입니다.***
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-s9gc9 (ro)

해당 문제를 해결하면서 편하게 yq로 데이터 조작을 하기위해서는 'Containers.nginx.State.Started'의 부분을 제거한 출력을 yq의 표준입력으로 받으면 됩니다. 아래의 예시를 살펴봅니다.

문제 라인 제거 후 Labels: 파싱

$ kubectl describe pod/nginx-label | sed '/Started:/d' | yq e '.Labels' -
app=nginx

문제 라인 제거 후 Volumes:부분 파싱

$ kubectl describe pod/nginx-label | sed '/Started:/d' | yq e '.Volumes' -
default-token-s9gc9:
  Type: Secret (a volume populated by a Secret)
  SecretName: default-token-s9gc9
  Optional: false

일단 yq를 이용하거나 awk로 이용해서 데이터 파싱을 하고있지만, 더 다양한 데이터환경에서 문제가 있다면 또 해결법을 찾아야할꺼같습니다.

더 괜찮은 방법있으시면 댓글로 알려주시면 감사하겠습니다. 긴글 읽어주셔서 감사합니다.🙌