티스토리 뷰

DOCKER

[DOCKER] Docker Compose

PeonyF 2024. 1. 8. 19:36
반응형

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단계 프로세스로 진행됨
    1. Dockerfile에 애플리케이션을 작성. → 어디서나 재현가능한 환경을 제공
    2. application 서비스들을 compose.yml에 정의하여 함께 실행될 수 있도록 함
    3. docker compose up을 통해 전체 앱을 실행

Docker Compose 구조

  1. Version : 지원 버전 확인
  2. 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 # 데이터베이스를 시작할 때 사용할 명령어 옵션을 지정

 

  1. 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"
  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

https://stackoverflow.com/questions/44543842/how-to-connect-locally-hosted-mysql-database-with-the-docker-container/61480668#61480668

https://marklee1117.tistory.com/93

반응형

'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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함