
Python-Ansible κΈ°λ°μΌλ‘ 리λ μ€ μλ²λ€μ μΌκ΄ κ΄λ¦¬νλ μΉ μλΉμ€λ₯Ό κ°λ°νλ μ€, E2E ν μ€νΈ λ¨κ³μμ μλ΅ μλ λ¬Έμ κ° μΉλͺ μ μΌλ‘ λλ¬λ¬λ€.
μλΉμ€λ Ansible νλ μ΄λΆμ μ€ννκΈ° μ μ, λμ μλ²λ€μ΄ μ€μ λ‘ μ΄μ μλμ§(Port, SSH, Ping λ±) μ¬μ μ κ²νλ λ‘μ§μ ν¬ν¨νκ³ μμλλ°, λ±λ‘λ μλ²κ° λ¨ 3λλΏμμλ μ 체 μν μ‘°νμ 10μ΄ μ΄μμ΄ μμλκ±°λ, μ¬ν κ²½μ° νλ‘ νΈμλ λ λλ§μ΄ μμ μ€ν¨νλ μν©κΉμ§ λ°μνλ€.
μΉ λμ보λ μ
μ₯μμλ μ¬μ©μκ° βμν μλ‘κ³ μΉ¨β ν λ² νμ λΏμΈλ°, κ²°κ³Όκ° μ€κΈ°κΉμ§ μ§μ°μ΄ λ°μνκ±°λ νμμμμΌλ‘ μΈν΄ λΉ νλ©΄μ΄ λ
ΈμΆλλ μ
μ΄μλ€.
μ΄μ νκ²½μμλ μμ~μλ°± λμ μλ²κ° λ±λ‘λ μ μλ€λ μ μ κ³ λ €νλ©΄, 3λμμ μ΄λ―Έ 10μ΄λΌλ©΄ 100λ μ΄μμμλ μλΉμ€κ° μ¬μ€μ λμν μ μλ ꡬ쑰λΌλ λμ ν κ²°λ‘ μ΄ λμ¨λ€.
μ‘°μ¬ κ²°κ³Ό, λ¬Έμ μ ν΅μ¬ μμΈμ λ¨μνλ€.
μλ² μ κ²μ΄ μμ°¨μ μΌλ‘ μ§νλλ©΄μ, μλ΅μ΄ μλ μλ²μ λν΄ νμμμ(μ΅λ 10μ΄) μ λ§€λ² κΈ°λ€λ¦¬κ³ μκΈ° λλ¬Έμ΄μλ€.
μ¦, "μλ² κ°μ Γ νμμμ" λ§νΌ λκΈ°νλ κ΅¬μ‘°κ° μ¨μ΄ μμκ³ , μ΄ λ°©μμ μλ² μκ° λμ΄λ μλ‘ μλ΅ μκ°μ΄ μ νμ μΌλ‘ λλ €μ§λ λ³λͺ© ꡬ쑰λ₯Ό λ§λ€κ³ μμλ€.
ν΄λΉ μλΉμ€λ μΉμμ λͺ¨λ μλ²μ νμ¬ μνλ₯Ό ν λ²μ μ‘°νν λ€, μ΄μμ΄ μλ μλ²λ€μ λν΄μλ§ Ansible νλ μ΄λΆμ μ€ννλλ‘ μ€κ³λμ΄ μμλ€. μ¦, βμλ² μν μ‘°νβλ Ansible μ€ν μ΄μ μ μ¬μ νν°λ§ μν μ νλ ν΅μ¬ λ¨κ³μκ³ , μ΄ λ¨κ³κ° λλ €μ§λ μκ° μ 체 μλ리μ€κ° λͺ¨λ λλ €μ§ μλ°μ μλ ꡬ쑰μλ€.
μλΉμ€ λ μ΄μ΄μ μμ‘΄μ± κ΄κ³λ μλμ κ°μλ€.
[LinuxServerService]
ββ linuxServerRepository β DB μλ² λͺ©λ‘ μ‘°ν (Mongo/SQL λ± μμμ±)
ββ inspector : SshRemoteInspector β μλ² μν νμΈ(Ping, SSH μ°κ²°, ν¬νΈ μ²΄ν¬ λ± λ€νΈμν¬ I/O)
ββ deployer : SshDeployer β μ€μ λ°°ν¬/λͺ
λ Ή μ€ν μ±
μ (SSH λͺ
λ Ή, μ€ν¬λ¦½νΈ μν)
ββ props : AnsibleProperties β YAML/Properties μ€μ λ§€ν (νκ²½ λ³μ/κ²½λ‘/timeout λ±)
ββ mongoTemplate β νμ μ 컀μ€ν
쿼리μ©
[SshRemoteInspector]
ββ CommandBuilder β nc/ssh λͺ
λ Ή μμ±
ββ ProcessRunner β λͺ
λ Ή μ€ν + νμμμ μ μ΄
λ¬Έμ μ νλ¦μ λ¨μνλ€.
μ¬μ©μ μμ² β Service β Repositoryμμ μλ² λͺ©λ‘ μ‘°ν
β inspector.probNetwork(server1)
β inspector.probNetwork(server2)
β inspector.probNetwork(server3)
β ... λͺ¨λ μλ² νμ ν μλ΅ λ°ν
μ΄ λ°©μμ μ½λ μμΌλ‘λ 무λν΄ λ³΄μΈλ€. νμ§λ§ βλ€νΈμν¬ I/Oλ μΈμ λ λ릴 μ μλ€βλ λ¨ νλμ νμ€μ μΈ μ‘°κ±΄μ κ°κ³Όνκ³ μμλ€. μλ₯Ό λ€μ΄, λ€μκ³Ό κ°μ μν©μ΄ λ°μνλ€κ³ νμ.
μμ°¨ ꡬ쑰μμλ server3μ νμμμμ΄ λλ λκΉμ§ μ 체 λ‘μ§μ΄ λ©μΆ°μ κΈ°λ€λ¦΄ μλ°μ μλ€. κ²°κ΅ κ²°κ³Όλ μλμ κ°μ΄ λλ€.
0.3μ΄ + 0.3μ΄ + 10μ΄ = μ½ 10.6μ΄
μ΄κ±΄ λ± 3λμΌ λ μ΄μΌκΈ°λ€.
λ°λ‘ μ΄ μ§μ μμ βμμ°¨ μ²λ¦¬βλ νμ₯ λΆκ°λ₯ν ꡬ쑰(Non-Scalable Architecture) λ‘ νμ λλ€.
μ¦, μ΄ λ¬Έμ λ μλμ λ¬Έμ κ° μλκ³ βꡬ쑰μ λ³λͺ©β μ λ¬Έμ μλ€.
μ 리νλ©΄, κΈ°μ‘΄ ꡬ쑰μ λ¬Έμ λ λ± λ κ°μ§λ‘ μμ½λλ€.
| νκ³ | μ€λͺ |
|---|---|
| λ³λ ¬μ±μ΄ μμ | μ¬λ¬ μλ²λ₯Ό λμμ 체ν¬νμ§ λͺ»νκ³ βν μ€λ‘ μμλλ‘β κ²μ¬ |
| Fail-Fast λΆμ¬ | μλ΅ μλ μλ²λ λκΉμ§ κΈ°λ€λ¦¬λ©° μ 체 μλ΅μ κ°λ‘λ§μ |
μ΄ κ΅¬μ‘°λ μλ²κ° 3λμΌ λλ βμβ¦ λ리μ§λ§ λμκ°λ€β μμ€μΌ λΏ,
μ΄μ λ¨κ³μμ μλ²κ° 50λ, 100λλ‘ λμ΄λλ μκ° μμ€ν
μ μ²΄κ° λ¬΄λμ§λ€.
λ°λΌμ κ°μ λ°©ν₯μ μμ°μ€λ½κ² λμΆλλ€.
β βλμμ κ²μ¬νκ³ , μλ΅ μλ μλ²λ λΉ λ₯΄κ² λ²λ¦¬κ³ , μ 체 μμ²μ μνμ μ ννλ€.β
μ¬κΈ°μ μ μ©ν κ°μ μ λ΅μ λ€μ 3κ°μ§λ€.
| μ λ΅ | μλ―Έ |
|---|---|
| Parallel(λμ μ€ν) | μλ² μμ μκ΄μμ΄ ν λ²μ μ κ² |
| Fail-Fast(λΉ λ₯Έ ν¬κΈ°) | μλ΅ μλ μλ²λ 1~2μ΄ λ΄ TIMEOUT μ²λ¦¬ |
| Global Timeout(μ 체 νλ) | μλ²κ° 100λμ¬λ μ 체 μ‘°νλ 2~3μ΄ λ΄ μλ£ |
κ³ μ βλΉλκΈ° + νμμμβ κ°μ λ¨μν ν€μλκ° μλλ€.
μ΄ μΈ κ°μ§λ μ΄μ κ°λ₯ν μμ€ν
μ΄ λ°λμ κ°μ ΈμΌ νλ λ€νΈμν¬ I/O μμ μ₯μΉλ€.
// λ³λ ¬ + Fail-Fast + Global Timebox
public NetworkStatusResponse probNetworkForServers(String groupName) throws Exception {
List<LinuxServer> servers = linuxServerRepository.findByGroupName(groupName);
if (servers.isEmpty()) return new NetworkStatusResponse(groupName, List.of());
int poolSize = Math.min(
Math.max(8, Runtime.getRuntime().availableProcessors() * 2),
Math.max(8, servers.size()));
ExecutorService ex = Executors.newFixedThreadPool(poolSize);
try {
List<CompletableFuture<NetworkStatusResponse.Item>> futures =
servers.stream()
.map(s -> CompletableFuture.supplyAsync(() -> {
NetProbe np = inspector.probNetwork(
s.getUserId(),
s.getIpAddress(),
s.getPort()
);
return new NetworkStatusResponse.Item(
s.getId(),
s.getIpAddress(),
s.getUserId(),
np.state(),
np.detail()
);
}, ex)
.orTimeout(2, TimeUnit.SECONDS) // κ°λ³ Fail-Fast
.exceptionally(e -> new NetworkStatusResponse.Item(
s.getId(),
s.getIpAddress(),
s.getUserId(),
NetState.TIMEOUT,
"timeout/" + e.getClass().getSimpleName()
))
).toList();
CompletableFuture<Void> all =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
try {
// μ 체 νμλ°μ€(μ: 2500ms) β λ€ μ λλλ μ¬κΈ°μ μ’
λ£
all.get(2500, TimeUnit.MILLISECONDS);
} catch (Exception ignored) {}
List<NetworkStatusResponse.Item> items = futures.stream()
.map(f -> f.getNow(null))
.filter(Objects::nonNull)
.toList();
return new NetworkStatusResponse(groupName, items);
} finally {
ex.shutdown();
}
κ°λ³ λ¨κ³ νμμμμ 1β2μ΄λ‘ κ³ μ (CommandBuilderμμ μ΄λ―Έ λ¨μΆλ μ΅μ μ¬μ©)
nc -n -w 2, ssh -o ConnectTimeout=2 -o ConnectionAttempts=1).
| νλͺ© | κ°μ μ | κ°μ ν |
|---|---|---|
| 3λ μλ² μ‘°ν | 10 ~ 12μ΄ | 2 ~ 2.5μ΄ |
| λ λλ§ μ€ν¨ | λΉλ² | μμ |
| ꡬ쑰 νμ₯μ± | μ·¨μ½ | μ°μ (100λ μ΄μ λμ κ°λ₯) |
| μ¬μ©μ κ²½ν | μ§μ°Β·λκΉ | μ¦μ μλ΅ |
μ΄λ² μ΄μλ₯Ό ν΅ν΄ μ»μ κ΅νμ λͺ ννλ€. μλ μ΅μ νμ λ¬Έμ κ° μλ ꡬ쑰μ νκ³λΌλ μ μ΄λ€.
βλ€νΈμν¬ I/Oλ₯Ό λ€λ£¨λ μλΉμ€μμ μμ°¨ μ²λ¦¬μ 무μ ν νμμμμ λ°λμ λ³λͺ©μ λ§λ λ€. νμ₯ κ°λ₯ν ꡬ쑰λ₯Ό μνλ€λ©΄ λ³λ ¬μ±(Concurrency)κ³Ό νμλ°μ€(Time-bound)κ° κΈ°λ³Έ μ μ κ° λμ΄μΌ νλ€.β
μλΉμ€λ μ΄μ μλ² μμ 무κ΄νκ² μμ μ μΈ μλ΅ μλλ₯Ό μ μ§νλ©°,
μ΄μμκ° μ€μκ°μΌλ‘ μλ² μνλ₯Ό λΉ λ₯΄κ² νμ
ν μ μλ ννλ‘ κ°μ λμλ€.