장애 조치(Failover)와 복구(Recovery)는 어떻게 설계하나요?

김상욱·2024년 12월 22일

장애 조치(Failover)와 복구(Recovery)는 어떻게 설계하나요?

장애 조치(Failover)란?

장애 조치는 시스템의 일부에 장애가 발생했을 때, 자동으로 다른 정상적인 시스템이나 서버로 트래픽이나 작업을 전환하는 과정입니다. 이를 통해 서비스의 지속성을 유지하고 사용자에게 최소한의 영향만을 주도록 합니다.

복구(Recovery)란?

복구는 장애가 발생한 시스템이나 서비스가 정상 상태로 돌아오도록 복구 작업을 수행하는 것을 말합니다. 이는 장애 원인을 해결하고, 시스템을 다시 안정적으로 운영할 수 있도록 하는 과정입니다.

Why important?

  • 가용성 향상 : 시스템이 항상 작동 상태를 유지하도록 도와줍니다.
  • 신뢰성 증대 ; 사용자에게 안정적인 서비스를 제공하여 신뢰를 얻을 수 있습니다.
  • 비용 절감 : 장애로 인한 다운타임을 최소화하여 비즈니스 손실을 줄일 수 있습니다.

Methods

장애 조치(Failover) 설계
  1. 이중화 구성(Redundancy)
  • 서버 이중화 : 여러 대의 서버를 운영하여 하나의 서버에 문제가 생겨도 다른 서버가 트래픽을 처리할 수 있도록 합니다.
  • 데이터베이스 이중화 : 마스터-슬레이브 또는 마스터-마스터 구성을 통해 데이터베이스의 가용성을 높입니다.
  1. 로드 밸런서 사용
  • 로드 밸런서를 통해 트래픽을 여러 서버에 분산시킵니다. 서버 중 하나가 다운되면 로드 밸런서가 자동으로 다른 서버로 트래픽을 전환합니다.
    ex) AWS의 Elastic Load Balancer(ELB), Nginx, HAProxy 등
  1. 헬스 체크(Health Check)
  • 로드 밸런서나 모니터링 시스템이 주기적으로 서버의 상태를 점검하여 장애가 발생한 서버를 감지하고 트래픽을 우회시킵니다.
  1. 세션 관리
  • 여러 서버 간에 세션 정보를 공유하거나, 세션 스토리지를 별도로 두어 사용자가 어느 서버로 연결되더라도 문제가 없도록 합니다.
    ex) Redis를 사용한 세션 스토리지
복구(Recovery) 설계
  1. 장애 원인 분석
  • 로그 분석, 모니터링 도구 등을 활용하여 장애의 원인을 파악합니다.
  1. 자동 복구 시스템 구축
  • 장애 발생 시 자동으로 서버를 재시작하거나 교체하는 스크립트를 작성합니다.
    ex) Kubernetes의 자가 치유 기능, AWS Auto Scaling 그룹
  1. 백업 및 복원 전략
  • 정기적인 데이터 백업을 통해 장애 시 데이터를 복원할 수 있도록 합니다.
    ex) 데이터베이스 스냅샷, 파일 시스템 백업

4 무중단 배포(Zero Downtime Deployment)

  • 새로운 버전의 애플리케이션을 배포할 때도 서비스 중단이 없도록 설계합니다.
    ex) Blue-Green Deployment, Canary Deployment

Apply to Java/spring backend

이중화된 Spring 애플리케이션 배포
  1. 클라우드 환경 활용
  • AWS, Azure, GCP와 같은 클라우드 서비스를 이용하여 여러 가용 영역(AZ)에 애플리케이션을 배포합니다.
  1. Spring Cloud Netflix 사용
  • Eureka : 서비스 디스커버리를 통해 여러 인스턴스 간의 통신을 관리합니다.
  • Hystrix : 서킷 브레이커 패턴을 구현하여 장애 발생 시 대체 로직을 수행합니다.
  1. 컨테이너화
  • Docker와 Kubernetes를 사용하여 애플리케이션을 컨테이너화하고, 자동 스케일링 및 자가 치유 기능을 활용합니다.
# application.yml
spring:
  cloud:
    netflix:
      eureka:
        client:
          service-url:
            defaultZone: http://eureka-server:8761/eureka/
// Hystrix 예제
@RestController
public class ExampleController {

    @GetMapping("/data")
    @HystrixCommand(fallbackMethod = "fallbackData")
    public String getData() {
        // 외부 서비스 호출 등
        return externalService.getData();
    }

    public String fallbackData() {
        return "Fallback response";
    }
}
로드 밸런서 설정
  • Nginx 예시
http {
    upstream backend {
        server backend1.example.com;
        server backend2.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

TIP

  • 모니터링 도구 활용 : Prometheus, Grafana, ELK 스택 등을 사용하여 시스템 상태를 실시간으로 모니터링합니다.
  • 테스트 자동화 : 장애 시나리오를 미리 테스트하여 실제 상황에서의 대응 능력을 키웁니다.
  • 문서화 : 장애 대응 절차와 복구 계획을 문서화하여 팀원들과 공유합니다.

취업 준비 중인 신입 Java/Spring 백엔드 개발자라면 이론뿐만 아니라 실습을 통해 장애 조치(Failover)와 복구(Recovery)에 대한 이해를 깊게 하는 것이 매우 중요합니다. 다음은 실습을 통해 이러한 개념을 익힐 수 있는 몇 가지 프로젝트와 연습 방법을 제안드립니다.

1. 간단한 Spring Boot 애플리케이션 배포 및 이중화

목표:

  • Spring Boot 애플리케이션을 여러 인스턴스로 배포하고 로드 밸런서를 통해 트래픽을 분산시켜 장애 조치(Failover)를 구현합니다.

실습 단계:

  1. Spring Boot 애플리케이션 생성:

    • 간단한 REST API를 제공하는 Spring Boot 애플리케이션을 만듭니다.
    • 예를 들어, /hello 엔드포인트가 "Hello, World!"를 반환하도록 설정합니다.
    @RestController
    public class HelloController {
        @GetMapping("/hello")
        public String hello() {
            return "Hello, World!";
        }
    }
  2. 애플리케이션 빌드 및 Docker 이미지 생성:

    • Dockerfile을 작성하여 애플리케이션을 컨테이너화합니다.
    FROM openjdk:17-jdk-alpine
    VOLUME /tmp
    COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
    ENTRYPOINT ["java","-jar","/app.jar"]
    • Docker 이미지를 빌드합니다.
    mvn clean package
    docker build -t my-spring-app .
  3. 여러 인스턴스 실행:

    • 동일한 Docker 이미지를 여러 포트에서 실행합니다.
    docker run -d -p 8081:8080 my-spring-app
    docker run -d -p 8082:8080 my-spring-app
  4. Nginx 로드 밸런서 설정:

    • Nginx를 설치하고 로드 밸런서 설정 파일을 작성합니다.
    http {
        upstream backend {
            server localhost:8081;
            server localhost:8082;
        }
    
        server {
            listen 80;
    
            location / {
                proxy_pass http://backend;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
            }
        }
    }
    • Nginx를 재시작하여 설정을 적용합니다.
    sudo systemctl restart nginx
  5. 장애 시나리오 테스트:

    • 한 인스턴스를 중지시켜 Nginx가 자동으로 다른 인스턴스로 트래픽을 전환하는지 확인합니다.
    docker stop <컨테이너 ID>
    • 브라우저나 Postman을 통해 /hello 엔드포인트에 접속하여 응답을 확인합니다.

2. Spring Cloud Netflix를 활용한 마이크로서비스 구축

목표:

  • Spring Cloud Netflix의 Eureka(서비스 디스커버리)와 Hystrix(회로 차단기)를 사용하여 간단한 마이크로서비스 아키텍처를 구축하고 장애 조치 및 복구를 구현합니다.

실습 단계:

  1. Eureka 서버 설정:

    • Spring Initializr를 사용하여 Eureka 서버 프로젝트를 생성합니다.
    • spring-cloud-starter-netflix-eureka-server 의존성을 추가합니다.
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }
    }
    • application.yml 설정:
    server:
      port: 8761
    
    eureka:
      client:
        register-with-eureka: false
        fetch-registry: false
      server:
        enable-self-preservation: false
  2. Eureka 클라이언트 서비스 설정:

    • 다른 Spring Boot 애플리케이션을 Eureka 클라이언트로 설정합니다.
    • spring-cloud-starter-netflix-eureka-client 의존성을 추가합니다.
    @SpringBootApplication
    @EnableEurekaClient
    public class ServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServiceApplication.class, args);
        }
    }
    • application.yml 설정:
    server:
      port: 8081
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
  3. Hystrix 설정 및 회로 차단기 적용:

    • spring-cloud-starter-netflix-hystrix 의존성을 추가합니다.
    • 애플리케이션에 Hystrix를 활성화합니다.
    @SpringBootApplication
    @EnableEurekaClient
    @EnableHystrix
    public class ServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(ServiceApplication.class, args);
        }
    }
    • 회로 차단기를 적용한 컨트롤러 작성:
    @RestController
    public class ExampleController {
    
        @GetMapping("/data")
        @HystrixCommand(fallbackMethod = "fallbackData")
        public String getData() {
            // 외부 서비스 호출 또는 의도적으로 예외 발생
            if (new Random().nextBoolean()) {
                throw new RuntimeException("Simulated service failure");
            }
            return "Successful response";
        }
    
        public String fallbackData() {
            return "Fallback response";
        }
    }
  4. 장애 시나리오 테스트:

    • /data 엔드포인트를 여러 번 호출하여 정상 응답과 Fallback 응답이 번갈아 나오는지 확인합니다.

3. Docker와 Kubernetes를 활용한 컨테이너 오케스트레이션 및 자가 치유

목표:

  • Docker와 Kubernetes를 사용하여 애플리케이션을 배포하고, Kubernetes의 자가 치유 기능을 통해 장애 조치 및 복구를 실습합니다.

실습 단계:

  1. Docker 컨테이너 이미지 준비:

    • 앞서 만든 Spring Boot 애플리케이션의 Docker 이미지를 준비합니다.
  2. Minikube 또는 Kind 설치:

    • 로컬 환경에서 Kubernetes 클러스터를 실행할 수 있는 Minikube 또는 Kind를 설치합니다.
    # Minikube 설치 예시
    curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
    sudo install minikube-linux-amd64 /usr/local/bin/minikube
    minikube start
  3. Kubernetes 배포 파일 작성:

    • deployment.ymlservice.yml 파일을 작성합니다.
    # deployment.yml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-spring-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: my-spring-app
      template:
        metadata:
          labels:
            app: my-spring-app
        spec:
          containers:
          - name: my-spring-app
            image: my-spring-app:latest
            ports:
            - containerPort: 8080
            livenessProbe:
              httpGet:
                path: /health
                port: 8080
              initialDelaySeconds: 15
              periodSeconds: 20
    # service.yml
    apiVersion: v1
    kind: Service
    metadata:
      name: my-spring-app-service
    spec:
      type: LoadBalancer
      ports:
      - port: 80
        targetPort: 8080
      selector:
        app: my-spring-app
  4. Kubernetes에 배포:

    • 배포 파일을 적용하여 애플리케이션을 배포합니다.
    kubectl apply -f deployment.yml
    kubectl apply -f service.yml
  5. 자가 치유 테스트:

    • 애플리케이션 포드 중 하나를 강제 종료하여 Kubernetes가 자동으로 새로운 포드를 생성하는지 확인합니다.
    kubectl delete pod <포드 이름>
    • kubectl get pods 명령어로 새로운 포드가 생성되는지 확인합니다.
  6. 헬스 체크 엔드포인트 추가:

    • Spring Boot 애플리케이션에 헬스 체크 엔드포인트(/health)를 추가합니다.
    @RestController
    public class HealthController {
        @GetMapping("/health")
        public ResponseEntity<String> health() {
            return ResponseEntity.ok("OK");
        }
    }

4. 클라우드 서비스를 활용한 고가용성 배포

목표:

  • AWS, Azure, 또는 GCP와 같은 클라우드 서비스를 사용하여 다중 가용 영역(AZ)에 애플리케이션을 배포하고 장애 조치 및 복구를 구현합니다.

실습 단계:

  1. 클라우드 계정 생성 및 설정:

    • AWS, Azure, 또는 GCP에 계정을 생성하고 CLI 도구를 설치합니다.
  2. EC2 인스턴스에 Spring Boot 애플리케이션 배포:

    • 여러 AZ에 걸쳐 EC2 인스턴스를 생성하고 애플리케이션을 배포합니다.
  3. Elastic Load Balancer(ELB) 설정:

    • ELB를 설정하여 여러 인스턴스로 트래픽을 분산시킵니다.
    • 헬스 체크를 설정하여 비정상 인스턴스를 자동으로 제외시킵니다.
  4. Auto Scaling 그룹 구성:

    • 트래픽 증가 시 자동으로 인스턴스를 추가하고, 장애 시 자동으로 인스턴스를 교체하도록 설정합니다.
  5. 장애 시나리오 테스트:

    • 인스턴스를 종료하여 ELB가 자동으로 트래픽을 다른 인스턴스로 전환하는지 확인합니다.
    • Auto Scaling 그룹이 자동으로 새로운 인스턴스를 생성하는지 확인합니다.

5. 데이터베이스 이중화 및 백업 복원 실습

목표:

  • 데이터베이스의 이중화(Master-Slave 또는 Master-Master)를 설정하고, 정기적인 백업 및 복원 전략을 실습합니다.

실습 단계:

  1. MySQL 데이터베이스 설정:

    • 두 개의 MySQL 인스턴스를 설정하여 Master-Slave 복제를 구성합니다.
  2. Spring Boot 애플리케이션과 데이터베이스 연결:

    • application.yml 파일에서 데이터베이스 연결 설정을 이중화된 데이터베이스로 구성합니다.
    spring:
      datasource:
        url: jdbc:mysql://master-db:3306/mydb
        username: user
        password: pass
      jpa:
        hibernate:
          ddl-auto: update
  3. 백업 및 복원 스크립트 작성:

    • 정기적으로 mysqldump를 사용하여 데이터베이스 백업을 수행하는 스크립트를 작성합니다.
    # backup.sh
    mysqldump -h master-db -u user -p'pass' mydb > backup.sql
    • 복원 시에는 mysql 명령어를 사용합니다.
    mysql -h master-db -u user -p'pass' mydb < backup.sql
  4. 장애 시나리오 테스트:

    • Master 데이터베이스를 중지시키고 Slave가 제대로 작동하는지 확인합니다.
    • 백업 파일을 사용하여 데이터베이스를 복원하는 과정을 테스트합니다.

6. 모니터링 및 알림 시스템 구축

목표:

  • Prometheus와 Grafana를 사용하여 애플리케이션 및 인프라의 상태를 모니터링하고, 장애 발생 시 알림을 받도록 설정합니다.

실습 단계:

  1. Prometheus 설치 및 설정:

    • Prometheus를 설치하고 Spring Boot 애플리케이션의 메트릭을 수집하도록 설정합니다.
    # prometheus.yml
    scrape_configs:
      - job_name: 'spring-app'
        metrics_path: '/actuator/prometheus'
        static_configs:
          - targets: ['localhost:8080']
  2. Grafana 설치 및 대시보드 설정:

    • Grafana를 설치하고 Prometheus를 데이터 소스로 추가합니다.
    • 애플리케이션의 메트릭을 시각화하는 대시보드를 생성합니다.
  3. Alertmanager 설정:

    • Prometheus Alertmanager를 설정하여 특정 메트릭 조건에 따라 알림을 받도록 구성합니다.
    # alertmanager.yml
    global:
      smtp_smarthost: 'smtp.example.com:587'
      smtp_from: 'alert@example.com'
      smtp_auth_username: 'user'
      smtp_auth_password: 'password'
    
    route:
      receiver: 'email'
    
    receivers:
      - name: 'email'
        email_configs:
          - to: 'your-email@example.com'
  4. 알림 규칙 작성:

    • Prometheus에 알림 규칙을 추가하여 예를 들어 응답 시간이 특정 임계값을 초과할 경우 알림을 보냅니다.
    # alert.rules
    groups:
      - name: spring-app-alerts
        rules:
          - alert: HighResponseTime
            expr: http_server_requests_seconds_sum / http_server_requests_seconds_count > 1
            for: 5m
            labels:
              severity: critical
            annotations:
              summary: "High response time detected"
              description: "The average response time is above 1 second for 5 minutes."

7. 무중단 배포 실습 (Blue-Green Deployment)

목표:

  • Blue-Green Deployment 전략을 사용하여 애플리케이션의 새로운 버전을 무중단으로 배포하고, 장애 시 빠르게 롤백하는 방법을 실습합니다.

실습 단계:

  1. 두 개의 배포 환경 구성:

    • bluegreen 두 개의 배포 환경을 설정합니다.
    • 각 환경에 동일한 Spring Boot 애플리케이션을 다른 버전으로 배포합니다.
  2. 로드 밸런서 설정:

    • 로드 밸런서를 사용하여 현재 활성화된 배포 환경으로 트래픽을 전환합니다.
    • 예를 들어, Nginx 설정에서 bluegreen 서버를 추가하고 활성 환경을 변경합니다.
  3. 배포 및 전환 테스트:

    • 새로운 버전을 green 환경에 배포하고, 로드 밸런서를 통해 트래픽을 green으로 전환합니다.
    • 문제가 발생하면 로드 밸런서를 다시 blue로 전환하여 빠르게 롤백합니다.
    # Nginx 예시
    http {
        upstream blue {
            server localhost:8081;
        }
    
        upstream green {
            server localhost:8082;
        }
    
        server {
            listen 80;
    
            location / {
                # 현재 활성화된 환경으로 트래픽 전환
                proxy_pass http://green;  # 또는 http://blue
            }
        }
    }

8. 추가 학습 자료 및 참고 링크

마무리

위의 실습 프로젝트를 통해 장애 조치(Failover)와 복구(Recovery)에 대한 실질적인 경험을 쌓을 수 있습니다. 각 프로젝트는 단계별로 진행하며, 문제를 해결하면서 개념을 보다 깊이 이해할 수 있을 것입니다. 또한, GitHub에 자신의 프로젝트를 올려 포트폴리오로 활용하면 면접 시 큰 도움이 될 것입니다. 꾸준한 실습과 학습을 통해 안정적이고 신뢰할 수 있는 백엔드 시스템을 설계하고 구현하는 능력을 키우시길 바랍니다. 화이팅!

0개의 댓글