서버 한 대에서 쿠버네티스를 어떻게 배치했나
서버 한 대를 넷으로 나누자 볼륨, 진입점, 부팅 순서, 배포 단위 문제가 생겼다. 저장 방식은 결국 세 번 옮겼다. 노드·네트워크·저장소·배포 단위를 어떻게 구성했고 어디서 막혔는지 적는다.
배경 — 왜 필요했나
앞 글에서 쿠버네티스를 계속 쓰기로 한 이유를 적었다.
물리 서버는 한 대다. 그 한 대를 하이퍼바이저로 나눠 가상 머신 넷을 만들고 그 위에 쿠버네티스를 올렸다(첫 글).
노드가 하나에서 넷이 되자 없던 문제가 생겼다.
- 볼륨. 넷이 되면 “그 데이터가 있는 노드”와 “파드가 뜬 노드”가 달라질 수 있다.
- 진입점. 바깥 주소는 어느 노드를 가리켜야 하나. 그 노드가 내려가면?
- 부팅 순서. 호스트를 재부팅하면 파드 수십 개가 동시에 뜬다. 데이터베이스보다 앱이 먼저 뜨면 실패한다.
- 배포 단위. 무엇을 묶어 배포할 것인가.
선택 — 갈림길에서 무엇을 골랐나
노드 — 넷으로 나누고, 하나는 일을 안 시킨다
컨트롤플레인 1대 + 워커 3대로 나눴다. 컨트롤플레인에는 관리만 맡기고 우리 서비스 파드는 뜨지 않게 막아 뒀다. 앱 부하에 밀려 느려지면 클러스터 전체가 흔들린다.
“왜 하필 워커 3대인가”에 대한 근거는 우리 기록에 없다. 남은 메모리를 나눈 결과다. 지금 다시 정한다면 워커가 둘이면 한 대를 재부팅할 때 나머지 한 대에 전부 몰리고, 넷이면 자원이 너무 잘게 쪼개진다고 쓸 것이다.
저장소 — 결국 세 번 옮겼다
1차. 노드에 묶인 볼륨을 걷어내고 공유 파일 서버로 통일했다. 단일 노드 시절에는 볼륨 다섯 개가 “그 노드의 디렉터리”를 직접 가리켰다. 노드가 넷이 되면서 파일 서버 하나로 옮겼다. 파일 서버는 디스크만 내주므로 가벼운 컨테이너로 띄웠다.
2차. 데이터베이스만 다시 떼어냈다. 공유 파일 서버에서 데이터베이스를 재시작했더니 “어디까지 썼는지”를 적어 두는 파일이 깨졌다(앞 글 2번). 네트워크 너머 쓰기가 늦게 반영돼 보장을 못 준다. 그래서 데이터베이스만 각 워커의 로컬 디스크로 내렸다.
3차. 로컬 디스크도 걷어내고 하이퍼바이저 볼륨으로 바꿨다. 로컬 디스크는 안전하지만 파드를 그 노드에 묶는다. 워커를 재부팅하면 거기 붙은 데이터베이스도 같이 내려간다. 하이퍼바이저가 디스크를 붙여 주면 파드가 다른 워커로 옮겨갈 때 디스크가 따라간다.
지금은 여럿이 같이 쓰는 볼륨은 공유 파일 서버, 혼자 쓰는 볼륨은 하이퍼바이저 블록으로 나눴다.
진입점 — 서비스 주소 대역을 노드 주소와 분리했다
바깥 주소를 노드 주소 대역에서 쓰면 노드를 늘리거나 갈아치울 때 섞인다. 서비스 진입점만 쓰는 별도 대역을 잘라 41개 주소를 배정하고 그 대역에서만 바깥 트래픽을 받는다. 같은 랜이라 장비를 새로 설정할 필요는 없었다.
진입점 하나가 트래픽을 이름별로 나눈다. 인증서는 주소가 늘어도 발급 절차를 밟지 않도록 와일드카드 한 장으로 통일했다. 다만 인증서는 네임스페이스 밖으로 못 나가므로 같은 인증서를 쓰는 네임스페이스마다 한 벌씩 발급해야 한다.
부팅 순서 — 세 단계로만 나눴다
호스트를 재부팅하면 파드가 한꺼번에 뜬다. 순서를 세밀하게 정하지 않고 세 단계만 뒀다. 데이터베이스·메시지 큐·인증이 가장 먼저, 빌드 서버·모니터링 같은 공용 도구가 그다음, 애플리케이션이 마지막이다. 자원이 모자랄 때도 같은 순서로 무엇을 남길지 정한다.
열 단계로 나누면 새로 올릴 때마다 “몇 번째인가”를 고민해야 한다. 그래서 셋으로 끊었다.
배포 단위 — 네임스페이스 하나가 배포 하나
배포 도구의 애플리케이션을 네임스페이스 단위로 쪼갰다(현재 11개). 하나가 깨져도 다른 동기화가 멈추지 않고 이름만 보고 어디에 속하는지 안다.
11개 중 10개는 자동 동기화다. 저장소가 바뀌면 반영하고, 클러스터가 저장소와 달라지면 되돌린다. 하나는 사람이 배포 시점을 잡아야 해서 수동으로 뒀다.
적용 — 실제 배치
[물리 서버 1대]
└─ 하이퍼바이저
├─ VM 컨트롤플레인 1 클러스터의 두뇌 — 서비스 파드는 안 뜬다
├─ VM 워커 3 서비스가 실제로 도는 곳
└─ 컨테이너 파일 서버 여럿이 같이 쓰는 볼륨
노드
| 노드 | 몫 | 자원 | 여기서 도는 것 |
|---|---|---|---|
| 컨트롤플레인 1대 | 클러스터 관리 | 8 vCPU / 8GB | 시스템 구성요소만(서비스 파드 안 뜸) |
| 워커 3대 | 서비스 | 각 10 vCPU / 32GB | 우리 서비스 + 클러스터 자신을 굴리는 몫 |
저장소 두 갈래
사용 중인 볼륨 21개 기준.
| 종류 | 성격 | 쓰는 곳 | 개수 |
|---|---|---|---|
| 공유 파일 서버 | 여러 파드가 동시에 읽고 쓴다 | 빌드 산출물, 메일, 정적 파일 | 7 |
| 하이퍼바이저 블록 | 한 파드가 독점, 파드를 따라 이동 | 데이터베이스, 메시지 큐, 오브젝트 저장소 | 14 |
진입점
- 외부·내부 진입점 14개, 전부 TLS, 전부 같은 진입점을 통과한다.
- 그중 7개는 내부망에서만 열린다(사내 운영 도구). 나머지가 바깥에 열린 서비스다.
- 웹 공격 필터를 켠 것이 5개, 요청 수 제한을 건 것이 5개다. 운영 도구 쪽은 필터를 끄고 접근 제한으로 막는다 — 내부 전용이라 필터의 값보다 오탐 비용이 크기 때문이다.
부팅·배포
- 부팅 우선순위 3단계(가장 높은 것 1000 / 공용 500 / 애플리케이션 100).
- 배포 애플리케이션 11개, 그중 10개 자동 동기화, 1개 수동.
실수한 것들
갈아엎으며 정리한 44건 중 배치를 정하다 막힌 것만 골랐다.
1. 파드 네트워크 대역을 두 곳에 적었는데 값이 달랐다
증상. 클러스터를 만들고 네트워크 플러그인을 설치했는데 파드끼리 통신이 안 됐다.
원인. 파드 주소 대역을 두 군데에 적게 돼 있다. 클러스터 초기화와 네트워크 플러그인 설정이다. 초기화 때는 설치 도구가 흔히 쓰는 값을 넣었고, 플러그인은 기본값을 그대로 들고 왔다. 둘이 다르면 클러스터는 A 대역을 나눠 주고 플러그인은 B 대역을 연결한다.
해결. 플러그인 설정을 클러스터 초기화 값에 맞췄다.
같은 값을 두 곳에 적어야 하는 설정은 반드시 어긋난다. 증상은 “네트워크가 이상하다”로 나타난다.
2. 진입점 주소를 두 가지 방법으로 지정하면 거부당한다
증상. 진입점에 원하는 주소를 넣었는데 배정되지 않았다.
원인. 주소 지정에는 옛 방식과 새 방식이 있는데, 둘을 같이 쓰면 “둘 다 가질 수 없다”며 거부된다. 참고 문서를 여러 개 조합하면 이 상태가 된다.
해결. 새 방식 하나만 남겼다. 진입점 대역을 노드 대역에서 떼고, 바깥 공유기의 전달 규칙도 새 주소로 옮겼다.
이 변경은 클러스터 안에서 끝나지 않았다. 공유기 설정도 같이 바꿔야 한다. 클러스터 바깥 설정은 파일로 관리되지 않아 사람 머릿속에만 남는다.
3. 진입점에 규칙을 넣었는데 조용히 무시됐다
증상. 특정 서비스 처리 규칙을 붙였는데 적용되지 않았다. 오류도 안 났다.
원인. 진입점 구성요소가 어느 버전부터 직접 작성한 규칙 삽입을 기본 차단한다. 잘못 쓰면 전체를 장악할 수 있다.
해결. 설정에서 명시적으로 허용하고 위험 수준도 올려 적었다.
적용이 안 되는데 오류도 없는 상태는 찾기 어렵다. 버전을 올린 뒤 “왜 이건 안 먹지”가 나오면 차단 목록부터 본다.
4. 이미지가 선언한 볼륨이 내 설정을 이긴다
증상. 이슈 트래커를 올렸는데 파드가 다시 뜰 때마다 설정과 사용자 정보가 초기 상태로 돌아갔다. 볼륨은 붙여 뒀는데도.
원인. 볼륨을 부모 디렉터리에 붙였는데, 이미지가 하위 디렉터리 두 개를 자기 볼륨으로 선언해 뒀다. 하위는 컨테이너의 임시 공간으로 빠진다. 겉보기에는 연결이 성공한다.
해결. 부모에 붙이지 말고 하위 경로 두 개에 각각 직접 붙였다.
볼륨을 붙였으면 파드를 강제로 지우고 다시 띄운 뒤 데이터가 남아 있는지 한 번 확인한다. 실제로 쓰이는 경로가 어느 디스크인지 봐야 제대로 붙었는지 안다.
5. 나머지 (짧게)
- 없는 옵션을 문서만 보고 넣었다 — 볼륨 경로를 보기 좋게 바꾸는 설정을 넣었는데 아무 일도 안 일어났다. 소스를 열어 보니 그 버전엔 그 기능이 아예 없었다. 동작에는 문제가 없어 그대로 뒀다.
- 파일 서버 경로가 안 맞았다 — 기존 볼륨과 새 파일 서버의 경로가 달랐다. 두 경로를 모두 내주도록 맞췄다.
- 노드에 파일 서버 클라이언트가 없었다 — 가상 머신 틀에 안 넣으면 노드를 추가할 때마다 반복된다. 틀에 넣었다.
결과
| 항목 | 갈아엎기 전 | 지금 |
|---|---|---|
| 노드 | 1 | 4 (컨트롤플레인 1 + 워커 3) |
| 볼륨 방식 | 노드 디렉터리 직접 사용 5개 | 공유 파일 7 + 하이퍼바이저 블록 14 |
| 볼륨이 노드를 묶나 | 묶는다 | 안 묶는다(파드를 따라 이동) |
| 서비스 진입점 주소 | 기록 없음 | 전용 대역 41개 |
| 진입점 TLS | 기록 없음 | 14개 전부 |
| 부팅 우선순위 | 없음 | 3단계 |
| 배포 애플리케이션 | 7 | 11 (자동 10 / 수동 1) |
| 쿠버네티스 | 1.26 | 1.36 |
“기록 없음”은 그때 값을 세어 두지 않아 모른다는 뜻이다.
이 배치로 워커 한 대를 마음대로 재부팅할 수 있게 됐다. 데이터베이스가 노드에 묶여 있을 때는 워커 재부팅이 곧 서비스 중단이었다. 지금은 파드가 옮겨가면 디스크가 따라간다.
남는 것
- 같은 값을 두 곳에 적어야 하는 설정은 어긋난다고 가정하라. 1·2번이 같은 유형이다. 어디에 이중으로 적혀 있는지 알아야 한다.
- 볼륨은 붙였다고 끝이 아니다. 파드를 지우고 다시 띄워 데이터가 남는지 확인해야 붙은 것이다(4번).
- 저장 방식은 한 번에 정해지지 않는다. 세 번 옮겼다. 이전 선택이 틀려서가 아니라 요구가 달라져서다. 공유가 필요한 것과 혼자 쓰는 것을 처음부터 나눴다면 두 번으로 줄었을 것이다.
아직 해결 못 한 것.
- 파드가 워커에 고르게 안 퍼져 있다. 지금 워커 세 대에 각각 35·32·15개가 떠 있다. 스케줄러는 뜰 때만 자리를 고르고 재배치하지 않는다. 재부팅하면 분포가 또 달라진다.
- 버린 볼륨 16개가 그대로 남아 있다. 로컬 디스크에서 하이퍼바이저 블록으로 옮기며 옛 볼륨을 실수로 지우지 않게 “보존”으로 뒀다. 검증이 끝났는데도 아직 회수하지 않았다.
- 컨트롤플레인이 한 대다. 그 가상 머신이 죽으면 클러스터를 조작할 수 없다(이미 떠 있는 파드는 계속 돈다). 물리 서버가 한 대인 이상 컨트롤플레인만 늘려도 의미가 제한적이라 미뤄 두고 있다.
- 클러스터 밖 설정은 여전히 사람 머릿속에 있다. 공유기 전달 규칙, 도메인 설정은 파일로 적혀 있지 않다.
물리 서버 한 대가 죽으면 넷이 같이 죽는다. 이번에 얻은 것은 가용성이 아니라 한 대를 만지는 동안 나머지가 버티는 정도다.