이미지 편집에 쓰던 외부 서비스가 문을 닫는다는 공지가 왔다. 기한은 두 달이었다.
Table of contents
Open Table of contents
얼마나 기대고 있었는지 봤다
무엇을 그 서비스로 하고 있었는지부터 찾았다.
$ grep -rn "imgsvc" --include=*.php application/ | wc -l
23
grep -rn 으로 세니 스물세 곳이었고 하는 일은 썸네일 생성과 워터마크와 형식 변환과 용량 축소 넷이었다. 넷 다 이미지를 그쪽에 올리고 결과를 받아 오는 방식이다.
더 큰 것은 따로 있었다. 만들어진 이미지를 그쪽 주소로 그대로 쓰고 있었다.
echo '<img src="http://imgsvc.example.net/t/' . $key . '/300x300.jpg">';
서비스가 닫히면 코드만 문제가 되는 것이 아니라 이미 나간 화면의 이미지가 전부 깨진다.
원본이 우리에게 있는지 셌다
LIKE '%imgsvc%' 로 세니 그쪽 주소를 쓰는 상품이 4만 2천 건이었고 게시글 본문에 들어간 것도 따로 있었다. 여기서 중요한 것은 원본이 우리에게 있느냐였는데 확인해 보니 절반만 있었다.
원본이 없는 것부터 내려받았다. 서비스가 닫히면 못 하는 일이기 때문이다.
foreach ($rows as $r) {
$data = file_get_contents($r['image_url']);
if ($data === false) { $fail[] = $r['no']; continue; }
file_put_contents('/data/upload/recovered/' . $r['no'] . '.jpg', $data);
}
2만 건을 내려받는 데 사흘이 걸렸다. 상대 서버에 부담을 주지 않으려고 usleep 으로 간격을 뒀다.
file_get_contents 로 원격 주소를 읽으려면 allow_url_fopen 이 켜져 있어야 한다. 그리고 이 함수는 default_socket_timeout 을 따르는데 기본이 60초다. 안 열리는 것 하나에 1분씩 걸리니 2만 건이면 그것만으로 며칠이 더 든다. 그래서 그 값을 짧게 잡고 실패한 것은 $fail 에 모아 나중에 다시 돌렸다.
대신할 것을 골랐다
네 가지 기능 중 셋은 서버에서 직접 할 수 있었고 워터마크까지 넣으면 넷 다 됐다.
$img = new Imagick($src);
$img->thumbnailImage(300, 300, true);
$img->writeImage($dst);
밖에 맡기고 있던 이유가 서버 부담이었는데 Imagick 으로 재 보니 이미지 하나에 0.4초였다. 올릴 때 한 번 하는 것이고 하루 200건이면 80초다. 밖에 맡길 이유가 없었다. 처음 정할 때 재 보지 않고 부담이 클 것이라고 짐작한 결과였다.
이미 나간 주소는 우리 쪽에서 같은 경로를 받아 넘기게 했다.
// /img/legacy/<key>/<size>.jpg
$local = '/data/upload/legacy/' . $key . '_' . $size . '.jpg';
if (file_exists($local)) { readfile($local); exit; }
그쪽 도메인이라 DNS 를 우리 쪽으로 돌리는 방법은 안 됐다. 결국 DB 와 본문의 주소를 바꿔야 했는데 str_replace 로 읽을 때 바꿔서 보여 주는 방식으로 먼저 막고 배치로 실제 값을 바꿨다.
기한이 있는 일의 순서
두 달이라는 기한이 있어 순서를 정했다. 원본 내려받기를 첫 주에 둔 것은 기한이 지나면 못 하는 일이기 때문이다. 무를 수 없는 것을 먼저 한다는 기준으로 나머지를 배치했다.
1주차 원본 없는 이미지 내려받기
2주차 대체 구현
3~4주차 새로 올라오는 것부터 우리 쪽으로
5~6주차 옛 주소를 바꾸는 배치
7주차 확인
8주차 여유
마지막 한 주를 여유로 둔 것이 도움이 됐다. 실제로 6주차에 문제가 하나 나왔다.
이 일 뒤에 밖에 기대고 있는 것을 전부 찾았다. 문자 발송과 주소 검색과 결제와 지도 넷이 더 있었고 각각 없어지면 어떻게 되는지를 적었다. 문자는 대체 업체가 있으나 규격이 달라 한 주가 필요하고, 주소 검색은 직접 입력으로 즉시 대체되며, 지도는 지도만 안 보이고 나머지는 정상이다. 결제는 없어지면 서비스가 멈추고 대체까지 서너 주가 걸린다. 무게가 다 달라서 결제만 계약 상태를 따로 확인해 뒀다.
정리
- 기대고 있던 것이 없어지면
grep -rn으로 얼마나 기대는지부터 센다 - 코드만이 아니라 DB 에 그쪽 주소가 박혀 있는지
LIKE로 센다 - 원본이 우리에게 있는지 확인하고 없으면 닫히기 전에 내려받는다
file_get_contents는allow_url_fopen과default_socket_timeout에 걸린다- 밖에 맡긴 이유를 다시 잰다 —
Imagick으로 재 보니 0.4초였다 - 무를 수 없는 일을 먼저 한다
- 기한이 있으면 여유를 한 주 둔다. 실제로 쓰게 된다
- 밖에 기대는 것을 전부 찾아 없어지면 어떻게 되는지 적어 둔다