도커(Docker)는 리눅스의 응용 프로그램들을 프로세스 격리 기술들을 사용해 컨테이너로 실행하고 관리하는 오픈 소스 프로젝트이다. 도커 공식 사이트를 빌리자면 다음과 같다.
도커 컨테이너는 일종의 소프트웨어를 소프트웨어의 실행에 필요한 모든 것을 포함하는 완전한 파일 시스템 안에 감싼다. 여기에는 코드, 런타임, 시스템 도구, 시스템 라이브러리 등 서버에 설치되는 무엇이든 아우른다. 이는 실행 중인 환경에 관계 없이 언제나 동일하게 실행될 것을 보증한다
간단히 요약하자면, 도커는 애플리케이션을 격리하여 배포하는데 착각하면 안 되는게, 가상화와는 전혀 다른 개념이다.
가상화 이전의 시대에서는 하나의 서버에 하나의 어플리케이션만 구동시켰다고 한다. 이렇다 보니 하나의 서버에 남는 자원이 많았다. 이런 비효율성을 극복하기 위해 등장한 기술이 가상화 기술이다. 그 중 하이퍼바이저 기반의 가상화가 가장 많이 이용되었다. 하이퍼바이저는 논리적으로 분할된 공간에서 VM이라는 독립된 가상환경을 만들고 호스트 시스템에서 VM에 깔린 게스트OS를 구동 및 모니터링하는 역할을 한다.
VM에는 위에서 언급한 게스트OS가 깔린다. 하지만 도커의 컨테이너는 그렇지 않다. 컨테이너에는 게스트OS를 설치하지 않는다. 이는 자원의 효율성 측면에서 차이가 난다. VM는 하나씩 늘 때마다 OS를 위한 자원을 할당해주어야 하는 반면에 도커는 어플리케이션을 구동하는데 필요한 모든 패키지만 있으면 컨테이너를 구동시킬 수 있다.
사실 도커를 결심한 건 개발 진행 이후였다. 왜냐하면 기타 툴로 생각한 PostgreSQL과 redis가 이미 내 로컬에 전부 세팅되어 있었기 때문. 그러다 문득 든 생각이 있었다.
만약 나 혼자 진행하는 프로젝트가 아니었다면?
무슨 말인고 하니, 내 로컬의 사양과 환경 세팅이 다른 협업 개발자들과의 환경과 분명 다른 점이 있을 텐데 그 간극을 채우는 데에도 시간이 소모될 것이다. 또한, 개발 환경과 다른 개발 환경 외에도 개발 환경과 배포 환경의 불일치 간극을 줄이기 위한 이유 역시 한몫했다.
사실 이전의 대용량 트래픽 제어 프로젝트에서 배포의 동작 트리거로써 도커를 활용했지만 그건 도커의 극히 일부만을 쓴 것에 불과하고 도커가 지닌 장점이나 목적성을 추구하지는 못했다. 그리고 당시에는 도커의 개념이 희미한 때라서 직접 써먹어봐야겠다는 생각까지 닿지 못할 정도로 바쁜 시기였으니...
초라하기 그지없는 도커 컴포즈 yml 파일...
이런 이유들이 결합돼서 도커를 공부할 겸 이번 스터디 프로젝트에 써먹어봐야겠다는 생각을 하게 됐다.
그래서 로컬에 세팅된 PostgreSQL과 redis를 삭제했다(?!)
...ㅋ..ㅋㅋ..ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
삭제하고 약간 후회(?)가 좀 됐긴 하지만, 어찌됐든 도커는 공부해야 할 부분이었으니 기왕 된 거 도커로 개발 환경(PostgreSQL과 redis)을 세팅해서 써먹어보자...! 라고 결심하고 도커 컨테이너 구축을 위해 이론과 자료들을 찾아다녔다.
사실 이 고민은 되돌아보면 공부 안 하고 한 고민이라 의미가 있는 부분은 아닌 것 같지만... 컨테이너의 개념이 희미했을 때 든 생각 중 하나였다.
굳이 컨테이너 하나씩 할당하지 말고, 모든 앱을 컨테이너에 넣을 수는 없을까
그 당시에는 희미한 고민 하나 정도로 치부하고 넘어갔던, 앱이나 툴 하나당 컨테이너 하나씩 할당하고 관리하는 것이 당연하게 생각됐었지만 지금은 트러블슈팅으로 삼아 조금 더 세심하게 고민할 부분이라 생각해서 고심하고 정리했다.
각 애플리케이션을 별도의 컨테이너에서 실행하면 하나의 애플리케이션에 문제가 생겨도 다른 애플리케이션에 영향을 미치지 않게 되는, 이른바 문제 격리를 이뤄낼 수 있으며 이는 이어서 각각의 컨테이너가 독립적으로 업데이트, 재배포, 롤백될 수 있음을 의미한다. 즉, 하나의 애플리케이션을 수정할 때 다른 애플리케이션에 영향을 주지 않을 수 있다.
단일 책임 원칙(Single Responsibility Principle)은 자바의 객체지향을 공부할 때 많이 봤던 키워드인데, 도커 컨테이너에서도 똑같이 나오는 키워드였다. 각 컨테이너는 하나의 애플리케이션이나 서비스에만 집중하여 관리 및 유지보수가 용이해진다.
또한, 환경의 일관성을 통해 개발, 테스트, 프로덕션 환경에서 동일한 애플리케이션 컨테이너를 사용함으로써 환경 차이로 인한 문제를 최소화하려면 결국 컨테이너의 격리가 필요해진다.
정말 이 방법이 가능할까...? 하며 찾다가 발견한 단일 컨테이너에서의 멀티 애플리케이션 사례를 보게 되었다.
# ...
RUN mkdir /app
COPY ./app1.jar /app/subApp.jar
COPY ./app2.jar /app/mainApp.jar
EXPOSE 8080 8081
COPY ./start_wrapper.sh /app
RUN chmod +x /app/wrapper_script.sh
CMD ["/app/wrapper_script.sh"]
위의 도커파일은 두 개의 앱 파일을 app
이라는 폴더에 복사한 후, wrapper_script.sh
로 묶어서 실행시킬 수 있는 내용을 담고 있다. sh
파일은 아래와 같다.
#!/bin/bash
# 서브APP 백그라운드 실행
nohup java -jar subApp.jar &
# MainApp 포그라운드 실행
java -jar mainApp.jar
출처 : https://happy-jjang-a.tistory.com/173
위의 출처 글에서도 볼 수 있지만 컨테이너에서 메인 앱은 일반적인 컨테이너처럼 포그라운드로 실행시키고, 나머지 앱은 백그라운드로 실행을 시키는 형식을 채용하고 있다.
나의 프로젝트에서 PostgreSQL과 redis는 메인과 서브의 관계가 아니었기 때문에 결과적으로 각자 컨테이너를 할당하는 방향으로 결정했다.
사실 백엔드 작업용 클라이언트를 잘 쓰지 않는다. 결국 터미널 명령어에 익숙해져야 하는 게 백엔드 개발자의 숙명(?)인지라 클라이언트에 의존해 딸깍 수행하는 식의 작업 처리를 그렇게 선호하는 편은 아니다. 그렇지만 도커는 처음 접하기도 하고, 컨테이너 관리가 용이하다는 뚜렷한 장점이 있어서 설치를 하게 됐다.
나는 이전에 프론트엔드를 공부하면서 homebrew
를 설치해뒀기 때문에 도커 역시 homebrew
로 설치했다.
# docker for mac설치
brew install docker docker-compose
# docker version 확인
docker --version
# docker compose version 확인
docker-compose --version
도커 버전 확인 후, 도커 엔진을 확인하기 위한 명령어 docker info
까지 확인하면 아래와 같은 정보가 뜨는데, 도커 서버와 관련해서 데몬 관련된 에러를 출력한다. 이 부분은 맥(정확히는 애플 칩)과 도커의 호환 문제라는데, 이걸 해결하기 위해 현재 시점에서는 도커의 특정 버전을 설치해야 한다고 한다.
참조 : https://ayush-billore.medium.com/docker-desktop-not-working-with-apple-mac-m1-1b24f21b2a73
근데 나는 도커 데스크탑을 실행시키면 별 문제없이 컨테이너 띄우고 실행할 수 있어서... 일단은 링크만 기억해두고 향후 추가 문제가 발생할 때, 해당 해결책을 실행할 예정이다.
도커 데스크탑은 아래의 링크에서 설치했다.
https://docs.docker.com/desktop/install/mac-install/
여담으로, 도커 데스크탑을 활용하기 위해서는 도커허브의 계정이 필요하다. 그 이유는 향후 내가 커스텀한 도커 이미지를 등록하고 배포 환경 등에서 해당 이미지를 기반으로 한 컨테이너를 활용하기 위함이다.