AWS 로드밸런서

진성대·2023년 10월 5일
0

DEVOPS

목록 보기
2/3

Application Load Balancer (ALB):

작동방식

  • ALB는 OSI 계층 7 (애플리케이션 계층)에서 동작하며 HTTP 및 HTTPS 트래픽을 로드 밸런싱합니다. 이것은 URL 경로 및 호스트 헤더와 같은 애플리케이션 레이어 정보에 따라 트래픽을 분산합니다.
import java.io.*;
import java.net.*;

public class ALBExample {
    public static void main(String[] args) {
        try {
            // 1. 인바운드 요청 수신
            int clientPort = 80; // HTTP 포트
            ServerSocket serverSocket = new ServerSocket(clientPort);
            Socket clientSocket = serverSocket.accept();
            
            // 클라이언트 요청을 읽습니다.
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            String clientRequest;
            StringBuilder requestBuilder = new StringBuilder();
            while ((clientRequest = in.readLine()) != null) {
                requestBuilder.append(clientRequest).append("\r\n");
                if (clientRequest.isEmpty()) {
                    break;
                }
            }
            
            // 클라이언트 요청 내용
            String clientRequestContent = requestBuilder.toString();
            System.out.println("Received client request:\n" + clientRequestContent);

            // 2. 요청 라우팅
            String selectedTargetGroup = determineTargetGroup(clientRequestContent);
            
            // 3. 대상 그룹 선택 (여기서는 임의로 선택)
            String selectedTarget = selectTarget(selectedTargetGroup);
            
            // 4. 대상 그룹 내에서 로드 밸런싱 (여기서는 임의로 선택)
            // 5. HTTP/HTTPS 헤더 조작 (필요한 경우 헤더 조작)
            
            // 6. 응답 반환 (여기서는 간단한 응답을 생성)
            String targetResponse = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!";
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            out.write(targetResponse);
            out.flush();
            
            // 클라이언트 소켓 및 서버 소켓 닫기
            clientSocket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static String determineTargetGroup(String clientRequest) {
        // 요청 내용을 분석하여 대상 그룹 선택 로직을 구현
        // 여기에서는 단순화한 예제이므로 특정 로직은 구현하지 않습니다.
        return "web-servers"; // 예제: 웹 서버 대상 그룹 선택
    }
    
    private static String selectTarget(String targetGroup) {
        // 대상 그룹 내에서 대상 선택 로직을 구현
        // 여기에서는 단순화한 예제이므로 특정 로직은 구현하지 않습니다.
        return "web-server-1"; // 예제: 첫 번째 웹 서버 선택
    }
}
  1. 인바운드 요청 수신: 클라이언트에서 ALB로 HTTP 또는 HTTPS 요청이 도착합니다. 이 요청은 ALB 리스너에 의해 수신됩니다. 리스너는 특정 포트(예: 80 또는 443)에서 대기하고 클라이언트로부터의 연결을 수신합니다.

  2. 요청 라우팅: ALB는 요청을 분석하여 어떤 대상 그룹으로 이를 전달할지 결정합니다. 대상 그룹은 서비스된 애플리케이션의 논리적인 구성 단위로, 여러 대상(예: EC2 인스턴스, 컨테이너, IP 주소)을 포함할 수 있습니다.

  3. 대상 그룹 선택: ALB는 요청을 처리할 대상 그룹을 선택합니다. 이 선택은 리스너 규칙에 따라 이루어집니다. 리스너 규칙은 요청 경로, 호스트 헤더, HTTP 메소드 및 기타 요청 속성에 따라 대상 그룹을 선택하기 위한 조건을 정의합니다.

  4. 대상 그룹 내에서 로드 밸런싱: 선택된 대상 그룹 내에서 ALB는 요청을 여러 대상(예: EC2 인스턴스)으로 분산합니다. 이 과정은 대상 그룹에 등록된 대상들 간에 트래픽이 균등하게 분배되도록 합니다.

  5. HTTP/HTTPS 헤더 조작: ALB는 필요한 경우 HTTP/HTTPS 헤더를 조작하거나 추가할 수 있습니다. 이를 통해 요청을 조작하거나 애플리케이션에 특정 정보를 제공할 수 있습니다.

  6. 응답 반환: ALB는 대상 그룹 내의 대상들로부터 받은 응답을 수집하고 클라이언트에게 반환합니다. 이 때 로드 밸런서는 대상의 응답을 재조립하여 클라이언트에게 제공하며, 서버 응답을 클라이언트에게 반환하는 동안 트래픽 로드 밸런싱을 수행합니다.

장점:

  • HTTP 및 HTTPS 트래픽을 분산하므로 웹 애플리케이션에 적합합니다.
  • 경로 기반 라우팅 및 호스트 기반 라우팅과 같은 고급 라우팅 기능을 제공합니다.
  • AWS Web Application Firewall (WAF)와 통합하여 보안 기능을 강화할 수 있습니다.
  • Lambda 함수, ECS 컨테이너, EC2 인스턴스 등 다양한 대상 유형을 지원합니다.

단점:

  • OSI 계층 7에서 동작하기 때문에 TCP/UDP 트래픽에는 사용할 수 없습니다.
    TCP 서버를 사용하여 트래픽 수신
import java.io.*;
import java.net.*;

public class TCPServerExample {
    public static void main(String[] args) {
        try {
            int serverPort = 8080; // 임의의 포트
            ServerSocket serverSocket = new ServerSocket(serverPort);
            System.out.println("TCP Server listening on port " + serverPort);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("Accepted incoming TCP connection.");
                
                // 클라이언트 요청을 읽고 처리하는 로직을 추가
                // 여기에서는 간단하게 클라이언트에 "Hello, TCP Client!"를 응답으로 보냅니다.
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                out.println("Hello, TCP Client!");
                
                clientSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ALB를 사용하여 HTTP 트래픽을 수신:

import com.sun.net.httpserver.*;

import java.io.IOException;
import java.io.OutputStream;

public class SimpleHttpServer {
    public static void main(String[] args) throws IOException {
        int port = 8080; // 임의의 포트
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
        server.createContext("/", new MyHandler());
        server.setExecutor(null); // 기본 executor 사용
        server.start();
        System.out.println("HTTP Server started on port " + port);
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange httpExchange) throws IOException {
            String response = "Hello, HTTP Client!";
            httpExchange.sendResponseHeaders(200, response.length());
            OutputStream os = httpExchange.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }
}
  • 라우팅 규칙이 복잡해질 수 있으며, 상황에 따라 구성하기 어려울 수 있습니다.

Network Load Balancer (NLB):

작동방식:

  • NLB는 OSI 계층 4 (전송 계층)에서 동작하며 TCP, UDP, TLS(SSL) 트래픽을 로드 밸런싱합니다. IP 주소와 포트 정보를 기반으로 트래픽을 분산합니다.
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class NetworkPacket {
    String sourceIP;
    String destIP;
    int sourcePort;
    int destPort;
    byte[] data;
    // ... 기타 필드와 메서드
}

class BackendServer {
    String ip;

    public void handlePacket(NetworkPacket packet) {
        // 패킷 처리 로직
    }
}

class SimpleNLB {
    private Map<String, List<BackendServer>> serverPool = new HashMap<>();

    public void registerBackend(String ip, BackendServer server) {
        serverPool.computeIfAbsent(ip, k -> new ArrayList<>()).add(server);
    }

    public void distributeTraffic(NetworkPacket packet) {
        // NLB는 대상 IP와 포트 정보를 기반으로 패킷을 분산
        List<BackendServer> servers = serverPool.get(packet.destIP);
        if (servers != null && !servers.isEmpty()) {
            // 여기서는 단순히 첫 번째 서버에게 트래픽을 전달합니다.
            // 실제 구현에서는 라운드 로빈, 최소 연결 수 등의 알고리즘을 사용할 수 있습니다.
            servers.get(0).handlePacket(packet);
        }
    }
}

public class NLBDemo {
    public static void main(String[] args) {
        SimpleNLB nlb = new SimpleNLB();
        nlb.registerBackend("192.168.0.1", new BackendServer());
        nlb.registerBackend("192.168.0.2", new BackendServer());

        NetworkPacket packet = new NetworkPacket();
        packet.destIP = "192.168.0.1";
        nlb.distributeTraffic(packet);
    }
}

장점:

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

enum Protocol {
    TCP, UDP
}

class Packet {
    Protocol protocol;
    String sourceIP;
    String destinationIP;
    // ... 기타 필드
}

class Server {
    private String ip;

    public Server(String ip) {
        this.ip = ip;
    }

    public void handlePacket(Packet packet) {
        System.out.println("Handling packet at server: " + ip);
        // 패킷 처리 로직
    }
}

class NLB {
    private String fixedIPAddress; // 고정 IP 주소
    private List<Server> servers = new CopyOnWriteArrayList<>();
    private AtomicInteger currentServerIndex = new AtomicInteger(0);

    public NLB(String fixedIPAddress) {
        this.fixedIPAddress = fixedIPAddress;
    }

    public void addServer(Server server) {
        servers.add(server);
    }

    public void processTraffic(Packet packet) {
        distributeTraffic(packet);
    }

    private void distributeTraffic(Packet packet) {
        if (servers.isEmpty()) {
            System.out.println("No servers available to handle packet");
            return;
        }

        int index = currentServerIndex.getAndUpdate(i -> (i + 1) % servers.size());
        Server selectedServer = servers.get(index);
        selectedServer.handlePacket(packet);
    }

    public String getFixedIPAddress() {
        return fixedIPAddress;
    }
}

public class NLBDemo {
    public static void main(String[] args) {
        NLB nlb = new NLB("192.168.1.100"); // 고정 IP 주소
        nlb.addServer(new Server("192.168.1.101"));
        nlb.addServer(new Server("192.168.1.102"));
        nlb.addServer(new Server("192.168.1.103"));

        // TCP 트래픽 예제
        Packet tcpPacket = new Packet();
        tcpPacket.protocol = Protocol.TCP;

        // UDP 트래픽 예제
        Packet udpPacket = new Packet();
        udpPacket.protocol = Protocol.UDP;

        nlb.processTraffic(tcpPacket);
        nlb.processTraffic(udpPacket);

        System.out.println("NLB is processing traffic on IP: " + nlb.getFixedIPAddress());
    }
}
  • TCP 및 UDP 트래픽을 로드 밸런싱하므로 다양한 애플리케이션에 사용 가능합니다.
  • 고성능을 제공하며, 대규모 네트워크 트래픽에 적합합니다.
  • 고정 IP 주소를 사용하여 인바운드 트래픽을 관리할 수 있습니다.
  • 빠른 대역폭 및 연결 지원을 제공합니다.

단점:

  • OSI 계층 4에서 동작하기 때문에 HTTP 레이어 정보에 따라 로드 밸런싱하지 않습니다.
  • 라우팅 및 리스너 구성이 다소 제한적입니다.

Classic Load Balancer (CLB):

어떻게 동작하는가? 이것은 이전 버전의 로드 밸런서로, OSI 계층 4에서 동작합니다. TCP 및 HTTP 트래픽을 로드 밸런싱합니다.

장점:

  • 간단한 구성 및 설정을 제공하며, 초기 버전의 AWS 로드 밸런서입니다.

단점:

  • 더 이상 새로운 사용 사례에 권장되지 않으며, ALB 및 NLB가 대체됩니다.
  • OSI 계층 7에서 동작하지 않으므로 HTTP 레이어 정보에 따라 로드 밸런싱하지 않습니다.

Elastic Load Balancer (ELB):

어떻게 동작하는가? ELB는 이전 버전의 AWS 로드 밸런서로, OSI 계층 4에서 동작합니다. TCP 및 HTTP 트래픽을 로드 밸런싱합니다. 이제는 이전 버전의 ELB 대신 ALB 및 NLB를 사용하는 것이 권장됩니다.

장점:

  • 간단한 구성을 제공합니다.

단점:

  • 새로운 기능 및 고급 설정을 제한적으로 지원하며, 더 이상 새로운 사용 사례에 권장되지 않습니다.
  • OSI 계층 7에서 동작하지 않으므로 HTTP 레이어 정보에 따라 로드 밸런싱하지 않습니다.

로드 밸런서를 선택할때 고려 사항

  1. OSI 계층에서 어떤 레벨에서 로드 밸런싱이 필요한가?
  2. 어떤 유형의 트래픽을 로드 밸런싱해야 하는가 (예: HTTP, TCP, UDP)?
  3. 고급 라우팅 또는 애플리케이션 기반 라우팅이 필요한가?
  4. 고성능 및 대역폭이 필요한가?
profile
신입 개발자

0개의 댓글