서버 두 대에 같은 프로그램을 올렸는데 한 대에서만 동작이 이상했다. 파일이 제대로 안 올라갔나 싶어 목록을 비교했다.
개수도 이름도 같았다.
Table of contents
Open Table of contents
목록만 비교하고 있었다
처음에 돌린 것은 이것이었다.
$ ssh srv-01 'ls /opt/collect/' > a.txt
$ ssh srv-02 'ls /opt/collect/' > b.txt
$ diff a.txt b.txt
diff 가 아무것도 안 냈다. 그래서 두 대가 같다고 판단했다.
그런데 ls 가 내놓는 것은 이름뿐이다. 같은 이름의 파일 안에 다른 내용이 들어 있어도 이 비교는 통과한다.
목록이 같다는 것과 내용이 같다는 것은 다른 말이었고 확인한 것과 확인해야 할 것이 어긋나 있었다.
해시로 비교했다
이름 대신 내용을 비교했다.
$ ssh srv-01 'cd /opt/collect && find . -type f -exec md5sum {} \; | sort' > a.txt
$ ssh srv-02 'cd /opt/collect && find . -type f -exec md5sum {} \; | sort' > b.txt
$ diff a.txt b.txt
md5sum 을 파일마다 걸고 sort 로 순서를 맞춘 뒤 대조한다. 이번에는 차이가 나왔다.
< 8f3a... ./config/collect.conf
---
> b21c... ./config/collect.conf
collect.conf 의 해시가 달랐다. 이름이 같으니 눈으로는 안 갈리는 차이였다.
원인 — 한 대만 다른 수집 주기
무엇이 다른지 보려고 양쪽 파일을 받아 대 봤다.
$ scp srv-01:/opt/collect/config/collect.conf a.conf
$ scp srv-02:/opt/collect/config/collect.conf b.conf
$ diff a.conf b.conf
< INTERVAL=60
---
> INTERVAL=10
INTERVAL 이 한 대만 10이었다. 시험하다 짧게 바꾸고 원래 값으로 안 고쳐 놓은 것이었다.
한 줄 차이인데 동작은 여섯 배로 갈렸고 파일 크기가 같아 크기 비교로도 안 잡혔을 차이였다.
판단 기준 — 달라도 되는 것
전부 같아야 하는 것은 아니었으므로 무엇이 같아야 하는지를 먼저 갈랐다.
| 파일 | 같아야 하나 |
|---|---|
| 실행 파일 | 같아야 함 |
| 공통 설정 | 같아야 함 |
| 호스트별 설정 | 달라야 함 (호스트 이름, 대상 장치 목록) |
| 로그 | 다름 |
| 임시 파일 | 다름 |
호스트별 설정은 다른 것이 정상이다. 그것까지 차이로 세면 매번 같은 항목이 나오고 그러면 목록 자체를 안 보게 된다.
그래서 제외 목록을 따로 뒀다.
# /etc/deploy/compare-exclude.txt
config/host.conf
logs/
tmp/
무엇이 달라도 되는지를 적어 두는 것 자체가 서버 구성의 문서가 됐다.
비교를 스크립트로 만들었다
매번 손으로 하기 어려워 스크립트로 옮겼다.
#!/bin/sh
A=${1:-srv-01}
B=${2:-srv-02}
DIR=/opt/collect
hashes() {
ssh "$1" "cd $DIR && find . -type f | grep -vf /etc/deploy/compare-exclude.txt | sort | xargs md5sum"
}
TA=$(mktemp); TB=$(mktemp)
hashes "$A" > "$TA"
hashes "$B" > "$TB"
NA=$(wc -l < "$TA"); NB=$(wc -l < "$TB")
if [ "$NA" -eq 0 ] || [ "$NB" -eq 0 ]; then
echo "비교 대상 없음 ($A: $NA, $B: $NB)"
exit 1
fi
echo "비교 대상 $A:${NA}개 $B:${NB}개"
diff "$TA" "$TB" && echo "동일"
rm -f "$TA" "$TB"
hashes 가 양쪽에서 해시 목록을 뽑고 grep -vf 로 제외 목록을 걸러 낸 뒤 두 결과를 diff 로 대조한다.
검증 — 대상이 0개면 실패
이 스크립트에서 가장 중요한 줄은 개수를 보는 부분이었다.
경로를 잘못 적으면 아무 파일도 안 잡히고 그러면 빈 목록 둘을 비교하게 되어 diff 는 차이 없음을 낸다.
그 출력은 통과처럼 보이지만 아무것도 안 잰 것이므로 NA 나 NB 가 0이면 exit 1 로 끝낸다.
비교한 개수도 함께 찍게 했는데 몇 개를 봤는지가 나와야 그 통과의 근거를 알 수 있기 때문이다.
배포 뒤에 돌렸다
이 비교를 배포 절차 끝에 붙였다.
./deploy.sh srv-01
./deploy.sh srv-02
./compare.sh srv-01 srv-02 || echo "배포 후 두 대가 다릅니다"
배포 직후에 두 대가 다르면 그것은 배포에 문제가 있었다는 뜻이다. 시간이 지난 뒤에 발견하면 운영 중에 누가 고친 것인지 배포가 잘못된 것인지 구분이 안 된다.
실제로 한 번 걸렸는데 한 대에 전송이 중간에 끊겨 파일이 잘려 있었고 이름과 개수는 멀쩡했다.
주의 — 심링크와 실체
비교하다가 한쪽에만 있는 파일이 나왔는데 확인해 보니 그것은 심링크였다.
$ ssh srv-01 'find /opt/collect -type l'
/opt/collect/data -> /mnt/data1/collect
find -type f 는 링크를 안 센다. 한 대는 data 가 링크고 다른 대는 실제 폴더라 개수가 어긋난 것이었다.
그래서 링크도 따로 확인하게 했다.
ssh "$1" "cd $DIR && find . -type l -exec sh -c 'echo \"\$1 -> \$(readlink \$1)\"' _ {} \;"
readlink 로 어디를 가리키는지까지 뽑아서 링크는 가리키는 곳을 비교하고 실체는 내용을 비교한다.
전송 도구가 링크를 따라가 실체를 복사하면 양쪽 구성이 달라진다. 그러면 지금은 같아 보여도 링크 대상이 바뀌는 날 두 대가 갈린다.
정리
- 파일 목록이 같다는 것과 내용이 같다는 것은 다르다
ls비교는 이름만 보므로 통과해도 근거가 안 된다md5sum으로 비교해야 내용 차이가 나온다- 크기까지 같은 차이가 있어 크기 비교로도 안 잡힌다
- 호스트별로 달라야 하는 것을 제외 목록으로 먼저 가른다
- 무엇이 달라도 되는지를 적은 목록이 그 자체로 구성 문서가 된다
- 비교 대상이 0개면
exit 1로 실패시킨다 - 배포 직후에 비교해야 전송 문제와 운영 중 변경이 갈린다
- 심링크는
find -type f에 안 세어지므로readlink로 따로 본다