DBMS/MariaDB2025. 11. 8. 19:59

Docker Compose를 이용하여 다음 기준에 맞게 MySQL을 설치해 보겠습니다.

 

설치 기준

  1. MySQL 이미지 
  2. 컨테이너가 호스트의 UID/GID로 실행
  3. 데이터, 설정, 로그를 볼륨으로 분리 관리
  4. 전용 네트워크 생성
  5. 로그 관리 (stdout + 파일 로그)

 

⚙️ 디렉토리 구조 예시

mysql/
 ├─ compose.yaml
 ├─ data/                # MySQL 실제 데이터 (호스트에 저장)
 ├─ logs/                # 로그 파일 저장
 └─ conf.d/              # 추가 설정 (예: my.cnf)

 

⚙️ compose.yaml

version: "3.9"

services:
  mysql:
    image: mysql:8.4
    container_name: mysql84
    user: "${UID}:${GID}"   # 현재 사용자 UID/GID 사용
    restart: unless-stopped

    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpass}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-appdb}
      MYSQL_USER: ${MYSQL_USER:-appuser}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-apppass}
      TZ: Asia/Seoul

    ports:
      - "3306:3306"

    volumes:
      - ./data:/var/lib/mysql
      - ./conf.d:/etc/mysql/conf.d
      - ./logs:/var/log/mysql

    networks:
      - mysql_net

    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  mysql_net:
    name: mysql_network
    driver: bridge

 

⚙️ 환경 변수 파일: .env (같은 디렉토리에)

UID=$(id -u)
GID=$(id -g)

MYSQL_ROOT_PASSWORD=rootpass
MYSQL_DATABASE=appdb
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass

 

⚙️ Disk Permission

1) securityContext 확인

Dockerfile이나 compose.yaml에서 정의할 UID, GID를 확인합니다.
ex) mysql 1001

 

2) Create User

먼저 해야 할 일은 mysql을 설치할 계정을 생성합니다.

 

● Create mysql User

groupadd -g 1001 -r mysql
useradd -c "mysql" -u 1001 -g mysql -s /bin/bash -r -p passwd mysql
usermod -aG docker mysql

 

● Create Docker User

groupadd -g 999 -r docker
useradd -c "docker" -u 999 -g docker -s /bin/bash -r -p passwd docker
usermod -aG docker docker

 

3) Directory Permission

chown -R mysql:mysql ./data
chown -R mysql:mysql ./conf.d
chown -R mysql:mysql ./logs

 

⚙️ mysql 설치

mysql 계정으로 login

 

⚠️ docker compose.env 파일의 변수 확장을 지원하지만, $(id -u) 같은 명령어 치환은 바로 안 됩니다.
따라서 아래처럼 쉘에서 직접 설정 후 실행하는 것이 좋습니다.

export UID=$(id -u)
export GID=$(id -g)
docker compose -f compose.yaml up -d

 

⚙️my.cnf 설정

conf.d/my.cnf 파일을 만들어서 로그 파일 위치나 설정을 조정할 수 있습니다.

 

예시 (conf.d/my.cnf):

[mysqld]
# 로그 설정
log_error = /var/log/mysql/error.log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

# 대소문자 구분 안 함 (테이블 이름)
lower_case_table_names = 1

# 기타 권장 설정
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

 

 

⚙️ Docker compose 실행

# UID/GID 환경변수 설정 후 실행
export UID=$(id -u)
export GID=$(id -g)
docker compose -f compose.yaml up -d

 

 

이 구성으로 다음이 가능합니다.

  • 컨테이너가 호스트 사용자 권한(UID/GID) 으로 실행되어 퍼미션 문제 최소화
  • 데이터/로그/설정을 개별 볼륨으로 관리
  • 전용 네트워크로 다른 서비스(MySQL 클라이언트 등)와 연동 가능
  • 로그 파일 + Docker 로그 드라이버 동시 관리

 

컨테이너 내부 또는 외부에서 다음처럼 접속할 수 있습니다.

 

⚙️ 컨테이너 내부 접속

docker exec -it mysql8 mysql -uappuser -papppass demodb

 

또는 MySQL 클라이언트로 접속

sudo apt install mysql-client-core-8.0
mysql -h 127.0.0.1 -P 3306 -uappuser -papppass demodb

 

item 테이블 생성 쿼리

CREATE TABLE IF NOT EXISTS item (
    item_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '상품 ID',
    item_name VARCHAR(100) NOT NULL COMMENT '상품명',
    category VARCHAR(50) DEFAULT NULL COMMENT '카테고리',
    price DECIMAL(10,2) NOT NULL COMMENT '가격',
    stock INT DEFAULT 0 COMMENT '재고 수량',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

 

샘플 데이터 삽입 쿼리

INSERT INTO item (item_name, category, price, stock)
VALUES
    ('USB Type-C Cable', 'Electronics', 9.99, 120),
    ('Wireless Mouse', 'Electronics', 24.50, 80),
    ('Mechanical Keyboard', 'Electronics', 79.00, 40),
    ('Notebook A5', 'Stationery', 3.50, 200),
    ('Ballpoint Pen', 'Stationery', 1.20, 500),
    ('Coffee Mug', 'Kitchen', 7.80, 150);

 

확인 쿼리

SELECT * FROM item;

 

init.sql 파일로 만들어서 Docker Compose에서 자동 초기화 설정

MySQL의 Docker 공식 이미지는 /docker-entrypoint-initdb.d/ 경로에 .sql 파일을 넣으면,
컨테이너 최초 실행 시 자동으로 실행됩니다.
이걸 이용해서 init.sql로 테이블과 샘플 데이터를 자동으로 세팅할 수 있습니다.

 

1️⃣ 디렉터리 구조 예시

mysql/
 ├─ compose.yaml
 ├─ data/
 ├─ logs/
 ├─ conf.d/
 │   └─ my.cnf
 └─ init/
     └─ init.sql

 

2️⃣ compose.yaml 수정

init.sql을 마운트하도록 volumes 항목을 수정합니다.

version: "3.9"

services:
  mysql:
    image: mysql:8.4
    container_name: mysql8
    user: "${UID}:${GID}"
    restart: unless-stopped

    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpass}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-demodb}
      MYSQL_USER: ${MYSQL_USER:-appuser}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-apppass}
      TZ: Asia/Seoul

    ports:
      - "3306:3306"

    volumes:
      - ./data:/var/lib/mysql
      - ./conf.d:/etc/mysql/conf.d
      - ./logs:/var/log/mysql
      - ./init:/docker-entrypoint-initdb.d   # 초기화 SQL 파일

    networks:
      - mysql_net

    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  mysql_net:
    name: mysql_network
    driver: bridge

 

3️⃣ init/init.sql 파일 내용

-- 데이터베이스 선택
USE demodb;

-- item 테이블 생성
CREATE TABLE IF NOT EXISTS item (
    item_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '상품 ID',
    item_name VARCHAR(100) NOT NULL COMMENT '상품명',
    category VARCHAR(50) DEFAULT NULL COMMENT '카테고리',
    price DECIMAL(10,2) NOT NULL COMMENT '가격',
    stock INT DEFAULT 0 COMMENT '재고 수량',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 샘플 데이터 삽입
INSERT INTO item (item_name, category, price, stock) VALUES
    ('USB Type-C Cable', 'Electronics', 9.99, 120),
    ('Wireless Mouse', 'Electronics', 24.50, 80),
    ('Mechanical Keyboard', 'Electronics', 79.00, 40),
    ('Notebook A5', 'Stationery', 3.50, 200),
    ('Ballpoint Pen', 'Stationery', 1.20, 500),
    ('Coffee Mug', 'Kitchen', 7.80, 150);

 

 

✅ 이 설정으로 컨테이너가 처음 시작될 때 자동으로 item 테이블 생성 + 데이터 삽입까지 완료됩니다.
다음에 docker compose down / up을 반복해도, 이미 생성된 데이터(./data)는 유지됩니다.

 

운영용 초기 스크립트 세트 구조

MySQL Docker 공식 이미지는 /docker-entrypoint-initdb.d/ 안에 있는 모든 .sql, .sql.gz, .sh 파일을 이름 순서대로 자동 실행합니다.
따라서 파일명을 숫자 접두사로 지정하면 실행 순서를 제어할 수 있습니다.

이 방식은 실제 서비스 환경에서도 데이터베이스 초기화/이관 자동화에 자주 쓰입니다.

 

1️⃣ 디렉터리 구조

mysql/
 ├─ compose.yaml
 ├─ data/
 ├─ logs/
 ├─ conf.d/
 │   └─ my.cnf
 └─ init/
     ├─ 01-schema.sql     # 스키마 정의
     ├─ 02-data.sql       # 초기 데이터
     ├─ 03-indexes.sql    # 인덱스 및 제약조건
     ├─ 04-views.sql      # 뷰 정의
     └─ 05-users.sql      # 추가 사용자/권한 설정

 

2️⃣ compose.yaml

이전과 동일하게 init 디렉터리 전체를 /docker-entrypoint-initdb.d/로 마운트합니다.

version: "3.9"

services:
  mysql:
    image: mysql:8.4
    container_name: mysql8
    user: "${UID}:${GID}"
    restart: unless-stopped

    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpass}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-demodb}
      MYSQL_USER: ${MYSQL_USER:-appuser}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-apppass}
      TZ: Asia/Seoul

    ports:
      - "3306:3306"

    volumes:
      - ./data:/var/lib/mysql
      - ./conf.d:/etc/mysql/conf.d
      - ./logs:/var/log/mysql
      - ./init:/docker-entrypoint-initdb.d   # 모든 초기화 SQL 자동 실행

    networks:
      - mysql_net

    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  mysql_net:
    name: mysql_network
    driver: bridge

 

3️⃣ SQL 파일 예시

01-schema.sql

USE demodb;

CREATE TABLE IF NOT EXISTS item (
    item_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '상품 ID',
    item_name VARCHAR(100) NOT NULL COMMENT '상품명',
    category VARCHAR(50) DEFAULT NULL COMMENT '카테고리',
    price DECIMAL(10,2) NOT NULL COMMENT '가격',
    stock INT DEFAULT 0 COMMENT '재고 수량',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

 

02-data.sql

USE demodb;

INSERT INTO item (item_name, category, price, stock) VALUES
    ('USB Type-C Cable', 'Electronics', 9.99, 120),
    ('Wireless Mouse', 'Electronics', 24.50, 80),
    ('Mechanical Keyboard', 'Electronics', 79.00, 40),
    ('Notebook A5', 'Stationery', 3.50, 200),
    ('Ballpoint Pen', 'Stationery', 1.20, 500),
    ('Coffee Mug', 'Kitchen', 7.80, 150);


03-indexes.sql

USE demodb;

-- 인덱스 생성
CREATE INDEX idx_item_name ON item (item_name);
CREATE INDEX idx_category ON item (category);


04-views.sql

USE demodb;

-- 상품 재고 요약 뷰
CREATE OR REPLACE VIEW view_item_summary AS
SELECT
    category,
    COUNT(*) AS total_items,
    SUM(stock) AS total_stock,
    ROUND(AVG(price), 2) AS avg_price
FROM item
GROUP BY category;


05-users.sql

USE demodb;

-- 읽기 전용 사용자 생성 (운영용 예시)
CREATE USER IF NOT EXISTS 'readonly'@'%' IDENTIFIED BY 'readonlypass';
GRANT SELECT ON demodb.* TO 'readonly'@'%';

-- 권한 적용
FLUSH PRIVILEGES;

 

4️⃣ 실행 절차

  1. 초기화 필요 시 데이터 폴더 정리
  2. docker compose down
  3. UID/GID 설정 후 mysql 설치
  4. 데이터, 인덱스, 뷰, 유저 모두 자동 적용 확인

 

docker exec -it mysql8 mysql -uappuser -papppass demodb -e "SHOW TABLES;"

Tables_in_demodb
item
view_item_summary

 

 

docker exec -it mysql8 mysql -uappuser -papppass demodb -e "SELECT * FROM view_item_summary;"
category total_items total_stock avg_price
Electronics 3 240 37.83
Kitchen 1 150 7.80
Stationery 2 700 2.35

 

파일명 역할
01-schema.sql 테이블 및 스키마 정의
02-data.sql 기본 데이터 삽입
03-indexes.sql 인덱스 및 제약조건 추가
04-views.sql 뷰 정의
05-users.sql 운영용 계정 및 권한 설정

 

이 구조는 실제 운영에서도 DB 초기 설정 및 재현 테스트에 유용하며, 파일별로 변경 추적(Git 관리)도 쉬워집니다.

 

SQL 구조를 CI/CD 파이프라인 (예: GitHub Actions, Jenkins) 에서 자동으로 테스트 및 배포되게 구성하는 YAML 워크플로 구성

지금까지 만든 MySQL 초기화 SQL 세트를 CI/CD 파이프라인에서 자동 테스트 및 배포하도록 아래 두 가지 방법으로 CI/CD Pipeline을 작성해 보겠습니다.


GitHub Actions
Jenkins Pipeline

 

1️⃣ GitHub Actions 워크플로 예시

파일 경로: .github/workflows/mysql-ci.yml

 

이 워크플로는 다음을 수행합니다:

  1. MySQL 8.4 컨테이너를 띄움
  2. init/*.sql 파일을 순서대로 실행
  3. DB 스키마/데이터 테스트 (테이블, 뷰, 유저 확인)
  4. 성공 시 “배포 단계” (예: Docker Hub 푸시 등) 로 확장 가능

 

name: MySQL CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  mysql-ci:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8.4
        env:
          MYSQL_ROOT_PASSWORD: rootpass
          MYSQL_DATABASE: demodb
          MYSQL_USER: appuser
          MYSQL_PASSWORD: apppass
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping -h localhost -prootpass"
          --health-interval=5s
          --health-timeout=2s
          --health-retries=10

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Wait for MySQL to be ready
        run: |
          echo "Waiting for MySQL..."
          for i in {1..20}; do
            if mysql -h 127.0.0.1 -P 3306 -uroot -prootpass -e "SELECT 1;" > /dev/null 2>&1; then
              echo "MySQL is up!"
              break
            fi
            sleep 3
          done

      - name: Run init SQL scripts
        run: |
          for f in $(ls -1 ./mysql/init/*.sql | sort); do
            echo "Running $f"
            mysql -h 127.0.0.1 -P 3306 -uroot -prootpass demodb < "$f"
          done

      - name: Validate schema and data
        run: |
          echo "Checking tables..."
          mysql -h 127.0.0.1 -P 3306 -uroot -prootpass demodb -e "SHOW TABLES;"
          echo "Checking view..."
          mysql -h 127.0.0.1 -P 3306 -uroot -prootpass demodb -e "SELECT * FROM view_item_summary;"
          echo "Checking user..."
          mysql -h 127.0.0.1 -P 3306 -uroot -prootpass -e "SELECT user, host FROM mysql.user WHERE user='readonly';"

      - name: CI Completed
        run: echo "MySQL schema/data validated successfully!"

 

확장  (CD 단계)

  • Docker Image Build + Push
  • - name: Build and Push Docker Image run: | docker build -t yourrepo/mysql-demo:latest . echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin docker push yourrepo/mysql-demo:latest
  • 배포 서버로 전송 (SSH or Kubernetes)
    GitHub Secrets를 사용해서 배포 서버에 자동 반영도 가능합니다.

 

2️⃣ Jenkins Pipeline 예시 (Declarative)

파일: Jenkinsfile

 

이 파이프라인은 Jenkins에서 다음을 수행합니다:

  1. Git 리포지토리에서 소스 체크아웃
  2. Docker Compose로 MySQL 서비스 실행
  3. SQL 스크립트 적용 및 테스트
  4. 성공 시 자동 배포 또는 알림

 

pipeline {
    agent any

    environment {
        MYSQL_ROOT_PASSWORD = 'rootpass'
        MYSQL_DATABASE = 'demodb'
        MYSQL_USER = 'appuser'
        MYSQL_PASSWORD = 'apppass'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build & Init MySQL') {
            steps {
                sh '''
                docker compose -f mysql/docker-config.yaml down
                rm -rf mysql/data/*
                export UID=$(id -u)
                export GID=$(id -g)
                docker compose -f mysql/docker-config.yaml up -d
                '''
            }
        }

        stage('Wait for MySQL') {
            steps {
                sh '''
                echo "Waiting for MySQL to start..."
                for i in {1..20}; do
                  if docker exec mysql8 mysqladmin ping -prootpass --silent; then
                    echo "MySQL is ready!"
                    break
                  fi
                  sleep 3
                done
                '''
            }
        }

        stage('Run SQL Tests') {
            steps {
                sh '''
                docker exec mysql8 mysql -uappuser -papppass demodb -e "SHOW TABLES;"
                docker exec mysql8 mysql -uappuser -papppass demodb -e "SELECT * FROM view_item_summary;"
                docker exec mysql8 mysql -uroot -prootpass -e "SELECT user, host FROM mysql.user WHERE user='readonly';"
                '''
            }
        }

        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                echo 'Deploying to production...'
                // 예시: SSH, Helm, Ansible 등과 연계 가능
            }
        }
    }

    post {
        always {
            sh 'docker compose -f mysql/docker-config.yaml down'
        }
        success {
            echo 'MySQL CI/CD pipeline completed successfully!'
        }
        failure {
            echo 'Build failed!'
        }
    }
}

 

 

이상으로 GitHub Actions과 Jenkins을 이용하여 CI/CD Pipeline 를 작성해 보았습니다.

Posted by freedream