์ง€์นจ: ํ”„๋กœ๋•์…˜ ์ „์— ansible ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ•

์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ!

์ €๋Š” ํ˜ธํ…” ์˜ˆ์•ฝ ์„œ๋น„์Šค์—์„œ DevOps ์—”์ง€๋‹ˆ์–ด๋กœ ์ผํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Ostrovok.ru. ์ด ๊ธ€์—์„œ๋Š” Ansible ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•œ ๊ฒฝํ—˜์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

Ostrovok.ru์—์„œ๋Š” ๊ตฌ์„ฑ ๊ด€๋ฆฌ์ž๋กœ ansible์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ์— ์šฐ๋ฆฌ๋Š” ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•  ํ•„์š”์„ฑ์„ ๋Š๊ผˆ์ง€๋งŒ ์ด๋ฅผ ์œ„ํ•œ ๋„๊ตฌ๊ฐ€ ๊ทธ๋ฆฌ ๋งŽ์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๊ฒƒ์€ Molecule ํ”„๋ ˆ์ž„์›Œํฌ์ผ ๊ฒƒ์ด๋ฏ€๋กœ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ์˜ ๋ฌธ์„œ์—๋Š” ๋งŽ์€ ํ•จ์ •์ด ์–ธ๊ธ‰๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๋ฐํ˜€์กŒ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋Ÿฌ์‹œ์•„์–ด๋กœ ๋œ ์ถฉ๋ถ„ํžˆ ์ƒ์„ธํ•œ ๋งค๋‰ด์–ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ธ€์„ ์“ฐ๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ง€์นจ: ํ”„๋กœ๋•์…˜ ์ „์— ansible ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ•

๋ถ„์ž

๋ถ„์ž - Ansible ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

๋‹จ์ˆœํ™”๋œ ์„ค๋ช…: ๋ถ„์ž๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ํ”Œ๋žซํผ(ํด๋ผ์šฐ๋“œ, ๊ฐ€์ƒ ๋จธ์‹ , ์ปจํ…Œ์ด๋„ˆ)์— ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์„น์…˜์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์šด์ „๊ธฐ์‚ฌ), ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์—์„œ ์—ญํ• ์„ ์‹คํ–‰ํ•œ ๋‹ค์Œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. ๋‹จ๊ณ„ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ Molecule์—์„œ ์ด๋ฅผ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์ด์ œ ๋”.

์•ฝ๊ฐ„์˜ ์ด๋ก 

๋ถ„์ž์˜ ๋‘ ๊ฐ€์ง€ ํ•ต์‹ฌ ์š”์†Œ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๋™์ธ์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค.

์‹œ๋‚˜๋ฆฌ์˜ค

์Šคํฌ๋ฆฝํŠธ์—๋Š” ๋ฌด์—‡์„, ์–ด๋””์„œ, ์–ด๋–ป๊ฒŒ, ์–ด๋–ค ์ˆœ์„œ๋กœ ์ˆ˜ํ–‰ํ• ์ง€์— ๋Œ€ํ•œ ์„ค๋ช…์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์—ญํ• ์—๋Š” ์—ฌ๋Ÿฌ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฐ ์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ผ ์žˆ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์ž…๋‹ˆ๋‹ค. <role>/molecule/<scenario>์—๋Š” ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์ž‘์—…์— ๋Œ€ํ•œ ์„ค๋ช…์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. default, ๋ถ„์ž๋กœ ์—ญํ• ์„ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ์˜ ์ด๋ฆ„์€ ๊ท€ํ•˜์—๊ฒŒ ๋‹ฌ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํฌ๋ฆฝํŠธ์—์„œ ์ผ๋ จ์˜ ํ…Œ์ŠคํŠธ ์ž‘์—…์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋งคํŠธ๋ฆญ์Šค์ด๋ฉฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

(๋ ˆ์ด๋ธ”์ด ๋ถ™์€ ๋‹จ๊ณ„ ?, ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.)

  • lint - ๋ฆฐํ„ฐ๋ฅผ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค yamllint ะธ flake8,
  • destroy - Molecule์˜ ๋งˆ์ง€๋ง‰ ์‹คํ–‰์—์„œ ์ธ์Šคํ„ด์Šค ์‚ญ์ œ(์žˆ๋Š” ๊ฒฝ์šฐ),
  • dependency? โ€” ํ…Œ์ŠคํŠธ๋œ ์—ญํ• ์˜ ansible ์ข…์†์„ฑ ์„ค์น˜,
  • syntax - ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ญํ• ์˜ ๊ตฌ๋ฌธ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ansible-playbook --syntax-check,
  • create - ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ ,
  • prepare? - ์ธ์Šคํ„ด์Šค ์ค€๋น„ ์˜ˆ: python2 ํ™•์ธ/์„ค์น˜
  • converge โ€” ํ…Œ์ŠคํŠธ ์ค‘์ธ ํ”Œ๋ ˆ์ด๋ถ ์‹คํ–‰,
  • idempotence - ๋ฉฑ๋“ฑ์„ฑ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํ”Œ๋ ˆ์ด๋ถ์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • side_effect? - ์—ญํ• ๊ณผ ์ง์ ‘์ ์ธ ๊ด€๋ จ์€ ์—†์ง€๋งŒ ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์ž‘์—…,
  • verify - ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ ๊ตฌ์„ฑ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. testinfra(๊ธฐ๋ณธ) /goss/inspec,
  • cleanup? - (์ƒˆ ๋ฒ„์ „์—์„œ) - ๋Œ€๋žต์ ์œผ๋กœ ๋งํ•˜๋ฉด, ๋ถ„์ž์˜ ์˜ํ–ฅ์„ ๋ฐ›์€ ์™ธ๋ถ€ ์ธํ”„๋ผ๋ฅผ "์ •๋ฆฌ"ํ•ฉ๋‹ˆ๋‹ค.
  • destroy - ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

์ด ์ˆœ์„œ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์— ์ ์šฉ๋˜์ง€๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ๊ฐ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ„๋„๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. molecule <command>. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ ๊ฐ cli ๋ช…๋ น์—๋Š” ๊ณ ์œ ํ•œ ์ž‘์—… ์ˆœ์„œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. molecule matrix <command>. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ช…๋ น์„ ์‹คํ–‰ํ•  ๋•Œ converge (ํ…Œ์ŠคํŠธ ์ค‘์ธ ํ”Œ๋ ˆ์ด๋ถ ์‹คํ–‰) ๋‹ค์Œ ์ž‘์—…์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

$ molecule matrix converge
...
โ””โ”€โ”€ default         # ะฝะฐะทะฒะฐะฝะธะต ัั†ะตะฝะฐั€ะธั
    โ”œโ”€โ”€ dependency  # ัƒัั‚ะฐะฝะพะฒะบะฐ ะทะฐะฒะธัะธะผะพัั‚ะตะน
    โ”œโ”€โ”€ create      # ัะพะทะดะฐะฝะธะต ะธะฝัั‚ะฐะฝัะฐ
    โ”œโ”€โ”€ prepare     # ะฟั€ะตะดะฝะฐัั‚ั€ะพะนะบะฐ ะธะฝัั‚ะฐะฝัะฐ
    โ””โ”€โ”€ converge    # ะฟั€ะพะณะพะฝ ะฟะปะตะนะฑัƒะบะฐ

์ด๋Ÿฌํ•œ ์ž‘์—…์˜ ์ˆœ์„œ๋Š” ํŽธ์ง‘๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชฉ๋ก์— ์žˆ๋Š” ํ•ญ๋ชฉ์ด ์ด๋ฏธ ์™„๋ฃŒ๋˜๋ฉด ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. ํ˜„์žฌ ์ƒํƒœ์™€ ์ธ์Šคํ„ด์Šค ๊ตฌ์„ฑ์€ Molecule์ด ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. $TMPDIR/molecule/<role>/<scenario>.

๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”. ? ansible-playbook ํ˜•์‹์œผ๋กœ ์›ํ•˜๋Š” ์ž‘์—…์„ ์„ค๋ช…ํ•˜๊ณ  ๋‹จ๊ณ„์— ๋”ฐ๋ผ ํŒŒ์ผ ์ด๋ฆ„์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. prepare.yml/side_effect.yml. ๋‹ค์Œ ํŒŒ์ผ์„ ์˜ˆ์ƒํ•˜์„ธ์š”. ๋ถ„์ž๋Š” ์Šคํฌ๋ฆฝํŠธ ํด๋”์— ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์šด์ „๊ธฐ์‚ฌ

๋“œ๋ผ์ด๋ฒ„๋Š” ํ…Œ์ŠคํŠธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์—”ํ„ฐํ‹ฐ์ž…๋‹ˆ๋‹ค.
Molecule์˜ ํ…œํ”Œ๋ฆฟ์ด ์ค€๋น„๋œ ํ‘œ์ค€ ๋“œ๋ผ์ด๋ฒ„ ๋ชฉ๋ก์€ Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated์ž…๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ํ…œํ”Œ๋ฆฟ์€ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. create.yml ะธ destroy.yml ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ์‚ญ์ œ๋ฅผ ๊ฐ๊ฐ ์„ค๋ช…ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ ํด๋”์— ์žˆ์Šต๋‹ˆ๋‹ค.
์•ž์„œ ์–ธ๊ธ‰ํ•œ ํŒŒ์ผ ์—†์ด ํ•ด๋‹น ๋ชจ๋“ˆ๊ณผ์˜ ์ƒํ˜ธ ์ž‘์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Docker ๋ฐ Vagrant๋Š” ์˜ˆ์™ธ์ž…๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ์‚ญ์ œ๋ฅผ ์œ„ํ•ด ํŒŒ์ผ์— ์‚ฌ์šฉ๋œ ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šค ๊ตฌ์„ฑ์— ๋Œ€ํ•œ ์ž‘์—…๋งŒ ์„ค๋ช…ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ์—”์ง€๋‹ˆ์–ด๊ฐ€ ์„ค๋ช…ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์ž„๋œ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ๊ฐ•์กฐํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๋“œ๋ผ์ด๋ฒ„๋Š” Docker์ž…๋‹ˆ๋‹ค.

์ด์ œ ์—ฐ์Šต์œผ๋กœ ๋„˜์–ด๊ฐ€์„œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ๊ณ ๋ คํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ

"hello world"๋กœ์„œ ๊ฐ„๋‹จํ•œ nginx ์„ค์น˜ ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋“œ๋ผ์ด๋ฒ„๋กœ docker๋ฅผ ์„ ํƒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๋ถ„๋“ค์ด docker๋ฅผ ์„ค์น˜ํ•ด ๋‘์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค(๊ทธ๋ฆฌ๊ณ  docker๊ฐ€ ๊ธฐ๋ณธ ๋“œ๋ผ์ด๋ฒ„๋ผ๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”).

์ค€๋น„ํ•˜์ž virtualenv ๊ทธ๋ฆฌ๊ณ  ๊ฑฐ๊ธฐ์— ์„ค์น˜ molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule ัƒัั‚ะฐะฝะพะฒะธั‚ ansible ะบะฐะบ ะทะฐะฒะธัะธะผะพัั‚ัŒ; docker ะดะปั ะดั€ะฐะนะฒะตั€ะฐ

๋‹ค์Œ ๋‹จ๊ณ„๋Š” ์ƒˆ ์—ญํ• ์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ƒˆ ์—ญํ• ๊ณผ ์ƒˆ ์Šคํฌ๋ฆฝํŠธ์˜ ์ดˆ๊ธฐํ™”๋Š” ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. molecule init <params>:

> molecule init role -r nginx
--> Initializing new role nginx...
Initialized role in <path>/nginx successfully.
> cd nginx
> tree -L 1
.
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ defaults
โ”œโ”€โ”€ handlers
โ”œโ”€โ”€ meta
โ”œโ”€โ”€ molecule
โ”œโ”€โ”€ tasks
โ””โ”€โ”€ vars

6 directories, 1 file

์ „ํ˜•์ ์ธ ansible ์—ญํ• ์ด ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ CLI ๋ถ„์ž์™€์˜ ๋ชจ๋“  ์ƒํ˜ธ ์ž‘์šฉ์€ ์—ญํ• ์˜ ๋ฃจํŠธ์—์„œ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

์—ญํ•  ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋ฌด์—‡์ด ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

> tree molecule/default/
molecule/default/
โ”œโ”€โ”€ Dockerfile.j2  # Jinja-ัˆะฐะฑะปะพะฝ ะดะปั Dockerfile
โ”œโ”€โ”€ INSTALL.rst.   # ะะตะผะฝะพะณะพ ะธะฝั„ะพั€ะผะฐั†ะธะธ ะพะฑ ัƒัั‚ะฐะฝะพะฒะบะต ะทะฐะฒะธัะธะผะพัั‚ะตะน ัั†ะตะฝะฐั€ะธั
โ”œโ”€โ”€ molecule.yml   # ะคะฐะนะป ะบะพะฝั„ะธะณัƒั€ะฐั†ะธะธ
โ”œโ”€โ”€ playbook.yml   # ะŸะปะตะนะฑัƒะบ ะทะฐะฟัƒัะบะฐ ั€ะพะปะธ
โ””โ”€โ”€ tests          # ะ”ะธั€ะตะบั‚ะพั€ะธั ั ั‚ะตัั‚ะฐะผะธ ัั‚ะฐะดะธะธ verify
    โ””โ”€โ”€ test_default.py

1 directory, 6 files

๊ตฌ์„ฑ์„ ๋ถ„์„ํ•ด๋ณด์ž molecule/default/molecule.yml (๋„์ปค ์ด๋ฏธ์ง€๋งŒ ๊ต์ฒด):

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: centos:7
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

์˜์กด

์ด ์„น์…˜์—์„œ๋Š” ์ข…์†์„ฑ์˜ ์†Œ์Šค์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์€ํ•˜, ์•”ํ‡˜์ง€, ๊ป๋ฐ๊ธฐ.

Shell์€ galaxy์™€ gilt๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉ๋˜๋Š” ๋ช…๋ น ์…ธ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์—ฌ๊ธฐ์— ์˜ค๋žซ๋™์•ˆ ๋จธ๋ฌผ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์„ ์  ์„œ๋ฅ˜ ๋น„์น˜.

์šด์ „์‚ฌ

์šด์ „์ž์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋„์ปค์ž…๋‹ˆ๋‹ค.

๋ณดํ’€

๋ฆฐํ„ฐ๋Š” yamllint์ž…๋‹ˆ๋‹ค.

๊ตฌ์„ฑ์˜ ์ด ๋ถ€๋ถ„์—์„œ ์œ ์šฉํ•œ ์˜ต์…˜์€ yamllint์— ๋Œ€ํ•œ ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์ง€์ •ํ•˜๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ linter๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

ํ”Œ๋žซํผ

์ธ์Šคํ„ด์Šค์˜ ๊ตฌ์„ฑ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
๋“œ๋ผ์ด๋ฒ„์ธ docker์˜ ๊ฒฝ์šฐ ๋ถ„์ž๋Š” ์ด ์„น์…˜์—์„œ ๋ฐ˜๋ณต๋˜๋ฉฐ ๋ชฉ๋ก์˜ ๊ฐ ์š”์†Œ๋Š” ๋‹ค์Œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Dockerfile.j2 ๋ณ€์ˆ˜๋กœ item.

ํ•„์š”ํ•œ ์šด์ „์ž์˜ ๊ฒฝ์šฐ create.yml ะธ destroy.yml, ์„น์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. molecule_yml.platforms, ์ด์— ๋Œ€ํ•œ ๋ฐ˜๋ณต์€ ์ด๋ฏธ ์ด ํŒŒ์ผ์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

Molecule์€ ansible ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์ธ์Šคํ„ด์Šค ์ œ์–ด๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ๊ฐ€๋Šฅํ•œ ์„ค์ • ๋ชฉ๋ก๋„ ์—ฌ๊ธฐ์—์„œ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด docker์˜ ๊ฒฝ์šฐ ๋ชจ๋“ˆ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. docker_container_module. ๋‹ค๋ฅธ ๋“œ๋ผ์ด๋ฒ„์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“ˆ์€ ๋‹ค์Œ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ ์  ์„œ๋ฅ˜ ๋น„์น˜.

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์–‘ํ•œ ๋“œ๋ผ์ด๋ฒ„์˜ ํ™œ์šฉ ์‚ฌ๋ก€๋„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ„์ž ์ž์ฒด์˜ ํ…Œ์ŠคํŠธ์—์„œ.

์—ฌ๊ธฐ์—์„œ ๊ต์ฒดํ•˜์„ธ์š” ์„ผํ† ์Šค:7 ์— ์šฐ๋ถ„ํˆฌ.

์ œ๊ณต์ž

"๊ณต๊ธ‰์ž" - ์ธ์Šคํ„ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ„ฐํ‹ฐ์ž…๋‹ˆ๋‹ค. Molecule์˜ ๊ฒฝ์šฐ ์ด๋Š” ansible์ด๋ฉฐ, ๊ทธ ์™ธ ์ง€์›์€ ์˜ˆ์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ ์ฃผ์˜์‚ฌํ•ญ์„ ๋‘๊ณ  ์ด ๋ถ€๋ถ„์„ ansible ํ™•์žฅ ๊ตฌ์„ฑ์ด๋ผ๊ณ  ๋ถ€๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์—ฌ๊ธฐ์—์„œ ๋งŽ์€ ์‚ฌํ•ญ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ ์ƒ๊ฐ์—๋Š” ์ฃผ์š” ์‚ฌํ•ญ์„ ๊ฐ•์กฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ํฌ๊ณก: ํŠน์ • ๋‹จ๊ณ„์—์„œ ์–ด๋–ค ํ”Œ๋ ˆ์ด๋ถ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

provisioner:
  name: ansible
  playbooks:
    create: create.yml
    destroy: ../default/destroy.yml
    converge: playbook.yml
    side_effect: side_effect.yml
    cleanup: cleanup.yml

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • ์˜ต์…˜: Ansible ์˜ต์…˜ ๋ฐ ํ™˜๊ฒฝ ๋ณ€์ˆ˜

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

๋Œ€๋ณธ

์Šคํฌ๋ฆฝํŠธ ์‹œํ€€์Šค์˜ ์ด๋ฆ„ ๋ฐ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.
ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋“  ๋ช…๋ น์˜ ๊ธฐ๋ณธ ๋™์ž‘ ๋งคํŠธ๋ฆญ์Šค๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค <command>_sequence ํ•„์š”ํ•œ ๋‹จ๊ณ„ ๋ชฉ๋ก์„ ์ •์˜ํ•˜์—ฌ ์ด์— ๋Œ€ํ•œ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
ํ”Œ๋ ˆ์ด๋ถ ์‹คํ–‰ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ๋•Œ ์ž‘์—… ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. molecule converge

# ะธะทะฝะฐั‡ะฐะปัŒะฝะพ:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

๊ฒ€

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์ด์— ๋Œ€ํ•œ ๋ฆฐํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ฆฐํ„ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. testinfra ะธ flake8. ๊ฐ€๋Šฅํ•œ ์˜ต์…˜์€ ์œ„์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

verifier:
  name: testinfra
  additional_files_or_dirs:
    - ../path/to/test_1.py
    - ../path/to/test_2.py
    - ../path/to/directory/*
  options:
    n: 1
  enabled: False
  env:
    FOO: bar
  lint:
    name: flake8
    options:
      benchmark: True
    enabled: False
    env:
      FOO: bar

์šฐ๋ฆฌ์˜ ์—ญํ• ๋กœ ๋Œ์•„๊ฐ€์ž. ํŒŒ์ผ์„ ํŽธ์ง‘ํ•ด๋ณด์ž tasks/main.yml ์ด๋Ÿฐ ์ข…๋ฅ˜์˜:

---
- name: Install nginx
  apt:
    name: nginx
    state: present

- name: Start nginx
  service:
    name: nginx
    state: started

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”. molecule/default/tests/test_default.py

def test_nginx_is_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running_and_enabled(host):
    nginx = host.service("nginx")
    assert nginx.is_running
    assert nginx.is_enabled

def test_nginx_config(host):
    host.run("nginx -t")

์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์‹คํ–‰๋งŒ ๋‚จ์•˜์Šต๋‹ˆ๋‹ค(์—ญํ• ์˜ ๋ฃจํŠธ์—์„œ ์ƒ๊ธฐ์‹œ์ผœ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค).

> molecule test

์Šคํฌ์ผ๋Ÿฌ ์•„๋ž˜์˜ ๊ธด ๋ฐฐ๊ธฐ ์žฅ์น˜:

--> Validating schema <path>/nginx/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

โ””โ”€โ”€ default
    โ”œโ”€โ”€ lint
    โ”œโ”€โ”€ destroy
    โ”œโ”€โ”€ dependency
    โ”œโ”€โ”€ syntax
    โ”œโ”€โ”€ create
    โ”œโ”€โ”€ prepare
    โ”œโ”€โ”€ converge
    โ”œโ”€โ”€ idempotence
    โ”œโ”€โ”€ side_effect
    โ”œโ”€โ”€ verify
    โ””โ”€โ”€ destroy

--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in <path>/nginx/...
Lint completed successfully.
--> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml...
Lint completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'syntax'

    playbook: <path>/nginx/molecule/default/playbook.yml

--> Scenario: 'default'
--> Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None)

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Create docker network(s)] ************************************************

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [instance]

    TASK [nginx : Install nginx] ***************************************************
    changed: [instance]

    TASK [nginx : Start nginx] *****************************************************
    changed: [instance]

    PLAY RECAP *********************************************************************
    instance                   : ok=3    changed=2    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/...
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: <path>/nginx/molecule/default, inifile:
    plugins: testinfra-1.16.0
collected 4 items

    tests/test_default.py ....                                               [100%]

    ========================== 4 passed in 27.23 seconds ===========================
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0

์šฐ๋ฆฌ์˜ ๋‹จ์ˆœํ•œ ์—ญํ• ์€ ๋ฌธ์ œ ์—†์ด ํ…Œ์ŠคํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์ž‘์—… ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ธฐ์–ตํ•ด๋‘์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. molecule test, ๊ธฐ๋ณธ ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ Molecule์€ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ช…๋ น์€ ๋””๋ฒ„๊น…์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

> molecule --debug <command> # debug info. ะŸั€ะธ ะพะฑั‹ั‡ะฝะพะผ ะทะฐะฟัƒัะบะต ะœะพะปะตะบัƒะปะฐ ัะบั€ั‹ะฒะฐะตั‚ ะปะพะณะธ.
> molecule converge          # ะžัั‚ะฐะฒะปัะตั‚ ะธะฝัั‚ะฐะฝั ะฟะพัะปะต ะฟั€ะพะณะพะฝะฐ ั‚ะตัั‚ะธั€ัƒะตะผะพะน ั€ะพะปะธ.
> molecule login             # ะ—ะฐะนั‚ะธ ะฒ ัะพะทะดะฐะฝะฝั‹ะน ะธะฝัั‚ะฐะฝั.
> molecule --help            # ะŸะพะปะฝั‹ะน ัะฟะธัะพะบ ะบะพะผะฐะฝะด.

๊ธฐ์กด ์—ญํ• 

๊ธฐ์กด ์—ญํ• ์— ์ƒˆ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์—ญํ•  ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

# ะฟะพะปะฝั‹ะน ัะฟะธัะพะบ ะดะพัั‚ัƒะฟะฝั‹ั… ะฟะฐั€ะฐะผะตั‚ั€ะพะฒ
> molecule init scenarion --help
# ัะพะทะดะฐะฝะธะต ะฝะพะฒะพะณะพ ัั†ะตะฝะฐั€ะธั
> molecule init scenario -r <role_name> -s <scenario_name>

์ด๊ฒƒ์ด ์—ญํ• ์˜ ์ฒซ ๋ฒˆ์งธ ์‹œ๋‚˜๋ฆฌ์˜ค์ธ ๊ฒฝ์šฐ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” -s ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์ƒ๋žต ๊ฐ€๋Šฅ default.

๊ฒฐ๋ก 

๋ณด์‹œ๋‹ค์‹œํ”ผ Molecule์€ ๊ทธ๋ฆฌ ๋ณต์žกํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ž์ฒด ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ƒˆ ์Šคํฌ๋ฆฝํŠธ ๋ฐฐํฌ๊ฐ€ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ์‚ญ์ œ ํ”Œ๋ ˆ์ด๋ถ์˜ ๋ณ€์ˆ˜ ํŽธ์ง‘์œผ๋กœ ์ค„์–ด๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ„์ž๋Š” CI ์‹œ์Šคํ…œ๊ณผ ์›ํ™œํ•˜๊ฒŒ ํ†ตํ•ฉ๋˜๋ฏ€๋กœ ํ”Œ๋ ˆ์ด๋ถ์˜ ์ˆ˜๋™ ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„์„ ์ค„์—ฌ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ด€์‹ฌ์„ ๊ฐ€์ ธ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. Ansible ์—ญํ• ์„ ํ…Œ์ŠคํŠธํ•œ ๊ฒฝํ—˜์ด ์žˆ๊ณ  Molecule๊ณผ ๊ด€๋ จ์ด ์—†๋‹ค๋ฉด ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์„ธ์š”!

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€