Docker compose
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. 컨테이너 네트워크 설정
먼저, Flask와 Redis가 같은 네트워크에 있도록 설정해야 합니다. 이를 위해 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. 전체 프로세스 요약
- 네트워크 생성: docker network create myapp-ne
- Redis 컨테이너 실행: docker run --name myredis --net=myapp-net -d -p 6379:6379 redi
- Flask 애플리케이션 컨테이너 실행: docker run -dit --net=myapp-net -p 9000:9000 --name=flaskapp flaskap
- 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 옵션을 사용하면 컨테이너들 간의 네트워크 통신이 가능합니다.