ΠΠΎΠ±ΡΡ Π΄Π΅Π½
ΠΠΌΠ°ΠΌΠ΅ Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΠΎΠ±Π»Π°ΡΠ½ΠΈ ΠΊΠ»ΡΡΡΠ΅ΡΠ° Ρ Π³ΠΎΠ»ΡΠΌ Π±ΡΠΎΠΉ Π²ΠΈΡΡΡΠ°Π»Π½ΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ Π²ΡΠ² Π²ΡΠ΅ΠΊΠΈ. ΠΠΈΠ΅ Ρ ΠΎΡΡΠ²Π°ΠΌΠ΅ ΡΠ΅Π»ΠΈΡ ΡΠΎΠ·ΠΈ Π±ΠΈΠ·Π½Π΅Ρ Π² Hetzner. ΠΡΠ² Π²ΡΠ΅ΠΊΠΈ ΠΊΠ»ΡΡΡΠ΅Ρ ΠΈΠΌΠ°ΠΌΠ΅ Π΅Π΄Π½Π° Π³Π»Π°Π²Π½Π° ΠΌΠ°ΡΠΈΠ½Π°, ΠΎΡ ΠΊΠΎΡΡΠΎ ΡΠ΅ ΠΏΡΠ°Π²ΠΈ ΠΌΠΎΠΌΠ΅Π½ΡΠ½Π° ΡΠ½ΠΈΠΌΠΊΠ° ΠΈ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ½ΠΎ ΡΠ΅ ΡΠ°Π·ΠΏΡΠΎΡΡΡΠ°Π½ΡΠ²Π° Π΄ΠΎ Π²ΡΠΈΡΠΊΠΈ Π²ΠΈΡΡΡΠ°Π»Π½ΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ Π² ΠΊΠ»ΡΡΡΠ΅ΡΠ°.
Π’Π°Π·ΠΈ ΡΡ Π΅ΠΌΠ° Π½Π΅ Π½ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ gitlab-runners Π½ΠΎΡΠΌΠ°Π»Π½ΠΎ, ΡΡΠΉ ΠΊΠ°ΡΠΎ Π²ΡΠ·Π½ΠΈΠΊΠ²Π°Ρ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ, ΠΊΠΎΠ³Π°ΡΠΎ ΡΠ΅ ΠΏΠΎΡΠ²ΡΡ ΠΌΠ½ΠΎΠ³ΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠ½ΠΈ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠ°Π½ΠΈ runner-ΠΈ, ΠΊΠΎΠ΅ΡΠΎ Π½ΠΈ ΠΏΠΎΠ΄ΡΠΈΠΊΠ½Π° Π΄Π° Π½Π°ΠΌΠ΅ΡΠΈΠΌ Π·Π°ΠΎΠ±ΠΈΠΊΠΎΠ»Π½ΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΈ Π΄Π° Π½Π°ΠΏΠΈΡΠ΅ΠΌ ΡΠ°Π·ΠΈ ΡΡΠ°ΡΠΈΡ/ΡΡΠΊΠΎΠ²ΠΎΠ΄ΡΡΠ²ΠΎ.
Π’ΠΎΠ²Π° Π²Π΅ΡΠΎΡΡΠ½ΠΎ Π½Π΅ Π΅ Π½Π°ΠΉ-Π΄ΠΎΠ±ΡΠ°ΡΠ° ΠΏΡΠ°ΠΊΡΠΈΠΊΠ°, Π½ΠΎ ΡΠΎΠ²Π° ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π°ΡΠ΅ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉ-ΡΠ΄ΠΎΠ±Π½ΠΎ ΠΈ ΠΏΡΠΎΡΡΠΎ.
ΠΠ° ΡΡΠΎΠΊΠ°, ΠΌΠΎΠ»Ρ, Π²ΠΈΠΆΡΠ΅ ΠΊΠ°Ρ.
ΠΠ΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΈ ΠΏΠ°ΠΊΠ΅ΡΠΈ Π½Π° Π³Π»Π°Π²Π½Π°ΡΠ° ΠΌΠ°ΡΠΈΠ½Π°:
- ΠΏΠΈΡΠΎΠ½
- Git
- ΡΠ°ΠΉΠ» Ρ ssh ΠΊΠ»ΡΡΠΎΠ²Π΅
ΠΠ±ΡΠΈΡΡ ΠΏΡΠΈΠ½ΡΠΈΠΏ Π·Π° Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ Π½Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ½ΠΎ ΠΈΠ·ΡΠ΅Π³Π»ΡΠ½Π΅ Π½Π° ΡΠ΅ΡΠ²Π°ΡΠ° Π½Π° Π²ΡΠΈΡΠΊΠΈ Π²ΠΈΡΡΡΠ°Π»Π½ΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ Π΅, ΡΠ΅ ΠΈΠΌΠ°ΡΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΠΌΠ°ΡΠΈΠ½Π°, Π½Π° ΠΊΠΎΡΡΠΎ ΡΠ΅ Π±ΡΠ΄Π΅ ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ°Π½ Ansible. ΠΡ ΡΠ°Π·ΠΈ ΠΌΠ°ΡΠΈΠ½Π° ansible ΡΠ΅ ΠΈΠ·ΠΏΡΠ°ΡΠΈ git pull ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ ΠΈ ΡΠ΅ ΡΠ΅ΡΡΠ°ΡΡΠΈΡΠ° ΡΡΠ»ΡΠ³Π°ΡΠ°, ΠΊΠΎΡΡΠΎ Π΅ Π°ΠΊΡΡΠ°Π»ΠΈΠ·ΠΈΡΠ°Π½Π°. ΠΠ° ΡΠ΅Π·ΠΈ ΡΠ΅Π»ΠΈ ΡΡΠ·Π΄Π°Π΄ΠΎΡ ΠΌΠ΅ ΠΎΡΠ΄Π΅Π»Π½Π° Π²ΠΈΡΡΡΠ°Π»Π½Π° ΠΌΠ°ΡΠΈΠ½Π° ΠΈΠ·Π²ΡΠ½ ΠΊΠ»ΡΡΡΠ΅ΡΠΈΡΠ΅ ΠΈ ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ°Ρ ΠΌΠ΅ Π½Π° Π½Π΅Ρ:
- ΠΏΠΈΡΠΎΠ½
- ansible
- gitlab-Π±Π΅Π³Π°Ρ
ΠΡ ΠΎΡΠ³Π°Π½ΠΈΠ·Π°ΡΠΈΠΎΠ½Π½ΠΈ Π²ΡΠΏΡΠΎΡΠΈ - ΡΡΡΠ±Π²Π° Π΄Π° ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠ°ΡΠ΅ gitlab-runner, Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ssh-keygen, Π΄Π° ΠΊΠ°ΡΠΈΡΠ΅ ΠΏΡΠ±Π»ΠΈΡΠ½ΠΈΡ ssh ΠΊΠ»ΡΡ Π½Π° ΡΠ°Π·ΠΈ ΠΌΠ°ΡΠΈΠ½Π° Π½Π° .ssh/authorized_keys
Π½Π° Π³Π»Π°Π²Π½Π°ΡΠ° ΠΌΠ°ΡΠΈΠ½Π°, ΠΎΡΠ²ΠΎΡΠ΅ΡΠ΅ ΠΏΠΎΡΡ 22 Π·Π° ansible Π½Π° Π³Π»Π°Π²Π½Π°ΡΠ° ΠΌΠ°ΡΠΈΠ½Π°.
Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΠΌΠ΅ ansible
Π’ΡΠΉ ΠΊΠ°ΡΠΎ Π½Π°ΡΠ°ΡΠ° ΡΠ΅Π» Π΅ Π΄Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΠ·ΠΈΡΠ°ΠΌΠ΅ Π²ΡΠΈΡΠΊΠΎ, ΠΊΠΎΠ΅ΡΠΎ Π΅ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ. ΠΡΠ² ΡΠ°ΠΉΠ» /etc/ansible/ansible.cfg
ΡΠ΅ ΡΠ°Π·ΠΊΠΎΠΌΠ΅Π½ΡΠΈΡΠ°ΠΌΠ΅ ΡΠ΅Π΄Π° host_key_checking = False
ΡΠ°ΠΊΠ° ΡΠ΅ ansible Π΄Π° Π½Π΅ ΠΈΡΠΊΠ° ΠΏΠΎΡΠ²ΡΡΠΆΠ΄Π΅Π½ΠΈΠ΅ Π·Π° Π½ΠΎΠ²ΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ.
Π‘Π»Π΅Π΄ ΡΠΎΠ²Π° ΡΡΡΠ±Π²Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ½ΠΎ Π΄Π° Π³Π΅Π½Π΅ΡΠΈΡΠ°ΡΠ΅ ΠΈΠ½Π²Π΅Π½ΡΠ°ΡΠ΅Π½ ΡΠ°ΠΉΠ» Π·Π° ansible, ΠΎΡΠΊΡΠ΄Π΅ΡΠΎ ΡΠ΅ Π²Π·Π΅ΠΌΠ΅ ip Π½Π° ΠΌΠ°ΡΠΈΠ½ΠΈΡΠ΅, Π½Π° ΠΊΠΎΠΈΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ git pull.
ΠΠΈΠ΅ Π³Π΅Π½Π΅ΡΠΈΡΠ°ΠΌΠ΅ ΡΠΎΠ·ΠΈ ΡΠ°ΠΉΠ» Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° API Π½Π° Hetzner, ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π²Π·Π΅ΠΌΠ΅ΡΠ΅ ΡΠΏΠΈΡΡΠΊΠ° Ρ Ρ ΠΎΡΡΠΎΠ²Π΅ ΠΎΡ Π²Π°ΡΠ°ΡΠ° AWS, Asure, Π±Π°Π·Π° Π΄Π°Π½Π½ΠΈ (ΠΈΠΌΠ°ΡΠ΅ API Π½ΡΠΊΡΠ΄Π΅, Π·Π° Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΡΠ΅ Π²Π°ΡΠΈΡΠ΅ ΡΠ°Π±ΠΎΡΠ΅ΡΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ, Π½Π°Π»ΠΈ?).
Π‘ΡΡΡΠΊΡΡΡΠ°ΡΠ° Π½Π° ΡΠ°ΠΉΠ»Π° Ρ ΠΈΠ½Π²Π΅Π½ΡΠ°ΡΠ° Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Π²Π°ΠΆΠ½Π° Π·Π° Ansible; ΡΡΡΠ±Π²Π° Π΄Π° ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΡΠ°ΠΊΠ°:
[Π³ΡΡΠΏΠΏΠ°]
ip-Π°Π΄ΡΠ΅Ρ
ip-Π°Π΄ΡΠ΅Ρ
[Π³ΡΡΠΏΠΏΠ°2]
ip-Π°Π΄ΡΠ΅Ρ
ip-Π°Π΄ΡΠ΅Ρ
ΠΠ° Π΄Π° Π³Π΅Π½Π΅ΡΠΈΡΠ°ΠΌΠ΅ ΡΠ°ΠΊΡΠ² ΡΠ°ΠΉΠ», ΡΠ΅ Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ ΠΏΡΠΎΡΡ ΡΠΊΡΠΈΠΏΡ (Π½Π΅ΠΊΠ° Π³ΠΎ Π½Π°ΡΠ΅ΡΠ΅ΠΌ vm_list
):
#!/bin/bash
echo [group] > /etc/ansible/cloud_ip &&
"Π²Π°Ρ CLI Π·Π°ΠΏΡΠΎΡ Π½Π° ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ IP Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΡ
ΠΌΠ°ΡΠΈΠ½ Π² ΠΊΠ»Π°ΡΡΠ΅ΡΠ΅" >> /etc/ansible/cloud_ip
echo " " >> /etc/ansible/cloud_ip
echo [group2] > /etc/ansible/cloud_ip &&
"Π²Π°Ρ CLI Π·Π°ΠΏΡΠΎΡ Π½Π° ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ IP Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΡ
ΠΌΠ°ΡΠΈΠ½ Π² Π΄ΡΡΠ³ΠΎΠΌ ΠΊΠ»Π°ΡΡΠ΅ΡΠ΅" >> /etc/ansible/cloud_ip
ΠΡΠ΅ΠΌΠ΅ Π΅ Π΄Π° ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ Π΄Π°Π»ΠΈ Ansible ΡΠ°Π±ΠΎΡΠΈ ΠΈ Π΅ ΠΏΡΠΈΡΡΠ΅Π»ΡΠΊΠΈ Π½Π°ΡΡΡΠΎΠ΅Π½ Ρ ΠΏΠΎΠ»ΡΡΠ°Π²Π°Π½Π΅ΡΠΎ Π½Π° IP Π°Π΄ΡΠ΅ΡΠΈ:
/etc/ansible/./vm_list && ansible -i /etc/ansible/cloud_ip -m shell -a 'hostname' group
Π Π΅Π·ΡΠ»ΡΠ°ΡΡΡ ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ΄ΡΡΠΆΠ° ΠΈΠΌΠ΅Π½Π°ΡΠ° Π½Π° Ρ
ΠΎΡΡΠΎΠ²Π΅ΡΠ΅ Π½Π° ΠΌΠ°ΡΠΈΠ½ΠΈΡΠ΅, Π½Π° ΠΊΠΎΠΈΡΠΎ Π΅ ΠΈΠ·ΠΏΡΠ»Π½Π΅Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°ΡΠ°.
ΠΡΠΊΠΎΠ»ΠΊΠΎ Π΄ΡΠΌΠΈ Π·Π° ΡΠΈΠ½ΡΠ°ΠΊΡΠΈΡΠ°:
- /etc/ansible/./vm_list - Π³Π΅Π½Π΅ΡΠΈΡΠ° ΡΠΏΠΈΡΡΠΊ Ρ ΠΌΠ°ΡΠΈΠ½ΠΈ
- -i - Π°Π±ΡΠΎΠ»ΡΡΠ΅Π½ ΠΏΡΡ Π΄ΠΎ ΡΠ°ΠΉΠ»Π° Ρ ΠΈΠ½Π²Π΅Π½ΡΠ°ΡΠ°
- -m - ΠΊΠ°ΠΆΠ΅ΡΠ΅ Π½Π° ansible Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΌΠΎΠ΄ΡΠ»Π° shell
- -a Π΅ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡΡ. ΠΡΡΠΊΠ° ΠΊΠΎΠΌΠ°Π½Π΄Π° ΠΌΠΎΠΆΠ΅ Π΄Π° Π±ΡΠ΄Π΅ Π²ΡΠ²Π΅Π΄Π΅Π½Π° ΡΡΠΊ
- Π³ΡΡΠΏΠ° β ΠΈΠΌΠ΅ΡΠΎ Π½Π° Π²Π°ΡΠΈΡ ΠΊΠ»ΡΡΡΠ΅Ρ. ΠΠΊΠΎ ΡΡΡΠ±Π²Π° Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΡΠΎΠ²Π° Π½Π° Π²ΡΠΈΡΠΊΠΈ ΠΊΠ»ΡΡΡΠ΅ΡΠΈ, ΠΏΡΠΎΠΌΠ΅Π½Π΅ΡΠ΅ Π³ΡΡΠΏΠ°ΡΠ° Π½Π° Π²ΡΠΈΡΠΊΠΈ
ΠΠ΅ΠΊΠ° ΠΎΡΠΈΠ΄Π΅ΠΌ ΠΏΠΎ-Π½Π°ΡΠ°ΡΡΠΊ - Π½Π΅ΠΊΠ° ΡΠ΅ ΠΎΠΏΠΈΡΠ°ΠΌΠ΅ Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ git pull Π½Π° Π½Π°ΡΠΈΡΠ΅ Π²ΠΈΡΡΡΠ°Π»Π½ΠΈ ΠΌΠ°ΡΠΈΠ½ΠΈ:
/etc/ansible/./vm_list && ansible -i /etc/ansible/cloud_ip -m shell -a 'cd /path/to/project && git pull' group
ΠΠΊΠΎ Π² ΠΈΠ·Ρ ΠΎΠ΄Π° Π²ΠΈΠ΄ΠΈΠΌ Π²Π΅ΡΠ΅ Π°ΠΊΡΡΠ°Π»Π΅Π½ ΠΈΠ»ΠΈ ΡΠ°Π·ΡΠΎΠ²Π°ΡΠ²Π°Π½Π΅ ΠΎΡ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ΡΠΎ, ΡΠΎΠ³Π°Π²Π° Π²ΡΠΈΡΠΊΠΎ ΡΠ°Π±ΠΎΡΠΈ.
Π‘Π΅Π³Π° Π²ΡΠΈΡΠΊΠΎ ΡΠΎΠ²Π° Π±Π΅ΡΠ΅ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ΠΎ Π·Π° ΡΠΎΠ²Π°
ΠΠ΅ΠΊΠ° Π½Π°ΡΡΠΈΠΌ Π½Π°ΡΠΈΡ ΡΠΊΡΠΈΠΏΡ Π΄Π° ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΡΠ²Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ½ΠΎ, ΠΊΠΎΠ³Π°ΡΠΎ ΡΠ΅ Π°Π½Π³Π°ΠΆΠΈΡΠ°ΠΌΠ΅ Ρ Π³Π»Π°Π²Π½ΠΈΡ ΠΊΠ»ΠΎΠ½ Π² gitlab
ΠΡΡΠ²ΠΎ, Π½Π΅ΠΊΠ° Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ Π½Π°ΡΠΈΡ ΡΠΊΡΠΈΠΏΡ ΠΏΠΎ-ΠΊΡΠ°ΡΠΈΠ² ΠΈ Π³ΠΎ ΠΏΠΎΡΡΠ°Π²ΠΈΠΌ Π² ΠΈΠ·ΠΏΡΠ»Π½ΠΈΠΌ ΡΠ°ΠΉΠ» (Π½Π΅ΠΊΠ° Π³ΠΎ Π½Π°ΡΠ΅ΡΠ΅ΠΌ exec_pull) -
#!/bin/bash
/etc/ansible/./get_vms && ansible -i /etc/ansible/cloud_ip -m shell -a "$@"
ΠΠ΅ΠΊΠ° Π΄Π° ΠΎΡΠΈΠ΄Π΅ΠΌ Π² Π½Π°ΡΠ°ΡΠ° gitlab ΠΈ Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ ΡΠ°ΠΉΠ» Π² ΠΏΡΠΎΠ΅ΠΊΡΠ° .gitlab-ci.yml
ΠΡΡΡΠ΅ ΠΏΠΎΡΡΠ°Π²ΡΠΌΠ΅ ΡΠ»Π΅Π΄Π½ΠΎΡΠΎ:
variables:
GIT_STRATEGY: none
VM_GROUP: group
stages:
- pull
- restart
run_exec_pull:
stage: pull
script:
- /etc/ansible/exec_pull 'cd /path/to/project/'$CI_PROJECT_NAME' && git pull' $VM_GROUP
only:
- master
run_service_restart:
stage: restart
script:
- /etc/ansible/exec_pull 'your_app_stop && your_app_start' $VM_GROUP
only:
- master
ΠΡΠΈΡΠΊΠΎ Π΅ Π³ΠΎΡΠΎΠ²ΠΎ. Π‘Π΅Π³Π° -
- Π½Π°ΠΏΡΠ°Π²Π΅ΡΠ΅ Π°Π½Π³Π°ΠΆΠΈΠΌΠ΅Π½Ρ
- Π Π°Π΄Π²Π°ΠΌ ΡΠ΅, ΡΠ΅ Π²ΡΠΈΡΠΊΠΎ ΡΠ°Π±ΠΎΡΠΈ
ΠΠΎΠ³Π°ΡΠΎ ΠΏΡΠ΅Ρ
Π²ΡΡΠ»ΡΡΠ΅ .yml ΠΊΡΠΌ Π΄ΡΡΠ³ΠΈ ΠΏΡΠΎΠ΅ΠΊΡΠΈ, ΠΏΡΠΎΡΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΡΠΎΠΌΠ΅Π½ΠΈΡΠ΅ ΠΈΠΌΠ΅ΡΠΎ Π½Π° ΡΡΠ»ΡΠ³Π°ΡΠ°, ΠΊΠΎΡΡΠΎ Π΄Π° ΡΠ΅ΡΡΠ°ΡΡΠΈΡΠ°ΡΠ΅, ΠΈ ΠΈΠΌΠ΅ΡΠΎ Π½Π° ΠΊΠ»ΡΡΡΠ΅ΡΠ°, Π½Π° ΠΊΠΎΠΉΡΠΎ ΡΠ΅ ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΡΠ²Π°Ρ Π°Π½Π·ΠΈΠ±ΡΠ» ΠΊΠΎΠΌΠ°Π½Π΄ΠΈΡΠ΅.
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com