티스토리 뷰
Docker Compose
단일 서버에서 여러개의 컨테이너를 하나의 서비스로 정의해 컨테이너의 묶음으로 관리할 수 있는 작업 환경을 제공하는 관리 도구
Docker Compose 사용 이유
여러 개의 컨테이너가 하나의 어플리케이션으로 동작시, 도커 컴포즈를 사용하지 않는다면, 이를 테스트하기 위해 각 컨테이너를 하나씩 생성해야 하기때문
ex. web application test = web server container,db container 각각 생성
Docker Compose 특징
- run 명령어의 옵션을 그대로 사용 가능
- 각 컨테이너의 의존성,네트워크,볼륨등을 함께 정의 가능
Docker Compose 작성 방법
- Compose에서는 YAML파일을 사용하여 application 서비스를 구성(명세)함
- Compose 파일의 기본 양식은 compose.yaml 혹은 compose.yml 이다.
- Compose는 다음과 같이 3단계 프로세스로 진행됨
- Dockerfile에 애플리케이션을 작성. → 어디서나 재현가능한 환경을 제공
- application 서비스들을 compose.yml에 정의하여 함께 실행될 수 있도록 함
- docker compose up을 통해 전체 앱을 실행
Docker Compose 구조
- Version : 지원 버전 확인
- Service : 도커 컴포즈로 생성할 각 컨테이너의 옵션을 정의
version: '3'
services:
web:
build: . # build 항목에 정의된 도커파일에서 이미지를 빌드해 서비스의 컨테이너를 생성하도록 설정(현재 디렉토리일 경우 .)
command: npm start # 컨테이너가 시작될 때 실행될 명령어를 지정
ports: #서비스의 컨테이너를 개방할 포트 설정
- "8080:80"
depends_on: # 이 서비스가 의존하는 다른 서비스를 지정
- db
links: # 다른 서비스에 서비스명만으로 접근할 수 있도록 설정
- db:database
extends: # 다른 yaml 파일이나 현재 yaml 파일에서 서비스 속성을 상속 받도록 설정
file: common-services.yml # 다른 파일에서 서비스 정의를 상속받음
service: webbase
db:
image: mysql
ports:
- "3306:3306"
environment: # 서비스의 컨테이너 내부에서 사용할 환경변수를 지정, 딕셔너리나 배열 형태로 사용할 수 있음
- MYSQL_ROOT_PASSWORD=password
- MYSQL_USER=user
- MYSQL_PASSWORD=password
- MYSQL_DATABASE=demodb
command: --default-authentication-plugin=mysql_native_password # 데이터베이스를 시작할 때 사용할 명령어 옵션을 지정
- Network : Service는 Network를 통해 서로 통신
networks:
mynetwork:
driver: overlay
driver_opts:
subnet: "255.255.255.0" # 스웜 모드나 주키퍼를 사용하는 환경이어야만 생성(일반적으로 overlay에선 사용x)
ipam:
driver: default # 'mydriver'는 예시이며, 실제 사용 가능한 IPAM 드라이버를 지정해야 합니다.
config:
- subnet: "172.20.0.0/16"
ip_range: "172.20.5.0/24"
gateway: "172.20.5.1"
- Volume : 서비스는 영구적으로 유지해야 할 데이터를 Volume에 저장하고 공유함
volumes:
myvolume:
driver: local # 'flocker'는 예시이며, 실제 사용할 볼륨 드라이버를 지정해야 합니다.
driver_opts:
type: "nfs" # 이것은 NFS 볼륨을 예로 든 것이며, 사용할 볼륨의 실제 옵션에 따라 달라질 수 있음
o: "addr=192.168.1.1,rw"
device: ":/path/to/dir"
external: true #원래 프로젝트마다 volumes을 생성, 이때 external 옵션 설정시 기존 volumes을 사용함
실제 연습
1.mysql 설치 및 기본 설정
만약 datagrip을 써도 mysql에 들어가서 user등록을 해줘야한다.. datagrip에서 될줄알고 한참을 했지만 실패했다..
https://study-ce.tistory.com/54 참고
1.apt update : sudo apt update
2.mysql 설치 : sudo apt install mysql-server (brew install mysql → brew services start mysql)
3.mysql 이름/password 설정
3-1. sudo mysql -u root -p
3-2. use mysql;
3-3. CREATE USER '{생성할 사용자 이름}'@'%' IDENTIFIED BY '{비밀번호}';
3-4. GRANT ALL PRIVILEGES ON *.* TO '{사용자 이름}'@'%';
3-5. FLUSH PRIVILEGES;
3-6. exit
sudo mysql -u root -p
use mysql;
CREATE USER '{생성할 사용자 이름}'@'%' IDENTIFIED BY '{비밀번호}';
GRANT ALL PRIVILEGES ON . TO '{사용자 이름}'@'%';
FLUSH PRIVILEGES;
exit;
4.mysql 설정 파일 수정(외부 접속 허용)
4-1. brew info mysql → ls /usr/local/etc/my.cnf or ls /usr/local/etc/my.cnf.d/sudo nano → /usr/local/etc/my.cnf
4-2. bind-address = 0.0.0.0 으로 수정(외부접속 허용)
4-3. sudo service mysql restart (or brew services restart mysql)
5.datagrip 연동
2.mysql table 작성
CREATE DATABASE IF NOT EXISTS dockerdb;
USE dockerdb;
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL
);
INSERT INTO users (username, email) VALUES ('chacha', 'chacha@example.com');
INSERT INTO users (username, email) VALUES ('chacha_test', 'chacha_test@example.com')
3.docker-compose.yaml과 dockerfile 작성
/dockerfile-folder/web/app.py
import pymysql
from flask import Flask, jsonify
app = Flask(__name__)
def get_db_connection():
return pymysql.connect(host='host.docker.internal',
user='docker_chacha',
password='1q2w3e4r',
db='dockerdb',
charset='utf8mb4')
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/users')
def list_users():
connection = get_db_connection()
cursor = connection.cursor(pymysql.cursors.DictCursor)
cursor.execute("SELECT * FROM users")
users = cursor.fetchall()
cursor.close()
connection.close()
return jsonify(users)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
/dockerfile-folder/web/requirements.txt
FLASK==3.0.0
# MYSQL==0.0.3
PyMySQL==0.10.1
cryptography
/dockerfile-folder/web/dockerfile
FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
ENV NAME dockertest
CMD ["python", "app.py"]
dockerfile-folder/docker-compose.yaml
*도커 컴포즈의 파일 이름은 꼭 docker-compose로 해줘야 한다.
version: '3.8'
services:
web:
build: ./web
ports:
- "5000:5000"
depends_on:
- db
environment:
DATABASE_USER: ${MYSQL_USER}
DATABASE_PASSWORD: ${MYSQL_PASSWORD}
DATABASE_NAME: ${MYSQL_DATABASE}
networks:
- backend
db:
image: mysql:8.2
container_name: mysql-container-web
environment:
MYSQL_ROOT_HOST: ${MYSQL_ROOT_HOST}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
ports:
- "3306:3306"
command: # 명령어 실행
- "mysqld"
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- ./mysql/conf.d:/etc/mysql/conf.d # MySQL 설정 파일 위치
- ./mysql/initdb.d/:/docker-entrypoint-initdb.d/ # 데이터베이스 초기화 sql
networks:
backend:
/dockerfile-folder/.env
MYSQL_ROOT_HOST=localhost
MYSQL_ROOT_PASSWORD=
MYSQL_USER=docker_chacha
MYSQL_PASSWORD=
MYSQL_DATABASE=dockerdb
4. 실행시켜서 확인한다.
docker-compose build
docker-compose up -d # Background로 도커 컴포즈 프로젝트 실행
#파일 수정 시
docker-compose down # 프로젝트 내 컨테이너 및 네트워크 종료 및 제거
docker-compose build --no-cache
docker-compose up
% docker-compose build
[+] Building 3.0s (11/11) FINISHED docker:desktop-linux
=> [web internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [web internal] load build definition from dockerfile 0.0s
=> => transferring dockerfile: 837B 0.0s
=> [web internal] load metadata for http://docker.io/library/python:3.8-slim 2.8s
=> [web auth] library/python:pull token for registry-1.docker.io 0.0s
=> [web 1/5] FROM http://docker.io/library/python:3.8-slim@sha256:d1cba0f8754d097bd333b8f3d4c655f37c2ede9042d1e7db69561d9eae2eebfa 0.0s
=> [web internal] load build context 0.0s
=> => transferring context: 1.80kB 0.0s
=> CACHED [web 2/5] WORKDIR /app 0.0s
=> CACHED [web 3/5] COPY requirements.txt . 0.0s
=> CACHED [web 4/5] RUN pip install --no-cache-dir -r requirements.txt 0.0s
=> [web 5/5] COPY . . 0.0s
=> [web] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:4012d3f308eda6190aa7e5796bdf1d42ea7d18f16391bf9c62f5db8e2cdb13c6 0.0s
=> => naming to http://docker.io/library/dockerfile-folder-web 0.0s
% docker-compose up -d
[+] Building 0.0s (0/0) docker:desktop-linux
[+] Running 2/2
✔ Container mysql-container-web Running 0.0s
✔ Container dockerfile-folder-web-1 Started 0.6s
(base) choimijung@choemijeong-ui-noteubug ~/Desktop/dockerfile-folder %
웹사이트에서 확인
발생했던 문제상황
문제1. unable to prepare context: path "/dockerfile-folder/web" not found
→ Docker Compose는 ./web 디렉토리에서 Dockerfile을 찾으려고 시도하지만 해당 디렉토리가 존재하지 않아 실패 아래와 같이 디렉토리 변경
/dockerfile-folder/
├── web/
│ ├── Dockerfile
│ ├── app.py
│ ├── requirements.txt
│ └── ... (기타 필요한 파일들)
└── docker-compose.yml
발생 이유 : docker-compose.yaml 파일이 아래와 같이 build가 ./web에서 진행되기 때문
문제2 . local mysql에 있지만 docker mysql에는 값이 없었다.(pymysql.err.ProgrammingError: (1146, "Table 'dockerdb.users' doesn't exist"))
→ docker-compose.yaml에 아래와 같이 추가
command: # 명령어 실행
- "mysqld"
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- ./mysql/conf.d:/etc/mysql/conf.d # MySQL 설정 파일 위치
- ./mysql/initdb.d/:/docker-entrypoint-initdb.d/ # 데이터베이스 초기화 sql
→ app.py의 host에 db대신 'host.docker.internal' 로 값 변경
def get_db_connection():
#기존 코드
return pymysql.connect(host='db',
#바뀐 코드
return pymysql.connect(host='host.docker.internal',
user='docker_chacha',
password='',
db='dockerdb',
charset='utf8mb4')
발생 이유
volume 추가:
hostsystem-container간의 디렉토리 공유를 위해서
./mysql/conf.d:/etc/mysql/conf.d: MySQL 설정 파일을 Docker 컨테이너와 공유
--character-set-server=utf8mb4: 문자 인코딩을 utf8mb4로 설정
./mysql/initdb.d/:/docker-entrypoint-initdb.d/:
→ MySQL 인스턴스가 처음 시작될 때 실행할 SQL 파일이나 스크립트를 담고 있 필요한 데이터베이스, 테이블, 기본 데이터 등을 자동으로 설정(users 테이블을 생성하는 SQL 파일 포함)
'host.docker.internal'로 변경:
'db'라는 호스트 이름은 일반적으로 Docker 네트워크 내에서 다른 컨테이너를 가리킬 때 사용(이것 때문에 엄청 고생했다..) 따라서 db라는 이름으로 연결하려고 시도하면 실제 데이터베이스 호스트를 찾을 수 없다.
host.docker.internal : 애플리케이션이 Docker 내부에서 실행되고 있더라도, 이 설정을 통해 Docker 외부에 있는 MySQL 서버에 접근 할 수 있다.
왜? docker container 내부에서 localhost는 container 자신을 의미함(lockerhost = docker conainer 내부)
문제2 발생시 확인 방법
1. docker-compose ps 사용해서 제대로 돌아가고 있는지 확인
% docker-compose ps # 도커 컴포즈 프로젝트 목록 확인
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dockerfile-folder-db-1 mysql:8.2 "docker-entrypoint.sh mysqld" db 11 minutes ago Up 11 minutes 3306/tcp, 33060/tcp
2. 정확하게 어느 부분에서 에러가 나고 있는지 확인
% docker-compose logs web
dockerfile-folder-web-1 | * Serving Flask app 'app'
dockerfile-folder-web-1 | * Debug mode: on
dockerfile-folder-web-1 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
dockerfile-folder-web-1 | * Running on all addresses (0.0.0.0)
dockerfile-folder-web-1 | * Running on http://127.0.0.1:5000
dockerfile-folder-web-1 | * Running on http://172.21.0.3:5000
dockerfile-folder-web-1 | Press CTRL+C to quit
dockerfile-folder-web-1 | * Restarting with stat
dockerfile-folder-web-1 | * Debugger is active!
dockerfile-folder-web-1 | * Debugger PIN: 124-494-128
dockerfile-folder-web-1 | 192.168.65.1 - - [03/Jan/2024 05:22:53] "GET / HTTP/1.1" 200 -
dockerfile-folder-web-1 | 192.168.65.1 - - [03/Jan/2024 05:23:00] "GET /users HTTP/1.1" 500 -
dockerfile-folder-web-1 | Traceback (most recent call last):
...
dockerfile-folder-web-1 | File "/app/app.py", line 24, in list_users
dockerfile-folder-web-1 | cursor.execute("SELECT * FROM users")
dockerfile-folder-web-1 | File "/usr/local/lib/python3.8/site-packages/pymysql/cursors.py", line 163, in execute
dockerfile-folder-web-1 | result = self._query(query)
...
dockerfile-folder-web-1 | File "/usr/local/lib/python3.8/site-packages/pymysql/protocol.py", line 223, in raise_for_error
dockerfile-folder-web-1 | err.raise_mysql_exception(self._data)
dockerfile-folder-web-1 | File "/usr/local/lib/python3.8/site-packages/pymysql/err.py", line 107, in raise_mysql_exception
dockerfile-folder-web-1 | raise errorclass(errno, errval)
dockerfile-folder-web-1 | pymysql.err.ProgrammingError: (1146, "Table 'dockerdb.users' doesn't exist")
dockerfile-folder-web-1 | 192.168.65.1 - - [03/Jan/2024 05:23:01] "GET /users?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 -
3-1.Flask 애플리케이션이 실행 중인 dockerfile-folder-web-1 컨테이너의 쉘에 접속 → app 코드 직접 입력
% docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
dockerfile-folder-db-1 mysql:8.2 "docker-entrypoint.sh mysqld" db 11 minutes ago Up 11 minutes 3306/tcp, 33060/tcp
dockerfile-folder-web-1 dockerfile-folder-web "python ./app.py" web 11 minutes ago Up 11 minutes 0.0.0.0:5000->5000/tcp
#ps로 Name확인 후 해당 container 접속
% docker exec -it dockerfile-folder-web-1 /bin/sh
# Python 실행
python
>>> import pymysql
>>>
>>> # Use the same credentials and host as in your Flask app
>>> conn = pymysql.connect(
... host='db', # This is the service name in docker-compose
... user='docker_chacha', # Your database username
... password='1111', # Your database password
... db='dockerdb', # Your database name
... )
>>>
>>> # Now, try to execute a simple query
>>> with conn.cursor() as cursor:
... cursor.execute("SHOW TABLES;")
... print(cursor.fetchall())
...
0
()
>>> conn.close()
#"SHOW TABLES;" 쿼리를 실행한 결과 0 ()가 반환됨 -> 해당 데이터베이스(dockerdb)에 테이블이 없다는 것을 의미
3-2. Flask 애플리케이션이 실행 중인 dockerfile-folder-web-1 컨테이너의 쉘에 접속-> mysql 접속 및 실행
dockerfile안에 내용 변경
FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt .
RUN apt-get update && apt-get install -y iputils-ping \
&& apt-get install -y --no-install-recommends default-mysql-client \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
ENV NAME dockertest
CMD ["python", "app.py"]
docker exec -it dockerfile-folder-db-1 /bin/bash
# MySQL 클라이언트 실행
mysql -u docker_chacha -p
# 패스워드 입력:
*참고자료
따라하며 배우는 도커와 CI환경 강의
https://velog.io/@whattsup_kim/Docker-Compose-Compose-Specification
https://seosh817.tistory.com/387
https://developerbee.tistory.com/236
https://shawn-dev.oopy.io/463a30bf-bf32-44e4-88be-8b4722e5549a
'DOCKER' 카테고리의 다른 글
[DOCKER] Docker compose에 nginx 적용기 (0) | 2024.01.15 |
---|---|
[Docker] Docker Volume (2) | 2024.01.02 |
[Docker] Docker 최적화 (0) | 2023.12.28 |
[Docker] Docker Container2 - 코드로 보는 개념 (0) | 2023.12.26 |
[Docker] Docker Image (0) | 2023.12.26 |