정적 배포가 파일을 올리면 끝나는 홀가분한 일이었다면, 이번에 다룰 서버 배포는 그보다 훨씬 조마조마한 일이에요. 살아 움직이는 프로그램을 돌아가는 채로 갈아 끼워야 하니까요. 저는 서버 배포를 처음 배울 때, 정적 배포의 감각으로 덤볐다가 서비스를 잠깐 끊어 먹고 나서야 둘이 완전히 다른 세계라는 걸 깨달았어요. 이번 편에서는 서버 배포가 왜 어려운지, 어떤 흐름으로 이뤄지는지 큰 그림을 그려 볼게요.
구체적인 무중단 기법이나 롤백은 뒤에 각각 전용 편으로 깊이 파고들 거예요. 여기서는 서버 배포라는 게 대체 무슨 일인지, 정적이랑 뭐가 다른지, 서버를 어디에 어떻게 올리는지, 그리고 초보가 자주 놓치는 준비 검사가 왜 중요한지를 편하게 짚어 볼게요. 이 큰 그림이 있어야 뒤에 나올 세밀한 기법들이 제자리를 찾거든요.
서버 배포는 왜 더 조심스러워요?
가장 큰 차이는 상태에 있어요. 정적 파일은 그냥 놓여 있는 물건이라 새 걸로 덮어도 아무 일이 안 나지만, 서버는 지금 이 순간에도 요청을 처리하고 있는 살아 있는 프로그램이거든요. 사용자가 로그인 중이고, 결제 요청이 오가고, 데이터가 저장되는 한복판에서 프로그램을 갈아 끼워야 하니 조심스러울 수밖에 없어요. 잘못하면 처리 중이던 요청이 공중에서 사라지죠.
둘째 차이는 시동 시간이에요. 정적 파일은 올리는 즉시 쓸 수 있지만, 서버 프로그램은 켜지고 나서 준비를 마치기까지 잠깐 시간이 걸려요. 설정을 읽고, 데이터베이스에 연결하고, 필요한 걸 메모리에 올리는 그 짧은 동안엔 아직 요청을 받을 준비가 안 된 거예요. 이 준비 시간을 무시하고 켜지자마자 요청을 흘려보내면, 사용자는 오류를 만나요.
셋째 차이는 되돌리기의 무게예요. 정적은 옛 파일로 덮으면 그만이지만, 서버는 데이터가 얽혀 있어 되돌리기가 늘 깔끔하진 않아요. 새 버전이 데이터 모양을 바꿔 놨는데 옛 버전으로 돌아가면, 옛 버전이 새 모양을 못 알아보는 일이 생기거든요. 그래서 서버 배포는 되돌릴 때도 데이터까지 함께 생각해야 해요.
이 세 가지 때문에 저는 서버 배포를 달리는 기차의 바퀴를 가는 일에 비유해요. 기차를 세우면 쉽지만, 세우면 승객이 불편하니까 달리면서 갈아야 하는 거죠. 그러려면 새 바퀴를 미리 준비해 두고, 하나씩 조심스럽게 교체하고, 문제가 생기면 즉시 옛 바퀴로 돌아갈 채비를 해 둬야 해요. 이 감각이 서버 배포의 핵심이에요. 정적 배포가 멈춰 있는 물건을 바꾸는 일이라면, 서버 배포는 움직이는 것을 움직이는 채로 바꾸는 일이라는 이 한 문장을 저는 늘 마음에 새겨 둬요.
그래서 저는 서버 배포를 준비할 때, 정적 배포보다 훨씬 많은 물음을 미리 던져요. 지금 처리 중인 요청은 어떻게 지킬지, 새 서버가 준비됐는지 어떻게 확인할지, 데이터 모양이 바뀌면 옛 버전은 괜찮은지, 잘못되면 어떻게 되돌릴지 같은 것들이요. 이 물음들에 미리 답을 준비해 두면 배포가 두렵지 않고, 준비 없이 덤비면 매번 조마조마해요. 서버 배포의 안정감은 결국 이 사전 준비의 촘촘함에서 나오더라고요.
서버는 어떻게 갈아 끼워요?
가장 순진한 방법은 옛 서버를 끄고 새 서버를 켜는 거예요. 하지만 이러면 끄고 켜는 사이에 서비스가 뚝 끊기죠. 사용자가 그 순간에 접속하면 아무것도 안 뜨니, 이 방식은 사람이 거의 안 쓰는 새벽에나 겨우 쓸 수 있어요. 저도 초창기엔 이렇게 했다가, 새벽에도 쓰는 사용자가 있다는 걸 알고 식은땀을 흘렸어요.
그래서 나온 게 새 걸 먼저 준비하고, 준비되면 옛 걸 치우는 방식이에요. 새 버전 서버를 옆에 하나 더 띄워 두고, 그게 완전히 준비된 걸 확인한 다음에야 요청을 새 서버로 돌리고 옛 서버를 내리는 거죠. 이러면 끊기는 순간이 없어요. 사용자는 어느 서버가 자기를 받는지도 모른 채 매끄럽게 넘어가요. 이걸 무중단 배포라 부르는데, 뒤 전용 편에서 방식들을 자세히 다룰 거예요.
여러 대를 굴린다면 하나씩 순서대로 교체하는 방법도 있어요. 서버가 다섯 대라면, 한 대를 빼서 새 버전으로 바꾸고 정상인 걸 확인한 뒤 다음 대로 넘어가는 거죠. 이러면 교체하는 동안에도 나머지가 요청을 받으니 서비스가 안 끊겨요. 다만 잠깐은 옛 버전이랑 새 버전이 같이 도는 구간이 생기니, 둘이 섞여도 탈이 안 나게 만들어 둬야 해요.
어느 방식이든 공통된 원칙은 새 게 확실히 준비된 걸 확인하기 전엔 옛 걸 안 치운다는 거예요. 이 순서만 지켜도 배포 사고의 상당수를 막을 수 있어요. 저는 배포 절차를 짤 때 늘 준비 확인이 교체보다 먼저 오게 두는지부터 점검해요. 순서가 뒤바뀌면 준비 안 된 서버로 사용자를 밀어 넣는 셈이 되거든요.
다행히 요즘은 이런 교체 절차를 손수 짤 일이 많이 줄었어요. 컨테이너를 관리해 주는 도구나 서버리스 서비스가 이 새 것 준비, 확인, 교체, 옛 것 정리의 흐름을 상당 부분 알아서 해 주거든요. 그래도 저는 안에서 무슨 일이 벌어지는지는 알아 두라고 권해요. 도구가 대신해 주더라도, 원리를 알아야 문제가 났을 때 어디를 봐야 할지 감이 잡히니까요. 자동화에 기대되 원리엔 눈감지 않는 균형이 필요해요.
서버가 준비됐는지 어떻게 알아요?
서버가 켜졌다고 일할 준비가 된 건 아니에요. 그래서 필요한 게 준비 검사예요. 새 서버한테 너 지금 요청 받을 수 있어? 하고 물어보는 특별한 주소를 하나 두는 거죠. 배포 도구는 이 주소를 계속 두드려 보고, 괜찮다는 답이 올 때까지 기다렸다가 요청을 흘려보내요. 이 검사가 없으면 준비도 안 된 서버로 사용자를 보내게 돼요.
준비 검사는 보통 두 종류로 나눠요. 하나는 살아 있니를 묻는 검사예요. 프로그램이 죽지 않고 돌고 있는지 확인하는 거죠. 다른 하나는 일할 수 있니를 묻는 검사예요. 데이터베이스 연결 같은 필요한 준비가 다 됐는지까지 확인하는 거죠. 이 둘을 갈라 두면, 살아는 있지만 아직 준비 안 된 상태를 정확히 구분할 수 있어요.
예를 들어 준비 검사 주소는 /healthz 같은 걸 두고, 여기 요청이 오면 데이터베이스에 가벼운 질문을 던져 보고 답이 잘 오면 괜찮다는 신호를, 안 오면 아직이라는 신호를 돌려주게 짜요. 이렇게 하면 배포 도구가 이 주소만 보고도 진짜 준비됐는지를 판단할 수 있어요. 저는 이 준비 검사를 서버의 양심이라 불러요. 스스로 준비 안 됐으면 안 됐다고 솔직히 말해 주니까요.
준비 검사를 소홀히 하면 배포는 성공했다는데 사용자는 오류를 보는 이상한 일이 벌어져요. 배포 도구는 서버가 켜진 것만 보고 다 됐다 여겼는데, 정작 그 서버는 아직 데이터베이스에 못 붙은 상태였던 거죠. 저는 이 함정에 여러 번 빠진 뒤로, 준비 검사가 진짜 준비를 반영하는지를 꼼꼼히 챙겨요. 그냥 살아 있음만 답하는 껍데기 검사는 오히려 위험하거든요.
다만 준비 검사를 너무 무겁게 만드는 것도 조심해야 해요. 검사할 때마다 데이터베이스에 큰 질문을 던지거나 바깥 서비스를 다 두드려 보면, 검사 자체가 부담이 되거든요. 게다가 바깥 서비스 하나가 잠깐 느려졌다고 우리 서버까지 준비 안 됨으로 판정되면, 멀쩡한 서버가 애먼 이유로 교체 대상이 되기도 해요. 그래서 저는 준비 검사를 딱 필요한 만큼만 가볍게 두려고 해요. 우리 서버가 일할 수 있는지에 집중하고, 남의 사정까지 다 짊어지진 않는 거죠.
서버를 어디에 어떻게 올려요?
서버를 올리는 방식은 시대에 따라 여러 갈래로 발전해 왔어요. 예전엔 서버 한 대를 통째로 빌려 거기에 프로그램을 깔고 손수 관리했어요. 자유롭지만 손이 많이 가고, 서버가 아프면 밤중에 달려가야 하는 고생이 따랐죠. 저도 이 방식으로 시작했는데, 관리에 드는 품이 만만치 않았어요.
그다음 널리 퍼진 게 컨테이너예요. 프로그램이랑 그게 필요로 하는 것들을 상자 하나에 통째로 담아 두는 방식인데, 이 상자는 어디서 열어도 똑같이 동작해요. 내 컴퓨터에선 되는데 서버에선 안 되는 고질적인 문제가 이걸로 많이 사라졌어요. 상자 하나를 올리면 그대로 도니, 배포가 훨씬 예측 가능해졌죠.
더 최근엔 서버리스라는 방식도 흔해졌어요. 우리가 서버를 직접 띄우고 관리하는 게 아니라, 코드 조각만 올려 두면 요청이 올 때 서비스가 알아서 실행해 주는 거예요. 준비 검사도, 시동 시간도 상당 부분 서비스가 챙겨 주니 관리 부담이 확 줄죠. 저는 요즘 새 프로젝트에선 서버리스로 될 일인지부터 따져 봐요. 될 일이면 관리가 정말 편하거든요.
어느 방식을 고를지는 정답이 없어요. 세밀한 제어가 필요하면 직접 관리를, 이식성이 중요하면 컨테이너를, 관리를 최대한 덜고 싶으면 서버리스를 고르는 식이죠. 저는 팀이 감당할 수 있는 관리량을 기준으로 정해요. 아무리 좋은 방식도 팀이 다룰 여력이 없으면 짐이 되거든요. 화려함보다 우리한테 맞는지가 먼저예요.
재밌는 건, 이 방식들이 서로 배타적이지 않다는 거예요. 한 서비스 안에서도 늘 켜져 있어야 하는 부분은 컨테이너로, 가끔 실행되면 되는 부분은 서버리스로 나눠 굴리는 섞어 쓰기가 흔하거든요. 저는 처음부터 완벽한 하나를 고르려 애쓰기보다, 작게 시작해 필요에 따라 옮겨 가는 편이에요. 서버리스로 시작했다가 트래픽이 커져 컨테이너로 옮기기도 하고, 그 반대도 있어요. 중요한 건 지금 우리한테 맞는 걸 고르되, 나중에 갈아탈 여지를 남겨 두는 유연함이에요.
서버 배포에서 뭘 챙겨야 해요?
서버 배포를 안전하게 하려면 몇 가지를 꼭 챙겨야 해요. 첫째는 처리 중이던 요청을 지키는 거예요. 옛 서버를 내릴 때 곧바로 죽이지 말고, 지금 처리 중인 요청은 끝까지 마치게 두고 새 요청만 안 받게 해야 해요. 이걸 우아한 종료라 부르는데, 이게 없으면 하필 그 순간 요청하던 사용자가 애먼 오류를 만나요.
둘째는 데이터 모양 변경을 조심히 다루는 거예요. 새 버전이 데이터 모양을 바꿔야 한다면, 옛 버전이랑 새 버전이 둘 다 견딜 수 있게 단계를 나눠서 바꿔야 해요. 한 번에 확 바꾸면, 잠깐 같이 도는 옛 버전이 새 모양을 못 알아봐 깨지거든요. 이 데이터 이야기는 무중단이랑 롤백 편에서 더 파고들 거예요.
셋째는 지켜보는 눈이에요. 서버 배포는 올린 직후가 가장 위험하니, 배포하자마자 오류가 늘지 않는지, 응답이 느려지지 않는지를 지켜봐야 해요. 이상하면 곧바로 되돌릴 수 있게요. 저는 배포 버튼을 누른 뒤 한동안 화면에서 눈을 안 떼요. 이 관측 이야기도 뒤에 모니터링 편에서 자세히 다룰게요.
넷째는 되돌릴 준비를 미리 해 두는 거예요. 서버 배포는 무슨 일이 생길지 모르니, 한 번에 이전으로 돌아갈 방법을 배포 전에 손에 쥐고 있어야 해요. 급할 때 되돌리는 법을 그제야 찾으면 이미 늦거든요. 저는 배포 전에 되돌리는 명령이 뭔지를 먼저 확인하고 나서야 새 버전을 올려요. 이 습관 하나가 급박한 순간에 당황을 침착으로 바꿔 줘요.
이 네 가지는 결국 사용자가 눈치채지 못하게 서버를 갈아 끼우기 위한 장치들이에요. 배포가 잘되면 사용자는 아무 일도 없었던 것처럼 서비스를 계속 쓰고, 그게 바로 서버 배포의 성공이거든요. 화려하게 티 나는 배포가 아니라, 있는지도 모르게 지나가는 배포가 가장 좋은 배포예요. 저는 배포가 조용할수록 잘한 거라고 후배들한테 늘 말해요.
이렇게 서버 배포의 큰 그림을 그려 봤어요. 살아 있는 프로그램을 다루니 정적보다 조심스럽지만, 새 걸 먼저 준비하고, 준비를 확인하고, 조심히 갈아 끼우고, 지켜보며, 되돌릴 채비를 해 둔다는 원칙만 지키면 충분히 안전하게 할 수 있어요. 다음 편에서는 이 배포의 밑바탕이 되는 환경 분리, 그러니까 개발과 운영을 어떻게 나누는지를 이야기해 볼게요.