[HAProxy] 무중단 배포

[원본 링크]

HAProxy를 사용할 경우 무중단 배포나 교체, scale-in을 달성하는 것은 어렵지 않다.
HAProxy 설정파일을 변경 후에 reload하면 그 자체로도 다운타임 없이 교체를 수행하므로, 설정파일을 잘 갈아끼우는 것만으로도 무중단 배포가 된다.

한번 시도해보자.
같은 동작을 하는 간단한 서버를 2개 실행한다.

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"ok"}`))

		fmt.Println("fast")
	})

	http.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
		// 5초 대기
		time.Sleep(5 * time.Second)

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"ok"}`))

		fmt.Println("slow")
	})

	// 서버 시작
	http.ListenAndServe(":8081", nil)
}
package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/fast", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"ok"}`))

		fmt.Println("fast")
	})

	http.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
		// 5초 대기
		time.Sleep(5 * time.Second)

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"ok"}`))

		fmt.Println("slow")
	})

	// 서버 시작
	http.ListenAndServe(":8082", nil)
}

그리고 HAProxy에서 일단은 하나만 라우팅한다.

frontend test-front
   mode http
   bind *:8000
   default_backend test-back

backend test-back
   server master 0.0.0.0:8081

잘 연결되었고

그걸 찌르는 녀석을 하나 띄웠다.
중간에 뭔가 끊긴다면 이 녀석이 감지할 것이다.

package main

import (
	"fmt"
	"net/http"
	"time"
)

func attack(i int) {
	fmt.Println("Start attack")
	for {
		// localhost:3000에 GET 요청을 보냄
		req, err := http.NewRequest("GET", "http://127.0.0.1:8000/slow", nil)
		if err != nil {
			panic(err)
		}

		// 요청 보내기
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			panic(err)
		}
		resp.Body.Close()

		fmt.Println("checked", i)
		// 0.1초 대기
		time.Sleep(time.Millisecond * 100)
	}
}

func main() {
	// 스레드 5개 띄우고 대기하기
	for i := 0; i < 5; i++ {
		go attack(i)
	}

	// 무한 대기
	for {
	}
}

그러고 돌려보면, 잘 돌아갈 것이다.

그러면 이제 서버를 교체해보자.

서버 목적지만 바꿔서 reload하면


중단 없이 요청이 계속해서 이어지고 있는 것을 볼 수 있을 것이다.

서버를 스케일아웃하고 싶다면 저 backend server 목록을 추가해서 reload하면 되고, 서버 일부를 다운하고 싶다면 빼서 reload하면 된다.
Blue-Green 배포 같은 것도 그냥 한번에 교체해서 reload하는 식으로 처리하면 된다. 나머지는 응용의 영역이다.



참조
https://www.haproxy.com/blog/rolling-updates-and-blue-green-deployments-with-kubernetes-and-haproxy