ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ ืึท ืกืขืจื•ื•ืขืจ ืฆื• ืฆืขื•ื•ื™ืงืœืขืŸ ืึท ืจืึทื™ืœืก ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื ื™ืฆืŸ Ansible

ื ื™ื˜ ืœืึทื ื’ ืฆื•ืจื™ืง ืื™ืš ื“ืืจืฃ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืขื˜ืœืขื›ืข Ansible ืคึผืœื™ื™ึทื‘ืึธืึธืงืก ืฆื• ืฆื•ื’ืจื™ื™ื˜ืŸ ื“ื™ ืกืขืจื•ื•ืขืจ ืคึฟืึทืจ ื“ื™ืคึผืœื•ื™ื™ื ื’ ืึท ืจืึทื™ืœืก ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ. ืื•ืŸ, ืกืึทืคึผืจื™ื™ื–ื™ื ื’ืœื™, ืื™ืš ืงืขืŸ ื ื™ืฉื˜ ื’ืขืคึฟื™ื ืขืŸ ืึท ืคึผืฉื•ื˜ ืฉืจื™ื˜-ื“ื•ืจืš-ืฉืจื™ื˜ ืžืึทื ื•ืึทืœ. ืื™ืš ื”ืึธื‘ ื ื™ืฉื˜ ื’ืขื•ื•ืึธืœื˜ ื ืึธื›ืžืึทื›ืŸ ืึทืŸ ืึทื ื“ืขืจืก ืฉืคึผื™ืœื‘ืึธืึธืง ืึธืŸ ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื•ื•ืึธืก ืื™ื– ื’ืขืฉืขืŸ, ืื•ืŸ ืฆื•ื ืกื•ืฃ ื”ืึธื‘ ืื™ืš ื’ืขืžื•ื–ื˜ ืœื™ื™ืขื ืขืŸ ื“ื™ ื“ืึทืงื™ื•ืžืขื ื˜ืึทืฆื™ืข, ืึทืœืฒืŸ ืึทืœืฆื“ื™ื ื’ ืฆื•ื ื•ื™ืคึฟืงืœืฒึทื‘ืŸ. ื˜ืึธืžืขืจ ืื™ืš ืงืขื ืขืŸ ื”ืขืœืคึฟืŸ ืขืžืขืฆืขืจ ืฆื• ืคืึทืจื’ื™ื›ืขืจืŸ ื“ืขื ืคึผืจืึธืฆืขืก ืžื™ื˜ ื“ื™ ื”ื™ืœืฃ ืคื•ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ.

ื“ืขืจ ืขืจืฉื˜ืขืจ ื–ืึทืš ืฆื• ืคึฟืึทืจืฉื˜ื™ื™ืŸ ืื™ื– ืึทื– ืึทื ืกื™ื‘ืœืข ื’ื™ื˜ ืื™ืจ ืึท ื‘ืึทืงื•ื•ืขื ืฆื•ื‘ื™ื ื“ ืฆื• ื“ื•ืจื›ืคื™ืจืŸ ืึท ืคึผืจืขื“ืขืคื™ื ืขื“ ืจืฉื™ืžื” ืคื•ืŸ ืึทืงืฉืึทื ื– ืื•ื™ืฃ ืึท ื•ื•ื™ื™ึทื˜ ืกืขืจื•ื•ืขืจ (s) ื“ื•ืจืš SSH. ืขืก ืื™ื– ืงื™ื™ืŸ ืžืึทื’ื™ืฉ ื“ืึธ, ืื™ืจ ืงืขื ืขืŸ ื ื™ืฉื˜ ื™ื ืกื˜ืึทืœื™ืจืŸ ืึท ืคึผืœื•ื’ื™ืŸ ืื•ืŸ ื‘ืึทืงื•ืžืขืŸ ืึท ื ื•ืœ ื“ืึทื•ื ื˜ื™ื™ื ื“ื™ืคึผืœื•ื™ืžืึทื ื˜ ืคื•ืŸ ื“ื™ื™ืŸ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืžื™ื˜ ื“ืึธืงืงืขืจ, ืžืึธื ื™ื˜ืึธืจื™ื ื’ ืื•ืŸ ืื ื“ืขืจืข ื’ื•ื“ื™ื– ืื•ื™ืก ืคื•ืŸ ื“ื™ ืงืขืกื˜ืœ. ืื™ืŸ ืกื“ืจ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืึท ืคึผืœื™ื™ึทื‘ืึธืึธืง, ืื™ืจ ืžื•ื–ืŸ ื•ื•ื™ืกืŸ ื•ื•ืึธืก ืคึผื•ื ืงื˜ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ื˜ืึธืŸ ืื•ืŸ ื•ื•ื™ ืฆื• ื˜ืึธืŸ ื“ืึธืก. ื“ืขืจืคึฟืึทืจ ื‘ื™ืŸ ืื™ืš ื ื™ืฉื˜ ืฆื•ืคึฟืจื™ื“ืŸ ืžื™ื˜ ื’ืจื™ื™ื˜-ื’ืขืžืื›ื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืงืก ืคึฟื•ืŸ GitHub, ืึธื“ืขืจ ืึทืจื˜ื™ืงืœืขืŸ ื•ื•ื™: "ืงืึธืคึผื™ ืื•ืŸ ืœื•ื™ืคืŸ, ืขืก ื•ื•ืขื˜ ืึทืจื‘ืขื˜ืŸ."

ื•ื•ืึธืก ืžื™ืจ ื“ืึทืจืคึฟืŸ?

ื•ื•ื™ ืื™ืš ื”ืื‘ ืฉื•ื™ืŸ ื’ืขื–ืื’ื˜, ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืึท ืคึผืœื™ื™ึทื‘ืึธืึธืง ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ืกืŸ ื•ื•ืึธืก ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ื˜ืึธืŸ ืื•ืŸ ื•ื•ื™ ืฆื• ื˜ืึธืŸ ื“ืึธืก. ื–ืืœ ืก ื‘ืึทืฉืœื™ืกืŸ ื•ื•ืึธืก ืžื™ืจ ื“ืึทืจืคึฟืŸ. ืคึฟืึทืจ ืึท ืจืึทื™ืœ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืขื˜ืœืขื›ืข ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื–: nginx, postgresql (ืจืขื“ื™ืก, ืขื˜ืง). ืื™ืŸ ื“ืขืจืฆื•, ืžื™ืจ ื“ืึทืจืคึฟืŸ ืึท ืกืคึผืขืฆื™ืคื™ืฉ ื•ื•ืขืจืกื™ืข ืคื•ืŸ โ€‹โ€‹ืจื•ื‘ื™ืŸ. ืขืก ืื™ื– ื‘ืขืกื˜ืขืจ ืฆื• ื™ื ืกื˜ืึทืœื™ืจืŸ ืขืก ื“ื•ืจืš rbenv (rvm, asdf ...). ืคืœื™ืกื ื“ื™ืง ืึทืœืข ื“ืขื ื•ื•ื™ ืึท ื•ื•ืึธืจืฆืœ ื‘ืึทื ื™ืฆืขืจ ืื™ื– ืฉื˜ืขื ื“ื™ืง ืึท ืฉืœืขื›ื˜ ื’ืขื“ืึทื ืง, ืึทื–ื•ื™ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืึทืคึฟืŸ ืึท ื‘ืึทื–ื•ื ื“ืขืจ ื‘ืึทื ื™ืฆืขืจ ืื•ืŸ ืงืึทื ืคื™ื’ื™ืขืจ ื–ื™ื™ืŸ ืจืขื›ื˜. ื ืึธืš ื“ืขื, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฆื•ืคึฟืขืœื™ืงืขืจ ืื•ื ื“ื–ืขืจ ืงืึธื“ ืฆื• ื“ื™ ืกืขืจื•ื•ืขืจ, ื ืึธื›ืžืึทื›ืŸ ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทื ื– ืคึฟืึทืจ nginx, postgres, ืขื˜ืง ืื•ืŸ ืึธื ื”ื™ื™ื‘ืŸ ืึทืœืข ื“ื™ ืกืขืจื•ื•ื™ืกืขืก.

ื•ื•ื™ ืึท ืจืขื–ื•ืœื˜ืึทื˜, ื“ื™ ืกื™ืงื•ื•ืึทื ืก ืคื•ืŸ ืึทืงืฉืึทื ื– ืื™ื– ื•ื•ื™ ื’ื™ื™ื˜:

  1. ืœืึธื’ื™ืŸ ื•ื•ื™ ื•ื•ืึธืจืฆืœ
  2. ื™ื ืกื˜ืึทืœื™ืจืŸ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื–
  3. ืฉืึทืคึฟืŸ ืึท ื ื™ื™ึทืข ื‘ืึทื ื™ืฆืขืจ, ืงืึทื ืคื™ื’ื™ืขืจ ืจืขื›ื˜, ssh key
  4. ืงืึทื ืคื™ื’ื™ืขืจ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื– (ื ื’ื™ื ืงืก ืขื˜ืง) ืื•ืŸ ืœื•ื™ืคืŸ ื–ื™ื™
  5. ืžื™ืจ ืžืึทื›ืŸ ืึท ื‘ืึทื ื™ืฆืขืจ ืื™ืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก (ืื™ืจ ืงืขื ื˜ ื’ืœื™ื™ืš ืฉืึทืคึฟืŸ ืึท ื“ืึทื˜ืึทื‘ื™ื™ืก)
  6. ืœืึธื’ื™ืŸ ื•ื•ื™ ืึท ื ื™ื™ึทืข ื‘ืึทื ื™ืฆืขืจ
  7. ื™ื ืกื˜ืึทืœื™ืจืŸ rbenv ืื•ืŸ ruby
  8. ื™ื ืกื˜ืึทืœื™ืจืŸ ื“ื™ ื‘ื•ื ื“ืœืขืจ
  9. ื•ืคึผืœืึธืึทื“ื™ื ื’ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืงืึธื“
  10. ืœืึธื ื˜ืฉื™ื ื’ ื“ื™ Puma ืกืขืจื•ื•ืขืจ

ื“ืขืจืฆื•, ื“ื™ ืœืขืฆื˜ืข ืกื˜ืึทื’ืขืก ืงืขื ืขืŸ ื–ื™ื™ืŸ ื“ื•ืจื›ื’ืขืงืึธื›ื˜ ืžื™ื˜ ืงืึทืคึผื™ืกื˜ืจืึทื ืึธ, ืื™ืŸ ืžื™ื ื“ืกื˜ืขืจ ืื•ื™ืก ืคื•ืŸ ื“ื™ ืงืขืกื˜ืœ ืขืก ืงืขื ืขืŸ ื ืึธื›ืžืึทื›ืŸ ืงืึธื“ ืื™ืŸ ืžืขืœื“ื•ื ื’ ื“ื™ื™ืจืขืงื˜ืขืจื™ื–, ื‘ืึทืฉื˜ื™ืžืขืŸ ื“ื™ ืžืขืœื“ื•ื ื’ ืžื™ื˜ ืึท ืกื™ืžืœื™ื ืง ื‘ื™ื™ ืžืฆืœื™ื— ื“ื™ืคึผืœื•ื™ืžืึทื ื˜, ื ืึธื›ืžืึทื›ืŸ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทื ื– ืคื•ืŸ ืึท ืฉืขืจื“ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ, ืจื™ืกื˜ืึทืจื˜ ืคึผื•ืžืึท, ืขื˜ืง. ืึทืœืข ื“ืขื ืงืขื ืขืŸ ื–ื™ื™ืŸ ื’ืขื˜ืืŸ ืžื™ื˜ Ansible, ืึธื‘ืขืจ ื•ื•ืึธืก?

ื˜ืขืงืข ืกื˜ืจื•ืงื˜ื•ืจ

ืึทื ืกื™ื‘ืœืข ื”ืื˜ ืฉื˜ืจืขื ื’ ื˜ืขืงืข ืกื˜ืจื•ืงื˜ื•ืจ ืคึฟืึทืจ ืึทืœืข ื“ื™ื™ืŸ ื˜ืขืงืขืก, ืึทื–ื•ื™ ืขืก ืื™ื– ื‘ืขืกื˜ืขืจ ืฆื• ื”ืึทืœื˜ืŸ ืขืก ืึทืœืข ืื™ืŸ ืึท ื‘ืึทื–ื•ื ื“ืขืจ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ. ื“ืขืจืฆื•, ืขืก ืื™ื– ื ื™ืฉื˜ ืึทื–ื•ื™ ื•ื•ื™ื›ื˜ื™ืง ืฆื™ ืขืก ื•ื•ืขื˜ ื–ื™ื™ืŸ ืื™ืŸ ื“ื™ ืจื™ื™ืœื– ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ื–ื™ืš, ืึธื“ืขืจ ืกืขืคึผืขืจืึทื˜ืœื™. ืื™ืจ ืงืขื ืขืŸ ืงืจืึธื ื˜ืขืงืขืก ืื™ืŸ ืึท ื‘ืึทื–ื•ื ื“ืขืจ ื’ื™ื˜ ืจื™ืคึผืึทื–ืึทื˜ืึธืจื™. ืคึผืขืจืกื ืึทืœื™, ืื™ืš ื’ืขืคึฟื•ื ืขืŸ ืขืก ืžืขืจืกื˜ ื‘ืึทืงื•ื•ืขื ืฆื• ืฉืึทืคึฟืŸ ืึทืŸ ืึธื ื•ื•ืขื ื“ืœืขืš ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืื™ืŸ ื“ื™ / config ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืคื•ืŸ ื“ื™ ืจื™ื™ืœื– ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืื•ืŸ ืงืจืึธื ืึทืœืฅ ืื™ืŸ ืื™ื™ืŸ ืจื™ืคึผืึทื–ืึทื˜ืึธืจื™.

ืคึผืฉื•ื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง

ืคึผืœื™ื™ึทื‘ืึธืึธืง ืื™ื– ืึท ื™ืžืœ ื˜ืขืงืข ื•ื•ืึธืก, ื ื™ืฆืŸ ืกืคึผืขืฆื™ืขืœ ืกื™ื ื˜ืึทืงืก, ื‘ืืฉืจื™ื™ื‘ื˜ ื•ื•ืึธืก Ansible ื–ืึธืœ ื˜ืึธืŸ ืื•ืŸ ื•ื•ื™. ืœืึธืžื™ืจ ืžืึทื›ืŸ ื“ื™ ืขืจืฉื˜ืขืจ ืคึผืœื™ื™ึทื‘ืึธืึธืง ื•ื•ืึธืก ื˜ื•ื˜ ื’ืึธืจื ื™ืฉื˜:

---
- name: Simple playbook
  hosts: all

ื“ืึธ ืžื™ืจ ืคืฉื•ื˜ ื–ืึธื’ืŸ ืึทื– ืื•ื ื“ื–ืขืจ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืื™ื– ื’ืขืจื•ืคืŸ Simple Playbook ืื•ืŸ ืึทื– ื–ื™ื™ึทืŸ ืื™ื ื”ืึทืœื˜ ื–ืึธืœ ื–ื™ื™ืŸ ืขืงืกืึทืงื™ื•ื˜ืึทื“ ืคึฟืึทืจ ืึทืœืข ืžื—ื ื•ืช. ืžื™ืจ ืงืขื ืขืŸ ืจืึทื˜ืขื•ื•ืขืŸ ืขืก ืื™ืŸ / ืึทื ืกืึทื‘ืึทืœ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืžื™ื˜ ื“ืขื ื ืึธืžืขืŸ playbook.yml ืื•ืŸ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ืœื•ื™ืคืŸ:

ansible-playbook ./playbook.yml

PLAY [Simple Playbook] ************************************************************************************************************************************
skipping: no hosts matched

Ansible ื–ืื’ื˜ ืึทื– ืขืก ืงืขืŸ ื ื™ืฉื˜ ืงื™ื™ืŸ ื”ืึธืกืฅ ื•ื•ืึธืก ื’ืœื™ื™ึทื›ืŸ ื“ื™ ืึทืœืข ืจืฉื™ืžื”. ื–ื™ื™ ืžื•ื–ืŸ ื–ื™ื™ืŸ ืœื™ืกื˜ืขื“ ืื™ืŸ ืึท ืกืคึผืขืฆื™ืขืœืข ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข.

ื–ืืœ ืก ืžืึทื›ืŸ ืขืก ืื™ืŸ ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ืึทื ืกืึทื‘ืึทืœ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ:

123.123.123.123

ื“ืึธืก ืื™ื– ื•ื•ื™ ืžื™ืจ ืคืฉื•ื˜ ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ื“ื™ ื‘ืึทืœืขื‘ืึธืก (ื™ื™ื“ื™ืœื™ ื“ืขืจ ื‘ืึทืœืขื‘ืึธืก ืคื•ืŸ ืื•ื ื“ื–ืขืจ ื•ื•ืคึผืก ืคึฟืึทืจ ื˜ืขืกื˜ื™ื ื’, ืึธื“ืขืจ ืื™ืจ ืงืขื ืขืŸ ืคืึทืจืฉืจื™ื™ึทื‘ืŸ ืœืึธืงืึทืœื”ืึธืกื˜) ืื•ืŸ ืจืึทื˜ืขื•ื•ืขืŸ ืขืก ืื•ื ื˜ืขืจ ื“ืขื ื ืึธืžืขืŸ inventory.
ืื™ืจ ืงืขื ืขืŸ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ืœื•ื™ืคืŸ ืึทื ืกืึทื‘ืึทืœ ืžื™ื˜ ืึทืŸ ื™ื ื•ื•ืขืจืึธืจื™ ื˜ืขืงืข:

ansible-playbook ./playbook.yml -i inventory
PLAY [Simple Playbook] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************

PLAY RECAP ************************************************************************************************************************************

ืื•ื™ื‘ ืื™ืจ ื”ืึธื‘ืŸ ssh ืึทืงืกืขืก ืฆื• ื“ื™ ืกืคึผืขืกื™ืคื™ืขื“ ื‘ืึทืœืขื‘ืึธืก, ืึทื ืกื™ื‘ืœืข ื•ื•ืขื˜ ืคืึทืจื‘ื™ื ื“ืŸ ืื•ืŸ ืงืœื™ื™ึทื‘ืŸ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื“ื™ ื•ื•ื™ื™ึทื˜ ืกื™ืกื˜ืขื. (ืคืขืœื™ืงื™ื™ึทื˜ TASK [ืงืœื™ื™ึทื‘ืŸ Facts]) ื ืึธืš ื•ื•ืึธืก ืขืก ื•ื•ืขื˜ ื’ืขื‘ืŸ ืึท ืงื•ืจืฅ ื‘ืึทืจื™ื›ื˜ ืื•ื™ืฃ ื“ืขืจ ื“ื•ืจื›ืคื™ืจื•ื ื’ (PLAY RECAP).

ื“ื•ืจืš ืคืขืœื™ืงื™ื™ึทื˜, ื“ื™ ืงืฉืจ ื ื™ืฆื˜ ื“ื™ ื ืืžืขืŸ ืื•ื ื˜ืขืจ ื•ื•ืึธืก ืื™ืจ ื–ืขื ื˜ ืœืึธื’ื“ ืื™ืŸ ื“ื™ ืกื™ืกื˜ืขื. ืขืก ืจื•ื‘ึฟ ืžืกืชึผืžื ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ืื•ื™ืฃ ื“ืขืจ ื‘ืึทืœืขื‘ืึธืก. ืื™ืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ื˜ืขืงืข, ืื™ืจ ืงืขื ืขืŸ ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ื•ื•ืึธืก ื‘ืึทื ื™ืฆืขืจ ืฆื• ื ื•ืฆืŸ ืฆื• ืคืึทืจื‘ื™ื ื“ืŸ ืžื™ื˜ ื“ื™ ืจื™ืžืึธื•ื˜_ื•ืกืขืจ ื“ื™ืจืขืงื˜ื™ื•ื•. ืื•ื™ืš, ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ืึท ื•ื•ื™ื™ึทื˜ ืกื™ืกื˜ืขื ืงืขืŸ ืึธืคื˜ ื–ื™ื™ืŸ ื•ืžื ื™ื™ื˜ื™ืง ืคึฟืึทืจ ืื™ืจ ืื•ืŸ ืื™ืจ ื–ืึธืœ ื ื™ืฉื˜ ื•ื•ื™ืกื˜ ืฆื™ื™ื˜ ืฆื• ื–ืึทืžืœืขืŸ ืขืก. ื“ื™ ืึทืจื‘ืขื˜ ืงืขื ืขืŸ ืื•ื™ืš ื–ื™ื™ืŸ ืคืึทืจืงืจื™ืคึผืœื˜:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  become: true
  gather_facts: no

ืคึผืจื•ึผื•ื•ื˜ ืœื•ื™ืคืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ื•ื•ื™ื“ืขืจ ืื•ืŸ ืžืึทื›ืŸ ื–ื™ื›ืขืจ ืึทื– ื“ื™ ืงืฉืจ ืื™ื– ืืจื‘ืขื˜ืŸ. (ืื•ื™ื‘ ืื™ืจ ืกืคึผืขืฆื™ืคื™ืฆื™ืจื˜ ื“ื™ ื•ื•ืึธืจืฆืœ ื‘ืึทื ื™ืฆืขืจ, ืื™ืจ ืื•ื™ืš ื“ืึทืจืคึฟืŸ ืฆื• ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ื“ื™ ื•ื•ืขืจืŸ: ืืžืช ื“ื™ืจืขืงื˜ื™ื•ื• ืื™ืŸ ืกื“ืจ ืฆื• ื’ืขื•ื•ื™ื ืขืŸ ืขืœืขื•ื•ืึทื˜ืขื“ ืจืขื›ื˜. ื•ื•ื™ ื’ืขืฉืจื™ื‘ืŸ ืื™ืŸ ื“ื™ ื“ืึทืงื™ื•ืžืขื ื˜ื™ื™ืฉืึทืŸ: become set to โ€˜trueโ€™/โ€™yesโ€™ to activate privilege escalation. ื›ืึธื˜ืฉ ืขืก ืื™ื– ื ื™ืฉื˜ ื’ืึธืจ ืงืœืึธืจ ื•ื•ืึธืก).

ื˜ืึธืžืขืจ ืื™ืจ ื•ื•ืขื˜ ื‘ืึทืงื•ืžืขืŸ ืึท ื˜ืขื•ืช ื’ืขืคึฟื™ืจื˜ ื“ื•ืจืš ื“ื™ ืคืึทืงื˜ ืึทื– ืึทื ืกื™ื‘ืœืข ืงืขืŸ ื ื™ืฉื˜ ื‘ืึทืฉืœื™ืกืŸ ื“ื™ ืคึผื™ื˜ื”ืึธืŸ ื™ื‘ืขืจื–ืขืฆืขืจ, ื“ืขืžืึธืœื˜ ืื™ืจ ืงืขื ืขืŸ ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ืขืก ืžืึทื ื™ื•ืึทืœื™:

ansible_python_interpreter: /usr/bin/python3 

ืื™ืจ ืงืขื ืขืŸ ื’ืขืคึฟื™ื ืขืŸ ืื•ื™ืก ื•ื•ื• ืื™ืจ ื”ืึธื‘ืŸ ืคึผื™ื˜ื”ืึธืŸ ืžื™ื˜ ื“ืขื ื‘ืึทืคึฟืขืœ whereis python.

ื™ื ืกื˜ืึธืœื™ื ื’ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื–

ื“ื™ ื ืึธืจืžืึทืœ ืคืึทืจืฉืคึผืจื™ื™ื˜ื•ื ื’ ืคื•ืŸ Ansible ื›ื•ืœืœ ืคื™ืœืข ืžืึทื“ื–ืฉื•ืœื– ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ืคืึทืจืฉื™ื“ืŸ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื–, ืึทื–ื•ื™ ืžื™ืจ ื˜ืึธืŸ ื ื™ื˜ ื”ืึธื‘ืŸ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ื‘ืึทืฉ ืกืงืจื™ืคึผืก ืคึฟืึทืจ ืงื™ื™ืŸ ืกื™ื‘ื”. ืื™ืฆื˜ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ืžืึทื“ื–ืฉื•ืœื– ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื“ื™ ืกื™ืกื˜ืขื ืื•ืŸ ื™ื ืกื˜ืึทืœื™ืจืŸ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื–. ืื™ืš ื”ืึธื‘ืŸ ื•ื‘ื•ื ื˜ื• ืœื™ื ื•ืงืก ืื•ื™ืฃ ืžื™ื™ืŸ ื•ื•ืคึผืก, ืึทื–ื•ื™ ืฆื• ื™ื ืกื˜ืึทืœื™ืจืŸ ืคึผืึทืงืึทื“ื–ืฉืึทื– ืื™ืš ื ื•ืฆืŸ apt-get ะธ ืžืึธื“ื•ืœืข ืคึฟืึทืจ ืขืก. ืื•ื™ื‘ ืื™ืจ ื ื•ืฆืŸ ืึท ืึทื ื“ืขืจืฉ ืึธืคึผืขืจื™ื™ื˜ื™ื ื’ ืกื™ืกื˜ืขื, ืื™ืจ ืงืขืŸ ื“ืึทืจืคึฟืŸ ืึท ืึทื ื“ืขืจืฉ ืžืึธื“ื•ืœืข (ื’ืขื“ืขื ืงืขืŸ, ืื™ืš ื’ืขื–ืื’ื˜ ืื™ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘ ืึทื– ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ืกืŸ ืื™ืŸ ืฉื˜ื™ื™ึทื’ืŸ ื•ื•ืึธืก ืื•ืŸ ื•ื•ื™ ืžื™ืจ ื•ื•ืขืœืŸ ื˜ืึธืŸ). ืึธื‘ืขืจ, ื“ื™ ืกื™ื ื˜ืึทืงืก ื•ื•ืขื˜ ืจื•ื‘ึฟ ืžืกืชึผืžื ื–ื™ื™ืŸ ืขื ืœืขืš.

ื–ืืœ ืก ื”ืขืกืึธืคืข ืื•ื ื“ื–ืขืจ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืžื™ื˜ ื“ื™ ืขืจืฉื˜ืขืจ ื˜ืึทืกืงืก:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  become: true
  gather_facts: no

  tasks:
    - name: Update system
      apt: update_cache=yes
    - name: Install system dependencies
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

ืึทืจื‘ืขื˜ ืื™ื– ืคึผื•ื ืงื˜ ื“ื™ ืึทืจื‘ืขื˜ ื•ื•ืึธืก Ansible ื•ื•ืขื˜ ื“ื•ืจื›ืคื™ืจืŸ ืื•ื™ืฃ ื•ื•ื™ื™ึทื˜ ืกืขืจื•ื•ืขืจืก. ืžื™ืจ ื’ืขื‘ืŸ ื“ื™ ืึทืจื‘ืขื˜ ืึท ื ืึธืžืขืŸ ืึทื–ื•ื™ ืžื™ืจ ืงืขื ืขืŸ ืฉืคึผื•ืจ ื–ื™ื™ึทืŸ ื“ื•ืจื›ืคื™ืจื•ื ื’ ืื™ืŸ ื“ื™ ืงืœืึธืฅ. ืื•ืŸ ืžื™ืจ ื‘ืึทืฉืจื™ื™ึทื‘ืŸ, ื ื™ืฆืŸ ื“ื™ ืกื™ื ื˜ืึทืงืก ืคื•ืŸ ืึท ืกืคึผืขืฆื™ืคื™ืฉ ืžืึธื“ื•ืœืข, ื•ื•ืึธืก ืขืก ื“ืึทืจืฃ ืฆื• ื˜ืึธืŸ. ืื™ืŸ ื“ืขื ืคืึทืœ apt: update_cache=yes - ื–ืื’ื˜ ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ืกื™ืกื˜ืขื ืคึผืึทืงืึทื“ื–ืฉืึทื– ื ื™ืฆืŸ ื“ื™ ืคื™ื™ื™ืง ืžืึธื“ื•ืœืข. ื“ื™ ืฆื•ื•ื™ื™ื˜ืข ื‘ืึทืคึฟืขืœ ืื™ื– ืึท ื‘ื™ืกืœ ืžืขืจ ืงืึธืžืคึผืœื™ืฆื™ืจื˜. ืžื™ืจ ืคืึธืจืŸ ืึท ืจืฉื™ืžื” ืคื•ืŸ ืคึผืึทืงืึทื“ื–ืฉืึทื– ืฆื• ื“ื™ ืคื™ื™ื™ืง ืžืึธื“ื•ืœืข ืื•ืŸ ื–ืึธื’ืŸ ืึทื– ื–ื™ื™ ื–ืขื ืขืŸ state ื–ืึธืœ ื•ื•ืขืจืŸ present, ื“ืึธืก ืื™ื–, ืžื™ืจ ื–ืึธื’ืŸ ื™ื ืกื˜ืึทืœื™ืจืŸ ื“ื™ ืคึผืึทืงืึทื“ื–ืฉืึทื–. ืื™ืŸ ืึท ืขื ืœืขืš ื•ื•ืขื’, ืžื™ืจ ืงืขื ืขืŸ ื–ืึธื’ืŸ ื–ื™ื™ ืฆื• ื•ื™ืกืžืขืงืŸ ื–ื™ื™, ืึธื“ืขืจ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื–ื™ื™ ื“ื•ืจืš ืคืฉื•ื˜ ื˜ืฉืึทื ื’ื™ื ื’ state. ื‘ื™ื˜ืข ื˜ืึธืŸ ืึทื– ืคึฟืึทืจ ืจื™ื™ืœื– ืฆื• ืึทืจื‘ืขื˜ืŸ ืžื™ื˜ postgresql, ืžื™ืจ ื“ืึทืจืคึฟืŸ ื“ื™ postgresql-contrib ืคึผืขืงืœ, ื•ื•ืึธืก ืžื™ืจ ื™ื ืกื˜ืึทืœื™ืจืŸ ืื™ืฆื˜. ื•ื•ื™ื“ืขืจ, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ืกืŸ ืื•ืŸ ื˜ืึธืŸ ื“ืึธืก; ืึทื ืกื™ื‘ืœืข ืื•ื™ืฃ ื–ื™ืš ื•ื•ืขื˜ ื ื™ืฉื˜ ื˜ืึธืŸ ื“ืึธืก.

ืคึผืจื•ึผื•ื•ื˜ ืœื•ื™ืคืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ื•ื•ื™ื“ืขืจ ืื•ืŸ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ืึทื– ื“ื™ ืคึผืึทืงืึทื“ื–ืฉืึทื– ื–ืขื ืขืŸ ืื™ื ืกื˜ืึทืœื™ืจืŸ.

ืงืจื™ื™ื™ื˜ื™ื ื’ ื ื™ื™ึท ื ื™ืฆืขืจืก.

ืฆื• ืึทืจื‘ืขื˜ืŸ ืžื™ื˜ ื™ื•ื–ืขืจื–, Ansible ืื•ื™ืš ื”ืื˜ ืึท ืžืึธื“ื•ืœืข - ื‘ืึทื ื™ืฆืขืจ. ืœืึธืžื™ืจ ืฆื•ื’ืขื‘ืŸ ื ืึธืš ืึท ืึทืจื‘ืขื˜ (ืื™ืš ื”ืึธื‘ ื‘ืึทื”ืึทืœื˜ืŸ ื“ื™ ืฉื•ื™ืŸ ื‘ืึทื•ื•ื•ืกื˜ ื˜ื™ื™ืœืŸ ืคื•ืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ื”ื™ื ื˜ืขืจ ื“ื™ ื‘ืึทืžืขืจืงื•ื ื’ืขืŸ ืึทื–ื•ื™ ื ื™ืฉื˜ ืฆื• ื ืึธื›ืžืึทื›ืŸ ืขืก ืื™ืŸ ื’ืื ืฆืŸ ื™ืขื“ืขืจ ืžืึธืœ):

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

ืžื™ืจ ืžืึทื›ืŸ ืึท ื ื™ื™ึทืข ื‘ืึทื ื™ืฆืขืจ, ืฉื˜ืขืœืŸ ืึท ืกื˜ืฉืขืœืœ ืื•ืŸ ืคึผืึทืจืึธืœ ืคึฟืึทืจ ืื™ื. ืื•ืŸ ื“ืขืžืึธืœื˜ ืžื™ืจ ืœื•ื™ืคืŸ ืื™ืŸ ืขื˜ืœืขื›ืข ืคึผืจืึธื‘ืœืขืžืก. ื•ื•ืึธืก ืื•ื™ื‘ ื“ื™ ื‘ืึทื ื™ืฆืขืจ ื ืขืžืขืŸ ื–ืึธืœ ื–ื™ื™ืŸ ืึทื ื“ืขืจืฉ ืคึฟืึทืจ ืคืึทืจืฉื™ื“ืขื ืข ืžื—ื ื•ืช? ืื•ืŸ ืกื˜ืึธืจื™ื ื’ ื“ื™ ืคึผืึทืจืึธืœ ืื™ืŸ ืงืœืึธืจ ื˜ืขืงืกื˜ ืื™ืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืื™ื– ืึท ื–ื™ื™ืขืจ ืฉืœืขื›ื˜ ื’ืขื“ืึทื ืง. ืฆื• ืึธื ื”ื™ื™ื‘ืŸ ืžื™ื˜, ืœืึธืžื™ืจ ืฉื˜ืขืœืŸ ื“ื™ ื ืืžืขืŸ ืื•ืŸ ืคึผืึทืจืึธืœ ืื™ืŸ ื•ื•ืขืจื™ืึทื‘ืึทืœื–, ืื•ืŸ ืื™ืŸ ื“ื™ ืกื•ืฃ ืคื•ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ ืื™ืš ื•ื•ืขื˜ ื•ื•ื™ื™ึทื–ืŸ ื•ื•ื™ ืฆื• ืขื ืงืจื™ืคึผื˜ ื“ื™ ืคึผืึทืจืึธืœ.

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

ื•ื•ืขืจื™ืึทื‘ืึทืœื– ื–ืขื ืขืŸ ืฉื˜ืขืœืŸ ืื™ืŸ ืคึผืœื™ื™ึทื‘ืึธืึธืงืก ื ื™ืฆืŸ ื˜ืึธืคึผืœ ื’ืขื’ืจื™ื™ึทื–ืœื˜ ื‘ืจื™ื™ืกืึทื–.

ืžื™ืจ ื•ื•ืขืœืŸ ืึธื ื•ื•ื™ื™ึทื–ืŸ ื“ื™ ื•ื•ืึทืœื•ืขืก ืคื•ืŸ ื“ื™ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืื™ืŸ ื“ื™ ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข:

123.123.123.123

[all:vars]
user=my_user
user_password=123qweasd

ื‘ื™ื˜ืข ื˜ืึธืŸ ื“ื™ ื“ื™ืจืขืงื˜ื™ื•ื• [all:vars] - ืขืก ื–ืื’ื˜ ืึทื– ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ื‘ืœืึธืง ืคื•ืŸ ื˜ืขืงืกื˜ ืื™ื– ื•ื•ืขืจื™ืึทื‘ืึทืœื– (ื•ื•ืึทืจืก) ืื•ืŸ ื–ื™ื™ ื–ืขื ืขืŸ ืึธื ื•ื•ืขื ื“ืœืขืš ืฆื• ืึทืœืข ืžื—ื ื•ืช (ืึทืœืข).

ื“ืขืจ ืคึผืœืึทืŸ ืื™ื– ืื•ื™ืš ื˜ืฉื™ืงืึทื•ื•ืข "{{ user_password | password_hash('sha512') }}". ื“ื™ ื–ืึทืš ืื™ื– ืึทื– ืึทื ืกื™ื‘ืœืข ื˜ื•ื˜ ื ื™ืฉื˜ ื™ื ืกื˜ืึทืœื™ืจืŸ ื“ื™ ื‘ืึทื ื™ืฆืขืจ ื“ื•ืจืš user_add ื•ื•ื™ ืื™ืจ ื•ื•ืึธืœื˜ ื˜ืึธืŸ ืขืก ืžืึทื ื™ื•ืึทืœื™. ืื•ืŸ ืขืก ืกืึทื•ื•ืขืก ืึทืœืข ื“ืึทื˜ืŸ ื’ืœื™ื™ึทืš, ื•ื•ืึธืก ืื™ื– ื•ื•ืึธืก ืžื™ืจ ืžื•ื–ืŸ ืื•ื™ืš ื’ืขืจ ื“ื™ ืคึผืึทืจืึธืœ ืื™ืŸ ืึท ื”ืึทืฉ ืื™ืŸ ืฉื˜ื™ื™ึทื’ืŸ, ื•ื•ืึธืก ืื™ื– ื•ื•ืึธืก ื“ืขื ื‘ืึทืคึฟืขืœ ื˜ื•ื˜.

ืœืึธืžื™ืจ ืœื™ื™ื’ืŸ ืื•ื ื“ื–ืขืจ ื‘ืึทื ื™ืฆืขืจ ืฆื• ื“ื™ ืกื•ื“ืึธ ื’ืจื•ืคึผืข. ืึธื‘ืขืจ, ืื™ื™ื“ืขืจ ื“ืขื ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืžืึทื›ืŸ ื–ื™ื›ืขืจ ืึทื– ืึทื–ืึท ืึท ื’ืจื•ืคึผืข ื™ื’ื–ื™ืกืฅ ื•ื•ื™ื™ึทืœ ืงื™ื™ืŸ ืื™ื™ื ืขืจ ื•ื•ืขื˜ ื˜ืึธืŸ ื“ืึธืก ืคึฟืึทืจ ืื•ื ื“ื–:

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Ensure a 'sudo' group
      group:
        name: sudo
        state: present
    - name: Add a new user
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"

ืึทืœืฅ ืื™ื– ื’ืึทื ืฅ ืคึผืฉื•ื˜, ืžื™ืจ ืื•ื™ืš ื”ืึธื‘ืŸ ืึท ื’ืจื•ืคึผืข ืžืึธื“ื•ืœืข ืคึฟืึทืจ ืงืจื™ื™ื™ื˜ื™ื ื’ ื’ืจื•ืคึผืขืก, ืžื™ื˜ ืึท ืกื™ื ื˜ืึทืงืก ื–ื™ื™ืขืจ ืขื ืœืขืš ืฆื• ืคื™ื™ื™ืง. ื“ืขืจื ืึธืš ืขืก ืื™ื– ื’ืขื ื•ื’ ืฆื• ืคืึทืจืฉืจื™ื™ึทื‘ืŸ ื“ืขื ื’ืจื•ืคึผืข ืฆื• ื“ืขืจ ื‘ืึทื ื™ืฆืขืจ (groups: "sudo").
ืขืก ืื™ื– ืื•ื™ืš ื ื•ืฆื™ืง ืฆื• ืœื™ื™ื’ืŸ ืึท ssh ืฉืœื™ืกืœ ืฆื• ื“ืขื ื‘ืึทื ื™ืฆืขืจ ืึทื–ื•ื™ ืึทื– ืžื™ืจ ืงืขื ืขืŸ ืงืœืึธืฅ ืื™ืŸ ื ื™ืฆืŸ ืขืก ืึธืŸ ืึท ืคึผืึทืจืึธืœ:

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Ensure a 'sudo' group
      group:
      name: sudo
        state: present
    - name: Add a new user
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"
    - name: Deploy SSH Key
      authorized_key:
        user: "{{ user }}"
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        state: present

ืื™ืŸ ื“ืขื ืคืึทืœ, ื“ื™ ืคึผืœืึทืŸ ืื™ื– ื˜ืฉื™ืงืึทื•ื•ืข "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" - ืขืก ืงืึทืคึผื™ื– ื“ื™ ืื™ื ื”ืึทืœื˜ ืคื•ืŸ ื“ื™ id_rsa.pub ื˜ืขืงืข (ื“ื™ื™ืŸ ื ืึธืžืขืŸ ืงืขืŸ ื–ื™ื™ืŸ ืึทื ื“ืขืจืฉ), ื“ืึธืก ืื™ื–, ื“ืขืจ ืฆื™ื‘ื•ืจ ื˜ื™ื™ืœ ืคื•ืŸ ื“ื™ ssh ืฉืœื™ืกืœ ืื•ืŸ ื•ืคึผืœืึธืึทื“ืก ืขืก ืฆื• ื“ืขืจ ืจืฉื™ืžื” ืคื•ืŸ ืึธื˜ืขืจื™ื™ื–ื“ ืฉืœื™ืกืœืขืŸ ืคึฟืึทืจ ื“ื™ ื‘ืึทื ื™ืฆืขืจ ืื•ื™ืฃ ื“ื™ ืกืขืจื•ื•ืขืจ.

ืจืึธืœืขืก

ืึทืœืข ื“ืจื™ื™ ื˜ืึทืกืงืก ืคึฟืึทืจ ืฉืืคืŸ ื ื•ืฆืŸ ืงืขื ืขืŸ ืœื™ื™ื›ื˜ ื–ื™ื™ืŸ ืงืœืึทืกืึทืคื™ื™ื“ ืื™ืŸ ืื™ื™ืŸ ื’ืจื•ืคึผืข ืคื•ืŸ โ€‹โ€‹ื˜ืึทืกืงืก, ืื•ืŸ ืขืก ื•ื•ืึธืœื˜ ื–ื™ื™ืŸ ืึท ื’ื•ื˜ืข ื’ืขื“ืึทื ืง ืฆื• ืงืจืึธื ื“ื™ ื’ืจื•ืคึผืข ืกืขืคึผืขืจืึทื˜ืœื™ ืคื•ืŸ ื“ื™ ื”ื•ื™ืคึผื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืึทื–ื•ื™ ืึทื– ืขืก ื•ื•ืขื˜ ื ื™ืฉื˜ ื•ื•ืึทืงืกืŸ ืฆื• ื’ืจื•ื™ืก. ืคึฟืึทืจ ื“ืขื ืฆื•ื•ืขืง, Ansible ื”ืื˜ ืจืึธืœืข.
ืœื•ื™ื˜ ื“ืขืจ ื˜ืขืงืข ืกื˜ืจื•ืงื˜ื•ืจ ื•ื•ืึธืก ืื™ื– ืื ื’ืขื•ื•ื™ื–ืŸ ืื™ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘, ื“ื™ ืจืึธืœืขืก ืžื•ื–ืŸ ื–ื™ื™ืŸ ื’ืขืฉื˜ืขืœื˜ ืื™ืŸ ืึท ื‘ืึทื–ื•ื ื“ืขืจ ืจืึธืœืข Directory, ืคึฟืึทืจ ื™ืขื“ืขืจ ืจืึธืœืข ืขืก ืื™ื– ืึท ื‘ืึทื–ื•ื ื“ืขืจ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ื ืึธืžืขืŸ, ืื™ืŸ ื“ื™ ื˜ืึทืกืงืก, ื˜ืขืงืขืก, ื˜ืขืžืคึผืœืึทื˜ืขืก, ืขื˜ืง.
ืœืึธืžื™ืจ ืžืึทื›ืŸ ืึท ื˜ืขืงืข ืกื˜ืจื•ืงื˜ื•ืจ: ./ansible/roles/user/tasks/main.yml (ืžื™ื™ืŸ ืื™ื– ื“ื™ ื”ื•ื™ืคึผื˜ ื˜ืขืงืข ื•ื•ืึธืก ื•ื•ืขื˜ ื–ื™ื™ืŸ ืœืึธื•ื“ื™ื“ ืื•ืŸ ืขืงืกืึทืงื™ื•ื˜ืึทื“ ื•ื•ืขืŸ ืึท ืจืึธืœืข ืื™ื– ืงืึธื ื ืขืงื˜ืขื“ ืฆื• ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง; ืื ื“ืขืจืข ืจืึธืœืข ื˜ืขืงืขืก ืงืขื ืขืŸ ื–ื™ื™ืŸ ืงืึธื ื ืขืงื˜ืขื“ ืฆื• ืขืก). ืื™ืฆื˜ ืื™ืจ ืงืขื ืขืŸ ืึทืจื™ื‘ืขืจืคื™ืจืŸ ืึทืœืข ื˜ืึทืกืงืก ืฉื™ื™ึทื›ื•ืช ืฆื• ื“ืขืจ ื‘ืึทื ื™ืฆืขืจ ืฆื• ื“ืขื ื˜ืขืงืข:

# Create user and add him to groups
- name: Ensure a 'sudo' group
  group:
    name: sudo
    state: present

- name: Add a new user
  user:
    name: "{{ user }}"
    shell: /bin/bash
    password: "{{ user_password | password_hash('sha512') }}"
    groups: "sudo"

- name: Deploy SSH Key
  authorized_key:
    user: "{{ user }}"
    key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
    state: present

ืื™ืŸ ื“ื™ ื”ื•ื™ืคึผื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง, ืื™ืจ ืžื•ื–ืŸ ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ืฆื• ื ื•ืฆืŸ ื“ื™ ื‘ืึทื ื™ืฆืขืจ ืจืึธืœืข:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  gather_facts: no

  tasks:
    - name: Update system
      apt: update_cache=yes
    - name: Install system dependencies
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

  roles:
    - user

ืื•ื™ืš, ืขืก ืงืขืŸ ื–ื™ื™ืŸ ื–ื™ื ืขืŸ ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื“ื™ ืกื™ืกื˜ืขื ืื™ื™ื“ืขืจ ืึทืœืข ืื ื“ืขืจืข ื˜ืึทืกืงืก; ืฆื• ื˜ืึธืŸ ื“ืึธืก, ืื™ืจ ืงืขื ืขืŸ ืจืขื ืึทืžืข ื“ืขื ื‘ืœืึธืง tasks ืื™ืŸ ื•ื•ืึธืก ื–ื™ื™ ื–ืขื ืขืŸ ื“ื™ืคื™ื™ื ื“ ืื™ืŸ pre_tasks.

ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ nginx

ืžื™ืจ ื–ืึธืœ ืฉื•ื™ืŸ ื”ืึธื‘ืŸ ื ื’ื™ื ืงืก ืื™ื ืกื˜ืึทืœื™ืจืŸ; ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืงืึทื ืคื™ื’ื™ืขืจ ืขืก ืื•ืŸ ืœื•ื™ืคืŸ ืขืก. ื–ืืœ ืก ื˜ืึธืŸ ืขืก ืจืขื›ื˜ ืึทื•ื•ืขืง ืื™ืŸ ื“ืขืจ ืจืึธืœืข. ืœืึธืžื™ืจ ืžืึทื›ืŸ ืึท ื˜ืขืงืข ืกื˜ืจื•ืงื˜ื•ืจ:

- ansible
  - roles
    - nginx
      - files
      - tasks
        - main.yml
      - templates

ืื™ืฆื˜ ืžื™ืจ ื“ืึทืจืคึฟืŸ ื˜ืขืงืขืก ืื•ืŸ ื˜ืขืžืคึผืœืึทื˜ืขืก. ื“ืขืจ ื—ื™ืœื•ืง ืฆื•ื•ื™ืฉืŸ ื–ื™ื™ ืื™ื– ืึทื– ืึทื ืกื™ื‘ืœืข ืงืึทืคึผื™ื– ื“ื™ ื˜ืขืงืขืก ื’ืœื™ื™ึทืš, ื•ื•ื™ ืื™ื–. ืื•ืŸ ื˜ืขืžืคึผืœืึทื˜ืขืก ืžื•ื–ืŸ ื”ืึธื‘ืŸ ื“ื™ j2 ืคืึทืจืœืขื ื’ืขืจื•ื ื’ ืื•ืŸ ื–ื™ื™ ืงืขื ืขืŸ ื ื•ืฆืŸ ื‘ื™ื™ึทื˜ืขื•ื•ื“ื™ืง ื•ื•ืึทืœื•ืขืก ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ื˜ืึธืคึผืœ ื’ืขื’ืจื™ื™ึทื–ืœื˜ ื‘ืจื™ื™ืกืึทื–.

ื–ืืœ ืก ื’ืขื‘ืŸ nginx ืื™ืŸ main.yml ื˜ืขืงืข. ืคึฟืึทืจ ื“ืขื ืžื™ืจ ื”ืึธื‘ืŸ ืึท ืกื™ืกื˜ืขื ืžืึธื“ื•ืœืข:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

ื“ืึธ ืžื™ืจ ื–ืึธื’ืŸ ื ื™ื˜ ื‘ืœื•ื™ื– ืึทื– nginx ืžื•ื–ืŸ ื–ื™ื™ืŸ ืกื˜ืึทืจื˜ืขื“ (ื“ืึธืก ืื™ื–, ืžื™ืจ ืงืึทื˜ืขืจ ืขืก), ืึธื‘ืขืจ ืžื™ืจ ื’ืœื™ื™ืš ื–ืึธื’ืŸ ืึทื– ืขืก ืžื•ื–ืŸ ื–ื™ื™ืŸ ืขื ื™ื™ื‘ืึทืœื“.
ืื™ืฆื˜ ืœืึธื–ืŸ ืื•ื ื“ื– ื ืึธื›ืžืึทื›ืŸ ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืขืก:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

- name: Copy the nginx.conf
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes

- name: Copy template my_app.conf
  template:
    src: my_app_conf.j2
    dest: /etc/nginx/sites-available/my_app.conf
    owner: root
    group: root
    mode: '0644'

ืžื™ืจ ืžืึทื›ืŸ ื“ื™ ื”ื•ื™ืคึผื˜ nginx ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืข (ืื™ืจ ืงืขื ืขืŸ ื ืขืžืขืŸ ืขืก ื’ืœื™ื™ึทืš ืคื•ืŸ ื“ื™ ืกืขืจื•ื•ืขืจ, ืึธื“ืขืจ ืฉืจื™ื™ึทื‘ืŸ ืขืก ื–ื™ืš). ืื•ืŸ ืื•ื™ืš ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืข ืคึฟืึทืจ ืื•ื ื“ื–ืขืจ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ ืื™ืŸ ื“ื™ ื–ื™ื™ื˜ืœืขืš_ืึทื•ื•ืึทื™ืœืึทื‘ืœืข ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ (ื“ืึธืก ืื™ื– ื ื™ื˜ ื ื™ื™ื˜ื™ืง ืึธื‘ืขืจ ื ื•ืฆื™ืง). ืื™ืŸ ื“ืขืจ ืขืจืฉื˜ืขืจ ืคืึทืœ, ืžื™ืจ ื ื•ืฆืŸ ื“ื™ ืงืึธืคึผื™ืข ืžืึธื“ื•ืœืข ืฆื• ืฆื™ื™ื›ืขื ืขืŸ ื˜ืขืงืขืก (ื“ื™ ื˜ืขืงืข ืžื•ื–ืŸ ื–ื™ื™ืŸ ืื™ืŸ /ansible/roles/nginx/files/nginx.conf). ืื™ืŸ ื“ื™ ืจื’ืข, ืžื™ืจ ื ืึธื›ืžืึทื›ืŸ ื“ื™ ืžื•ืกื˜ืขืจ, ืคืึทืจื‘ื™ื™ึทื˜ืŸ ื“ื™ ื•ื•ืึทืœื•ืขืก ืคื•ืŸ ื“ื™ ื•ื•ืขืจื™ืึทื‘ืึทืœื–. ื“ืขืจ ืžื•ืกื˜ืขืจ ื–ืึธืœ ื–ื™ื™ืŸ ืื™ืŸ /ansible/roles/nginx/templates/my_app.j2). ืื•ืŸ ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืขืคึผืขืก ื•ื•ื™ ื“ืึธืก:

upstream {{ app_name }} {
  server unix:{{ app_path }}/shared/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name {{ server_name }} {{ inventory_hostname }};
  root {{ app_path }}/current/public;

  try_files $uri/index.html $uri.html $uri @{{ app_name }};
  ....
}

ื‘ืึทืฆืึธืœืŸ ื•ืคืžืขืจืงื–ืึทืžืงื™ื™ึทื˜ ืฆื• ื“ื™ ื™ื ืกืขืจืฅ {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} - ื“ืึธืก ื–ืขื ืขืŸ ืึทืœืข ื•ื•ืขืจื™ืึทื‘ืึทืœื– ื•ื•ืขืžืขื ืก ื•ื•ืึทืœื•ืขืก Ansible ื•ื•ืขื˜ ืคืึทืจื‘ื™ื™ึทื˜ืŸ ื“ื™ ืžื•ืกื˜ืขืจ ืื™ื™ื“ืขืจ ืงืึทืคึผื™ื™ื ื’. ื“ืึธืก ืื™ื– ื ื•ืฆื™ืง ืื•ื™ื‘ ืื™ืจ ื ื•ืฆืŸ ืึท ืคึผืœื™ื™ึทื‘ืึธืึธืง ืคึฟืึทืจ ืคืึทืจืฉื™ื“ืขื ืข ื’ืจื•ืคึผืขืก ืคื•ืŸ ืžื—ื ื•ืช. ืคึฟืึทืจ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืžื™ืจ ืงืขื ืขืŸ ืœื™ื™ื’ืŸ ืื•ื ื“ื–ืขืจ ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข:

[production]
123.123.123.123

[staging]
231.231.231.231

[all:vars]
user=my_user
user_password=123qweasd

[production:vars]
server_name=production
app_path=/home/www/my_app
app_name=my_app

[staging:vars]
server_name=staging
app_path=/home/www/my_stage
app_name=my_stage_app

ืื•ื™ื‘ ืžื™ืจ ืื™ืฆื˜ ืงืึทื˜ืขืจ ืื•ื ื“ื–ืขืจ ืคึผืœื™ื™ึทื‘ืึธืึธืง, ืขืก ื•ื•ืขื˜ ื“ื•ืจื›ืคื™ืจืŸ ื“ื™ ืกืคึผืขืกื™ืคื™ืขื“ ื˜ืึทืกืงืก ืคึฟืึทืจ ื‘ื™ื™ื“ืข ืžื—ื ื•ืช. ืึธื‘ืขืจ ืื™ืŸ ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ืฆื™ื™ื˜, ืคึฟืึทืจ ืึท ืกื˜ืึทื’ื™ื ื’ ื‘ืึทืœืขื‘ืึธืก, ื“ื™ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ื•ื•ืขื˜ ื–ื™ื™ืŸ ืึทื ื“ืขืจืฉ ืคื•ืŸ ื“ื™ ืคึผืจืึธื“ื•ืงืฆื™ืข ืึธื ืขืก, ืื•ืŸ ื ื™ื˜ ื‘ืœื•ื™ื– ืื™ืŸ ืจืึธืœืขืก ืื•ืŸ ืคึผืœื™ื™ึทื‘ืึธืึธืงืก, ืึธื‘ืขืจ ืื•ื™ืš ืื™ืŸ nginx ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทื ื–. {{ inventory_hostname }} ื˜ืึธืŸ ื ื™ื˜ ื“ืึทืจืคึฟืŸ ืฆื• ื–ื™ื™ืŸ ืกืคึผืขืกื™ืคื™ืขื“ ืื™ืŸ ื“ื™ ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข - ื“ืึธืก ืกืคึผืขืฆื™ืขืœืข ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืื•ืŸ ื“ืขืจ ื‘ืึทืœืขื‘ืึธืก ืคึฟืึทืจ ื•ื•ืึธืก ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืื™ื– ืื™ืฆื˜ ืคืœื™ืกื ื“ื™ืง ืื™ื– ืกื˜ืึธืจื“ ื“ืึธืจื˜.
ืื•ื™ื‘ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ื”ืึธื‘ืŸ ืึท ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข ืคึฟืึทืจ ืขื˜ืœืขื›ืข ืžื—ื ื•ืช, ืึธื‘ืขืจ ื‘ืœื•ื™ื– ืœื•ื™ืคืŸ ืคึฟืึทืจ ืื™ื™ืŸ ื’ืจื•ืคึผืข, ื“ืึธืก ืงืขืŸ ื–ื™ื™ืŸ ื’ืขื˜ืืŸ ืžื™ื˜ ื“ื™ ืคืืœื’ืขื ื“ืข ื‘ืึทืคึฟืขืœ:

ansible-playbook -i inventory ./playbook.yml -l "staging"

ืืŸ ืื ื“ืขืจ ืึธืคึผืฆื™ืข ืื™ื– ืฆื• ื”ืึธื‘ืŸ ื‘ืึทื–ื•ื ื“ืขืจ ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืขืก ืคึฟืึทืจ ืคืึทืจืฉื™ื“ืขื ืข ื’ืจื•ืคึผืขืก. ืึธื“ืขืจ ืื™ืจ ืงืขื ืขืŸ ืคืึทืจื‘ื™ื ื“ืŸ ื“ื™ ืฆื•ื•ื™ื™ ืึทืคึผืจืึธื•ื˜ืฉื™ื– ืื•ื™ื‘ ืื™ืจ ื”ืึธื‘ืŸ ืคื™ืœืข ืคืึทืจืฉื™ื“ืขื ืข ืžื—ื ื•ืช.

ืœืึธืžื™ืจ ื’ื™ื™ืŸ ืฆื•ืจื™ืง ืฆื• ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ nginx. ื ืึธืš ืงืึทืคึผื™ื™ื ื’ ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืขืก, ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืึทืคึฟืŸ ืึท ืกื™ืžืœื™ื ืง ืื™ืŸ sitest_enabled ืฆื• my_app.conf ืคึฟื•ืŸ sites_available. ืื•ืŸ ืจื™ืกื˜ืึทืจื˜ nginx.

... # old code in mail.yml

- name: Create symlink to sites-enabled
  file:
    src: /etc/nginx/sites-available/my_app.conf
    dest: /etc/nginx/sites-enabled/my_app.conf
    state: link

- name: restart nginx
  service:
    name: nginx
    state: restarted

ืึทืœืฅ ืื™ื– ืคึผืฉื•ื˜ ื“ืึธ - ื•ื•ื™ื“ืขืจ ืึทื ืกืึทื‘ืึทืœ ืžืึทื“ื–ืฉื•ืœื– ืžื™ื˜ ืึท ืคืขืจืœื™ ื ืึธืจืžืึทืœ ืกื™ื ื˜ืึทืงืก. ืื‘ืขืจ ืขืก ืื™ื– ืื™ื™ืŸ ืคื•ื ื˜. ืขืก ืื™ื– ืงื™ื™ืŸ ืคื•ื ื˜ ืฆื• ืจื™ืกื˜ืึทืจื˜ื™ื ื’ nginx ื™ืขื“ืขืจ ืžืึธืœ. ื”ืึธื‘ืŸ ืื™ืจ ื‘ืืžืขืจืงื˜ ืึทื– ืžื™ืจ ื˜ืึธืŸ ื ื™ื˜ ืฉืจื™ื™ึทื‘ืŸ ืงืึทืžืึทื ื“ื– ื•ื•ื™: "ื˜ืึธืŸ ื“ืึธืก ื•ื•ื™ ื“ืึธืก", ื“ื™ ืกื™ื ื˜ืึทืงืก ืงื•ืงื˜ ืžืขืจ ื•ื•ื™ "ื“ืึธืก ื–ืึธืœ ื”ืึธื‘ืŸ ื“ืขื ืฉื˜ืึทื˜". ืื•ืŸ ืจื•ื‘ึฟ ืึธืคื˜ ื“ืึธืก ืื™ื– ืคึผื•ื ืงื˜ ื•ื•ื™ ืึทื ืกืึทื‘ืึทืœ ืึทืจื‘ืขื˜. ืื•ื™ื‘ ื“ื™ ื’ืจื•ืคึผืข ืฉื•ื™ืŸ ื™ื’ื–ื™ืกืฅ, ืึธื“ืขืจ ื“ื™ ืกื™ืกื˜ืขื ืคึผืขืงืœ ืื™ื– ืฉื•ื™ืŸ ืื™ื ืกื˜ืึทืœื™ืจืŸ, ืึทื ืกื™ื‘ืœืข ื•ื•ืขื˜ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ืคึฟืึทืจ ื“ืขื ืื•ืŸ ื”ืึธืคึผืงืขืŸ ื“ื™ ืึทืจื‘ืขื˜. ืื•ื™ืš, ื˜ืขืงืขืก ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ืงืึทืคึผื™ื“ ืื•ื™ื‘ ื–ื™ื™ ื’ืึธืจ ื’ืœื™ื™ึทื›ืŸ ื•ื•ืึธืก ืื™ื– ืฉื•ื™ืŸ ืื•ื™ืฃ ื“ื™ ืกืขืจื•ื•ืขืจ. ืžื™ืจ ืงืขื ืขืŸ ื ื•ืฆืŸ ื“ืขื ืื•ืŸ ืจื™ืกื˜ืึทืจื˜ nginx ื‘ืœื•ื™ื– ืื•ื™ื‘ ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืขืก ื–ืขื ืขืŸ ื˜ืฉื™ื™ื ื“ื–ืฉื“. ืขืก ืื™ื– ืึท ืจืขื’ื™ืกื˜ืจื™ืจืŸ ื“ื™ืจืขืงื˜ื™ื•ื• ืคึฟืึทืจ ื“ืขื:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

- name: Copy the nginx.conf
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes
  register: restart_nginx

- name: Copy template my_app.conf
  template:
    src: my_app_conf.j2
    dest: /etc/nginx/sites-available/my_app.conf
    owner: root
    group: root
    mode: '0644'
  register: restart_nginx

- name: Create symlink to sites-enabled
  file:
    src: /etc/nginx/sites-available/my_app.conf
    dest: /etc/nginx/sites-enabled/my_app.conf
    state: link

- name: restart nginx
  service:
    name: nginx
    state: restarted
  when: restart_nginx.changed

ืื•ื™ื‘ ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืขืก ืขื ื“ืขืจื•ื ื’ืขืŸ, ืึท ืงืึธืคึผื™ืข ื•ื•ืขื˜ ื–ื™ื™ืŸ ื’ืขืžืื›ื˜ ืื•ืŸ ื“ื™ ื‘ื™ื™ึทื˜ืขื•ื•ื“ื™ืง ื•ื•ืขื˜ ื–ื™ื™ืŸ ืจืขื’ื™ืกื˜ืจื™ืจื˜ restart_nginx. ืื•ืŸ ื‘ืœื•ื™ื– ืื•ื™ื‘ ื“ื™ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืื™ื– ืจืขื’ื™ืกื˜ืจื™ืจื˜, ื“ื™ ืกืขืจื•ื•ื™ืก ื•ื•ืขื˜ ื–ื™ื™ืŸ ืจื™ืกื˜ืึทืจื˜ื™ื“.

ืื•ืŸ, ืคื•ืŸ ืงื•ืจืก, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืœื™ื™ื’ืŸ ื“ื™ nginx ืจืึธืœืข ืฆื• ื“ื™ ื”ื•ื™ืคึผื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง.

ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ postgresql

ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื’ืขื‘ืŸ Postgresql ื ื™ืฆืŸ systemd ืื™ืŸ ื“ื™ ื–ืขืœื‘ืข ื•ื•ืขื’ ื•ื•ื™ ืžื™ืจ ื”ืึธื‘ืŸ ืžื™ื˜ nginx, ืื•ืŸ ืื•ื™ืš ืฉืึทืคึฟืŸ ืึท ื‘ืึทื ื™ืฆืขืจ ื•ื•ืึธืก ืžื™ืจ ื•ื•ืขืœืŸ ื ื•ืฆืŸ ืฆื• ืึทืงืกืขืก ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืื•ืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ื–ื™ืš.
ืœืึธืžื™ืจ ืžืึทื›ืŸ ืึท ืจืึธืœืข /ansible/roles/postgresql/tasks/main.yml:

# Create user in postgresql
- name: enable postgresql and start
  systemd:
    name: postgresql
    state: started
    enabled: yes

- name: Create database user
  become_user: postgres
  postgresql_user:
    name: "{{ db_user }}"
    password: "{{ db_password }}"
    role_attr_flags: SUPERUSER

- name: Create database
  become_user: postgres
  postgresql_db:
    name: "{{ db_name }}"
    encoding: UTF-8
    owner: "{{ db_user }}"

ืื™ืš ื•ื•ืขืœ ื ื™ืฉื˜ ื‘ืึทืฉืจื™ื™ึทื‘ืŸ ื•ื•ื™ ืฆื• ืœื™ื™ื’ืŸ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืฆื• ื™ื ื•ื•ืึทื ื˜ืึธืจื™, ื“ืึธืก ืื™ื– ืฉื•ื™ืŸ ื’ืขื˜ืืŸ ืคื™ืœืข ืžืึธืœ, ื•ื•ื™ ื’ืขื–ื•ื ื˜ ื•ื•ื™ ื“ื™ ืกื™ื ื˜ืึทืงืก ืคื•ืŸ ื“ื™ postgresql_db ืื•ืŸ postgresql_user ืžืึทื“ื–ืฉื•ืœื–. ืžืขืจ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ืงืขื ืขืŸ ื–ื™ื™ืŸ ื’ืขืคึฟื•ื ืขืŸ ืื™ืŸ ื“ื™ ื“ืึทืงื™ื•ืžืขื ื˜ื™ื™ืฉืึทืŸ. ื“ื™ ืžืขืจืกื˜ ื˜ืฉื™ืงืึทื•ื•ืข ื“ื™ืจืขืงื˜ื™ื•ื• ื“ืึธ ืื™ื– become_user: postgres. ื“ืขืจ ืคืึทืงื˜ ืื™ื– ืึทื– ื“ื•ืจืš ืคืขืœื™ืงื™ื™ึทื˜, ื‘ืœื•ื™ื– ื“ืขืจ ืคึผืึธืกื˜ื’ืจืขืก ื‘ืึทื ื™ืฆืขืจ ื”ืื˜ ืึทืงืกืขืก ืฆื• ื“ื™ ืคึผืึธืกื˜ื’ืจืขืกืงืœ ื“ืึทื˜ืึทื‘ื™ื™ืก ืื•ืŸ ื‘ืœื•ื™ื– ืœืึธื•ืงืึทืœื™. ื“ืขืจ ื“ื™ืจืขืงื˜ื™ื•ื• ืึทืœืึทื•ื– ืื•ื ื“ื– ืฆื• ื•ื™ืกืคื™ืจืŸ ืงืึทืžืึทื ื“ื– ืื•ื™ืฃ ื‘ื™ื›ืึทืฃ ืคื•ืŸ ื“ืขื ื‘ืึทื ื™ืฆืขืจ (ืื•ื™ื‘ ืžื™ืจ ื”ืึธื‘ืŸ ืึทืงืกืขืก, ืคื•ืŸ ืœื•ื™ืฃ).
ืื•ื™ืš, ืื™ืจ ืงืขืŸ ื”ืึธื‘ืŸ ืฆื• ืœื™ื™ื’ืŸ ืึท ืฉื•ืจื” ืฆื• pg_hba.conf ืฆื• ืœืึธื–ืŸ ืึท ื ื™ื™ึทืข ื‘ืึทื ื™ืฆืขืจ ืึทืงืกืขืก ืฆื• ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก. ื“ืขื ืงืขื ืขืŸ ื–ื™ื™ืŸ ื’ืขื˜ืืŸ ืื™ืŸ ื“ื™ ื–ืขืœื‘ืข ื•ื•ืขื’ ื•ื•ื™ ืžื™ืจ ื˜ืฉื™ื™ื ื“ื–ืฉื“ ื“ื™ nginx config.

ืื•ืŸ ื“ืึธืš ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืœื™ื™ื’ืŸ ื“ื™ postgresql ืจืึธืœืข ืฆื• ื“ื™ ื”ื•ื™ืคึผื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง.

ื™ื ืกื˜ืึธืœื™ื ื’ ืจื•ื‘ื™ ื“ื•ืจืš rbenv

Ansible ื˜ื•ื˜ ื ื™ืฉื˜ ื”ืึธื‘ืŸ ืžืึทื“ื–ืฉื•ืœื– ืคึฟืึทืจ ืืจื‘ืขื˜ืŸ ืžื™ื˜ rbenv, ืึธื‘ืขืจ ืขืก ืื™ื– ืื™ื ืกื˜ืึทืœื™ืจืŸ ื“ื•ืจืš ืงืœืึธื•ื ื™ื ื’ ืึท ื’ื™ื˜ ืจื™ืคึผืึทื–ืึทื˜ืึธืจื™. ื“ืขืจื™ื‘ืขืจ, ื“ืขื ืคึผืจืึธื‘ืœืขื ื•ื•ืขืจื˜ ื“ื™ ืžืขืจืกื˜ ื ื™ื˜-ื ืึธืจืžืึทืœ. ืœืึธืžื™ืจ ืžืึทื›ืŸ ืคึฟืึทืจ ืื™ืจ ืึท ืจืึธืœืข /ansible/roles/ruby_rbenv/main.yml ืื•ืŸ ืœืืžื™ืจ ืื ื”ื™ื™ื‘ืŸ ืื•ื™ืกืคื™ืœืŸ:

# Install rbenv and ruby
- name: Install rbenv
  become_user: "{{ user }}"
  git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenv

ืžื™ืจ ื•ื•ื™ื“ืขืจ ื ื•ืฆืŸ ื“ื™ word_user ื“ื™ืจืขืงื˜ื™ื•ื• ืฆื• ืึทืจื‘ืขื˜ืŸ ืื•ื ื˜ืขืจ ื“ืขืจ ื‘ืึทื ื™ืฆืขืจ ื•ื•ืึธืก ืžื™ืจ ื‘ืืฉืืคืŸ ืคึฟืึทืจ ื“ื™ ืฆื•ื•ืขืงืŸ. ื–ื™ื ื˜ rbenv ืื™ื– ืื™ื ืกื˜ืึทืœื™ืจืŸ ืื™ืŸ ื–ื™ื™ืŸ ื”ื™ื™ื ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ, ืื•ืŸ ื ื™ืฉื˜ ื’ืœืึธื•ื‘ืึทืœื™. ืื•ืŸ ืžื™ืจ ืื•ื™ืš ื ื•ืฆืŸ ื“ื™ ื’ื™ื˜ ืžืึธื“ื•ืœืข ืฆื• ืงืœืึธื•ืŸ ื“ื™ ืจื™ืคึผืึทื–ืึทื˜ืึธืจื™, ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ืจืขืคึผืึธ ืื•ืŸ ื“ืขืกื˜.

ื“ืขืจื ืึธืš, ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืจืขื’ื™ืกื˜ืจื™ืจืŸ rbenv init ืื™ืŸ bashrc ืื•ืŸ ืœื™ื™ื’ืŸ rbenv ืฆื• PATH ื“ืึธืจื˜. ืคึฟืึทืจ ื“ืขื ืžื™ืจ ื”ืึธื‘ืŸ ื“ื™ lineinfile ืžืึธื“ื•ืœืข:

- name: Add rbenv to PATH
  become_user: "{{ user }}"
  lineinfile:
    path: ~/.bashrc
    state: present
    line: 'export PATH="${HOME}/.rbenv/bin:${PATH}"'

- name: Add rbenv init to bashrc
  become_user: "{{ user }}"
  lineinfile:
    path: ~/.bashrc
    state: present
    line: 'eval "$(rbenv init -)"'

ื“ืขืจื ืึธืš ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื™ื ืกื˜ืึทืœื™ืจืŸ ruby_build:

- name: Install ruby-build
  become_user: "{{ user }}"
  git: repo=https://github.com/rbenv/ruby-build.git dest=~/.rbenv/plugins/ruby-build

ืื•ืŸ ืœืขืกืึธืฃ ื™ื ืกื˜ืึทืœื™ืจืŸ ืจื•ื‘ื™ืŸ. ื“ืึธืก ืื™ื– ื“ื•ืจื›ื’ืขืงืึธื›ื˜ ื“ื•ืจืš ืจื‘ื ื‘, ื“ืึธืก ื”ื™ื™ืกื˜, ืคืฉื•ื˜ ืžื™ื˜ ื“ืขืจ ื‘ืึทืคืขืœ ื‘ืึทืฉ:

- name: Install ruby
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}/.rbenv/bin:${PATH}"
    eval "$(rbenv init -)"
    rbenv install {{ ruby_version }}
  args:
    executable: /bin/bash

ืžื™ืจ ื–ืึธื’ืŸ ื•ื•ืึธืก ื‘ืึทืคึฟืขืœ ืฆื• ื•ื™ืกืคื™ืจืŸ ืื•ืŸ ืžื™ื˜ ื•ื•ืึธืก. ืึธื‘ืขืจ, ื“ืึธ ืžื™ืจ ื˜ืจืขืคืŸ ื“ื™ ืคืึทืงื˜ ืึทื– ืึทื ืกื™ื‘ืœืข ืงืขืŸ ื ื™ืฉื˜ ืœื•ื™ืคืŸ ื“ื™ ืงืึธื“ ืงืึทื ื˜ื™ื™ื ื“ ืื™ืŸ bashrc ืื™ื™ื“ืขืจ ืคืœื™ืกื ื“ื™ืง ื“ื™ ืงืึทืžืึทื ื“ื–. ื“ืขื ืžื™ื˜ืœ ืึทื– rbenv ื•ื•ืขื˜ ื–ื™ื™ืŸ ื“ื™ืคื™ื™ื ื“ ื’ืœื™ื™ึทืš ืื™ืŸ ื“ื™ ื–ืขืœื‘ืข ืฉืจื™ืคื˜.

ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ืคึผืจืึธื‘ืœืขื ืื™ื– ืจืขื›ื˜ ืฆื• ื“ืขื ืคืึทืงื˜ ืึทื– ื“ื™ ืฉืึธืœ ื‘ืึทืคึฟืขืœ ื”ืื˜ ืงื™ื™ืŸ ืฉื˜ืึทื˜ ืคื•ืŸ ืึท ืึทื ืกืึทื‘ืึทืœ ืคื•ื ื˜ ืคื•ืŸ ืžื™ื™ื ื•ื ื’. ืึทื– ืื™ื–, ืขืก ื•ื•ืขื˜ ื–ื™ื™ืŸ ืงื™ื™ืŸ ืึธื˜ืึทืžืึทื˜ื™ืง ื˜ืฉืขืง ืฆื™ ื“ื™ ื•ื•ืขืจืกื™ืข ืคื•ืŸ โ€‹โ€‹ืจื•ื‘ื™ ืื™ื– ืื™ื ืกื˜ืึทืœื™ืจืŸ ืึธื“ืขืจ ื ื™ืฉื˜. ืžื™ืจ ืงืขื ืขืŸ ื˜ืึธืŸ ื“ืึธืก ื–ื™ืš:

- name: Install ruby
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}/.rbenv/bin:${PATH}"
    eval "$(rbenv init -)"
    if ! rbenv versions | grep -q {{ ruby_version }}
      then rbenv install {{ ruby_version }} && rbenv global {{ ruby_version }}
    fi
  args:
    executable: /bin/bash

ืึทืœืข ื•ื•ืึธืก ื‘ืœื™ื™ื‘ื˜ ืื™ื– ืฆื• ื™ื ืกื˜ืึทืœื™ืจืŸ ื‘ื•ื ื“ืœืขืจ:

- name: Install bundler
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}/.rbenv/bin:${PATH}"
    eval "$(rbenv init -)"
    gem install bundler

ืื•ืŸ ื•ื•ื™ื“ืขืจ, ืœื™ื™ื’ืŸ ืื•ื ื“ื–ืขืจ ืจืึธืœืข ruby_rbenv ืฆื• ื“ื™ ื”ื•ื™ืคึผื˜ ืคึผืœื™ื™ึทื‘ืึธืึธืง.

ืฉืขืจื“ ื˜ืขืงืขืก.

ืื™ืŸ ืึทืœื’ืขืžื™ื™ืŸ, ื“ื™ ืกืขื˜ืึทืคึผ ืงืขืŸ ื–ื™ื™ืŸ ื’ืขืขื ื“ื™ืงื˜ ื“ืึธ. ื•ื•ื™ื™ึทื˜ืขืจ, ืึทืœืข ื•ื•ืึธืก ื‘ืœื™ื™ื‘ื˜ ืื™ื– ืฆื• ืœื•ื™ืคืŸ ืงืึทืคึผื™ืกื˜ืจืึทื ืึธ ืื•ืŸ ืขืก ื•ื•ืขื˜ ื ืึธื›ืžืึทื›ืŸ ื“ื™ ืงืึธื“ ื–ื™ืš, ืžืึทื›ืŸ ื“ื™ ื ื™ื™ื˜ื™ืง ื“ื™ื™ืจืขืงื˜ืขืจื™ื– ืื•ืŸ ืงืึทื˜ืขืจ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ (ืื•ื™ื‘ ืึทืœืฅ ืื™ื– ืงืึทื ืคื™ื’ื™ืขืจื“ ืจื™ื›ื˜ื™ืง). ืึธื‘ืขืจ, ืงืึทืคึผื™ืกื˜ืจืึทื ืึธ ืึธืคื˜ ืจื™ืงื•ื•ื™ื™ืขืจื– ื ืึธืš ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ื˜ืขืงืขืก, ืึทื–ืึท ื•ื•ื™ database.yml ืึธื“ืขืจ .env ื–ื™ื™ ืงืขื ืขืŸ ื–ื™ื™ืŸ ืงืึทืคึผื™ื“ ืคึผื•ื ืงื˜ ื•ื•ื™ ื˜ืขืงืขืก ืื•ืŸ ื˜ืขืžืคึผืœืึทื˜ืขืก ืคึฟืึทืจ nginx. ืขืก ืื™ื– ื‘ืœื•ื™ื– ืื™ื™ืŸ ืกืึทื˜ืึทืœื˜ื™. ืื™ื™ื“ืขืจ ืงืึทืคึผื™ื™ื ื’ ื˜ืขืงืขืก, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืึทืคึฟืŸ ืึท ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืกื˜ืจื•ืงื˜ื•ืจ ืคึฟืึทืจ ื–ื™ื™, ืขืคึผืขืก ื•ื•ื™ ื“ืึธืก:

# Copy shared files for deploy
- name: Ensure shared dir
  become_user: "{{ user }}"
  file:
    path: "{{ app_path }}/shared/config"
    state: directory

ืžื™ืจ ืกืคึผืขืฆื™ืคื™ืฆื™ืจืŸ ื‘ืœื•ื™ื– ืื™ื™ืŸ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ ืื•ืŸ ืึทื ืกื™ื‘ืœืข ื•ื•ืขื˜ ืื•ื™ื˜ืึธืžืึทื˜ื™ืฉ ืฉืึทืคึฟืŸ ืคืึธื˜ืขืจ ืึธื ืขืก ืื•ื™ื‘ ื ื™ื™ื˜ื™ืง.

ืึทื ืกื™ื‘ืœืข ื•ื•ืึธืœื˜

ืžื™ืจ ื”ืึธื‘ืŸ ืฉื•ื™ืŸ ื’ืขืคึฟื•ื ืขืŸ ื“ืขื ืคืึทืงื˜ ืึทื– ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืงืขื ืขืŸ ืึทื ื˜ื”ืึทืœื˜ืŸ ืกื•ื“ ื“ืึทื˜ืŸ ืึทื–ืึท ื•ื•ื™ ื“ื™ ืคึผืึทืจืึธืœ ืคื•ืŸ ื“ื™ ื‘ืึทื ื™ืฆืขืจ. ืื•ื™ื‘ ืื™ืจ ื”ืึธื˜ ื‘ืืฉืืคืŸ .env ื˜ืขืงืข ืคึฟืึทืจ ื“ื™ ืึทืคึผืœืึทืงื™ื™ืฉืึทืŸ, ืื•ืŸ database.yml ื“ืขืžืึธืœื˜ ืขืก ืžื•ื–ืŸ ื–ื™ื™ืŸ ืืคื™ืœื• ืžืขืจ ืึทื–ืึท ืงืจื™ื˜ื™ืฉ ื“ืึทื˜ืŸ. ืขืก ื•ื•ืึธืœื˜ ื–ื™ื™ืŸ ื’ื•ื˜ ืฆื• ื‘ืึทื”ืึทืœื˜ืŸ ื–ื™ื™ ืคื•ืŸ ืคึผืจื™ื™ื ื’ ืื•ื™ื’ืŸ. ืคึฟืึทืจ ื“ืขื ืฆื•ื•ืขืง ืขืก ืื™ื– ื’ืขื ื™ืฆื˜ ืึทื ืกืึทื‘ืึทืœ ื•ื•ืึธืœื˜.

ื–ืืœ ืก ืžืึทื›ืŸ ืึท ื˜ืขืงืข ืคึฟืึทืจ ื•ื•ืขืจื™ืึทื‘ืึทืœื– /ansible/vars/all.yml (ื“ืึธ ืื™ืจ ืงืขื ืขืŸ ืžืึทื›ืŸ ืคืึทืจืฉื™ื“ืขื ืข ื˜ืขืงืขืก ืคึฟืึทืจ ืคืึทืจืฉื™ื“ืขื ืข ื’ืจื•ืคึผืขืก ืคื•ืŸ ืžื—ื ื•ืช, ืคึผื•ื ืงื˜ ื•ื•ื™ ืื™ืŸ ื“ื™ ื™ื ื•ื•ืึทื ื˜ืึธืจื™ ื˜ืขืงืข: production.yml, staging.yml, ืขื˜ืง).
ืึทืœืข ื•ื•ืขืจื™ืึทื‘ืึทืœื– ื•ื•ืึธืก ืžื•ื–ืŸ ื–ื™ื™ืŸ ื™ื ืงืจื™ืคึผื˜ื™ื“ ืžื•ื–ืŸ ื–ื™ื™ืŸ ื˜ืจืึทื ืกืคืขืจื“ ืฆื• ื“ืขื ื˜ืขืงืข ืžื™ื˜ ื ืึธืจืžืึทืœ ื™ืžืœ ืกื™ื ื˜ืึทืงืก:

# System vars
user_password: 123qweasd
db_password: 123qweasd

# ENV vars
aws_access_key_id: xxxxx
aws_secret_access_key: xxxxxx
aws_bucket: bucket_name
rails_secret_key_base: very_secret_key_base

ื ืึธืš ื•ื•ืึธืก ื“ื™ ื˜ืขืงืข ืงืขื ืขืŸ ื–ื™ื™ืŸ ื™ื ืงืจื™ืคึผื˜ื™ื“ ืžื™ื˜ ื“ื™ ื‘ืึทืคึฟืขืœ:

ansible-vault encrypt ./vars/all.yml

ื’ืขื•ื•ื™ื™ื ื˜ืœืขืš, ื•ื•ืขืŸ ืขื ืงืจื™ืคึผื˜ื™ื ื’, ืื™ืจ ื•ื•ืขื˜ ื“ืึทืจืคึฟืŸ ืฆื• ืฉื˜ืขืœืŸ ืึท ืคึผืึทืจืึธืœ ืคึฟืึทืจ ื“ืขืงืจื™ืคึผื˜ื™ืึธืŸ. ืื™ืจ ืงืขื ืขืŸ ื–ืขืŸ ื•ื•ืึธืก ื•ื•ืขื˜ ื–ื™ื™ืŸ ื™ืŸ ื“ืขืจ ื˜ืขืงืข ื ืึธืš ืจื•ืคืŸ ื“ืขื ื‘ืึทืคึฟืขืœ.

ื“ื•ืจืš ืžื™ื˜ืœ ืคื•ืŸ ansible-vault decrypt ื“ื™ ื˜ืขืงืข ืงืขื ืขืŸ ื–ื™ื™ืŸ ื“ืขืงืจื™ืคึผื˜ื™ื“, ืžืึทื“ืึทืคื™ื™ื“ ืื•ืŸ ื“ืขืžืึธืœื˜ ื™ื ืงืจื™ืคึผื˜ื™ื“ ื•ื•ื™ื“ืขืจ.

ืื™ืจ ื˜ืึธืŸ ื ื™ื˜ ื“ืึทืจืคึฟืŸ ืฆื• ื“ืขืงืจื™ืคึผื˜ ื“ื™ ื˜ืขืงืข ืฆื• ืึทืจื‘ืขื˜ืŸ. ืื™ืจ ืงืจืึธื ืขืก ื™ื ืงืจื™ืคึผื˜ื™ื“ ืื•ืŸ ืœื•ื™ืคืŸ ื“ื™ ืคึผืœื™ื™ึทื‘ืึธืึธืง ืžื™ื˜ ื“ื™ ืึทืจื’ื•ืžืขื ื˜ --ask-vault-pass. Ansible ื•ื•ืขื˜ ืคืจืขื’ืŸ ืคึฟืึทืจ ื“ื™ ืคึผืึทืจืึธืœ, ืฆื•ืจื™ืงืงืจื™ื’ืŸ ื“ื™ ื•ื•ืขืจื™ืึทื‘ืึทืœื– ืื•ืŸ ื•ื™ืกืคื™ืจืŸ ื“ื™ ื˜ืึทืกืงืก. ืึทืœืข ื“ืึทื˜ืŸ ื•ื•ืขื˜ ื–ื™ื™ืŸ ื™ื ืงืจื™ืคึผื˜ื™ื“.

ื“ื™ ื’ืึทื ืฅ ื‘ืึทืคึฟืขืœ ืคึฟืึทืจ ืขื˜ืœืขื›ืข ื’ืจื•ืคึผืขืก ืคื•ืŸ ืžื—ื ื•ืช ืื•ืŸ ืึทื ืกืึทื‘ืึทืœ ื•ื•ืึธืœื˜ ื•ื•ืขื˜ ืงื•ืงืŸ ืขืคึผืขืก ื•ื•ื™ ื“ืึธืก:

ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-pass

ืึธื‘ืขืจ ืื™ืš ื•ื•ืขืœ ื ื™ืฉื˜ ื’ืขื‘ืŸ ืื™ืจ ื“ื™ ืคื•ืœ ื˜ืขืงืกื˜ ืคื•ืŸ ืคึผืœื™ื™ึทื‘ืึธืึธืงืก ืื•ืŸ ืจืึธืœืขืก, ืฉืจื™ื™ึทื‘ืŸ ืขืก ื–ื™ืš. ื•ื•ื™ื™ึทืœ ืึทื ืกืึทื‘ืึทืœ ืื™ื– ืึทื–ื•ื™ - ืื•ื™ื‘ ืื™ืจ ื˜ืึธืŸ ื ื™ื˜ ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื•ื•ืึธืก ื“ืึทืจืฃ ืฆื• ื–ื™ื™ืŸ ื’ืขื˜ืืŸ, ืขืก ื•ื•ืขื˜ ื ื™ืฉื˜ ื˜ืึธืŸ ื“ืึธืก ืคึฟืึทืจ ืื™ืจ.

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’