рдХреНрд▓рд╕реНрдЯрд░ рдХреЗ рд▓рд┐рдП рдЕрдкрдирд╛ рдЦреБрдж рдХрд╛ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдХреИрд╕реЗ рдмрдирд╛рдПрдВ

рдирдорд╕реНрддреЗ! рд╣рдо рд▓реЛрдЧреЛрдВ рдХреЛ рдмрдбрд╝реЗ рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдХреНрд▓рд╕реНрдЯрд░ рдХреЗ рдмрд┐рдирд╛ рдмрдбрд╝реЗ рдбреЗрдЯрд╛ рдкрд░ рдПрдХ рд╢реИрдХреНрд╖рд┐рдХ рдХрд╛рд░реНрдпрдХреНрд░рдо рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реИ, рдЬрд┐рд╕ рдкрд░ рд╕рднреА рдкреНрд░рддрд┐рднрд╛рдЧреА рдПрдХ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ рдХрд╛рд░рдг рд╕реЗ, рд╣рдорд╛рд░реЗ рдХрд╛рд░реНрдпрдХреНрд░рдо рдореЗрдВ рдпрд╣ рд╣рдореЗрд╢рд╛ рд╣реЛрддрд╛ рд╣реИ :) рд╣рдо рдЗрд╕рдХреЗ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди, рдЯреНрдпреВрдирд┐рдВрдЧ рдФрд░ рдкреНрд░рд╢рд╛рд╕рди рдореЗрдВ рд▓рдЧреЗ рд╣реБрдП рд╣реИрдВ, рдФрд░ рд▓реЛрдЧ рд╕реАрдзреЗ рд╡рд╣рд╛рдВ MapReduce рдиреМрдХрд░рд┐рдпрд╛рдВ рд▓реЙрдиреНрдЪ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рд╕реНрдкрд╛рд░реНрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред

рдЗрд╕ рдкреЛрд╕реНрдЯ рдореЗрдВ рд╣рдо рдЖрдкрдХреЛ рдмрддрд╛рдПрдВрдЧреЗ рдХрд┐ рдХреИрд╕реЗ рд╣рдордиреЗ рдХреНрд▓рд╛рдЙрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рд▓рд┐рдЦрдХрд░ рдЕрд╕рдорд╛рди рдХреНрд▓рд╕реНрдЯрд░ рд▓реЛрдбрд┐рдВрдЧ рдХреА рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд┐рдпрд╛ Mail.ru рдХреНрд▓рд╛рдЙрдб рд╕реЙрд▓реНрдпреВрд╢рдВрд╕.

рд╕рдорд╕реНрдпрд╛

рд╣рдорд╛рд░реЗ рдХреНрд▓рд╕реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╕рд╛рдорд╛рдиреНрдп рдореЛрдб рдореЗрдВ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдирд┐рдкрдЯрд╛рди рдЕрддреНрдпрдзрд┐рдХ рдЕрд╕рдорд╛рди рд╣реИ. рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рдХрдХреНрд╖рд╛рдПрдВ рд╣реЛрддреА рд╣реИрдВ, рдЬрдм рд╕рднреА 30 рд▓реЛрдЧ рдФрд░ рдПрдХ рд╢рд┐рдХреНрд╖рдХ рдХреНрд▓рд╕реНрдЯрд░ рдореЗрдВ рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдпрд╛ рдлрд┐рд░, рд╕рдордп рд╕реАрдорд╛ рд╕реЗ рдкрд╣рд▓реЗ рдРрд╕реЗ рджрд┐рди рд╣реЛрддреЗ рд╣реИрдВ рдЬрдм рднрд╛рд░ рдмрд╣реБрдд рдмрдврд╝ рдЬрд╛рддрд╛ рд╣реИред рдмрд╛рдХреА рд╕рдордп рдХреНрд▓рд╕реНрдЯрд░ рдЕрдВрдбрд░рд▓реЛрдб рдореЛрдб рдореЗрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред

рд╕рдорд╛рдзрд╛рди #1 рдПрдХ рдРрд╕рд╛ рдХреНрд▓рд╕реНрдЯрд░ рд░рдЦрдирд╛ рд╣реИ рдЬреЛ рдЪрд░рдо рднрд╛рд░ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░реЗрдЧрд╛, рд▓реЗрдХрд┐рди рдмрд╛рдХреА рд╕рдордп рдирд┐рд╖реНрдХреНрд░рд┐рдп рд░рд╣реЗрдЧрд╛ред

рд╕рдорд╛рдзрд╛рди #2 рдПрдХ рдЫреЛрдЯрд╛ рдХреНрд▓рд╕реНрдЯрд░ рд░рдЦрдирд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдЖрдк рдХрдХреНрд╖рд╛рдУрдВ рд╕реЗ рдкрд╣рд▓реЗ рдФрд░ рдкреАрдХ рд▓реЛрдб рдХреЗ рджреМрд░рд╛рди рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдиреЛрдбреНрд╕ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред

рд╕рдорд╛рдзрд╛рди #3 рдПрдХ рдЫреЛрдЯрд╛ рдХреНрд▓рд╕реНрдЯрд░ рд░рдЦрдирд╛ рдФрд░ рдПрдХ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рд▓рд┐рдЦрдирд╛ рд╣реИ рдЬреЛ рдХреНрд▓рд╕реНрдЯрд░ рдХреЗ рд╡рд░реНрддрдорд╛рди рд▓реЛрдб рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░реЗрдЧрд╛ рдФрд░, рд╡рд┐рднрд┐рдиреНрди рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдХреНрд▓рд╕реНрдЯрд░ рд╕реЗ рдиреЛрдбреНрд╕ рдЬреЛрдбрд╝ рдФрд░ рд╣рдЯрд╛ рджреЗрдЧрд╛ред

рдЗрд╕ рдкреЛрд╕реНрдЯ рдореЗрдВ рд╣рдо рд╕рдорд╛рдзрд╛рди #3 рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗред рдпрд╣ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдЖрдВрддрд░рд┐рдХ рдХрд╛рд░рдХреЛрдВ рдХреЗ рдмрдЬрд╛рдп рдмрд╛рд╣рд░реА рдХрд╛рд░рдХреЛрдВ рдкрд░ рдЕрддреНрдпрдзрд┐рдХ рдирд┐рд░реНрднрд░ рд╣реИ, рдФрд░ рдкреНрд░рджрд╛рддрд╛ рдЕрдХреНрд╕рд░ рдЗрд╕реЗ рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рд╣рдо Mail.ru рдХреНрд▓рд╛рдЙрдб рд╕реЙрд▓реНрдпреВрд╢рдВрд╕ рдХреНрд▓рд╛рдЙрдб рдЗрдВрдлреНрд░рд╛рд╕реНрдЯреНрд░рдХреНрдЪрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ MCS API рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рд▓рд┐рдЦрддреЗ рд╣реИрдВред рдФрд░ рдЪреВрдВрдХрд┐ рд╣рдо рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рд╕рд┐рдЦрд╛рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдордиреЗ рдпрд╣ рджрд┐рдЦрд╛рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдХрд┐ рдЖрдк рдЕрдкрдиреЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╛рди рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдХреИрд╕реЗ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдЕрдкрдиреЗ рдХреНрд▓рд╛рдЙрдб рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ

.. рдкреВрд░реНрд╡рд╛рдкреЗрдХреНрд╖рд╛рдПрдБ

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ Hadoop рдХреНрд▓рд╕реНрдЯрд░ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рдо рдПрдЪрдбреАрдкреА рд╡рд┐рддрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред

рдЖрдкрдХреЗ рдиреЛрдбреНрд╕ рдХреЛ рд╢реАрдШреНрд░рддрд╛ рд╕реЗ рдЬреЛрдбрд╝рдиреЗ рдФрд░ рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЗ рдкрд╛рд╕ рдиреЛрдбреНрд╕ рдХреЗ рдмреАрдЪ рднреВрдорд┐рдХрд╛рдУрдВ рдХрд╛ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╡рд┐рддрд░рдг рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

  1. рдорд╛рд╕реНрдЯрд░ рдиреЛрдб. рдЦреИрд░, рдпрд╣рд╛рдВ рд╕рдордЭрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдХреБрдЫ рднреА рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИ: рдХреНрд▓рд╕реНрдЯрд░ рдХрд╛ рдореБрдЦреНрдп рдиреЛрдб, рдЬрд┐рд╕ рдкрд░, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕реНрдкрд╛рд░реНрдХ рдбреНрд░рд╛рдЗрд╡рд░ рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдпрджрд┐ рдЖрдк рдЗрдВрдЯрд░реИрдХреНрдЯрд┐рд╡ рдореЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред
  2. рджрд┐рдирд╛рдВрдХ рдиреЛрдб. рдпрд╣ рд╡рд╣ рдиреЛрдб рд╣реИ рдЬрд┐рд╕ рдкрд░ рдЖрдк рдПрдЪрдбреАрдПрдлрдПрд╕ рдкрд░ рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЬрд╣рд╛рдВ рдЧрдгрдирд╛ рд╣реЛрддреА рд╣реИред
  3. рдХрдВрдкреНрдпреВрдЯрд┐рдВрдЧ рдиреЛрдб. рдпрд╣ рдПрдХ рдиреЛрдб рд╣реИ рдЬрд╣рд╛рдВ рдЖрдк рдПрдЪрдбреАрдПрдлрдПрд╕ рдкрд░ рдХреБрдЫ рднреА рд╕рдВрдЧреНрд░рд╣реАрдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЬрд╣рд╛рдВ рдЧрдгрдирд╛ рд╣реЛрддреА рд╣реИред

рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд┐рдВрджреБред рддреАрд╕рд░реЗ рдкреНрд░рдХрд╛рд░ рдХреЗ рдиреЛрдбреНрд╕ рдХреЗ рдХрд╛рд░рдг рдСрдЯреЛрд╕реНрдХреЗрд▓рд┐рдВрдЧ рд╣реЛрдЧреАред рдпрджрд┐ рдЖрдк рджреВрд╕рд░реЗ рдкреНрд░рдХрд╛рд░ рдХреЗ рдиреЛрдбреНрд╕ рд▓реЗрдирд╛ рдФрд░ рдЬреЛрдбрд╝рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреА рдЧрддрд┐ рдмрд╣реБрдд рдХрдо рд╣реЛрдЧреА - рдбреАрдХрдореАрд╢рдирд┐рдВрдЧ рдФрд░ рдкреБрдирдГ рдХрдорд┐рдЯрд┐рдВрдЧ рдореЗрдВ рдЖрдкрдХреЗ рдХреНрд▓рд╕реНрдЯрд░ рдкрд░ рдШрдВрдЯреЛрдВ рд▓рдЧреЗрдВрдЧреЗред рдирд┐рдГрд╕рдВрджреЗрд╣, рдпрд╣ рд╡рд╣ рдирд╣реАрдВ рд╣реИ рдЬрд┐рд╕рдХреА рдЖрдк рдСрдЯреЛрд╕реНрдХреЗрд▓рд┐рдВрдЧ рд╕реЗ рдЕрдкреЗрдХреНрд╖рд╛ рдХрд░рддреЗ рд╣реИрдВред рдпрд╛рдиреА рд╣рдо рдкрд╣рд▓реЗ рдФрд░ рджреВрд╕рд░реЗ рдкреНрд░рдХрд╛рд░ рдХреЗ рдиреЛрдбреНрд╕ рдХреЛ рдирд╣реАрдВ рдЫреВрддреЗ рд╣реИрдВред рд╡реЗ рдПрдХ рдиреНрдпреВрдирддрдо рд╡реНрдпрд╡рд╣рд╛рд░реНрдп рдХреНрд▓рд╕реНрдЯрд░ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдХрд╛рд░реНрдпрдХреНрд░рдо рдХреА рдкреВрд░реА рдЕрд╡рдзрд┐ рдХреЗ рджреМрд░рд╛рди рдореМрдЬреВрдж рд░рд╣реЗрдЧрд╛ред

рддреЛ, рд╣рдорд╛рд░рд╛ рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдкрд╛рдпрдерди 3 рдореЗрдВ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИ, рдХреНрд▓рд╕реНрдЯрд░ рд╕реЗрд╡рд╛рдУрдВ, рдЙрдкрдпреЛрдЧреЛрдВ рдХреЛ рдкреНрд░рдмрдВрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрдмрд╛рд░реА рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ Mail.ru рдХреНрд▓рд╛рдЙрдб рд╕реЙрд▓реНрдпреВрд╢рдВрд╕ рд╕реЗ рдПрдкреАрдЖрдИ (рдПрдорд╕реАрдПрд╕) рдорд╢реАрдиреЛрдВ рдХреЛ рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдПред

рд╕рдорд╛рдзрд╛рди рд╡рд╛рд╕реНрддреБрдХрд▓рд╛

  1. рдореЙрдбреНрдпреВрд▓ autoscaler.py. рдЗрд╕рдореЗрдВ рддреАрди рд╡рд░реНрдЧ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ: 1) рдЕрдВрдмрд╛рд░реА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрдп, 2) рдПрдорд╕реАрдПрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрдп, 3) рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдХреЗ рддрд░реНрдХ рд╕реЗ рд╕реАрдзреЗ рд╕рдВрдмрдВрдзрд┐рдд рдХрд╛рд░реНрдпред
  2. рд▓рд┐рдкрд┐ observer.py. рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдк рд╕реЗ рдЗрд╕рдореЗрдВ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдирд┐рдпрдо рд╢рд╛рдорд┐рд▓ рд╣реИрдВ: рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХреЛ рдХрдм рдФрд░ рдХрд┐рд╕ рдХреНрд╖рдг рдХреЙрд▓ рдХрд░рдирд╛ рд╣реИред
  3. рд╡рд┐рдиреНрдпрд╛рд╕ рдлрд╛рдЗрд▓ config.py. рдЗрд╕рдореЗрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдСрдЯреЛрд╕реНрдХреЗрд▓рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдордд рдиреЛрдбреНрд╕ рдХреА рдПрдХ рд╕реВрдЪреА рдФрд░ рдЕрдиреНрдп рдкреИрд░рд╛рдореАрдЯрд░ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ рдЬреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдХ рдирдпрд╛ рдиреЛрдб рдЬреЛрдбрд╝реЗ рдЬрд╛рдиреЗ рдХреЗ рдХреНрд╖рдг рд╕реЗ рдХрд┐рддрдиреА рджреЗрд░ рддрдХ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдиреА рд╣реИред рдХрдХреНрд╖рд╛рдУрдВ рдХреА рд╢реБрд░реБрдЖрдд рдХреЗ рд▓рд┐рдП рдЯрд╛рдЗрдорд╕реНрдЯреИрдореНрдк рднреА рд╣реИрдВ, рддрд╛рдХрд┐ рдХрдХреНрд╖рд╛ рд╕реЗ рдкрд╣рд▓реЗ рдЕрдзрд┐рдХрддрдо рдЕрдиреБрдордд рдХреНрд▓рд╕реНрдЯрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗред

рдЖрдЗрдП рдЕрдм рдкрд╣рд▓реА рджреЛ рдлрд╛рдЗрд▓реЛрдВ рдХреЗ рдЕрдВрджрд░ рдХреЛрдб рдХреЗ рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рджреЗрдЦреЗрдВред

1. Autoscaler.py рдореЙрдбреНрдпреВрд▓

рдЕрдВрдмрд╛рд░реА рдХрдХреНрд╖рд╛

рдХреНрд▓рд╛рд╕ рдпреБрдХреНрдд рдХреЛрдб рдХрд╛ рдПрдХ рдЯреБрдХрдбрд╝рд╛ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ Ambari:

class Ambari:
    def __init__(self, ambari_url, cluster_name, headers, auth):
        self.ambari_url = ambari_url
        self.cluster_name = cluster_name
        self.headers = headers
        self.auth = auth

    def stop_all_services(self, hostname):
        url = self.ambari_url + self.cluster_name + '/hosts/' + hostname + '/host_components/'
        url2 = self.ambari_url + self.cluster_name + '/hosts/' + hostname
        req0 = requests.get(url2, headers=self.headers, auth=self.auth)
        services = req0.json()['host_components']
        services_list = list(map(lambda x: x['HostRoles']['component_name'], services))
        data = {
            "RequestInfo": {
                "context":"Stop All Host Components",
                "operation_level": {
                    "level":"HOST",
                    "cluster_name": self.cluster_name,
                    "host_names": hostname
                },
                "query":"HostRoles/component_name.in({0})".format(",".join(services_list))
            },
            "Body": {
                "HostRoles": {
                    "state":"INSTALLED"
                }
            }
        }
        req = requests.put(url, data=json.dumps(data), headers=self.headers, auth=self.auth)
        if req.status_code in [200, 201, 202]:
            message = 'Request accepted'
        else:
            message = req.status_code
        return message

рдКрдкрд░, рдЙрджрд╛рд╣рд░рдг рдХреЗ рддреМрд░ рдкрд░, рдЖрдк рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ stop_all_services, рдЬреЛ рд╡рд╛рдВрдЫрд┐рдд рдХреНрд▓рд╕реНрдЯрд░ рдиреЛрдб рдкрд░ рд╕рднреА рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рд░реЛрдХ рджреЗрддрд╛ рд╣реИред

рдХрдХреНрд╖рд╛ рдХреЗ рдкреНрд░рд╡реЗрд╢ рджреНрд╡рд╛рд░ рдкрд░ Ambari рдЖрдк рдирд┐рдХрд▓рд┐рдП:

  • ambari_url, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЬреИрд╕реЗ 'http://localhost:8080/api/v1/clusters/',
  • cluster_name - рдЕрдВрдмрд╛рд░реА рдореЗрдВ рдЖрдкрдХреЗ рдХреНрд▓рд╕реНрдЯрд░ рдХрд╛ рдирд╛рдо,
  • headers = {'X-Requested-By': 'ambari'}
  • рдФрд░ рдЕрдВрджрд░ auth рдЕрдВрдмрд╛рд░реА рдХреЗ рд▓рд┐рдП рдЖрдкрдХрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб рдпрд╣рд╛рдВ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ: auth = ('login', 'password').

рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рд╕реНрд╡рдпрдВ REST API рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЕрдВрдмрд╛рд░реА рдХреЛ рдХреБрдЫ рдХреЙрд▓реЛрдВ рд╕реЗ рдЕрдзрд┐рдХ рдХреБрдЫ рдирд╣реАрдВ рд╣реИред рддрд╛рд░реНрдХрд┐рдХ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ, рд╣рдо рдкрд╣рд▓реЗ рдПрдХ рдиреЛрдб рдкрд░ рдЪрд▓ рд░рд╣реА рд╕реЗрд╡рд╛рдУрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдХреНрд▓рд╕реНрдЯрд░ рдкрд░, рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдиреЛрдб рдкрд░, рд╕реВрдЪреА рд╕реЗ рд░рд╛рдЬреНрдп рдореЗрдВ рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рддреЗ рд╣реИрдВред INSTALLED. рд╕рднреА рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ, рдиреЛрдбреНрд╕ рдХреЛ рд░рд╛рдЬреНрдп рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрдп Maintenance рдЖрджрд┐ рд╕рдорд╛рди рджрд┐рдЦрддреЗ рд╣реИрдВ - рд╡реЗ рдПрдкреАрдЖрдИ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХреЗрд╡рд▓ рдХреБрдЫ рдЕрдиреБрд░реЛрдз рд╣реИрдВред

рдХреНрд▓рд╛рд╕ рдПрдо.рд╕реА.рдПрд╕

рдХреНрд▓рд╛рд╕ рдпреБрдХреНрдд рдХреЛрдб рдХрд╛ рдПрдХ рдЯреБрдХрдбрд╝рд╛ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ Mcs:

class Mcs:
    def __init__(self, id1, id2, password):
        self.id1 = id1
        self.id2 = id2
        self.password = password
        self.mcs_host = 'https://infra.mail.ru:8774/v2.1'

    def vm_turn_on(self, hostname):
        self.token = self.get_mcs_token()
        host = self.hostname_to_vmname(hostname)
        vm_id = self.get_vm_id(host)
        mcs_url1 = self.mcs_host + '/servers/' + self.vm_id + '/action'
        headers = {
            'X-Auth-Token': '{0}'.format(self.token),
            'Content-Type': 'application/json'
        }
        data = {'os-start' : 'null'}
        mcs = requests.post(mcs_url1, data=json.dumps(data), headers=headers)
        return mcs.status_code

рдХрдХреНрд╖рд╛ рдХреЗ рдкреНрд░рд╡реЗрд╢ рджреНрд╡рд╛рд░ рдкрд░ Mcs рд╣рдо рдХреНрд▓рд╛рдЙрдб рдХреЗ рдЕрдВрджрд░ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдЖрдИрдбреА рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЖрдИрдбреА, рд╕рд╛рде рд╣реА рдЙрд╕рдХрд╛ рдкрд╛рд╕рд╡рд░реНрдб рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВред рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ vm_turn_on рд╣рдо рдорд╢реАрдиреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдХреЛ рдЪрд╛рд▓реВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдпрд╣рд╛рдВ рддрд░реНрдХ рдереЛрдбрд╝рд╛ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реИред рдХреЛрдб рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ, рддреАрди рдЕрдиреНрдп рдлрд╝рдВрдХреНрд╢рди рдХреЙрд▓ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ: 1) рд╣рдореЗрдВ рдПрдХ рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, 2) рд╣рдореЗрдВ рд╣реЛрд╕реНрдЯрдирд╛рдо рдХреЛ рдПрдорд╕реАрдПрд╕ рдореЗрдВ рдорд╢реАрди рдХреЗ рдирд╛рдо рдореЗрдВ рдмрджрд▓рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, 3) рдЗрд╕ рдорд╢реАрди рдХреА рдЖрдИрдбреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред рдЗрд╕рдХреЗ рдмрд╛рдж, рд╣рдо рдмрд╕ рдПрдХ рдкреЛрд╕реНрдЯ рдЕрдиреБрд░реЛрдз рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕ рдорд╢реАрди рдХреЛ рд▓реЙрдиреНрдЪ рдХрд░рддреЗ рд╣реИрдВред

рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдХрд╛рд░реНрдп рдЗрд╕ рдкреНрд░рдХрд╛рд░ рджрд┐рдЦрддрд╛ рд╣реИ:

def get_mcs_token(self):
        url = 'https://infra.mail.ru:35357/v3/auth/tokens?nocatalog'
        headers = {'Content-Type': 'application/json'}
        data = {
            'auth': {
                'identity': {
                    'methods': ['password'],
                    'password': {
                        'user': {
                            'id': self.id1,
                            'password': self.password
                        }
                    }
                },
                'scope': {
                    'project': {
                        'id': self.id2
                    }
                }
            }
        }
        params = (('nocatalog', ''),)
        req = requests.post(url, data=json.dumps(data), headers=headers, params=params)
        self.token = req.headers['X-Subject-Token']
        return self.token

рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдХреНрд▓рд╛рд╕

рдЗрд╕ рд╡рд░реНрдЧ рдореЗрдВ рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд▓реЙрдЬрд┐рдХ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдлрд╝рдВрдХреНрд╢рди рд╢рд╛рдорд┐рд▓ рд╣реИрдВред

рдЗрд╕ рд╡рд░реНрдЧ рдХреЗ рд▓рд┐рдП рдХреЛрдб рдХрд╛ рдПрдХ рдЯреБрдХрдбрд╝рд╛ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рджрд┐рдЦрддрд╛ рд╣реИ:

class Autoscaler:
    def __init__(self, ambari, mcs, scaling_hosts, yarn_ram_per_node, yarn_cpu_per_node):
        self.scaling_hosts = scaling_hosts
        self.ambari = ambari
        self.mcs = mcs
        self.q_ram = deque()
        self.q_cpu = deque()
        self.num = 0
        self.yarn_ram_per_node = yarn_ram_per_node
        self.yarn_cpu_per_node = yarn_cpu_per_node

    def scale_down(self, hostname):
        flag1 = flag2 = flag3 = flag4 = flag5 = False
        if hostname in self.scaling_hosts:
            while True:
                time.sleep(5)
                status1 = self.ambari.decommission_nodemanager(hostname)
                if status1 == 'Request accepted' or status1 == 500:
                    flag1 = True
                    logging.info('Decomission request accepted: {0}'.format(flag1))
                    break
            while True:
                time.sleep(5)
                status3 = self.ambari.check_service(hostname, 'NODEMANAGER')
                if status3 == 'INSTALLED':
                    flag3 = True
                    logging.info('Nodemaneger decommissioned: {0}'.format(flag3))
                    break
            while True:
                time.sleep(5)
                status2 = self.ambari.maintenance_on(hostname)
                if status2 == 'Request accepted' or status2 == 500:
                    flag2 = True
                    logging.info('Maintenance request accepted: {0}'.format(flag2))
                    break
            while True:
                time.sleep(5)
                status4 = self.ambari.check_maintenance(hostname, 'NODEMANAGER')
                if status4 == 'ON' or status4 == 'IMPLIED_FROM_HOST':
                    flag4 = True
                    self.ambari.stop_all_services(hostname)
                    logging.info('Maintenance is on: {0}'.format(flag4))
                    logging.info('Stopping services')
                    break
            time.sleep(90)
            status5 = self.mcs.vm_turn_off(hostname)
            while True:
                time.sleep(5)
                status5 = self.mcs.get_vm_info(hostname)['server']['status']
                if status5 == 'SHUTOFF':
                    flag5 = True
                    logging.info('VM is turned off: {0}'.format(flag5))
                    break
            if flag1 and flag2 and flag3 and flag4 and flag5:
                message = 'Success'
                logging.info('Scale-down finished')
                logging.info('Cooldown period has started. Wait for several minutes')
        return message

рд╣рдо рдкреНрд░рд╡реЗрд╢ рдХреЗ рд▓рд┐рдП рдХрдХреНрд╖рд╛рдПрдВ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддреЗ рд╣реИрдВред Ambari ╨╕ Mcs, рдиреЛрдбреНрд╕ рдХреА рдПрдХ рд╕реВрдЪреА рдЬрд┐рдиреНрд╣реЗрдВ рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдорддрд┐ рджреА рдЧрдИ рд╣реИ, рд╕рд╛рде рд╣реА рдиреЛрдб рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдкреИрд░рд╛рдореАрдЯрд░: YARN рдореЗрдВ рдиреЛрдб рдХреЛ рдЖрд╡рдВрдЯрд┐рдд рдореЗрдореЛрд░реА рдФрд░ рд╕реАрдкреАрдпреВред 2 рдЖрдВрддрд░рд┐рдХ рдкреИрд░рд╛рдореАрдЯрд░ q_ram, q_cpu рднреА рд╣реИрдВ, рдЬреЛ рдХрддрд╛рд░ рд╣реИрдВред рдЙрдирдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рд╣рдо рд╡рд░реНрддрдорд╛рди рдХреНрд▓рд╕реНрдЯрд░ рд▓реЛрдб рдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддреЗ рд╣реИрдВред рдпрджрд┐ рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдкрд┐рдЫрд▓реЗ 5 рдорд┐рдирдЯ рдореЗрдВ рд▓реЛрдб рдореЗрдВ рд▓рдЧрд╛рддрд╛рд░ рд╡реГрджреНрдзрд┐ рд╣реБрдИ рд╣реИ, рддреЛ рд╣рдо рдирд┐рд░реНрдгрдп рд▓реЗрддреЗ рд╣реИрдВ рдХрд┐ рд╣рдореЗрдВ рдХреНрд▓рд╕реНрдЯрд░ рдореЗрдВ +1 рдиреЛрдб рдЬреЛрдбрд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдХреНрд▓рд╕реНрдЯрд░ рдЕрд▓реНрдкрдЙрдкрдпреЛрдЧ рд╕реНрдерд┐рддрд┐ рдХреЗ рд▓рд┐рдП рднреА рдпрд╣реА рд╕рдЪ рд╣реИред

рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдЬреЛ рдПрдХ рдорд╢реАрди рдХреЛ рдХреНрд▓рд╕реНрдЯрд░ рд╕реЗ рдирд┐рдХрд╛рд▓рддрд╛ рд╣реИ рдФрд░ рдХреНрд▓рд╛рдЙрдб рдореЗрдВ рд░реЛрдХрддрд╛ рд╣реИред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдбреАрдХрдореАрд╢рдирд┐рдВрдЧ рд╣реЛрддреА рд╣реИ YARN Nodemanager, рдлрд┐рд░ рдореЛрдб рдЪрд╛рд▓реВ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ Maintenance, рдлрд┐рд░ рд╣рдо рдорд╢реАрди рдкрд░ рд╕рднреА рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рд░реЛрдХ рджреЗрддреЗ рд╣реИрдВ рдФрд░ рдХреНрд▓рд╛рдЙрдб рдореЗрдВ рд╡рд░реНрдЪреБрдЕрд▓ рдорд╢реАрди рдХреЛ рдмрдВрдж рдХрд░ рджреЗрддреЗ рд╣реИрдВред

2. рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдСрдмреНрдЬрд░реНрд╡рд░.py

рд╡рд╣рд╛рдВ рд╕реЗ рдирдореВрдирд╛ рдХреЛрдб:

if scaler.assert_up(config.scale_up_thresholds) == True:
        hostname = cloud.get_vm_to_up(config.scaling_hosts)
        if hostname != None:
            status1 = scaler.scale_up(hostname)
            if status1 == 'Success':
                text = {"text": "{0} has been successfully scaled-up".format(hostname)}
                post = {"text": "{0}".format(text)}
                json_data = json.dumps(post)
                req = requests.post(webhook, data=json_data.encode('ascii'), headers={'Content-Type': 'application/json'})
                time.sleep(config.cooldown_period*60)

рдЗрд╕рдореЗрдВ, рд╣рдо рдЬрд╛рдВрдЪрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдХреНрд▓рд╕реНрдЯрд░ рдХреА рдХреНрд╖рдорддрд╛ рдмрдврд╝рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдерд┐рддрд┐рдпрд╛рдВ рдмрдирд╛рдИ рдЧрдИ рд╣реИрдВ рдФрд░ рдХреНрдпрд╛ рд░рд┐рдЬрд░реНрд╡ рдореЗрдВ рдХреЛрдИ рдорд╢реАрди рд╣реИ, рдЙрдирдореЗрдВ рд╕реЗ рдПрдХ рдХрд╛ рд╣реЛрд╕реНрдЯрдирд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЗрд╕реЗ рдХреНрд▓рд╕реНрдЯрд░ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рд╣рдорд╛рд░реА рдЯреАрдо рдХреЗ рд╕реНрд▓реИрдХ рдкрд░ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рд╕рдВрджреЗрд╢ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд░реЗрдВред рдЬрд┐рд╕рдХреЗ рдмрд╛рдж рдпреЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ cooldown_period, рдЬрдм рд╣рдо рдХреНрд▓рд╕реНрдЯрд░ рд╕реЗ рдХреБрдЫ рднреА рдЬреЛрдбрд╝рддреЗ рдпрд╛ рд╣рдЯрд╛рддреЗ рдирд╣реАрдВ рд╣реИрдВ, рдмрд▓реНрдХрд┐ рдХреЗрд╡рд▓ рд▓реЛрдб рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░рддреЗ рд╣реИрдВред рдпрджрд┐ рдпрд╣ рд╕реНрдерд┐рд░ рд╣реЛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдЗрд╖реНрдЯрддрдо рд▓реЛрдб рдореВрд▓реНрдпреЛрдВ рдХреЗ рджрд╛рдпрд░реЗ рдореЗрдВ рд╣реИ, рддреЛ рд╣рдо рдмрд╕ рдирд┐рдЧрд░рд╛рдиреА рдЬрд╛рд░реА рд░рдЦрддреЗ рд╣реИрдВред рдпрджрд┐ рдПрдХ рдиреЛрдб рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рдерд╛, рддреЛ рд╣рдо рджреВрд╕рд░рд╛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред

рдРрд╕реЗ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдЬрдм рд╣рдорд╛рд░реЗ рд╕рд╛рдордиреЗ рдХреЛрдИ рдкрд╛рда рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╣рдо рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдПрдХ рдиреЛрдб рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реЛрдЧрд╛, рдЗрд╕рд▓рд┐рдП рд╣рдо рддреБрд░рдВрдд рд╕рднреА рдореБрдлреНрдд рдиреЛрдбреНрд╕ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдкрд╛рда рдХреЗ рдЕрдВрдд рддрдХ рдЙрдиреНрд╣реЗрдВ рд╕рдХреНрд░рд┐рдп рд░рдЦрддреЗ рд╣реИрдВред рдпрд╣ рдЧрддрд┐рд╡рд┐рдзрд┐ рдЯрд╛рдЗрдорд╕реНрдЯреИрдореНрдк рдХреА рд╕реВрдЪреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╣реЛрддрд╛ рд╣реИред

рдирд┐рд╖реНрдХрд░реНрд╖

рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рдЙрди рдорд╛рдорд▓реЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдЪреНрдЫрд╛ рдФрд░ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╕рдорд╛рдзрд╛рди рд╣реИ рдЬрдм рдЖрдк рдЕрд╕рдорд╛рди рдХреНрд▓рд╕реНрдЯрд░ рд▓реЛрдбрд┐рдВрдЧ рдХрд╛ рдЕрдиреБрднрд╡ рдХрд░рддреЗ рд╣реИрдВред рдЖрдк рдПрдХ рд╕рд╛рде рдкреАрдХ рд▓реЛрдб рдХреЗ рд▓рд┐рдП рд╡рд╛рдВрдЫрд┐рдд рдХреНрд▓рд╕реНрдЯрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рд╕рд╛рде рд╣реА рдЗрд╕ рдХреНрд▓рд╕реНрдЯрд░ рдХреЛ рдЕрдВрдбрд░рд▓реЛрдб рдХреЗ рджреМрд░рд╛рди рдирд╣реАрдВ рд░рдЦрддреЗ рд╣реИрдВ, рдЬрд┐рд╕рд╕реЗ рдкреИрд╕реЗ рдХреА рдмрдЪрдд рд╣реЛрддреА рд╣реИред рдЦреИрд░, рд╕рд╛рде рд╣реА рдпрд╣ рд╕рдм рдЖрдкрдХреА рднрд╛рдЧреАрджрд╛рд░реА рдХреЗ рдмрд┐рдирд╛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рд╣реЛрддрд╛ рд╣реИред рдСрдЯреЛрд╕реНрдХреЗрд▓рд░ рд╕реНрд╡рдпрдВ рдХреНрд▓рд╕реНрдЯрд░ рдкреНрд░рдмрдВрдзрдХ рдПрдкреАрдЖрдИ рдФрд░ рдХреНрд▓рд╛рдЙрдб рдкреНрд░рджрд╛рддрд╛ рдПрдкреАрдЖрдИ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рдПрдХ рд╕реЗрдЯ рд╕реЗ рдЬреНрдпрд╛рджрд╛ рдХреБрдЫ рдирд╣реАрдВ рд╣реИ, рдЬреЛ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рддрд░реНрдХ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИред рдЖрдкрдХреЛ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдпрд╛рдж рд░рдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рдиреЛрдбреНрд╕ рдХреЛ 3 рдкреНрд░рдХрд╛рд░реЛрдВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП, рдЬреИрд╕рд╛ рдХрд┐ рд╣рдордиреЗ рдкрд╣рд▓реЗ рд▓рд┐рдЦрд╛ рдерд╛ред рдФрд░ рдЖрдк рдЦреБрд╢ рд░рд╣реЗрдВрдЧреЗ.

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╝реЗрдВ