-
컨테이너와 컨테이너 사이의 통신인프라/도커 2024. 1. 3. 21:47
지난 글에 이어서 서버와 DB를 함께 다양한 환경에서 사용해보겠습니다.
아래 순서로 글을 진행해보겠습니다.
- 호스트와 컨테이너 사이의 통신
1. DB (호스트) & 서버 (호스트) 실행시키기
2. DB (호스트) & 서버 (도커) 실행시키기
3. DB (도커) & 서버 (호스트) 실행시키기
- 컨테이너 사이의 통신
4. 한 개의 컨테이너에서 DB & 서버 실행시키기
5. 각각 다른 컨테이너에서 DB & 서버 실행시키기
6. 도커 컴포즈로 DB & 서버 관리하기
4. 한 개의 컨테이너에서 DB & 서버 실행시키기
로컬 환경에서 DB와 서버를 한 곳에서 실행시키 듯이 컨테이너에서도 가능은 합니다.
스프링 서버와 mysql을 실행시킬 수 있는 환경을 컨테이너에 구현합니다.
# Ubuntu 베이스 이미지 사용 FROM ubuntu:20.04 # 필요한 도구 설치 RUN apt-get update \ && apt-get install -y wget software-properties-common gnupg # Temurin 저장소 추가 및 Java 17 설치 RUN wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - \ && add-apt-repository --yes https://packages.adoptium.net/artifactory/deb/ \ && apt-get update \ && apt-get install -y temurin-17-jdk # MySQL 설치 RUN apt-get install -y mysql-server WORKDIR workspace/app COPY ./settings.gradle /workspace/app/ COPY ./build.gradle /workspace/app/ COPY ./gradlew /workspace/app/ COPY ./gradle /workspace/app/gradle/ COPY ./src /workspace/app/src/ RUN /workspace/app/gradlew build RUN mv /workspace/app/build/libs/myapp-0.0.1-SNAPSHOT.jar /workspace/app/app.jar # 실행 파일 복사 및 실행 권한 부여 COPY ./init.sh /init.sh RUN chmod +x /init.sh # 애플리케이션 실행 및 MySQL 설정 CMD ["/init.sh"]
초기화 스크립트를 작성해서 추가해줍니다.
#!/bin/bash # MySQL 서비스 시작 service mysql start # MySQL 설정 mysql -e "CREATE DATABASE sqltest;" mysql -e "CREATE USER 'user1' IDENTIFIED BY 'Uuser123!';" mysql -e "GRANT ALL PRIVILEGES ON sqltest.* TO 'user1';" # Java 애플리케이션 실행 java -jar /workspace/app/app.jar
테스트 요청도 스크립트로 만들어서 스크립트를 호출해 줍니다.
curl -X POST http://localhost:8090/post -H 'Content-Type: application/json' -d '{"title": "hi title"}'; curl http://localhost:8090/post/1;
application.yml도 sql의 유저에 맞게 사용해줍니다.
같은 도커 컨테이너 속에 들어있기 때문에 localhost를 사용하여 통신이 가능합니다.
이것은 좋은 방법일까요?
A container's main running process is the ENTRYPOINT and/or CMD at the end of the Dockerfile. It's best practice to separate areas of concern by using one service per container.
공식 문서를 찾아보면 도커의 best practice는 한 컨테이너에 한 개의 서비스를 사용할 것을 권하고 있습니다.
몇 가지 이유를 생각해 볼 수 있습니다.
1. 확장성: 단일 서비스를 사용한다면 부담없이 필요에 따라 컨테이너를 추가할 수 있을 것입니다.
2. 유지보수성: 컨테이너의 문제가 발생했을 때, 문제의 원인을 찾기가 수월합니다.
3. 격리성: 하나의 서비스에서 생긴 문제가 다른 컨테이너에 영향을 주지 않기 때문에 관리에 용이합니다.
5. 각각 다른 컨테이너에서 DB & 서버 실행시키기
4번은 좋지 않다는 것을 느껴보기 위해 한번 진행해본 예제 입니다.
실행부터도 이미 기존에 도커를 사용하던 느낌이 아니고 이미지도 한 개당 1.5GB씩 합니다.
이번에는 다른 2개의 컨테이너에서 실행시켜 보겠습니다.
먼저 mysql을 mydb라는 이름으로 컨테이너에 실행시켜보겠습니다.
$ docker run -d --name=mydb -p 3308:3306 -e MYSQL_ROOT_PASSWORD=12345678 mysql
마찬가지로 서버도 컨테이너에 올려보았으나, 디비 연결에 실패합니다.
$ docker build -t myapp . $ docker run -it -p 8090:8080 myapp
localhost를 바라보는 것이 문제겠네요.
어떻게 해결할 수 있을까요?
* 해결방법1 - 호스트 ip를 사용한다. ( 권장x )
바로 드는 생각은 이전에 해본 호스트 ip를 사용하는 방법입니다.
호스트 ip를 확인하고 3308번으로 접속해보겠습니다.
이렇게 실행하니 동작은 합니다만 요청이 상당히 돌아가는 느낌이 듭니다.
다른 방법을 살펴보겠습니다.
* 해결방법2 - 사용자 정의의 네트워크 사용
먼저 별도의 네트워크 환경을 생성합니다.
# 네트워크 조회 $ docker network ls # 네트워크 생성 $ docker network create mywebapp
mywebapp 이라는 네트워크가 생성된 것을 확인할 수 있습니다.
network옵션을 주려면 새로운 컨테이너를 만들어야합니다.
기존의 컨테이너는 제거하고 새로운 mysql을 위한 컨테이너를 실행하고 database를 생성합니다.
# 네트워크 옵션으로 실행 $ docker run -d --network=mywebapp --name mydb -p 3308:3306 -e MYSQL_ROOT_PASSWORD=12345678 mysql mysql> create database sqltest;
이제 실행되고 있는 컨테이너가 할당받은 ip를 확인합니다.
$ docker inspect mydb or $ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mydb
많은 정보를 주는데 끝에 보면 IPAddress를 통해서 해당 컨테이너가 실행 중인 Ip가 확인이 가능하다.
그럼 이 ip를 이용해서 서버를 띄워보자.
서버가 잘 실행되는 것을 확인할 수 있습니다.. 감동..
또 다른 방법으로는 ip를 사용하는 대신에 컨테이너의 이름을 사용할 수도 있습니다.
저는 mysql이 떠있는 컨테이너의 이름을 mydb로 설정하였으니, 다음과 같이 application.yml을 수정해보겠습니다.
컨테이너의 이름을 사용하여도 서버가 잘 실행됩니다.
6. 도커 컴포즈로 DB & 서버 관리하기
같이 사용하는 2개의 프로그램을 각각 관리하는게 번거롭게 느껴집니다.
도커 컴포즈를 이용하면 여러 개의 컨테이너 설정을 하나의 파일에서 정의하고 관리할 수 있습니다.
docker-compose.yml 파일을 작성해보겠습니다.
version: '3' services: spring: container_name: mywebserver build: . ports: - "8090:8080" depends_on: mysql: condition: service_healthy networks: - my-network mysql: container_name: mydb image: mysql environment: - MYSQL_ROOT_PASSWORD=12345678 - MYSQL_DATABASE=sqltest ports: - "3308:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-uroot", "-p12345678", "-h", "localhost"] interval: 10s timeout: 5s retries: 10 networks: - my-network # volumes: # - ./initQuery.sql:/docker-entrypoint-initdb.d/initQuery.sql networks: my-network:
- services: docker-compose로 실행될 서비스를 적는 프로퍼티입니다. 현재 2개의 서비스를 실행할 예정이기 때문에 spring과 mysql을 적어 주었습니다. 개별 서비스 이름은 사용자가 정할 수 있습니다.
- build: spring 서비스에서 build를 사용하고 있습니다. build는 Dockerfile의 경로를 적게 되어있습니다. 해당 경로의 Dockerfile을 사용해서 이미지를 만들고 실행시킵니다.
- image: docker hub에 있는 이미지를 다운받으려면 image 프로퍼티를 사용합니다.
- container_name: 컨테이너의 tag 이름입니다.
- depends_on: 해당 서비스가 다른 서비스에 종속된다는 것을 보여주는 프로퍼티입니다. 의존하고 있는 서비스가 실행된 후에 해당 서비스가 실행됩니다.
- networks: 도커 컴포즈를 사용하면 기본적으로 동일 네트워크에서 실행이됩니다. 네트워크 이름을 명시적으로 적을 수 있는 프로퍼티입니다. 가장 아래에 위치한 networks와 함께 사용해야합니다.
- volumes: 호스트가 가지고 있는 파일 시스템의 일부를 컨테이너에 공유하는 프로퍼티입니다. 주석 처리된 내용은 현재 경로에 있는 initQuery.sql 파일을 docker-entrypoint-initdb.d 폴더에 initQuery.sql로 복사하는 내용입니다. mysql은 실행시에 docker-entrypoint-initdb.d 에 있는 모든 sql파일을 실행합니다. 따라서 초기화를 위한 별도의 sql파일을 작성해서 추가할 수 있습니다.
docker-compose 파일을 실행시켜보겠습니다.
$ docker-compose up -d
컨테이너 이름과 함께 로그가 찍히는 것을 확인할 수 있습니다.
docker-compose로 묶어서 관리하는 편이 유지 보수에 훨씬 편리함이 느껴집니다.
'인프라 > 도커' 카테고리의 다른 글
컨테이너에서 Docker 명령어 사용하기 (1) 2024.01.13 호스트와 컨테이너 사이의 통신 (0) 2024.01.03 호스트 빌드에서 도커의 멀티 스테이지 빌드까지 (2) 2024.01.03 자주 쓰이는 도커 명령어 정리 (1) 2023.12.21