도커와 도커-컴포즈와 circleci 같은 ci 툴을 공부하면서 다루는 yaml에 대해 헷갈리는 것들이 많이 한번쯤 정리해 보자는 취지에서 글을 쓰게 되었습니다.
🥕 YAML example
# https://docs.ansible.com/ansible/2.3/YAMLSyntax.html
# Employee records
- martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
docker:
- item : Basketball
quantity: 4
위 예제를 보고 자료의 구성이나 자료구조가 바로 이해가신다면 글을 보지 않으셔도 무방합니다.
📃 공식 도큐먼트를 먼저 참고하실 분은 🔹yaml.org/spec/1.2 를 참고해 주세요
https://yaml.org/spec/1.2/spec.html 1절에 나와있는 yaml에 대한 소개
Chapter 1. Introduction
“YAML Ain’t Markup Language” (abbreviated YAML) is a data serialization language designed to be human-friendly and work well with modern programming languages for common everyday tasks. This specification is both an introduction to the YAML language and the concepts supporting it, and also a complete specification of the information needed to develop applications for processing YAML.
json 이후로 인간친화적인 문서형태를 고민하던 후발주자인 yaml 은 YAML Ain’t Markup Language” (abbreviated YAML) 라는 뜻을 가지고, 데이터 직렬화를 지원하기 위해 만들었다고 한다.
실제로 json을 사용하면서 주석과 trailing comma를 지원하지 않는 다는 것에 불편함을 느끼고 있었는데, json 이 최고라는 나의 관점을 바꾸는 계기가 되어준 언어 되시겠다.
주로 도커나 도커 컴포즈를 하면서 정말 깔끔한 환경설정을 가능하게 하는 신기한 언어이면서 동시에 개념이 정립되지 않았기에 여러가지 의문점도 가지게 해주었다.
yaml을 사용하는 곳이 실제로 좀 많은 것 같은데, 요약한 블로그를 돌아다녀보면 다들 나랑 달리 능력자라 헷갈리는 부분이 없었나보다 했다. 특히 이건 되고, 왜 둘의 차이가 뭐지 하며 고민을 품게 하는 데에는 dash("-") 라는 부호의 사용이 명확하지 않은데 있었다.
# circle ci 예제
jobs:
simple_build: # job names
docker: # docker image
- image: noname2048/askbanana:simple
auth:
username: noname2048
password: $DOCKERHUB_PASSWORD
# image 들여쓰기를 다르게 해보았다. (그래도 같은 내용이다)
jobs:
simple_build: # job names
docker: # docker image
- image: noname2048/askbanana:simple
auth:
username: noname2048
password: $DOCKERHUB_PASSWORD
# json 으로 바꾸면
{
"jobs": {
"simple_build": {
"docker": [
{
"image": "noname2048/askbanana:simple",
"auth": {
"username": "noname2048",
"password": "$DOCKERHUB_PASSWORD"
}
}
]
}
}
}
주요한 특징은
마이너한 특징 (세부특징) 은
yaml.org 1.2 버전(2009) 에서 다음 full length example (figure 2.8) 을 가져와 보았다.
---
Time: 2001-11-23 15:01:42 -5
User: ed
Warning:
This is an error message
for the log file
---
Time: 2001-11-23 15:02:31 -5
User: ed
Warning:
A slightly different error
message.
---
Date: 2001-11-23 15:03:17 -5
User: ed
Fatal:
Unknown variable "bar"
Stack:
- file: TopClass.py
line: 23
code: |
x = MoreObject("345\n")
- file: MoreClass.py
line: 58
code: |-
foo = bar
주로 리스트와 해시(딕셔너리)를 섞어놓은 느낌이 온다면 아마 조금만 공부하면 실제 사용하는데 무리가 없을 것이라 생각한다.
그러면 공식문서와 같이 주요 기호와 구조를 배워보자.
2.1. Collections
YAML’s block collections use indentation for scope and begin each entry on its own line. Block sequences indicate each entry with a dash and space ( “- ”). Mappings use a colon and space (“: ”) to mark each key: value pair. Comments begin with an octothorpe (also called a “hash”, “sharp”, “pound”, or “number sign” - “#”).
아래는 예제를 좀 더 읽어보고, yaml에 등장하는 기호를 정리해 보았다.
기호 | 내용 |
---|---|
--- | 내용의 시작을 나타낸다 (생략가능) |
... | 내용의 끝을 나타낸다 (생략가능) |
# | 주석을 작성할때 쓴다 |
- | block sequence 표현자 |
| | 줄바꿈이 보존되는 (preserved, In literals) scalar 표현 |
> | 줄바꿈이 space로 변경되는 (In the folded scalars) scalar 표현 |
! | tag |
!! | tag에 대한 shorthand 표현 (약속어) |
? | complex key 표현, set (unordered) 표현 |
' | The single-quoted style is useful when escaping is not needed. |
" | The double-quoted style provides escape sequences. |
& | gloabl variable 선언 |
기타 | \b \x0 \u 등 스칼라 표현과 관련해서 쓰는 표현들 |
공식문서를 통해 우선 3가지의 개념을 확립하면, 실제에 사용하는데 큰 무리는 없는 듯하다...
주요 용어는 공식에서 사용하던걸 가져오고, 괄호안에는 파이썬과 유사한 개념을 적어 보았다.
이제 예제를 풀어보자.
스칼라가 단독으로 쓰인 예제는 아직 못 읽어보았다. 즉, 자료구조의 개념으로는 시퀀스와 매핑만 존재하고, scalar 은 데이터를 이루는 말이라고 생각해도 될 듯 하다.
예제를 보면서 json2yaml를 이용해 실제 json으로 어떻게 파싱되는지 보는것이 큰 공부가 되었다. 다른 사이트 중에는 다르게 동작하는게 있어 오히려 헷갈려서, 파싱기가 제대로 동작하는 것 같지 않다는 생각에 다른 것과 비교해보는 것이 도움이 되었다.
아래의 번호는 공식문서에 나오는 예제에 대해 임의로 번호를 매긴것입니다. 실제예제는 링크를 통해 확인해주세요 😀
# yaml
- Mark McGwire
- Sammy Sosa
- Ken Griffey
# json
[
"Mark McGwire",
"Sammy Sosa",
"Ken Griffey"
]
# yaml
hr: 65 # Home runs
avg: 0.278 # Batting average
rbi: 147 # Runs Batted In
# json
{
"hr": 65,
"avg": 0.278,
"rbi": 147
}
# yaml
american:
- Boston Red Sox
- Detroit Tigers
- New York Yankees
# json
{
"american": [
"Boston Red Sox",
"Detroit Tigers",
"New York Yankees"
]
}
# yaml
-
name: Mark McGwire
hr: 65
avg: 0.278
-
name: Sammy Sosa
hr: 63
avg: 0.288
# json
[
{
"name": "Mark McGwire",
"hr": 65,
"avg": 0.278
},
{
"name": "Sammy Sosa",
"hr": 63,
"avg": 0.288
}
]
# yaml
-
- name
- hr
- avg
- [Mark McGwire, 65, 0.278]
# json
[
[
"name",
"hr",
"avg"
],
[
"Mark McGwire",
65,
0.278
]
]
# yaml
Mark McGwire: {hr: 65, avg: 0.278}
Sammy Sosa:
hr: 63,
avg: 0.288
# json
{
"Mark McGwire": {
"hr": 65,
"avg": 0.278
},
"Sammy Sosa": {
"hr": 63,
"avg": 0.288
}
}
Repeated nodes (objects) are first identified by an anchor (marked with the ampersand - “&”), and are then aliased (referenced with an asterisk - “*”) thereafter.
# yaml
hr:
- Mark McGwire
- &SS Sammy Sosa
rbi:
- *SS
- Ken Griffey
# json
{
"hr": [
"Mark McGwire",
"Sammy Sosa"
],
"rbi": [
"Sammy Sosa",
"Ken Griffey"
]
}
question mark and space (“? ”) indicate a complex mapping key.
# yaml
? - Detroit Tigers
- Chicago cubs
:
- 2001-07-23
? [ New York Yankees,
Atlanta Braves ]
: [ 2001-07-02, 2001-08-12,
2001-08-14 ]
# json
{
"[\"Detroit Tigers\", \"Chicago cubs\"]": [
"2001-07-23"
],
"[\"New York Yankees\", \"Atlanta Braves\"]": [
"2001-07-02",
"2001-08-12",
"2001-08-14"
]
}
이것 때문에 공부를 시작하고 글을 쓰게 된 계기였다.
item 밑의 quantity 가 seq 형인지 map 형인지, 어디에 포함되는지 많이 헷갈렸었다. dash("-") 가 쓰인 곳은 map을 인자로 사용하는 seq 형으로, 다음 대시가 나오기 전까지의 인자를 map으로 묶어 seq 형에 넣는다.
# json
# Products purchased
- item : Super Hoop
quantity: 1
- item : Basketball
quantity: 4
- item : Big Shoes
quantity: 1
# yaml
[
{
"item": "Super Hoop",
"quantity": 1
},
{
"item": "Basketball",
"quantity": 4
},
{
"item": "Big Shoes",
"quantity": 1
}
]
연습문제 (이해했다고 생각된다면 1번과 2번의 차이를 말해보자...)
# 01
Fatal:
Unknown variable "bar"
Stack:
- file: TopClass.py
line: 23 # dash 추가됨!!!!
code: |
x = MoreObject("345\n")
- file: MoreClass.py
line: 58
code: |-
foo = bar
# 02
Fatal:
Unknown variable "bar"
Stack:
- file: TopClass.py
- line: 23 # dash 추가됨!!!!
code: |
x = MoreObject("345\n")
- file: MoreClass.py
line: 58
code: |-
foo = bar
# 03
jobs:
- simple_build:
filters:
branches:
only:
- master
# 04
jobs:
- simple_build:
filters:
branches:
only:
- master
마지막 실제 사용 예제를 올린다. 허접하지만, circleci 에서 (ci 툴) 환경설정을 하면서 직접 사용했던 파일이다. 이때 docker 하위 node에 (이제 와서 node라고 표현을 써서 그렇지만 😅, xml과 같은 트리구조의 형태이기 때문에 yaml.org에서도 node라고 표현. 비슷한 개념의 DOM을 생각해보자.) 존재하는 dash("-") 에 대해서 어떻게 생각해야할지 고민이 많았는데, 해결이 되었다.
version: 2.1
jobs:
simple_build: # job names
docker: # docker image
- image: noname2048/askbanana:simple
auth:
username: noname2048
password: $DOCKERHUB_PASSWORD
environment:
DEBUG: "False"
DB: "local"
working_directory: /askbanana/django_project # ~/project # default
steps:
- run:
name: send to codecov
command: |
coverage run ./manage.py test
codecov -t $CODECOV_TOKEN
결과적으로 대쉬가 존재하는 이유는 job에서 docker를 이용하여 ci 프로세스를 진행할때, 여러개의 도커를 사용할 수 있기 때문이였다. 여기서는 한개의 도커를 사용하고, 0번째 시퀀스 아래에는 해당 도커를 정의하는 image와 그 이미지를 가져올 수 있는 auth 가 정의되어있다.
enviroment가 바깥에 나와있는 이유는 아마 모든 docker image에 공통적용이 되서 그런가 보다 하고 넘어가기로 했다.(ㅎㅎ;)
마찬가지로 steps 안에는 여러개의 run이 존재할 수 있다.