Π£Ρ‡ΠΈΠΌ сС Π΄Π° Ρ€Π°Π·Π³Ρ€ΡŠΡ‰Π°ΠΌΠ΅ микросСрвизи. Част 1. Spring Boot ΠΈ Docker

Π£Ρ‡ΠΈΠΌ сС Π΄Π° Ρ€Π°Π·Π³Ρ€ΡŠΡ‰Π°ΠΌΠ΅ микросСрвизи. Част 1. Spring Boot ΠΈ Docker

Π—Π΄Ρ€Π°Π²Π΅ΠΉ, Π₯Π°Π±Ρ€.

Π’ Ρ‚Π°Π·ΠΈ статия искам Π΄Π° сподСля ΠΎΠΏΠΈΡ‚Π° си Π² ΡΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΡƒΡ‡Π΅Π±Π½Π° срСда Π·Π° СкспСримСнтиранС с микросСрвизи. ΠŸΡ€ΠΈ ΠΈΠ·ΡƒΡ‡Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° всСки Π½ΠΎΠ² инструмСнт Π²ΠΈΠ½Π°Π³ΠΈ ΠΆΠ΅Π»Π°Π΅Ρ… Π΄Π° Π³ΠΎ ΠΏΡ€ΠΎΠ±Π²Π°ΠΌ Π½Π΅ само Π½Π° Π»ΠΎΠΊΠ°Π»Π½Π°Ρ‚Π° машина, Π½ΠΎ ΠΈ Π² ΠΏΠΎ-рСалистични условия. Π—Π°Ρ‚ΠΎΠ²Π° Ρ€Π΅ΡˆΠΈΡ… Π΄Π° създам опростСно микросСрвисно ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ впослСдствиС Ρ‰Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° β€˜ΠΎΠ±Π²Π΅ΡˆΠ²Π°ΠΌβ€™ с Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ интСрСсни Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ. ΠžΡΠ½ΠΎΠ²Π½ΠΎΡ‚ΠΎ изискванС към ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π΅ Π΄Π° бъдС максимално Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»Π½ΠΎ Π±Π»ΠΈΠ·ΠΊΠΎ Π΄ΠΎ Ρ€Π΅Π°Π»Π½Π°Ρ‚Π° систСма.

ΠŸΡŠΡ€Π²ΠΎΠ½Π°Ρ‡Π°Π»Π½ΠΎ Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ… ΡΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π½Π° няколко ΡΡ‚ΡŠΠΏΠΊΠΈ:

  1. БъздаванС Π½Π° Π΄Π²Π° ΡΡŠΡ€Π²ΠΈΡΠ° β€” β€˜Π±Π΅ΠΊΠ΅Π½Π΄β€™ (backend) ΠΈ β€˜ΡˆΠ»ΡŽΠ·β€™ (gateway), ΠΎΠΏΠ°ΠΊΠΎΠ²Π°Π½Π΅ Π² docker ΠΎΠ±Ρ€Π°Π·ΠΈ ΠΈ настройванС Π½Π° ΡΡŠΠ²ΠΌΠ΅ΡΡ‚Π½Π°Ρ‚Π° ΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Π°.

    ΠšΠ»ΡŽΡ‡ΠΎΠ²ΠΈ Π΄ΡƒΠΌΠΈ: Java 11, Spring Boot, Docker, оптимизация Π½Π° изобраТСния.

  2. Π Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° конфигурация Π·Π° Kubernetes ΠΈ Ρ€Π°Π·ΠΏΠΎΠ»Π°Π³Π°Π½Π΅ Π½Π° систСмата Π² Google Kubernetes Engine.

    ΠšΠ»ΡŽΡ‡ΠΎΠ²ΠΈ Π΄ΡƒΠΌΠΈ: Kubernetes, GKE, ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° рСсурси, Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ ΠΌΠ°Ρ‰Π°Π±ΠΈΡ€Π°Π½Π΅, сСкрСти.

  3. БъздаванС Π½Π° Ρ‡Π°Ρ€Ρ‚ с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° Helm 3 Π·Π° ΠΏΠΎ-Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° кластСра.

    ΠšΠ»ΡŽΡ‡ΠΎΠ²ΠΈ Π΄ΡƒΠΌΠΈ: Helm 3, Ρ€Π°Π·ΠΏΠΎΠ»Π°Π³Π°Π½Π΅ Π½Π° Ρ‡Π°Ρ€Ρ‚.

  4. Настройка Π½Π° Jenkins ΠΈ ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ Π·Π° Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ разпространСниС Π½Π° ΠΊΠΎΠ΄Π° Π² кластСра.

    ΠšΠ»ΡŽΡ‡ΠΎΠ²ΠΈ Π΄ΡƒΠΌΠΈ: конфигурация Π½Π° Jenkins, плъгини, ΠΎΡ‚Π΄Π΅Π»Π½ΠΎ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Π·Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ.

На всяка ΡΡ‚ΡŠΠΏΠΊΠ° ΠΏΠ»Π°Π½ΠΈΡ€Π°ΠΌ Π΄Π° посвСтя ΠΎΡ‚Π΄Π΅Π»Π½Π° статия.

Π€ΠΎΠΊΡƒΡΡŠΡ‚ Π½Π° Ρ‚ΠΎΠ·ΠΈ Ρ†ΠΈΠΊΡŠΠ» статии Π½Π΅ Π΅ Π²ΡŠΡ€Ρ…Ρƒ Ρ‚ΠΎΠ²Π° ΠΊΠ°ΠΊ Π΄Π° пишСм микросСрвизи, Π° ΠΊΠ°ΠΊ Π΄Π° Π³ΠΈ Π½Π°ΠΊΠ°Ρ€Π°ΠΌΠ΅ Π΄Π° работят Π² Π΅Π΄ΠΈΠ½Π½Π° систСма. Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‡Π΅ всички Ρ‚Π΅Π·ΠΈ Π½Π΅Ρ‰Π° ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ са извън отговорността Π½Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, смятам, Ρ‡Π΅ Π΅ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ Π΄Π° смС Π·Π°ΠΏΠΎΠ·Π½Π°Ρ‚ΠΈ с тях ΠΏΠΎΠ½Π΅ Π½Π° 20% (ΠΊΠΎΠ΅Ρ‚ΠΎ, ΠΊΠ°ΠΊΡ‚ΠΎ Π΅ извСстно, Π΄Π°Π²Π° 80% ΠΎΡ‚ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚Π°). Някои бСзспорно Π²Π°ΠΆΠ½ΠΈ Ρ‚Π΅ΠΌΠΈ, ΠΊΠ°Ρ‚ΠΎ осигуряванС Π½Π° бСзопасност, Ρ‰Π΅ Π±ΡŠΠ΄Π°Ρ‚ оставСни извън ΠΎΠ±Ρ…Π²Π°Ρ‚Π° Π½Π° Ρ‚ΠΎΠ·ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ Π°Π²Ρ‚ΠΎΡ€ΡŠΡ‚ Π½Π΅ Ρ€Π°Π·Π±ΠΈΡ€Π° ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΡ‚ тях; систСмата сС създава ΠΈΠ·ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ Π·Π° Π»ΠΈΡ‡Π½Π° ΡƒΠΏΠΎΡ‚Ρ€Π΅Π±Π°. Π©Π΅ сС Ρ€Π°Π΄Π²Π°ΠΌ Π½Π° всякакви мнСния ΠΈ конструктивна ΠΊΡ€ΠΈΡ‚ΠΈΠΊΠ°.

БъздаванС Π½Π° микросСрвизи

Π‘ΡŠΡ€Π²ΠΈΠ·ΠΈΡ‚Π΅ бяха написани Π½Π° Java 11 с ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° Spring Boot. ΠœΠ΅ΠΆΠ΄ΡƒΡΡŠΡ€Π²ΠΈΡΠ½ΠΎΡ‚ΠΎ взаимодСйствиС Π΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΈΡ€Π°Π½ΠΎ с ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° REST. ΠŸΡ€ΠΎΠ΅ΠΊΡ‚ΡŠΡ‚ Ρ‰Π΅ Π²ΠΊΠ»ΡŽΡ‡Π²Π° ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΎ количСство тСстовС (Π·Π° Π΄Π° ΠΈΠΌΠ° ΠΊΠ°ΠΊΠ²ΠΎ Π΄Π° сС тСства Π² Jenkins). Π˜Π·Ρ…ΠΎΠ΄Π½ΠΈΡΡ‚ ΠΊΠΎΠ΄ Π½Π° ΡΡŠΡ€Π²ΠΈΠ·ΠΈΡ‚Π΅ Π΅ Π΄ΠΎΡΡ‚ΡŠΠΏΠ΅Π½ Π² GitHub: Π±Π΅ΠΊΠ΅Π½Π΄ ΠΈ шлюз.

Π—Π° Π΄Π° ΠΈΠΌΠ°Ρ‚Π΅ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ Π΄Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚Π΅ ΡΡŠΡΡ‚ΠΎΡΠ½ΠΈΠ΅Ρ‚ΠΎ Π½Π° всСки ΠΎΡ‚ услугитС, Π² тяхната зависимост Π΅ Π΄ΠΎΠ±Π°Π²Π΅Π½ Spring Actuator. Π’ΠΎΠΉ Ρ‰Π΅ създадС ΠΊΡ€Π°ΠΉΠ½Π° Ρ‚ΠΎΡ‡ΠΊΠ° /actuator/health ΠΈ Ρ‰Π΅ Π²Ρ€ΡŠΡ‰Π° статус 200, Π°ΠΊΠΎ услугата Π΅ Π³ΠΎΡ‚ΠΎΠ²Π° Π΄Π° ΠΏΡ€ΠΈΠ΅ΠΌΠ° Ρ‚Ρ€Π°Ρ„ΠΈΠΊ, ΠΈΠ»ΠΈ 504 Π² случай Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ. Π’ Ρ‚ΠΎΠ·ΠΈ случай Ρ‚ΠΎΠ²Π° Π΅ доста Ρ„Π°Π»ΡˆΠΈΠ²Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ услугитС са ΠΌΠ½ΠΎΠ³ΠΎ прости, ΠΈ ΠΏΡ€ΠΈ някакъв форсмаТор Ρ‚Π΅ ΠΏΠΎ-скоро Ρ‰Π΅ станат напълно Π½Π΅Π΄ΠΎΡΡ‚ΡŠΠΏΠ½ΠΈ, ΠΎΡ‚ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ Π΄Π° запазят частична работоспособност. Но Π² Ρ€Π΅Π°Π»Π½ΠΈ систСми Actuator ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΏΠΎΠΌΠΎΠ³Π½Π΅ Π·Π° диагностициранС Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΏΡ€Π΅Π΄ΠΈ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π΄Π° Π·Π°ΠΏΠΎΡ‡Π½Π°Ρ‚ Π΄Π° сС ΡΠ±Π»ΡŠΡΠΊΠ²Π°Ρ‚ с Π½Π΅Π³ΠΎ. НапримСр, ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ с Π΄ΠΎΡΡ‚ΡŠΠΏΠ° Π΄ΠΎ Π‘Π”, Ρ‰Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ Π΄Π° Ρ€Π΅Π°Π³ΠΈΡ€Π°ΠΌΠ΅, спирайки Π΄Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π°ΠΌΠ΅ заявки с ΠΏΠΎΠ²Ρ€Π΅Π΄Π΅Π½ СкзСмпляр Π½Π° услугата.

БСрвис Backend

Π‘Π΅Ρ€Π²ΠΈΠ·ΡŠΡ‚ Π±Π΅ΠΊΠ΅Π½Π΄ просто Ρ‰Π΅ Π±Ρ€ΠΎΠΈ ΠΈ Ρ‰Π΅ Π²Ρ€ΡŠΡ‰Π° броя Π½Π° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΡ‚Π΅ заявки.

Код Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅Ρ€Π°:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/requests")
    public Long getRequestsCount() {
        return counter.incrementAndGet();
    }
}

ВСст Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅Ρ€Π°:

@WebMvcTest(RequestsCounterController.class)
public class RequestsCounterControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void firstRequest_one() throws Exception {
        mockMvc.perform(get("/requests"))
            .andExpect(status().isOk())
            .andExpect(MockMvcResultMatchers.content().string("1"));
    }
}

БСрвис Gateway

Π¨Π»ΡŽΠ·ΡŠΡ‚ Ρ‰Π΅ прСнасочва заявкитС към сСрвиза Π±Π΅ΠΊΠ΅Π½Π΄, допълвайки Π³ΠΈ със слСдната информация:

  • id Π½Π° шлюза. Π’ΠΎΠ²Π° Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Π·Π° Π΄Π° ΠΌΠΎΠΆΠ΅ ΠΏΠΎ ΠΎΡ‚Π³ΠΎΠ²ΠΎΡ€Π° Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π° Π΄Π° сС Ρ€Π°Π·Π»ΠΈΡ‡Π°Ρ‚ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈΡ‚Π΅ СкзСмпляри Π½Π° шлюза.
  • Някакво "сСкрСтно" Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅ ΠΈΠ³Ρ€Π°Π΅ Π²Π°ΠΆΠ½Π° роля ΠΊΠ°Ρ‚ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ Π²Π°ΠΆΠ½Π° ΠΏΠ°Ρ€ΠΎΠ»Π° (β„– ΠΊΠ»ΡŽΡ‡ Π·Π° ΠΊΡ€ΠΈΠΏΡ‚ΠΈΡ€Π°Π½Π΅ Π½Π° Π²Π°ΠΆΠ΅Π½ бисквитСн Ρ„Π°ΠΉΠ»)

ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡ Π² application.properties:

backend.url=http://localhost:8081
instance.id=${random.int}
secret="default-secret"

АдаптСр Π·Π° ΡΠ²ΡŠΡ€Π·Π²Π°Π½Π΅ с Π±Π΅ΠΊΠ΅Π½Π΄Π°:

@Service
public class BackendAdapter {

    private static final String REQUESTS_ENDPOINT = "/requests";

    private final RestTemplate restTemplate;

    @Value("${backend.url}")
    private String backendUrl;

    public BackendAdapter(RestTemplateBuilder builder) {
        restTemplate = builder.build();
    }

    public String getRequests() {
        ResponseEntity response = restTemplate.getForEntity(
backendUrl + REQUESTS_ENDPOINT, String.class);
        return response.getBody();
    }
}

ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅Ρ€:

@RestController
@RequiredArgsConstructor
public class EndpointController {

    private final BackendAdapter backendAdapter;

    @Value("${instance.id}")
    private int instanceId;

    @Value("${secret}")
    private String secret;

    @GetMapping("/")
    public String getRequestsCount() {
        return String.format("Π‘Ρ€ΠΎΠΉ Π½Π° заявкитС %s (шлюз %d, сСкрСт %s)", backendAdapter.getRequests(), instanceId, secret);
    }
}

Π‘Ρ‚Π°Ρ€Ρ‚ΠΈΡ€Π°Π½Π΅:

Π‘Ρ‚Π°Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ Π±Π΅ΠΊΠ΅Π½Π΄Π°:

./mvnw package -DskipTests
java -Dserver.port=8081 -jar target/microservices-backend-1.0.0.jar

Π‘Ρ‚Π°Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ шлюза:

./mvnw package -DskipTests
java -jar target/microservices-gateway-1.0.0.jar

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ²Π°ΠΌΠ΅:

$ curl http://localhost:8080/
Π‘Ρ€ΠΎΠΉ заявки 1 (шлюз 38560358, сСкрСт "default-secret")

Всичко Ρ€Π°Π±ΠΎΡ‚ΠΈ. ВниматСлният Ρ‡ΠΈΡ‚Π°Ρ‚Π΅Π» Ρ‰Π΅ ΠΎΡ‚Π±Π΅Π»Π΅ΠΆΠΈ, Ρ‡Π΅ Π½ΠΈΡ‰ΠΎ Π½Π΅ Π½ΠΈ ΠΏΡ€Π΅Ρ‡ΠΈ Π΄Π° сС ΠΎΠ±ΡŠΡ€Π½Π΅ΠΌ Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ към Π±Π΅ΠΊΠ΅Π½Π΄Π°, заобикаляйки шлюза (http://localhost:8081/requests). Π—Π° Π΄Π° Π³ΠΎ ΠΏΠΎΠΏΡ€Π°Π²ΠΈΠΌ, услугитС трябва Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ ΠΎΠ±Π΅Π΄ΠΈΠ½Π΅Π½ΠΈ Π² Π΅Π΄Π½Π° ΠΌΡ€Π΅ΠΆΠ°, Π° навън Π΄Π° "ΡΡ‚ΡŠΡ€Ρ‡ΠΈ" само ΡˆΠ»ΡŽΠ·ΡŠΡ‚.
Π‘ΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ°, ΠΈ Π΄Π²Π΅Ρ‚Π΅ услуги сподСлят Π΅Π΄Π½Π° Ρ„Π°ΠΉΠ»ΠΎΠ²Π° систСма, ΡΡŠΠ·Π΄Π°Π²Π°Ρ‚ ΠΏΠΎΡ‚ΠΎΡ†ΠΈ ΠΈ Π² Π΅Π΄ΠΈΠ½ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° Π·Π°ΠΏΠΎΡ‡Π½Π°Ρ‚ Π΄Π° си ΠΏΡ€Π΅Ρ‡Π°Ρ‚. НС Π±ΠΈ Π±ΠΈΠ»ΠΎ лошо Π΄Π° ΠΈΠ·ΠΎΠ»ΠΈΡ€Π°ΠΌΠ΅ Π½Π°ΡˆΠΈΡ‚Π΅ микросСрвизи. Π’ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° сС постигнС Ρ‡Ρ€Π΅Π· разнасянС Π½Π° прилоТСнията Π½Π° Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ машини (ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠ°Ρ€ΠΈ, слоТно), ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»Π½ΠΈ машини (рСсурсоСмко, Π±Π°Π²Π΅Π½ старт) ΠΈΠ»ΠΈ Ρ‡Ρ€Π΅Π· контСйнСризация. ΠžΡ‡Π°ΠΊΠ²Π°Π½ΠΎ, ΠΈΠ·Π±ΠΈΡ€Π°ΠΌΠ΅ трСтия Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ ΠΈ Docker ΠΊΠ°Ρ‚ΠΎ инструмСнт Π·Π° контСйнСризация.

Docker

Ако Π½Π°ΠΊΡ€Π°Ρ‚ΠΊΠΎ, Docker създава ΠΈΠ·ΠΎΠ»ΠΈΡ€Π°Π½ΠΈ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΈ, ΠΏΠΎ Π΅Π΄ΠΈΠ½ Π·Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅. Π—Π° Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ Docker, трябва Π΄Π° Π½Π°ΠΏΠΈΡˆΠ΅Ρ‚Π΅ Dockerfile β€” инструкция Π·Π° ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π°Π½Π΅ ΠΈ стартиранС Π½Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΈΠ·Π³Ρ€Π°Π΄ΠΈΡ‚Π΅ ΠΎΠ±Ρ€Π°Π·, Π΄Π° Π³ΠΎ ΠΊΠ°Ρ‡ΠΈΡ‚Π΅ Π² Ρ€Π΅Π³ΠΈΡΡ‚ΡŠΡ€Π° Π½Π° ΠΎΠ±Ρ€Π°Π·ΠΈΡ‚Π΅ (β„– DockerHub) ΠΈ с Π΅Π΄Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π° Π΄Π° Ρ€Π°Π·Π²Π΅Ρ€Π½Π΅Ρ‚Π΅ своя микросСрвис Π² всяка ΠΊΠΎΠ½Ρ‚Π°ΠΌΠΈΠ½ΠΈΡ€ΠΈΠ·ΠΈΡ€Π°Π½Π° срСда.

Dockerfile

Π•Π΄Π½Π° ΠΎΡ‚ Π½Π°ΠΉ-Π²Π°ΠΆΠ½ΠΈΡ‚Π΅ характСристики Π½Π° ΠΎΠ±Ρ€Π°Π·Π° Π΅ Ρ€Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ ΠΌΡƒ. ΠšΠΎΠΌΠΏΠ°ΠΊΡ‚Π΅Π½ ΠΎΠ±Ρ€Π°Π· ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ Ρ‰Π΅ сС ΠΈΠ·Ρ‚Π΅Π³Π»ΠΈ ΠΎΡ‚ отдалСчСния Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ, Ρ‰Π΅ Π·Π°Π΅ΠΌΠ° ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ място ΠΈ услугата Π²ΠΈ Ρ‰Π΅ стартира ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ. ВсСки ΠΎΠ±Ρ€Π°Π· сС ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π° Π½Π° основата Π½Π° Π±Π°Π·ΠΎΠ² ΠΎΠ±Ρ€Π°Π·, ΠΈ сС ΠΏΡ€Π΅ΠΏΠΎΡ€ΡŠΡ‡Π²Π° Π΄Π° ΠΈΠ·Π±Π΅Ρ€Π΅Ρ‚Π΅ Π½Π°ΠΉ-минималистичния Π²Π°Ρ€ΠΈΠ°Π½Ρ‚. Π”ΠΎΠ±ΡŠΡ€ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π΅ Alpine β€” пълСн дистрибутив Π½Π° Linux с ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΈ.

ΠŸΡŠΡ€Π²ΠΎ, Π½Π΅ΠΊΠ° ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° напишСм Dockerfile "Π² Π»ΠΎΠ±" (Π²Π΅Π΄Π½Π°Π³Π° Ρ‰Π΅ ΠΊΠ°ΠΆΠ°, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° Π΅ лош Π½Π°Ρ‡ΠΈΠ½, Π½Π΅ Π³ΠΎ ΠΏΡ€Π°Π²Π΅Ρ‚Π΅):

FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine
ADD . /src
WORKDIR /src
RUN ./mvnw package -DskipTests
EXPOSE 8080
ENTRYPOINT ["java","-jar","target/microservices-gateway-1.0.0.jar"]

Π’ΡƒΠΊ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π±Π°Π·ΠΎΠ² ΠΎΠ±Ρ€Π°Π·, основан Π½Π° Alpine, с Π²Π΅Ρ‡Π΅ инсталирани JDK Π·Π° ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π°Π½Π΅ Π½Π° нашия ΠΏΡ€ΠΎΠ΅ΠΊΡ‚. Π‘ ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ‚Π° ADD добавямС Ρ‚Π΅ΠΊΡƒΡ‰Π°Ρ‚Π° дирСктория src Π² ΠΎΠ±Ρ€Π°Π·Π°, отбСлязвамС я Π·Π° Ρ€Π°Π±ΠΎΡ‚Π½Π° (WORKDIR) ΠΈ стартирамС ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π°Π½Π΅Ρ‚ΠΎ. Команда EXPOSE 8080 сигнализира Π½Π° Docker, Ρ‡Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΏΠΎΡ€Ρ‚Π° 8080 (Ρ‚ΠΎΠ²Π° Π½Π΅ ΠΏΡ€Π°Π²ΠΈ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π΄ΠΎΡΡ‚ΡŠΠΏΠ½ΠΎ навън, Π½ΠΎ позволява Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΎΡ‚ Π΄Ρ€ΡƒΠ³ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π² ΡΡŠΡ‰Π°Ρ‚Π° Docker ΠΌΡ€Π΅ΠΆΠ°).

Π—Π° Π΄Π° ΠΎΠΏΠ°ΠΊΠΎΠ²Π°Ρ‚Π΅ услугитС Π² ΠΎΠ±Ρ€Π°Π·ΠΈ, трябва Π΄Π° ΠΈΠ·ΠΏΡŠΠ»Π½ΠΈΡ‚Π΅ ΠΊΠΎΠΌΠ°Π½Π΄ΠΈΡ‚Π΅ ΠΎΡ‚ ΠΊΠΎΡ€Π΅Π½Π° Π½Π° всСки ΠΏΡ€ΠΎΠ΅ΠΊΡ‚:

docker image build . -t msvc-backend:1.0.0

Π’ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ΠΎΠ±Ρ€Π°Π· с Ρ€Π°Π·ΠΌΠ΅Ρ€ 456 ΠœΠ±Π°ΠΉΡ‚Π° (ΠΎΡ‚ ΠΊΠΎΠΈΡ‚ΠΎ основният ΠΎΠ±Ρ€Π°Π· JDK 340 Π·Π°Π΅ ΠœΠ±Π°ΠΉΡ‚Π°). И всичко Ρ‚ΠΎΠ²Π°, ΠΊΠ°Ρ‚ΠΎ класовСтС Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π½ΠΈ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° сС прСброят Π½Π° ΠΏΡ€ΡŠΡΡ‚ΠΈ. Π—Π° Π΄Π° Π½Π°ΠΌΠ°Π»ΠΈΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° ΠΎΠ±Ρ€Π°Π·Π° Π½ΠΈ:

  • ИзползвамС многостСпСнна компилация. На ΠΏΡŠΡ€Π²ΠΈΡ Π΅Ρ‚Π°ΠΏ Ρ‰Π΅ ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€Π°ΠΌΠ΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°, Π½Π° втория Ρ‰Π΅ инсталирамС JRE, Π° Π½Π° трСтия Ρ‰Π΅ ΠΊΠΎΠΏΠΈΡ€Π°ΠΌΠ΅ всичко Ρ‚ΠΎΠ²Π° Π² Π½ΠΎΠ² чист Alpine ΠΎΠ±Ρ€Π°Π·. Π’ крайния ΠΎΠ±Ρ€Π°Π· Ρ‰Π΅ останат само Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΈΡ‚Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΈ.
  • Π©Π΅ сС възползвамС ΠΎΡ‚ ΠΌΠΎΠ΄ΡƒΠ»ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° java. Π—Π°ΠΏΠΎΡ‡Π²Π°ΠΉΠΊΠΈ ΠΎΡ‚ Java 9, с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° инструмСнта jlink ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° създадСм JRE само ΠΎΡ‚ Π½ΡƒΠΆΠ½ΠΈΡ‚Π΅ ΠΌΠΎΠ΄ΡƒΠ»ΠΈ.

Π—Π° Π»ΡŽΠ±ΠΎΠΏΠΈΡ‚Π½ΠΈΡ‚Π΅, Π΅Ρ‚ΠΎ Π΅Π΄Π½Π° Π΄ΠΎΠ±Ρ€Π° статия Π·Π° ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚Π΅ Π·Π° намаляванС Π½Π° Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° ΠΎΠ±Ρ€Π°Π·Π°. https://habr.com/ru/company/ruvds/blog/485650/.

ΠšΡ€Π°Π΅Π½ Dockerfile:

FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine as builder
ADD . /src
WORKDIR /src
RUN ./mvnw package -DskipTests

FROM alpine:3.10.3 as packager
RUN apk --no-cache add openjdk11-jdk openjdk11-jmods
ENV JAVA_MINIMAL="/opt/java-minimal"
RUN /usr/lib/jvm/java-11-openjdk/bin/jlink 
    --verbose 
    --add-modules 
        java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument 
    --compress 2 --strip-debug --no-header-files --no-man-pages 
    --release-info="add:IMPLEMENTOR=radistao:IMPLEMENTOR_VERSION=radistao_JRE" 
    --output "$JAVA_MINIMAL"

FROM alpine:3.10.3
LABEL maintainer="Anton Shelenkov anshelen@yandex.ru"
ENV JAVA_HOME=/opt/java-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"
COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
COPY --from=builder /src/target/microservices-backend-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

ΠŸΡ€Π΅ΡΡŠΠ·Π΄Π°Π²Π°ΠΌΠ΅ ΠΎΠ±Ρ€Π°Π·Π°, ΠΈ Ρ‚ΠΎΠΉ Π² ΠΊΡ€Π°ΠΉΠ½Π° смСтка намаля Π΄ΠΎ 6 ΠΏΡŠΡ‚ΠΈ, достигайки 77 ΠœΠ±Π°ΠΉΡ‚Π°. НС Π΅ Π·Π»Π΅. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π°, Π³ΠΎΡ‚ΠΎΠ²ΠΈΡ‚Π΅ ΠΎΠ±Ρ€Π°Π·ΠΈ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° сС ΠΊΠ°Ρ‡Π°Ρ‚ Π² Ρ€Π΅Π³ΠΈΡΡ‚ΡŠΡ€Π° с ΠΎΠ±Ρ€Π°Π·ΠΈ, Π·Π° Π΄Π° са Π΄ΠΎΡΡ‚ΡŠΠΏΠ½ΠΈ Π·Π° свалянС ΠΎΡ‚ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚.

Π‘ΡŠΠ²ΠΌΠ΅ΡΡ‚Π½ΠΎ стартиранС Π½Π° услуги Π² Docker.

ΠŸΡŠΡ€Π²ΠΎ, Π½Π°ΡˆΠΈΡ‚Π΅ услуги трябва Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ Π² Π΅Π΄Π½Π° ΠΌΡ€Π΅ΠΆΠ°. Π’ Docker ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‚ няколко Ρ‚ΠΈΠΏΠ° ΠΌΡ€Π΅ΠΆΠΈ, ΠΈ Π½ΠΈΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π½Π°ΠΉ-простия ΠΎΡ‚ тях β€” bridge, позволяващ Π΄Π° ΠΎΠ±Π΅Π΄ΠΈΠ½ΠΈΠΌ Π² ΠΌΡ€Π΅ΠΆΠ° ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΈ, стартирани Π½Π° Π΅Π΄ΠΈΠ½ хост. Π©Π΅ създадСм ΠΌΡ€Π΅ΠΆΠ° с слСдната ΠΊΠΎΠΌΠ°Π½Π΄Π°:

docker network create msvc-network

Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° Ρ‰Π΅ стартирамС ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° Π·Π° Π±Π΅ΠΊΠ΅Π½Π΄ с ΠΈΠΌΠ΅Ρ‚ΠΎ β€˜backend’ с ΠΎΠ±Ρ€Π°Π· microservices-backend:1.0.0:

docker run -dit --name backend --network msvc-net microservices-backend:1.0.0

Π‘Ρ‚ΠΎΠΈ Π΄Π° сС ΠΎΡ‚Π±Π΅Π»Π΅ΠΆΠΈ, Ρ‡Π΅ bridge ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° прСдоставя Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ service discovery Π·Π° ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΈΡ‚Π΅ ΠΏΠΎ Ρ‚Π΅Ρ…Π½ΠΈΡ‚Π΅ ΠΈΠΌΠ΅Π½Π°. ВоСст, услугата Π·Π° Π±Π΅ΠΊΠ΅Π½Π΄ Ρ‰Π΅ бъдС Π΄ΠΎΡΡ‚ΡŠΠΏΠ½Π° Π²ΡŠΡ‚Ρ€Π΅ Π² ΠΌΡ€Π΅ΠΆΠ°Ρ‚Π° Π½Π° Docker Π½Π° адрСс http://backend:8080.

Π‘Ρ‚Π°Ρ€Ρ‚ΠΈΡ€Π°ΠΌΠ΅ шлюза:

docker run -dit -p 80:8080 --env secret=my-real-secret --env BACKEND_URL=http://backend:8080/ --name gateway --network msvc-net microservices-gateway:1.0.0

Π’ Ρ‚ΠΎΠ·ΠΈ Π΅ΠΊΠΈΠΏ ΡƒΠΊΠ°Π·Π²Π°ΠΌΠ΅, Ρ‡Π΅ прСнасочвамС ΠΏΠΎΡ€Ρ‚Π° 80 Π½Π° нашия хост към ΠΏΠΎΡ€Ρ‚Π° 8080 Π½Π° ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°. ΠžΠΏΡ†ΠΈΡΡ‚Π° env ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π·Π° Π·Π°Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° срСдата, ΠΊΠΎΠΈΡ‚ΠΎ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ Ρ‰Π΅ Π±ΡŠΠ΄Π°Ρ‚ ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Π½ΠΈ ΠΎΡ‚ Spring ΠΈ Ρ‰Π΅ замСнят свойствата ΠΎΡ‚ application.properties.

Π‘Π»Π΅Π΄ стартиранС ΠΈΠ·Π²ΠΈΠΊΠ²Π°ΠΌΠ΅ http://localhost/ ΠΈ сС увСрявамС, Ρ‡Π΅ всичко Ρ€Π°Π±ΠΎΡ‚ΠΈ, ΠΊΠ°ΠΊΡ‚ΠΎ ΠΈ ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΈΡ ΠΏΡŠΡ‚.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Π’ ΠΊΡ€Π°ΠΉΠ½Π° смСтка ΡΡŠΠ·Π΄Π°Π΄ΠΎΡ…ΠΌΠ΅ Π΄Π²Π° прости микросСрвиса, ΠΎΠΏΠ°ΠΊΠΎΠ²Π°Ρ…ΠΌΠ΅ Π³ΠΈ Π² Docker ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΈ ΠΈ Π³ΠΈ стартирахмС Π½Π° Π΅Π΄Π½Π° машина. Π’ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π°Ρ‚Π° систСма ΠΎΠ±Π°Ρ‡Π΅ ΠΈΠΌΠ° Ρ€Π΅Π΄ΠΈΡ†Π° Π½Π΅Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ†ΠΈ:

  • Π›ΠΎΡˆΠ° отказоустойчивост β€” всичко Ρ€Π°Π±ΠΎΡ‚ΠΈ Π½Π° Π΅Π΄ΠΈΠ½ ΡΡŠΡ€Π²ΡŠΡ€
  • Π›ΠΎΡˆΠ° мащабируСмост β€” ΠΏΡ€ΠΈ ΡƒΠ²Π΅Π»ΠΈΡ‡Π°Π²Π°Π½Π΅ Π½Π° Π½Π°Ρ‚ΠΎΠ²Π°Ρ€Π²Π°Π½Π΅Ρ‚ΠΎ Π±ΠΈ Π±ΠΈΠ»ΠΎ Π΄ΠΎΠ±Ρ€Π΅ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ Π΄Π° сС Ρ€Π°Π·Π³Ρ€ΡŠΡ‰Π°Ρ‚ Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΈ инстанции Π½Π° услугитС ΠΈ Π΄Π° сС балансира Π½Π°Ρ‚ΠΎΠ²Π°Ρ€Π²Π°Π½Π΅Ρ‚ΠΎ ΠΌΠ΅ΠΆΠ΄Ρƒ тях
  • БлоТност ΠΏΡ€ΠΈ стартиранС β€” Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠ° Π΅ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΎ 3 ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ, всяка с ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ (Ρ‚ΠΎΠ²Π° само Π·Π° 2 услуги)

Π—Π° Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅ Π½Π° горСпосочСнитС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‚ Ρ€Π΅Π΄ΠΈΡ†Π° Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ, ΠΊΠ°Ρ‚ΠΎ Docker Swarm, Nomad, Kubernetes ΠΈΠ»ΠΈ OpenShift. Ако цялата систСма Π΅ написана Π½Π° Java, ΠΌΠΎΠΆΠ΅ Π΄Π° сС Ρ€Π°Π·Π³Π»Π΅Π΄Π° Π½Π° Spring Cloud (Π΄ΠΎΠ±Ρ€Π° статия).

Π’ слСдващата част Ρ‰Π΅ Ρ€Π°Π·ΠΊΠ°ΠΆΠ° ΠΊΠ°ΠΊ Настравах Kubernetes ΠΈ Π΄Π΅ΠΏΠ»ΠΎΠΉΠ²Π°Ρ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π² Google Kubernetes Engine.

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС със Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS ΠΈ VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС със Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS ΠΈ VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster