Gom3rye 2025. 6. 23. 18:43

Docker Compose

  • Dockerfile은 하나의 애플리케이션을 이미지로 만드는 수단
  • 여러 개의 애플리케이션을 패키징하는 경우 Dockerfile을 이용하게 되면 여러 개의 Dockerfile을 이용해야 한다.
  • 여러 개의 애플리케이션을 한 번에 패키징하는 도구가 Docker Compose이다.
    • 시스템 구축과 관련된 명령어를 하나의 텍스트 파일에 기재해 한 번에 시스템 전체를 실행하고 폐기까지 한 번에 하도록 도와주는 도구
    • 공통의 목적을 갖는 애플리케이션 스택을 docker compose yaml 코드로 정의해서 한 번에 서비스를 올리고 관리할 수 있는 도구가 docker compose이다.
      • backend-read/write, frontend, db-mongo/mysql ⇒ 총 5개의 dockerfile이 필요함 → docker compose 로 관리
  • 작성 방법은 docker 명령어와 유사하기는 하지만 다르다.
  • up 명령을 수행하면 docker run 명령과 유사하게 정의 파일에 기재된 내용대로 이미지를 내려받고 컨테이너를 생성하고 실행하는데 정의 파일에는 네트워크나 볼륨에 대한 정의도 기재하는 것이 가능하다.
  • down 명령은 컨테이너 및 네트워크를 정지 및 삭제하는 것으로 볼륨과 이미지는 삭제하지 않으며 네트워크와 컨테이너를 삭제하지 않고 종료만 하고자 하는 경우에는 stop

Docker Compose와 Dockerfile

  • docker compose는 docker run 명령을 여러 개 모아 놓은 것과 같은데 컨테이너와 주변 환경을 생성하며 네트워크와 볼륨까지 만들 수 있다.
  • dockerfile은 이미지를 만들기 위한 것으로 네트워크나 볼륨은 만들 수 없다.

설치

  • Docker의 기본 도구가 아니라서 별도로 설치를 해야 한다.
  • Windows나 Mac에서 Docker Desktop을 설치한 경우는 별도로 설치할 필요가 없다.
  • Linux에서는 설치를 해야 한다.
sudo curl -L "<https://github.com/docker/compose/releases/latest/download/docker-compose-$>(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
sudo chown 계정 /usr/local/bin/docker-compose
# sudo chown kyla /usr/local/bin/docker-compose
docker-compose --version

작성법

  • 기본적인 작성 및 실행
    • 컨테이너 생성 명령
    docker run --name apa000ex1 -d -p 8080:80 httpd
    
    • vi docker-compose.yml
    version: "3"
    services:
    	apa000ex1:
    		image: httpd
    		ports:
    			- 8080:80
    		restart: always
    
    • 컨테이너 실행
    docker-compose up -d
    

Maria DB를 docker-compose로 실행

docker run 명령

docker run --name mariadb -d -p 3306:3306 --restart=always -e MYSQL_ROOT_PASSWORD=패스워드 mariadb

vi docker-compose.yml

version: "3"
services:
  mariadb:
    image: mariadb
    environment:
      - MYSQL_ROOT_PASSWORD=비밀번호
    ports:
      - 3306:3306
    restart: always

컨테이너 실행

docker-compose up -d

컨테이너 만들어졌는지 확인

docker ps

docker-compose down

 

작성 방법

  • 주항목
    • version
    • services
    • networks
    • volumes
  • 정의 내용
    • image
    • networks: --net
    • volumes: -v
    • ports: -p
    • environment: -e
    • depends_on: 다른 서비스에 대한 의존 관계
    • restart: 컨테이너 종료 시 재시작 여부(no, always, on-failure, unless-stopped)
  • 버전 정의
    • version: “3.8” : 버전에 따라 지원할 수 있는 도커의 최소 버전이 결정된다.
  • 서비스 정의
    • 도커에서는 컨테이너라고 하지만 도커 컴포즈에서는 service라는 개념을 사용한다.
    • services 하위의 실행할 컨테이너 서비스를 작성하고 컨테이너 실행에 필요한 옵션을 작성한다.
    • image 속성을 이용해서 docker hub나 private registry에서 이미지를 가져오지만 build 속성을 이용해서 Dockerfile을 가지고 이미지를 생성하는 것도 가능하다.
    • build 속성을 이용할 때 위치가 다른 경우에는 context 라는 하위 속성을 이용해서 경로를 설정할 수 있고 dockerfile 속성을 이용해서 Dockerfile의 이름을 직접 설정하는 것도 가능하다.
    • 하위 옵션
      • container_name
      • ports
      • expose
      • networks
      • volumes
      • environment
      • command
      • restart
      • depends_on
  • 네트워크 정의
    • networks라는 속성을 이용한다.
  • 볼륨 정의
    • volumes 하위 속성으로 설정한다.

실습

  • 데이터베이스는 MySQL을 사용
  • 웹은 wordpress를 사용
  • 위의 2가지를 이용한 웹 애플리케이션 서비스를 구성
# 일반적인 도커 명령으로 위의 프로젝트 개발 환경 구축 (볼륨 2개를 생성)
docker volume create mydb_data
docker volume create myweb_data

# 2개의 컨테이너 간의 연결을 위해서 네트워크를 생성
docker network create myapp-net

docker network ls

# MySQL 컨테이너 생성
docker run --name=mysql_app -v mydb_data:/var/lib/mysql --restart=always -p 3306:3306 --net=myapp-net -e MYSQL_ROOT_PASSWORD=패쓰워드 -e MYSQL_DATABASE=kyla -e MYSQL_USER=kyla -e MYSQL_PASSWORD=비밀번호 mysql:8.0

# Wordpress 컨테이너 생성
docker run --name=wordpress_app -v myweb_data:/var/www/html -v ${PWD}/myweb-log:/var/log --restart=always -p 8888:80 --net=myapp-net -e WORDPRESS_DB_HOST=mysql_app:3306 -e WORDPRESS_DB_NAME=kyla -e WORDPRESS_DB_USER=kyla -e WORDPRESS_DB_PASSWORD=비밀번호 --link mysql_app:mysql wordpress:5.7

MySQL 컨테이너 생성

docker run -d --name=mysql_app \\
  -v mydb_data:/var/lib/mysql \\
  --restart=always \\
  -p 3306:3306 \\
  --net=myapp-net \\
  -e MYSQL_ROOT_PASSWORD=비밀번호 \\
  -e MYSQL_DATABASE=kyla \\
  -e MYSQL_USER=kyla \\
  -e MYSQL_PASSWORD=비밀번호 \\
  mysql:8.0

Wordpress 컨테이너 생성

docker run -d --name=wordpress_app \\
  -v myweb_data:/var/www/html \\
  -v ${PWD}/myweb-log:/var/log \\
  --restart=always \\
  -p 8888:80 \\
  --net=myapp-net \\
  -e WORDPRESS_DB_HOST=mysql_app:3306 \\
  -e WORDPRESS_DB_NAME=kyla \\
  -e WORDPRESS_DB_USER=kyla \\
  -e WORDPRESS_DB_PASSWORD=비밀번호 \\
  --link mysql_app:mysql \\
  wordpress:5.7

docker-compose.yml

version: "3.9"
services:
  mydb:
    image: mysql
    container_name: mysql_app
    volumes:
	    - mydb_data:/var/lib/mysql
    restart: always
    ports:
	    - "3306:3306"
	  networks:
		  - backend-net
		environment:
			MYSQL_ROOT_PASSWORD: 비밀번호
			MYSQL_DATABASE: kyla
			MYSQL_USER: kyla
			MYSQL_PASSWORD: 비밀번호
			
	myweb:
		image: wordpress:5.7
		container_name: wordpress_app
		ports:
			- "8888:80"
		networks:
			- backend-net
			- frontend-net
		volumes:
			- myweb_data:/var/www/html
			- ${PWD}/myweb-log:/var/log
		environment:
			WORDPRESS_DB_HOST: mydb:3306
			WORDPRESS_DB_USER: kyla
			WORDPRESS_DB_PASSWORD: 비밀번호
			WORDPRESS_DB_NAME: kyla
		depends_on:
			- mydb
		
networks:
	backend-net: {}
	frontend-net: {}
			
volumes:
	mydb_data: {}
	myweb_data: {}
docker-compose up -d

Python의 플라스크로 웹 피이지를 출력하고 redis의 인메모리 데이터베이스에서 웹 페이지 액세스 카운트를 캐시해서 페이지에 출력

  • 레디스 컨테이너 생성 방법
docker run --name myredis -d -p 6379:6379 redis
# 확인
docker ps
  • 파이썬 flask 애플리케이션 생성(가상 환경을 만들어서 수행: 모든 패키지가 requirements에 기록되는 것을 방지)
python3 -m venv ./myvenv
# 활성화
source myvenv/bin/activate
  • 필요한 패키지 설치
pip install flask
pip install redis
  • 파이썬 패키지 의존성을 파일로 전송
pip freeze > requirements.txt
  • 플라스크를 이용한 웹 페이지 생성 - vi app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
def web_hit_cnt():
    with redis.StrictRedis(host='localhost', port=6379) as conn:
        return conn.incr("hits")

@app.route("/")
def hello():
    cnt = web_hit_cnt()
    return "<p>Web Access Count: {} times</p>".format(cnt)
    
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9000, debug=True) 
  • 실행
python app.py
# 포트포워딩 했는데도 안되면 ufw allow 9000 해주기
  • python 애플리케이션을 이미지로 만들 수 있는 Dockerfile 생성 - vi Dockerfile
FROM python:3.10-slim
ENV LIBRARY_PATH=/lib:/usr/lib
ENV FLASK_APP=py_app
ENV FLASK_ENV=development

EXPOSE 9000
WORKDIR /py_app
COPY . .
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]
  • 이미지 빌드
docker build -t flaskapp .
  • 컨테이너 실행
docker run -dit -p 9000:9000 --name=flaskapp flaskapp

 

→ 안되는 게 당연

app.py로 실행할 때는 localhost에서 Redis와 연결할 수 있지만, Docker 컨테이너를 사용하면 기본적으로 네트워크가 격리되어 있어서 Flask 애플리케이션 컨테이너가 Redis 컨테이너에 접근할 수 없기 때문

문제의 핵심

  • app.py를 실행할 때는 Flask 애플리케이션이 **호스트 머신의 Redis 서버(127.0.0.1)**에 접근할 수 있습니다. localhost로 연결되므로 별다른 네트워크 설정이 필요하지 않죠.
  • 그러나 flaskapp Docker 컨테이너를 실행하면, Flask 애플리케이션이 컨테이너 내부에서 실행되고, 이 컨테이너는 localhost를 자신의 컨테이너로 인식합니다. 즉, localhost:6379로 연결하면 Redis 컨테이너가 아닌 호스트 시스템의 Redis에 접근하려고 시도하게 되죠. 하지만 Flask와 Redis는 각각 다른 컨테이너에서 실행되므로 서로 연결되지 않습니다.

해결책

이 문제를 해결하려면 컨테이너 간 네트워크 연결을 설정해줘야 합니다. Docker에서는 이를 네트워크를 사용하여 해결할 수 있습니다.

1. 컨테이너 네트워크 설정

먼저, FlaskRedis가 같은 네트워크에 있도록 설정해야 합니다. 이를 위해 Docker의 Custom Bridge Network를 사용하여 두 컨테이너를 동일한 네트워크에 연결할 수 있습니다.

네트워크 만들기

먼저 docker network create 명령어로 네트워크를 하나 만들어 줍니다:

docker network create myapp-net

이렇게 하면 myapp-net이라는 사용자 정의 네트워크가 생성됩니다. 이제 Redis와 Flask 애플리케이션을 이 네트워크에 연결해야 합니다.

2. Redis 컨테이너를 네트워크에 연결

redis 컨테이너를 실행할 때 --net=myapp-net 옵션을 사용하여 Redis 컨테이너를 myapp-net 네트워크에 연결합니다:

docker run --name myredis --net=myapp-net -d -p 6379:6379 redis

이렇게 하면 myredis라는 이름의 Redis 컨테이너가 myapp-net 네트워크에 연결되고, 다른 컨테이너들도 이 네트워크를 통해 Redis에 접근할 수 있게 됩니다.

3. Flask 애플리케이션 컨테이너를 네트워크에 연결

Flask 애플리케이션 컨테이너를 실행할 때도 같은 네트워크에 연결하도록 합니다:

docker run -dit --net=myapp-net -p 9000:9000 --name=flaskapp flaskap

이렇게 하면 flaskapp 컨테이너도 myapp-net 네트워크에 연결됩니다.

4. Flask 애플리케이션에서 Redis 접근

이제 Flask 애플리케이션 코드에서 localhost 대신 Redis 컨테이너 이름인 myredis를 사용하여 Redis에 접근해야 합니다.

Flask 애플리케이션에서 Redis 연결 부분을 수정합니다:

def web_hit_cnt():
    with redis.StrictRedis(host='myredis', port=6379) as conn:
        return conn.incr("hits")

여기서 host='myredis'는 myredis라는 컨테이너 이름을 의미합니다. Docker에서는 같은 네트워크에 속한 컨테이너들끼리 이름을 통해 서로 통신할 수 있기 때문에, myredis라는 이름을 사용하면 해당 컨테이너로 연결됩니다.

5. 전체 프로세스 요약

  1. 네트워크 생성: docker network create myapp-ne
  2. Redis 컨테이너 실행: docker run --name myredis --net=myapp-net -d -p 6379:6379 redi
  3. Flask 애플리케이션 컨테이너 실행: docker run -dit --net=myapp-net -p 9000:9000 --name=flaskapp flaskap
  4. Flask 애플리케이션 코드 수정: def web_hit_cnt(): with redis.StrictRedis(host='myredis', port=6379) as conn: return conn.incr("hits")

이제 Flask 컨테이너가 myredis 컨테이너에 접근할 수 있게 됩니다. myredis는 Redis의 서비스 이름이므로 Docker 네트워크 안에서 다른 컨테이너들이 이를 통해 Redis에 연결할 수 있습니다.


6. 확인

위의 설정을 완료하고 Flask 애플리케이션을 실행한 후, 브라우저에서 http://localhost:9000으로 접속하여 웹 페이지에 액세스 카운트가 증가하는지 확인할 수 있습니다.

추가 팁:

  • Docker Compose 사용: 여러 컨테이너를 함께 관리할 때 docker-compose를 사용하는 것도 좋은 방법입니다. docker-compose.yml 파일을 사용하면 네트워크와 컨테이너의 설정을 한 번에 정의할 수 있습니다.
  • 컨테이너 간 통신: docker run 명령어에서 -net=myapp-net 옵션을 사용하면 컨테이너들 간의 네트워크 통신이 가능합니다.
728x90
반응형