과거에 저는 버스 챗봇 서비스를 구축했었고, 현재는 AWS LightSail로 서비스를 돌리고 있었습니다.
하지만 생각보다 서버 비용이 만만치 않아져서, 친구들과 함께 구축한 실서버로 서비스를 옮기기로 결정하였고, 그 과정을 간단하게 기록하였습니다.
버스 챗봇 서비스에 대하여는 아래 블로그를 참고해주세요.
03년간 챗봇을 운영해온 이야기
그때 당시에 ES6 문법에 푹 빠져있던 저는 nodejs + express (+ES6) 문법을 도입하였습니다. 그때는 참 그게 편리하다 생각했는데 지금 다시 생각해보니 차라리 처음부터 Typescript
를 도입하면 되지않았을까? mjs로 하면 좋지 않았을까? 싶었던게, babel-node
로 서비스를 구동하였기에 살짝 아쉬움이 남았습니다.
그래서 이번에 한번더 옮기는 김에, TS를 적용하기로 하였고, 후배들의 도움을 받아 빠르게 Typescript 코드로 변환하였습니다.
오래된 코드지만, 여러 가지로 도와준 cheonHwi, Gi Beom Gwon 에게 감사를 전합니다.
server 코드의 경우, 기존 코드에서 type을 지정하는것이 대부분이어서 어려움은 없었습니다.
다만, openAPI의 경우는 조금 이야기가 달랐는데 정의된 모든 Tag들을 interface로 정의해야할까? 라는 생각이 들어 고민이 많이 들었지만, 내부에서 써야하는 데이터들만 interface로 정의하였고, 기존의 모든 변수들의 타입을 지정하는 단계로만 진행을 하였습니다. ES6 문법에서 Type만 정의했기에 그렇게 시간은 걸리지 않았습니다.
지금까지 docker를 잘 다뤄보지 못했고, 친구들 모두 백엔드 Devops 를 어느정도 다뤄본 친구들이었기에, 비교적 지식이 부족했고, 친구들은 우리 서버에 docker를 기반으로한 portainer를 도입했으며, 배포를 아주 쉽게 할 수 있다고 했습니다....만
docker를 몰랐기에 이게 배포가 쉬운건가..? 라는 생각이 들긴했는데..
이건 제가 docker를 몰랐기에 일어난 이슈였다고 크게 생각이 듭니다.
Portainer는 docker를 쉽게 배포하고, 사용할 수 있게하는 웹 UI 툴로,
여러 container들을 한곳에서 관리하고 편하게 배포할 수 있게할 수 있습니다.
전 이번에 친구들의 구축해둔 이 portainer에서 작업을 진행했고, docker를 배우면서 여러 고민들을 하게 되었습니다.
기존의 운영중인 서버의 경우, DB를 사용하지않고, 단순히 요청이 들어오면 응답을 해주는 구조를 가지는 단일 서비스의 구조를 가지고있었습니다.
일단 docker를 처음 공부해보면서 크게 두가지 배포방식을 이해했는데,
container
stack
즉, 이전할 때 필요한 방식은 docker container가 비교적 유리했고, container 방식으로 배포를 진행하려했으나....
Portainer에서 스택의 Git 연동을 확인하고 나서, Container 또한 가능할지 궁금했지만 아쉽게도 지원되지 않음을 알게 되었습니다. 물론 image는 git으로 불러와서 container에 적용하는것은 가능했지만, 왜 이런지에 대한 명확한 이유는 알지 못했고, 추측하기로 컨테이너의 목적 중 하나인 즉각적인 실행 및 배포가 그 이유일 수 있다는 생각이 들었습니다. 이 부분은 추후에 더 조사해볼 필요가 있을 것 같습니다.
일단 크게 두가지의 선택지가 있었습니다.
이때를 기준으로하면 Dockerfile로 Container 배포는 성공했는데, 수동으로 배포해야한다는 단점이 있었습니다. 이 상태에서 docker-compose.yml
을 추가하여 stack 구조로 가는것이 과연 옳은지 고민했고, 우선 Container 에서 작동하는 것을 확인한 후 Docker Hub를 이용해 CI/CD를 구축하기로 결정하였습니다.
또한 GitHub Actions를 이용한 배포를 고려하고 있었기 때문에, 배포가 용이한 Docker Hub를 선택하게 되었습니다.
그렇게 코드를 docker image로 만들고 DockerHub에 업로드한 다음 portainer에서 작업을 이어나갔습니다.
이 과정에서 친구들이 왜 portainer를 이용하면 docker를 쉽게 배포할 수 있다 라고 한지 이해하게 되었는데, 우선 첫째로, docker hub에 public 으로 업로드된 image를 불러와 배포할 수있고, 임의의 호스트 번호를 부여할 수도 있으며, 여러 환경들을 쉽게 설정할 수 있다는것이 가장 큰 장점이었습니다.
원래라면 오래걸릴 배포를 생각보다 빨리 끝내서 놀라웠지만..
처음해본게 빨리 끝난다면 반드시 새로운 문제가 생기기 마련이죠..
분명 3분전에 업데이트 했던 코드 업데이트가 되지않았습니다.
제가 생각했던 건 ,
docker hub 업로드 → portainer에서 업로드 확인 → 자동 업데이트
아쉽게도 portainer의 Community Bussion 에서는 Update를 지원하지않았고,
Recreate → Re-pull image를 통해 업데이트를 진행했습니다.
이 방식은 사진과 같이 처음부터 다시 Container를 재생성하기 때문에 데이터가 유지되지 않습니다.
하지만 서비스에는 영향이 없는 편이기에, 한동안은 Re-pull을 통해 update를 진행할 예정입니다.
이 문제에 대해 조사하던 중 Watchtower
에 대해 알게되었는데, Watchtower
는 Docker Container의 기본 이미지 업데이트를 자동화하는 프로세스이며, dockerhub에 새 이미지를 푸쉬하는것으로 기존 Container의 실행중인 프로세스를 종료하고, 기존과 동일한 옵션으로 다시 시작하는 기능을 가집니다.
지금은 docker에 대해 공부중인 단계이기에 이것 또한 공부하면서 도입해볼 생각입니다.
이번에 튜링의 사과 Github Action 입문 세미나를 듣게되었고, 이 배운것을 이 서비스에 응용할 수 있었습니다.
main
branch에 push 될때 Docker hub에 업데이트되도록 코드를 작성했고, 간단한 CI/CD를 구축했습니다.
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
| don't push to prod
| on Fridays
|___________|
(\__/) ||
(•ㅅ•) ||
/ づ
사실 지금 바로 배포해도 문제는 없지만, 언제나 prod 배포는 무섭습니다.
개발 완료 시점이 최근인데, 계속 테스트를 해보고 배포는 해봐야겠죠.
지금 보니 이 서비스를 굴린지 8년 정도 지났는데, 바뀐게 얼마없었다는게 참 아쉬웠고,
그래도 첫 서비스인데 조금더 다듬어보고 싶은 마음에 이번에 새로 진행하게 되었습니다.
뭔가 가능하면 데이터 수집이나 여러가지를 덧붙이고 싶은데 떠오르질 않네요.
읽어 주셔서 감사합니다.