Skip to content
isdnetworks
Go back

만들기만 하고 잇는 것을 빠뜨렸다

수집 자료를 큰 디스크에 쌓으려고 구조를 잡았다.

/opt/collect/data  →  /mnt/nvme/collect

배포 스크립트에 폴더 생성을 넣었다. 며칠 뒤 시스템 디스크가 찼다.

Table of contents

Open Table of contents

연결이 안 만들어졌다

확인해 보니 /opt/collect/data 가 링크가 아니었다.

$ ls -ld /opt/collect/data
drwxr-xr-x 2 collect collect 4096 Jan 19 03:00 /opt/collect/data

맨 앞 글자가 d 다. 링크면 l 이어야 하는데 실제 폴더였다.

배포 스크립트를 열어 봤다.

mkdir -p /opt/collect/data
mkdir -p /opt/collect/logs

mkdir 로 폴더를 만들기만 하고 ln 으로 잇는 줄이 없었다. 설계에는 있었는데 스크립트에 안 들어간 것이다.

원인 — 오류 없이 지나간 빠짐

빠진 단계가 왜 안 드러났는지를 봤다. mkdir 자체는 성공하기 때문이다.

서비스도 그 경로가 있으니 정상으로 쓴다. 쓰기가 되고 읽기가 되니 아무 데서도 오류가 안 난다.

만들기는 성공하고 잇기는 아예 없으니 실패할 자리가 없었다. 자료가 다른 장치에 쌓인다는 것만 다르다.

이런 종류의 빠짐은 용량이 찰 때가 되어서야 드러난다. 조용히 잘못된 상태가 며칠 동안 이어졌다.

순서를 지켰다

고칠 때는 순서가 중요했다. 그 폴더에 이미 자료가 들어 있었기 때문이다.

TARGET=/mnt/nvme/collect
LINK=/opt/collect/data

mkdir -p "$TARGET"

if [ -L "$LINK" ]; then
    :                                   # 이미 링크면 그대로
elif [ -d "$LINK" ]; then
    mv "$LINK"/* "$TARGET"/ 2>/dev/null  # 쌓인 것을 옮긴다
    rmdir "$LINK"
    ln -s "$TARGET" "$LINK"
else
    ln -s "$TARGET" "$LINK"
fi

이미 링크면 그대로 두고 폴더면 안의 것을 mv 로 옮긴 뒤 링크로 바꾼다. 그냥 지우고 만들면 쌓인 자료를 잃는다.

지울 때 rmdir 을 쓴 것은 비어 있을 때만 지우기 위해서다. rm -rf 를 쓰면 옮기기가 실패했어도 그대로 지워 버린다.

rmdir 이 거부하면 그 자리에서 옮기기가 덜 됐다는 것이 드러난다. 실패해야 할 때 실패하는 명령을 고르는 것이 여기서 중요했다.

주의 — 링크 생성 명령의 성질

링크를 만들고 나서 하나를 더 확인해야 했다. ln 은 가리키는 대상이 없어도 링크를 만들고 종료 코드 0으로 끝난다.

경로를 한 글자 틀리게 적어도 링크는 생기고 명령은 정상으로 보인다. 그 링크는 아무 데도 안 닿는다.

링크가 있다는 것과 그것이 해소된다는 것은 다른 확인이다. 명령의 성공을 결과의 성공으로 삼으면 안 되는 자리가 여기였다.

확인을 넣었다

만든 뒤에 세 가지를 봤다.

if [ ! -L "$LINK" ]; then
    echo "링크가 아닙니다: $LINK"
    ls -ld "$LINK"
    exit 1
fi

RESOLVED=$(readlink -f "$LINK")
if [ "$RESOLVED" != "$TARGET" ]; then
    echo "링크 대상이 다릅니다: $RESOLVED (기대: $TARGET)"
    exit 1
fi

if [ ! -w "$LINK" ]; then
    echo "쓰기 불가: $LINK"
    exit 1
fi

echo "링크 확인: $LINK -> $RESOLVED"

-L 로 링크인지 보고 readlink -f 로 어디를 가리키는지 보고 -w 로 쓸 수 있는지 본다. 셋 중 하나라도 어긋나면 exit 1 이다.

기대한 대상 경로를 문자열로 비교하는 것이 핵심이었다. 링크이기만 하면 통과시키면 엉뚱한 데를 가리키는 링크가 그대로 지나간다.

실제로 써 봤다

확인만으로 부족해서 실제로 써 봤다.

TESTFILE="$LINK/.write-test-$$"
if ! echo test > "$TESTFILE" 2>/dev/null; then
    echo "쓰기 시험 실패: $LINK"
    exit 1
fi

# 실제로 대상 쪽에 생겼는지 확인
if [ ! -f "$TARGET/$(basename "$TESTFILE")" ]; then
    echo "링크를 통해 쓴 파일이 대상에 없습니다"
    exit 1
fi
rm -f "$TESTFILE"

링크를 통해 쓰고 대상 쪽에서 그 파일을 찾는다. 지정한 방식과 다른 방식으로 되읽는 것이라 링크가 엉뚱한 곳을 가리키면 여기서 걸린다.

$$ 로 프로세스 번호를 붙인 것은 여러 번 돌려도 안 겹치게 하기 위해서다. 시험 파일은 확인이 끝나면 지운다.

검증 — 경로별 장치 확인

이 일을 겪고 나서 어느 경로가 어느 장치에 있는지를 확인하게 됐다.

$ df -h /opt/collect/data /opt/collect/logs /var/log
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  1.8T  120G  1.6T   7% /mnt/nvme
/dev/sda2        50G   38G   12G  77% /
/dev/sda2        50G   38G   12G  77% /

df 에 경로를 여러 개 주면 각각이 어느 장치에 있는지 한 번에 나온다. data 만 큰 디스크에 있고 나머지 둘은 시스템 디스크였다.

로그도 옮겨야 했다. 하루 2기가씩 쌓이고 있어서 링크를 고쳐도 시스템 디스크는 계속 찰 상황이었다.

재발 방지 — 마운트별 감시

디스크 감시는 있었는데 전체만 보고 있었다. 마운트별로 보게 고쳤다.

df -h | awk 'NR>1 && $5+0 > 80 {print $6 " " $5}'

awk 로 다섯째 열의 사용률을 숫자로 바꿔 80을 넘는 마운트만 뽑는다. 전체 합으로 보면 큰 디스크가 비어 있어서 평균이 낮게 나온다.

이것을 넣고 나서 시스템 디스크가 차기 전에 알게 됐다. 합쳐서 보면 안 보이던 것이 나눠서 보니 보였다.

정리


Share this post on:

Previous Post
양방향이 바꾸는 것
Next Post
특허 둘이 같은 폴더에 있었다