docker compose 에서 db 연결하려면 어떻게 해야하지?
프로젝트 진행 중 Docker 와 관련하여 이전 부터 있던 궁금증을 해결하기 위해!
겪었던 문제들을 풀어보려고 합니다~
당연히 알지~ 하며 당당하게 말을 했으나 현실은 docker 변수 붙여넣기에 달인이 된 지난날
언제 한번 모르는게 터질텐데...하면서 미루던 날
프로젝트 설정 중 DataBase와 연결을 해야하는 부분에서 시작을 했는데
시작은 단순한 의문에서 시작이 되었다.
docker-compose 에서 전부 연결하면 안되나...?
많은 분들이 의문을 가질 거라 생각한다...
초기 Docker 환경을 설정했을 때는 제대로 알지도 못했고 DB 환경이 docker 내부가 아닌 외부 인터넷으로 연결할수 있도록 되어있어서 단순히 container 와의 연결이 의미가 없었다.
하지만 서버를 여러개 만들다보니 당연 서버마다 데이터 베이스를 생성해야하는 일이 발생했다
물론! 하나의 데이터베이스를 실행하고 database를 분간하여 해도 됬지만
추후 서비스간의 분리를 위해 철저하게 나누고 싶었다
그래서 처음 구성한 방법은 mysql container 를 독립적으로 실행시키고
docker-compose 를 통해 app container 가 생성될때 연결하도록 헀다
그랬더니 데이터 베이스를 키울 때마다 문제가 발생하게 되는데
contianer의 IP 를 환경변수에 직접 넣어줘야 했음...
A database port - 3307:3306 | B database port - 9009:3306
이렇게 사용하는건 정말 아닌 것 같아 docker-compose 내부로 옮겨 통신을 하려고 시도를 하게 된다.
docker-compose 에 대해 잘못된 생각을 갖고 있었기 때문인데 container 간의 시작과 끝이 동시에 일어날 수 밖에 없다고 믿었기 때문이다.
docker-compose up
을 하게 되면 작성되어있는 모든 container
가 올라가고
docker-compose down
을 하게되면 모든 container
가 내려가니
당연 나는 APP conatiner
만 update
하고 싶은데 database
까지 같이 내려가고 싶지 않았기 때문이다.
끝까지 서비스와 데이터 베이스가 독립적으로 구성하고 싶다는 의지...!
당연히 이렇게 초창기에 진행하면서 docker-compose 의 의미가 점점 퇴색 되는 걸로 보아 이렇게 하는건 잘못 되었구나라는 생각이 강했다
초기 목표는 docker-compse 에 app, db 컨테이너를 서로 연결하는 것
그를 위한 docker-network 에 대한 개념 정리 부터 시작했다.
docker network
container 간의 통신이 될 수 있도록 연결해주는 기능
container 마다 독립적인 port 를 갖고 있고 컨테이너를 실행할 때는
해당 container의 port 와 host 라고 하는 docker container 를 관리하는 기능과 연결을 한다.
host port 는 실제 컴퓨터의 port 와 연결이 되고 정상적으로 연결이 된다면
우리가 해당 컴퓨터의서 특정 port 를 호출하면 host 가 연결된 container 의 port 로 전송한다
그래서 docker-compose.yml 에서 port 설정을 "3307:43307"
으로 표기를 한다.
"실제 port:container 내부 port"
여기서는 port 번호가 host에 하나만 연결이 되기 때문에 container 의 IP 는 중요하지가 않다.
따라서 초기 내가 구성했것이 가능헀다
그럼 container 에서 host port 를 호출 하면 되잖아..!?
그럼 해결하는 거아닌가? 하며
services:
app:
container_name: APP_service
env_file:
- .env
prots:
- "8080:8080"
db:
container_name: APP_database
ports:
- "3307:3306"
# .env
MYSQL_HOST=localhost
PORT=3307
이렇게 구성하고 실행했더니 당연히...연결실패...!
저번에 겪은 실패와 동일한데
docker host 의 IP 또한 실제 컴퓨터의 IP 와 정확히 매핑이 되지 않기 때문에 localhost 는 docker host 의 IP 가 아닌 컴퓨터자체의 IP 가 되어 버린다.
따라서 host의 IP 를 적어야하는데 이는 host.docker.internal 이라는 변수에 담겨져 있다.
# .env
MYSQL_HOST=host.docker.internal
PORT=3307
자 이제 IP 도 맞췄으니 되겠다 라고 생각했지만...
여기서 조금 멘붕이 빠지긴 했다.
container 내부에서도 host의 IP 를 알면 연결이 되야할텐데..mysql 접속이라 안되는 것같았다.
이유는 PING 을 통해 테스트를 해보았는데
host.docker.internal 은 ping 테스트가 되는데
host.docker.internal:3307 로는 통신 테스트가 되질 않는다는 것...다음과 같은 에러가 뜬다.
Name or service not known
하는 수 없이 조금씩 어려워하던 docker-network 를 공부할 때가 왔다고 확신했다
docker-compsose 간의 통신은 network 옵션을 써서 해결을 한다.
docker-network 는 container 간의 통신을 하기 위한 기능으로
같은 네트워크에 있는 container 는 서로 통신이 가능하다
network:
- appNetwork # networks 옵션 사용시 꼭 명시할 것!
services:
app:
container_name: APP_service
networks:
- appNetwork # 같은 이름으로 되어있으면 같은 네트워크
db:
container_name: APP_database
networks:
- appNetwork # 같은 이름으로 되어있으면 같은 네트워크
아무것도 설정하지 않은 default 설정을 따라가게 되는데
1. 새로운 network 생성
2. docker-compose 에 있는 container 가 같은 network 로 등록이 된다.
이때 기존에 있던 network 를 사용하려면 network 명시에 external: true
이라는 옵션을 줘야한다.
여기까지 구성하고 정말 뿌듯한 마음으로 docker-compose 를 실행했지만 여전히 database 연결이 실패했는데
이유는 단순히 container IP 가 수시로 변경되면서 db container의 IP가 일정하지 않은 것이다.
다행히 같은 network 에서는 container 이름이 container IP 와 매핑이 된다는 걸 알게 되었다.
그래서 DATABASE 의 URL 을 위의 예시처럼 APP_databaase
로 했더니 이제야 제대로된 연결을 할 수 있게 되었다.
그래서 최종 파일은
network:
- appNetwork: # 네트워크 유지를 위해 새로 만든 네트워크 사용
- external: true
services:
app:
container_name: APP_service
networks:
- appNetwork # 같은 이름으로 되어있으면 같은 네트워크
env_file:
- .env
prots:
- "8080:8080"
db:
container_name: APP_database
networks:
- appNetwork
ports:
- "3307:3306"
# .env
MYSQL_URL= APP_database # container_name 설정
MYSQL_PORT= 3306 # container 의 내부 포트로 연결
(생략)
이렇게 완성됬다.
그다음 목표는 이제 따로 따로 관리를 하고 싶어졌다.
즉, CI/CD 를 통해 이미지 파일이 변경되어도
app container 만 재시작을 하고 싶었는데
이부분은 너무 간단해서 어이가 없을 정도였다... 왜 진작 찾질 않았는가....!!!!
명령어 중 docer-compose pull 이라는 명령어는
표기된 container 들을 중 새로운 이미지파일 push되었을 때 전부 다시 다운 받는 명령어인데
일단 다음처럼 끝에 명시한 app 이름을 적으면 해당 container 만 pull 을 받는다..!
docker compose app
또하나 충격적인 부분인데
이전에 컨테이너 업데이르를 하려면
docker compose down
docker compose up
두 명령어의 반복이였다.
그치만...! 하나의 container 만 재시작을 할수가 있었다..
docker compose restart app
역시 뒤에 명시한 서비스이름을 붙여주면 되었다..
역시 처음 배울 때 제대로 배워야....!
정리하자면 container를 독립적으로 운행하고 있다면!
docker-network 를 통해 각각의 container 가 통신이 가능하도록 하고
container_name 을 통해 각 containter IP 에 접속하면 된다!
각종 환경변수 변경과 ping 과 같은 테스트가 없었다면 확실하게 못알았을 경험이였다