sudo 권한, 콘솔 접근 수단, 현재 SSH 세션과 별도의 검증 세션
네트워크 · 구성 레시피
HAProxy HTTP 로드밸런서 구성
Ubuntu에서 HAProxy를 사설 주소에 먼저 구성하고, 백엔드 상태 검사·설정 문법 검사·실제 요청 검증을 통과한 뒤에만 안전하게 reload하는 운영 절차입니다.BEFORE YOU START
시작 전에 준비하세요
로드밸런서 사설 IP·서비스 포트·허용할 클라이언트 CIDR
각 백엔드의 고정 사설 IP, 포트, Host 헤더, 인증 없이 최소 정보만 반환하는 상태 확인 경로
기존 프록시가 있다면 설정 백업과 트래픽 우회 또는 복구 절차
권장 대상 단일 장애점을 줄이기 위해 내부 웹 애플리케이션 앞에 HTTP 로드밸런서를 두려는 개발자·운영자
FOLLOW THE RECIPE
8단계 구성·점검 레시피
운영체제와 네트워크 역할 확인
HAProxy가 수신할 사설 주소와 백엔드가 있는 경로를 먼저 확인합니다. 인터넷 공개 주소에 바로 바인딩하는 예시는 사용하지 않습니다.
cat /etc/os-releaseip -brief address
ip routeapt-cache policy haproxysystemctl --failed --no-pager- 수신 주소 <LB_PRIVATE_IP>가 실제 NIC에 존재한다.
- 백엔드 주소는 외부 공개 주소가 아니라 승인된 내부 경로다.
- 클라이언트가 사용할 Host 이름과 TLS 종단 위치를 정했다.
수신 IP가 유동 주소이거나 백엔드 경로가 NAT에 의존한다면 먼저 네트워크 설계를 고정합니다.
현재 포트 점유와 각 백엔드의 실제 상태 응답을 확인합니다.
포트·백엔드·기존 구성 사전 점검
설정 파일을 만들기 전에 수신 포트 충돌과 백엔드 상태를 각각 확인합니다. TCP 연결만이 아니라 애플리케이션 상태 경로의 HTTP 응답을 기준으로 삼습니다.
sudo ss -ltnp | grep ':<LB_PORT> '출력이 없을 때 미사용 상태입니다. 출력이 있으면 해당 프로세스의 소유 팀을 확인합니다.curl --fail --show-error --silent --max-time 5 -H 'Host: <SERVICE_HOST>' http://<BACKEND1_PRIVATE_IP>:<BACKEND_PORT>/healthzcurl --fail --show-error --silent --max-time 5 -H 'Host: <SERVICE_HOST>' http://<BACKEND2_PRIVATE_IP>:<BACKEND_PORT>/healthzdpkg -l haproxy
systemctl status haproxy --no-pager- 두 백엔드가 같은 상태 코드와 호환 가능한 응답을 반환한다.
- 상태 경로는 데이터 변경을 만들지 않고 비밀정보를 반환하지 않는다.
- 수신 포트의 기존 사용 여부와 변경 영향을 기록했다.
한 백엔드라도 직접 요청에 실패하면 로드밸런서 구성보다 해당 백엔드 또는 네트워크를 먼저 복구합니다.
패키지 변경 목록을 검토하고 HAProxy를 설치합니다.
배포판 패키지로 HAProxy 설치
Ubuntu 보안 저장소의 패키지를 사용합니다. 무인 승인 없이 변경 목록을 읽고 설치합니다.
sudo apt updateapt-get --simulate install haproxysudo apt-get install haproxyhaproxy -vv- 예상하지 못한 패키지 제거·교체가 없다.
- 설치된 HAProxy가 필요한 HTTP 기능과 TLS 기능을 제공한다.
- 패키지 저장소와 보안 업데이트 정책을 기록했다.
외부 저장소나 소스 빌드가 필요하다면 이 레시피와 섞지 말고 수명주기·서명 검증을 별도 설계합니다.
기본 설정을 백업하고 사설 수신·상태 검사를 구성합니다.
백업 후 프런트엔드와 상태 검사 구성
원본을 고유한 접미사로 보관하고, 사설 주소에만 바인딩합니다. 백엔드는 초기 상태 검사를 통과하기 전 트래픽을 받지 않도록 init-state down을 사용합니다.
date -u +%Y%m%dT%H%M%SZ출력값을 <BACKUP_SUFFIX>에 사용하고 변경 기록에 남깁니다.sudo cp --preserve=all --no-clobber /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.<BACKUP_SUFFIX>.baksudoedit /etc/haproxy/haproxy.cfgsudo stat -c '%U:%G %a %n' /etc/haproxy/haproxy.cfgglobal
log /dev/log local0
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
timeout connect 5s
timeout client 30s
timeout server 30s
frontend app_front
bind <LB_PRIVATE_IP>:<LB_PORT>
default_backend app_back
backend app_back
balance roundrobin
option httpchk GET /healthz
http-check send hdr Host <SERVICE_HOST>
http-check expect status 200
server app1 <BACKEND1_PRIVATE_IP>:<BACKEND_PORT> check init-state down rise 3 fall 3
server app2 <BACKEND2_PRIVATE_IP>:<BACKEND_PORT> check init-state down rise 3 fall 3자리표시자를 실제 사설 주소와 포트로 바꿉니다. 상태 경로가 200 이외의 정상 코드를 사용한다면 애플리케이션 계약에 맞춰 expect 조건을 조정합니다.- 원본 백업이 덮어쓰기 없이 생성됐다.
- bind 주소는 특정 사설 IP이며 관리·통계 UI를 외부에 추가하지 않았다.
- 연결·클라이언트·서버 timeout이 명시돼 있다.
- 모든 백엔드에 능동 상태 검사가 있다.
문법이 맞더라도 잘못된 Host 또는 상태 경로는 모든 백엔드를 DOWN으로 만듭니다. 시작 전 직접 요청 결과와 다시 대조합니다.
문법 검사가 성공한 경우에만 서비스를 시작하거나 reload합니다.
문법 검사 후 안전하게 반영
기존 SSH 세션과 우회 경로를 유지합니다. 설정 검사가 실패하면 서비스에 반영하지 않습니다.
sudo haproxy -c -f /etc/haproxy/haproxy.cfgsudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl enable --now haproxy.servicesudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy.service이미 active인 서비스의 변경 때만 사용합니다. 최초 시작 명령과 중복 실행하지 않습니다.systemctl status haproxy.service --no-pager
sudo journalctl -u haproxy.service -n 80 --no-pager- haproxy -c가 Valid configuration을 반환했다.
- reload 명령은 문법 검사와 &&로 연결돼 있다.
- 기존 세션과 우회 경로를 검증 완료 때까지 유지한다.
서비스가 시작됐어도 백엔드가 모두 DOWN이면 배포 완료가 아닙니다. 로그의 health check 실패부터 확인합니다.
백엔드 직접 요청과 HAProxy 경유 요청을 비교합니다.
실제 요청과 장애 전환 검증
프로세스 상태가 아니라 실제 HTTP 경로를 검증합니다. 계획된 테스트 환경에서 한 백엔드를 점검 모드로 전환해도 서비스가 유지되는지 확인합니다.
curl --fail --show-error --silent --max-time 5 -H 'Host: <SERVICE_HOST>' http://<LB_PRIVATE_IP>:<LB_PORT>/healthzsudo ss -ltnp | grep ':<LB_PORT> 'systemctl is-active haproxy.service
systemctl is-enabled haproxy.servicesudo journalctl -u haproxy.service --since '-10 minutes' --no-pager- 별도 클라이언트에서 승인한 Host와 경로로 정상 응답을 받는다.
- 백엔드 한 대가 계획된 점검 상태일 때 다른 정상 백엔드가 응답한다.
- 복구된 백엔드는 rise 횟수를 충족한 뒤 다시 투입된다.
직접 백엔드는 정상인데 HAProxy만 실패하면 bind·Host·health check를, 둘 다 실패하면 백엔드 또는 경로를 조사합니다.
수신 범위와 로그·헤더 신뢰 경계를 최종 점검합니다.
노출 범위와 헤더 신뢰 경계 점검
HAProxy 포트는 승인한 클라이언트 CIDR에서만 접근하게 합니다. 인터넷 공개가 필요하면 TLS 인증서·HTTP 보안 헤더·상위 WAF를 별도 검증합니다.
sudo ufw status numberedsudo ufw --dry-run allow proto tcp from <CLIENT_CIDR> to <LB_PRIVATE_IP> port <LB_PORT>sudo ufw allow proto tcp from <CLIENT_CIDR> to <LB_PRIVATE_IP> port <LB_PORT>UFW가 이미 active이고 SSH 허용 및 콘솔 복구 경로가 확인된 경우에만 적용합니다. 이 레시피에서 UFW 자체를 활성화하지 않습니다.sudo ss -ltnp | grep ':<LB_PORT> '
sudo ufw status numbered- 로드밸런서 포트는 승인한 CIDR에만 노출된다.
- 관리 통계 화면이나 runtime socket을 TCP 공개하지 않았다.
- X-Forwarded-For 같은 전달 헤더는 신뢰하는 프록시 경로에서만 사용한다.
- TLS를 종료한다면 개인키 권한·갱신·체인 검증과 외부 HTTPS 시험이 별도로 완료됐다.
포트가 예상보다 넓게 보이면 서비스 자체보다 방화벽·클라우드 보안그룹·NAT를 함께 확인합니다.
문제 발생 시 검증된 이전 설정만 선택해 복원합니다.
이전 설정 복원과 제한 규칙 원복
현재 세션과 우회 경로를 유지한 채 이번 작업의 백업 접미사를 명시적으로 고릅니다. 복원 설정도 문법 검사를 통과한 경우에만 reload합니다.
sudo find /etc/haproxy -maxdepth 1 -type f -name 'haproxy.cfg.*.bak' -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sortsudo test -f /etc/haproxy/haproxy.cfg.<BACKUP_SUFFIX>.baksudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.failed.<ROLLBACK_SUFFIX>sudo cp --preserve=all /etc/haproxy/haproxy.cfg.<BACKUP_SUFFIX>.bak /etc/haproxy/haproxy.cfgsudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy.servicesudo ufw delete allow proto tcp from <CLIENT_CIDR> to <LB_PRIVATE_IP> port <LB_PORT>이번 작업이 추가한 정확한 규칙임을 ufw status numbered에서 확인한 경우에만 실행합니다.curl --fail --show-error --silent --max-time 5 -H 'Host: <SERVICE_HOST>' http://<LB_PRIVATE_IP>:<LB_PORT>/healthz- 변경 기록과 일치하는 백업 하나를 명시적으로 선택했다.
- 복원 설정의 문법 검사가 성공한 뒤에만 reload했다.
- HAProxy 경유 실제 요청과 백엔드 상태를 다시 확인했다.
- 기존 SSH 세션과 우회 경로를 검증 완료까지 유지했다.
이전 설정도 검증에 실패하면 reload하지 말고 보존한 failed 파일과 백업 차이를 검토합니다.
장애 원인을 설정·백엔드·네트워크로 분리해 후속 조치합니다.
SECURITY CHECK
운영 전 마지막 보안 점검
- 특정 사설 IP와 승인된 클라이언트 CIDR만 사용한다.
- 모든 설정 반영은 haproxy -c 성공 후에만 수행한다.
- 상태 경로는 비밀정보를 반환하거나 상태를 변경하지 않는다.
- 통계·runtime 관리 인터페이스를 인터넷에 공개하지 않는다.
- 운영 TLS는 검증된 인증서와 자동 갱신 시험을 별도로 갖춘다.
COMMON ERRORS
자주 막히는 지점
모든 백엔드가 DOWN으로 표시됨
- 증상
- HAProxy는 실행 중이지만 요청이 503으로 응답합니다.
- 가능한 원인
- 상태 경로·Host 헤더·백엔드 포트가 실제 애플리케이션과 다르거나 방화벽이 상태 검사를 차단할 수 있습니다.
- 확인 순서
- HAProxy 호스트에서 각 백엔드 healthz를 직접 요청하고 설정의 httpchk 조건과 응답 코드를 대조합니다.
reload 뒤 새 연결만 실패함
- 증상
- 기존 연결은 유지되지만 새로운 요청이 timeout 또는 503입니다.
- 가능한 원인
- 새 프로세스의 bind 실패, 백엔드 경로 오류 또는 방화벽 범위 불일치일 수 있습니다.
- 확인 순서
- journal, ss, haproxy -c, 실제 경유 요청을 확인하고 검증된 이전 설정으로 복원합니다.
일부 요청에서 원래 클라이언트 정보가 잘못됨
- 증상
- 애플리케이션 로그의 IP 또는 스킴이 기대와 다릅니다.
- 가능한 원인
- 전달 헤더를 누락했거나 신뢰할 수 없는 클라이언트의 헤더까지 애플리케이션이 신뢰할 수 있습니다.
- 확인 순서
- 전체 프록시 홉을 문서화하고, 신뢰 프록시에서 설정한 헤더만 애플리케이션이 사용하도록 제한합니다.
PRIMARY REFERENCES
공식 문서
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.