Π HabrΓ© ΠΈΠΌΠ° ΠΌΠ½ΠΎΠ³ΠΎ ΡΡΠ°ΡΠΈΠΈ Π·Π° ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ, Π½ΠΎ ΠΌΠ°Π»ΠΊΠΎ ΠΎΠΏΠΈΡΠ²Π°Ρ ΠΏΡΠΈΠΌΠ΅ΡΠΈ Π·Π° ΡΠΎΠ²Π° ΠΊΠ°ΠΊ ΡΠ°Π±ΠΎΡΡΡ ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ ΠΈ Π΄ΠΎΠΊΠ΅Ρ Π°Π³Π΅Π½ΡΠΈΡΠ΅. ΠΡΠΈΡΠΊΠΈ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΈ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΈ Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΏΡΠΎΠ΅ΠΊΡΠΈ ΠΊΠ°ΡΠΎ
ΠΠ½Π΅Ρ ΠΈΠΌΠ° ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°: Jenkins 2 Π΅ ΡΡΡΠ°Ρ
ΠΎΡΠ΅Π½ Π·Π° ΡΠ°Π±ΠΎΡΠ°
ΠΠ°ΡΠΎ Π·Π°ΠΏΠΎΡΠ½Π°Ρ Π΄Π° ΡΠ΅ΡΠ°Π²Π°ΠΌ ΡΠΎΠ·ΠΈ ΠΏΡΠΎΠ±Π»Π΅ΠΌ?
Π’ΡΠΉ ΠΊΠ°ΡΠΎ ΡΠΌΠ΅ Π² ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΡ
- Π³ΠΎΠ»ΡΠΌΠΎ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π²ΡΠ΅ΠΌΠ΅ Π·Π° ΠΈΠ·ΠΏΡΠ»Π½Π΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅ΡΠΎ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΡΠΈΡΠ΅ Π·Π°Π±ΡΠ°Π²ΡΡ Π΄Π° ΠΏΠΎΡΠΈΡΡΡΡ;
- ΠΈΠΌΠ° ΠΊΠΎΠ½ΡΠ»ΠΈΠΊΡΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°Π·Π»ΠΈΡΠ½ΠΈ Π²Π΅ΡΡΠΈΠΈ Π½Π° Π΅Π΄Π½ΠΎ ΠΈ ΡΡΡΠΎ Π²ΡΠ΅ΠΌΠ΅ Π·Π° ΠΈΠ·ΠΏΡΠ»Π½Π΅Π½ΠΈΠ΅;
- ΠΡΠ΅ΠΊΠΈ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΡΠ΅ Π½ΡΠΆΠ΄Π°Π΅ ΠΎΡ ΡΠ°Π·Π»ΠΈΡΠ΅Π½ Π½Π°Π±ΠΎΡ ΠΎΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΈ.
ΠΠΌΠ° ΠΈ Π΄ΡΡΠ³ΠΈ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ, Π½ΠΎ Π½Π΅ΠΊΠ° Π²ΠΈ ΠΊΠ°ΠΆΠ° Π·Π° ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ΡΠΎ.
ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ Π² Docker
Π’ΡΠΉ ΠΊΠ°ΡΠΎ Docker Π²Π΅ΡΠ΅ Π΅ Π΄ΠΎΠ±ΡΠ΅ ΡΡΡΠ°Π½ΠΎΠ²Π΅Π½ Π² ΡΠ²Π΅ΡΠ° Π½Π° ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠ°ΡΠ°, ΠΏΠΎΡΡΠΈ Π²ΡΠΈΡΠΊΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΡΡΠ°ΡΡΠΈΡΠ° Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° Docker. ΠΠΎΠ΅ΡΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π΅ Π΄Π° ΠΈΠΌΠ°ΠΌ Jenkins Π² Docker ΠΈ Π΄Π° ΠΌΠΎΠ³Π° Π΄Π° ΡΡΠ°ΡΡΠΈΡΠ°ΠΌ Π΄ΡΡΠ³ΠΈ Docker ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΈ. Π’ΠΎΠ·ΠΈ Π²ΡΠΏΡΠΎΡ Π·Π°ΠΏΠΎΡΠ½Π° Π΄Π° ΡΠ΅ Π·Π°Π΄Π°Π²Π° ΠΎΡΠ΅ ΠΏΡΠ΅Π· 2013 Π³. Π² ΡΡΠ°ΡΠΈΡΡΠ° β
ΠΠ°ΠΊΡΠ°ΡΠΊΠΎ, ΠΏΡΠΎΡΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ°ΡΠ΅ ΡΠ°ΠΌΠΈΡ Docker Π² ΡΠ°Π±ΠΎΡΠ΅Ρ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ ΠΈ Π΄Π° ΠΌΠΎΠ½ΡΠΈΡΠ°ΡΠ΅ ΡΠ°ΠΉΠ»Π° /var/run/docker.sock
.
ΠΡΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠ΅Π½ Dockerfile, ΠΊΠΎΠΉΡΠΎ ΡΠ΅ ΠΎΠΊΠ°Π·Π° Π·Π° ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ.
FROM jenkins/jenkins:lts
USER root
RUN apt-get update &&
apt-get -y install apt-transport-https
ca-certificates
curl
gnupg2
git
software-properties-common &&
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey &&
add-apt-repository
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")
$(lsb_release -cs)
stable" &&
apt-get update &&
apt-get -y install docker-ce &&
usermod -aG docker jenkins
RUN curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
RUN apt-get clean autoclean && apt-get autoremove βyes && rm -rf /var/lib/{apt,dpkg,cache,log}/
USER jenkins
ΠΠΎ ΡΠΎΠ·ΠΈ Π½Π°ΡΠΈΠ½ ΠΏΠΎΠ»ΡΡΠΈΡ ΠΌΠ΅ Docker ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ, ΠΊΠΎΠΉΡΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΈΠ·ΠΏΡΠ»Π½ΡΠ²Π° Docker ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ Π½Π° Ρ ΠΎΡΡ ΠΌΠ°ΡΠΈΠ½Π°ΡΠ°.
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° Π½Π° ΠΊΠΎΠΌΠΏΠΈΠ»Π°ΡΠΈΡ
ΠΠ΅ΠΎΡΠ΄Π°Π²Π½Π° ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ ΠΏΠΎΠ»ΡΡΠΈ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡΠ° Π΄Π° ΠΎΠΏΠΈΡΠ΅ ΡΠ²ΠΎΠΈΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π°, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ
Π’Π°ΠΊΠ° ΡΠ΅ Π½Π΅ΠΊΠ° ΠΏΠΎΡΡΠ°Π²ΠΈΠΌ ΡΠΏΠ΅ΡΠΈΠ°Π»Π΅Π½ Dockerfile Π² ΡΠ°ΠΌΠΎΡΠΎ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅, ΠΊΠΎΠ΅ΡΠΎ ΡΠ΅ ΡΡΠ΄ΡΡΠΆΠ° Π²ΡΠΈΡΠΊΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ΡΠΎ. ΠΠΎ ΡΠΎΠ·ΠΈ Π½Π°ΡΠΈΠ½ ΡΠ°ΠΌΠΈΡΡ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΏΠΎΠ΄Π³ΠΎΡΠ²ΠΈ ΠΏΠΎΠ²ΡΠΎΡΡΠ΅ΠΌΠ° ΡΡΠ΅Π΄Π° ΠΈ Π½ΡΠΌΠ° Π΄Π° ΡΠ΅ Π½Π°Π»Π°Π³Π° Π΄Π° ΠΈΡΠΊΠ° ΠΎΡ OPS Π΄Π° ΠΈΠ½ΡΡΠ°Π»ΠΈΡΠ° ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½Π° Π²Π΅ΡΡΠΈΡ Π½Π° Node.JS Π½Π° Ρ ΠΎΡΡΠ°.
FROM node:12.10.0-alpine
RUN npm install yarn -g
Π’ΠΎΠ²Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΊΠΎΠΌΠΏΠΈΠ»Π°ΡΠΈΡ Π΅ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΡΡΠΎ Π·Π° ΠΏΠΎΠ²Π΅ΡΠ΅ΡΠΎ Node.JS ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ. ΠΠ°ΠΊΠ²ΠΎ ΡΠ΅ ΡΡΠ°Π½Π΅, Π°ΠΊΠΎ Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ ΠΈΠΌΠ°ΡΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π·Π° JVM ΠΏΡΠΎΠ΅ΠΊΡ Ρ Π²ΠΊΠ»ΡΡΠ΅Π½ Π²ΡΡΡΠ΅ ΡΠΎΠ½Π°ΡΠ΅Π½ ΡΠΊΠ΅Π½Π΅Ρ? ΠΠΈΠ΅ ΡΡΠ΅ ΡΠ²ΠΎΠ±ΠΎΠ΄Π½ΠΈ Π΄Π° ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΈ Π·Π° ΡΠ³Π»ΠΎΠ±ΡΠ²Π°Π½Π΅.
FROM adoptopenjdk/openjdk12:latest
RUN apt update
&& apt install -y
bash unzip wget
RUN mkdir -p /usr/local/sonarscanner
&& cd /usr/local/sonarscanner
&& wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip
&& unzip sonar-scanner-cli-3.3.0.1492-linux.zip
&& mv sonar-scanner-3.3.0.1492-linux/* ./
&& rm sonar-scanner-cli-3.3.0.1492-linux.zip
&& rm -rf sonar-scanner-3.3.0.1492-linux
&& ln -s /usr/local/sonarscanner/bin/sonar-scanner /usr/local/bin/sonar-scanner
ENV PATH $PATH:/usr/local/sonarscanner/bin/
ENV SONAR_RUNNER_HOME /usr/local/sonarscanner/bin/
ΠΠΏΠΈΡΠ°Ρ ΠΌΠ΅ Π°ΡΠ΅ΠΌΠ±Π»ΠΈΡΠ°ΡΠ°ΡΠ° ΡΡΠ΅Π΄Π°, Π½ΠΎ ΠΊΠ°ΠΊΠ²ΠΎ ΠΎΠ±ΡΠΎ ΠΈΠΌΠ° ΠΠΆΠ΅Π½ΠΊΠΈΠ½Ρ Ρ Π½Π΅Ρ? Π Π°Π³Π΅Π½ΡΠΈΡΠ΅ Π½Π° Jenkins ΠΌΠΎΠ³Π°Ρ Π΄Π° ΡΠ°Π±ΠΎΡΡΡ Ρ ΡΠ°ΠΊΠΈΠ²Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Π½Π° Docker ΠΈ Π΄Π° Π³ΠΈ ΡΡΠ·Π΄Π°Π²Π°Ρ Π²ΡΡΡΠ΅ΡΠ½ΠΎ.
stage("Build project") {
agent {
docker {
image "project-build:${DOCKER_IMAGE_BRANCH}"
args "-v ${PWD}:/usr/src/app -w /usr/src/app"
reuseNode true
label "build-image"
}
}
steps {
sh "yarn"
sh "yarn build"
}
}
ΠΠΈΡΠ΅ΠΊΡΠΈΠ²Π° agent
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΈΠΌΡΡΠ΅ΡΡΠ²ΠΎ docker
ΠΊΡΠ΄Π΅ΡΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΏΠΎΡΠΎΡΠΈΡΠ΅:
- ΠΈΠΌΠ΅ΡΠΎ Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° Π·Π° ΡΠ³Π»ΠΎΠ±ΡΠ²Π°Π½Π΅ ΡΠΏΠΎΡΠ΅Π΄ Π²Π°ΡΠ°ΡΠ° ΠΏΠΎΠ»ΠΈΡΠΈΠΊΠ° Π·Π° ΠΈΠΌΠ΅Π½ΡΠ²Π°Π½Π΅;
- Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΈ, Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΈ Π·Π° ΡΡΠ°ΡΡΠΈΡΠ°Π½Π΅ Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅, ΠΊΡΠ΄Π΅ΡΠΎ Π² Π½Π°ΡΠΈΡ ΡΠ»ΡΡΠ°ΠΉ ΠΌΠΎΠ½ΡΠΈΡΠ°ΠΌΠ΅ ΡΠ΅ΠΊΡΡΠ°ΡΠ° Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡ ΠΊΠ°ΡΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡ Π²ΡΡΡΠ΅ Π² ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°.
Π ΠΎΡΠ΅ Π² ΡΡΡΠΏΠΊΠΈΡΠ΅ Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ Π½ΠΈΠ΅ ΠΏΠΎΡΠΎΡΠ²Π°ΠΌΠ΅ ΠΊΠΎΠΈ ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ Π΄Π° ΡΠ΅ ΠΈΠ·ΠΏΡΠ»Π½ΡΡ Π² Π°Π³Π΅Π½ΡΠ° Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ Π½Π° Docker. Π’ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° Π±ΡΠ΄Π΅ Π²ΡΠΈΡΠΊΠΎ, ΡΠ°ΠΊΠ° ΡΠ΅ Π°Π· ΡΡΡΠΎ ΡΡΠ°ΡΡΠΈΡΠ°ΠΌ Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ Π½Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ansible.
ΠΠΎ-Π΄ΠΎΠ»Ρ ΠΈΡΠΊΠ°ΠΌ Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ° ΠΎΠ±Ρ Jenkinsfile, ΠΊΠΎΠΉΡΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΈΠ·Π³ΡΠ°Π΄ΠΈ ΠΎΡ ΠΏΡΠΎΡΡΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Node.JS.
def DOCKER_IMAGE_BRANCH = ""
def GIT_COMMIT_HASH = ""
pipeline {
options {
buildDiscarder(
logRotator(
artifactDaysToKeepStr: "",
artifactNumToKeepStr: "",
daysToKeepStr: "",
numToKeepStr: "10"
)
)
disableConcurrentBuilds()
}
agent any
stages {
stage("Prepare build image") {
steps {
sh "docker build -f Dockerfile.build . -t project-build:${DOCKER_IMAGE_BRANCH}"
}
}
stage("Build project") {
agent {
docker {
image "project-build:${DOCKER_IMAGE_BRANCH}"
args "-v ${PWD}:/usr/src/app -w /usr/src/app"
reuseNode true
label "build-image"
}
}
steps {
sh "yarn"
sh "yarn build"
}
}
post {
always {
step([$class: "WsCleanup"])
cleanWs()
}
}
}
ΠΠ°ΠΊΠ²ΠΎ ΡΡΠ°Π½Π°?
ΠΠ»Π°Π³ΠΎΠ΄Π°ΡΠ΅Π½ΠΈΠ΅ Π½Π° ΡΠΎΠ·ΠΈ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ΅ΡΠΈΡ ΠΌΠ΅ ΡΠ»Π΅Π΄Π½ΠΈΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ:
- Π²ΡΠ΅ΠΌΠ΅ΡΠΎ Π·Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°Π½Π΅ Π½Π° ΡΠ³Π»ΠΎΠ±ΡΠ²Π°Π½Π΅ΡΠΎ Π½Π° ΡΡΠ΅Π΄Π°ΡΠ° Π΅ Π½Π°ΠΌΠ°Π»Π΅Π½ΠΎ Π΄ΠΎ 10 - 15 ΠΌΠΈΠ½ΡΡΠΈ Π½Π° ΠΏΡΠΎΠ΅ΠΊΡ;
- Π½Π°ΠΏΡΠ»Π½ΠΎ ΠΏΠΎΠ²ΡΠΎΡΡΠ΅ΠΌΠ° ΡΡΠ΅Π΄Π° Π·Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ, ΡΡΠΉ ΠΊΠ°ΡΠΎ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π³ΠΎ ΠΈΠ·Π³ΡΠ°Π΄ΠΈΡΠ΅ ΠΏΠΎ ΡΠΎΠ·ΠΈ Π½Π°ΡΠΈΠ½ Π½Π° Π²Π°ΡΠΈΡ Π»ΠΎΠΊΠ°Π»Π΅Π½ ΠΊΠΎΠΌΠΏΡΡΡΡ;
- Π½ΡΠΌΠ° ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ Ρ ΠΊΠΎΠ½ΡΠ»ΠΈΠΊΡΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°Π·Π»ΠΈΡΠ½ΠΈ Π²Π΅ΡΡΠΈΠΈ Π½Π° ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠΈ Π·Π° ΡΠ³Π»ΠΎΠ±ΡΠ²Π°Π½Π΅;
- Π²ΠΈΠ½Π°Π³ΠΈ ΡΠΈΡΡΠΎ ΡΠ°Π±ΠΎΡΠ½ΠΎ ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²ΠΎ, ΠΊΠΎΠ΅ΡΠΎ Π½Π΅ ΡΠ΅ Π·Π°Π΄ΡΡΡΡΠ²Π°.
Π‘Π°ΠΌΠΎΡΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π΅ ΠΏΡΠΎΡΡΠΎ ΠΈ ΠΎΡΠ΅Π²ΠΈΠ΄Π½ΠΎ ΠΈ Π²ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ Π½ΡΠΊΠΎΠΈ ΠΏΡΠ΅Π΄ΠΈΠΌΡΡΠ²Π°. ΠΠ°, ΠΏΡΠ°Π³ΡΡ Π·Π° Π²Π»ΠΈΠ·Π°Π½Π΅ ΡΠ΅ ΠΏΠΎΠ²ΠΈΡΠΈ ΠΌΠ°Π»ΠΊΠΎ Π² ΡΡΠ°Π²Π½Π΅Π½ΠΈΠ΅ Ρ ΠΏΡΠΎΡΡΠΈΡΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ Π·Π° ΡΠ³Π»ΠΎΠ±ΠΊΠΈ, Π½ΠΎ ΡΠ΅Π³Π° ΠΈΠΌΠ° Π³Π°ΡΠ°Π½ΡΠΈΡ, ΡΠ΅ Π²ΠΈΠ½Π°Π³ΠΈ ΡΠ΅ Π±ΡΠ΄Π΅ ΠΈΠ·Π³ΡΠ°Π΄Π΅Π½ ΠΈ ΡΠ°ΠΌΠΈΡΡ ΡΠ°Π·ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΈΠ·Π±Π΅ΡΠ΅ Π²ΡΠΈΡΠΊΠΎ, ΠΊΠΎΠ΅ΡΠΎ Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ Π·Π° Π½Π΅Π³ΠΎΠ²ΠΈΡ ΠΏΡΠΎΡΠ΅Ρ Π½Π° ΠΈΠ·Π³ΡΠ°ΠΆΠ΄Π°Π½Π΅.
ΠΠΎΠΆΠ΅ΡΠ΅ ΡΡΡΠΎ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ, ΠΊΠΎΠ΅ΡΠΎ ΡΡΠ±ΡΠ°Ρ
ΠΠΎΠΊΠ°ΡΠΎ ΠΏΠΈΡΠ΅Ρ
ΡΠ°Π·ΠΈ ΡΡΠ°ΡΠΈΡ, Π²ΡΠ·Π½ΠΈΠΊΠ½Π° Π΄ΠΈΡΠΊΡΡΠΈΡ ΠΎΡΠ½ΠΎΡΠ½ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ΡΠΎ Π½Π° Π°Π³Π΅Π½ΡΠΈ Π½Π° ΠΎΡΠ΄Π°Π»Π΅ΡΠ΅Π½ΠΈ ΡΡΡΠ²ΡΡΠΈ, Π·Π° Π΄Π° Π½Π΅ ΡΠ΅ Π·Π°ΡΠ΅ΠΆΠ΄Π° Π³Π»Π°Π²Π½ΠΈΡΡ Π²ΡΠ·Π΅Π» Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΠΏΠ»ΡΠ³ΠΈΠ½
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com