게임 서버가 여러 개 떠 있는데 각각 포트가 다르다. 어느 것이 몇 번인지 매번 문서를 찾았는데 그 문서도 실제와 달랐다.
Table of contents
Open Table of contents
실제로 무엇이 떠 있나
문서 대신 서버에서 직접 봤다.
$ netstat -tlnp | grep LISTEN
tcp 0.0.0.0:8080 java (웹)
tcp 0.0.0.0:9001 game (게임 1)
tcp 0.0.0.0:9002 game (게임 2)
tcp 0.0.0.0:9100 game (게임 3?)
tcp 0.0.0.0:7777 chat
tcp 0.0.0.0:12000 admin
netstat -tlnp 로 지금 듣고 있는 포트와 그것을 쓰는 프로세스를 함께 뽑았다. ss -tlnp 도 같은 것을 준다.
9001과 9002는 규칙이 보이는데 9100이 튄다. 7777과 12000은 왜 그 번호인지 모르겠어서 물어보니 각자 만들 때 비어 있는 번호를 골랐다고 했다.
뽑아 보니 문서에 없는 것도 있고 문서에는 있는데 안 떠 있는 것도 있었다. 문서는 어느 시점의 상태를 적어 둔 것이고 그 뒤로 갱신되지 않은 것이었다.
규칙을 정했다
새로 만들 것부터 규칙을 적용하기로 했다.
90xx 게임 서버 9001, 9002, ...
91xx 게임 서버(개발) 9101, 9102, ... (운영 + 100)
80xx 웹 8001, 8002
81xx 웹(개발) 8101, 8102
70xx 보조 서비스 7001(채팅), 7002(랭킹)
71xx 보조(개발)
개발용은 운영 번호에 100을 더한다. 운영이 9001이면 개발은 9101이고 그러면 개발 서버의 포트를 안 찾아도 운영 번호에서 계산된다.
대역을 고를 때 걸리는 것이 하나 있었다. 커널이 나가는 연결에 임시로 쓰는 포트 범위가 따로 있는데 리눅스에서는 sysctl 의 net.ipv4.ip_local_port_range 가 그것을 정한다. 그 안에 서비스 포트를 두면 서버를 띄우기 전에 그 번호가 임시로 잡혀 있을 수 있고 그러면 기동이 실패하므로 대역을 그 범위 밖으로 잡았다.
옮길지 남길지의 판단
이미 도는 것들을 옮기는 것은 다른 문제였다. 포트를 바꾸면 iptables 설정과 클라이언트 설정과 감시 설정을 다 고쳐야 하고 중단 없이 하려면 두 포트를 동시에 열어야 한다.
옮기는 것과 남기는 것을 갈랐다.
| 서비스 | 판단 |
|---|---|
| 게임 3 (9100) | 옮김 — 규칙에 가깝고 영향 적음 |
| 채팅 (7777) | 남김 — 클라이언트 배포 필요 |
| 관리자 (12000) | 옮김 — 내부용이라 영향 적음 |
전부 옮기는 것이 깔끔하지만 값보다 비용이 컸다. 남기는 것은 예외로 문서에 적었는데 예외를 적어 두지 않으면 다음 사람이 규칙이 안 지켜진 것으로 보고 다시 검토한다.
문서를 실제에서 만들었다
손으로 적은 문서는 낡으므로 서버에서 뽑아 만들게 했다.
#!/bin/sh
for h in game-01 game-02 web-01; do
echo "=== $h ==="
ssh "$h" "netstat -tlnp 2>/dev/null | awk '/LISTEN/ {print \$4, \$7}'"
done
ssh 로 각 호스트의 netstat 출력을 awk 로 추려 모으고 이것을 crontab 으로 돌려 결과를 문서에 붙였다. 실제와 다를 수가 없다.
돌려 보니 문서에 없던 것이 두 개 나왔다. 누가 시험용으로 띄워 놓고 안 내린 것이었다.
검증 — 충돌 확인과 방화벽
새 서비스를 띄울 때 그 포트가 비어 있는지 확인하는 것을 시작 스크립트에 넣었다.
PORT=9003
if netstat -tln | grep -q ":${PORT} "; then
echo "포트 ${PORT} 가 이미 사용 중입니다"
netstat -tlnp | grep ":${PORT} "
exit 1
fi
이미 쓰는 프로세스까지 보여 주니 그냥 실패보다 다음 행동이 정해진다. lsof -i :포트 로도 같은 것을 본다.
이 확인이 없으면 더 나쁜 일이 난다. 포트를 잡는 서버는 bind 가 EADDRINUSE 로 실패했을 때 그 자리에서 끝나지 않고 다음 번호로 올라가는 것이 많아서 기동이 언제나 성공으로 보이고 그대로 쌓인다.
포트 규칙이 생기니 방화벽 설정도 단순해졌다.
90xx 게임 클라이언트 대역에서 허용
91xx 사내 대역에서만 허용
80xx 전체 허용 (앞단 경유)
개별 포트를 하나씩 여는 대신 iptables 에서 범위로 처리한다. 새 게임 서버를 띄울 때 방화벽을 안 건드려도 된다.
규칙이 있으면 그 규칙 위에 다른 것을 얹을 수 있다. 번호가 제각각이면 매번 개별 설정이다.
정리
- 규칙 없이 붙인 번호는 매번 찾아야 하고 손으로 적은 문서는 낡는다
netstat -tlnp로 실제로 무엇이 듣고 있는지 직접 본다- 대역을 용도별로 나누고 개발용은 운영 번호에 100을 더해 쓴다
sysctl의net.ipv4.ip_local_port_range밖으로 대역을 잡는다- 이미 도는 것을 옮길지는 비용과 값을 견줘 정한다
- 남기는 것은 예외로 적어야 다음 사람이 다시 검토하지 않는다
- 문서는
ssh와netstat출력에서 뽑아 만든다 - 시작 스크립트에서 충돌을 확인하고 쓰는 프로세스까지 보여 준다
- 확인이 없으면
EADDRINUSE를 삼키고 다음 번호로 올라가 쌓인다 - 규칙이 있으면
iptables를 대역으로 처리할 수 있다