-- 데이터베이스 선택 (사전에 생성된 mydb 사용)
USE test_seokhan;
-- products 테이블을 생성 (이미 존재하면 생략하고 에러 없이 무시함)
CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY, -- 고유 ID: 자동 증가하는 정수형 기본키
name VARCHAR(100), -- 상품 이름: 최대 100자까지 가능한 가변 길이 문자열
price DECIMAL(10, 2) -- 상품 가격: 소수점 포함 숫자 (최대 10자리 중 소수 2자리)
);
-- 1. DB가 없으면 생성
CREATE DATABASE IF NOT EXISTS test_seokhan;
-- 2. 사용자 생성 (존재하면 무시)
CREATE USER IF NOT EXISTS 'seokhan'@'%' IDENTIFIED BY 'k8spass';
-- 3. 권한 부여
GRANT ALL PRIVILEGES ON test_seokhan.* TO 'seokhan'@'%';
FLUSH PRIVILEGES;
-- 4. 테이블 생성
USE test_seokhan;
CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10, 2)
);
FROM bitnami/mariadb:latest
ENV MARIADB_ROOT_PASSWORD=k8spass \
MARIADB_DATABASE=test_seokhan \
MARIADB_USER=seokhan \
MARIADB_PASSWORD=k8spass
# 테이블 생성 SQL 복사
COPY init.sql /docker-entrypoint-initdb.d/
FROM bitnami/mariadb:latest
# 루트 패스워드는 여전히 필수 (MariaDB 실행 시 반드시 필요)
ENV MARIADB_ROOT_PASSWORD=k8spass
# 사용자/DB는 init.sql에서 생성하므로 ENV에서 제거 가능
# ENV MARIADB_DATABASE=test_seokhan
# ENV MARIADB_USER=seokhan
# ENV MARIADB_PASSWORD=k8spass
# 초기 SQL 실행 파일 복사
COPY init.sql /docker-entrypoint-initdb.d/
sudo docker run -d --name test-mariadb \
-p 3306:3306 \
mariadb:test
docker rm -f test-mariadb
docker run -d --name test-mariadb -p 3306:3306 mariadb:test
docker exec -it test-mariadb mariadb -u root -p
# 비밀번호: k8spass
USE test_seokhan;
SHOW TABLES;
DESC products;
#생성한 USER로도 확인
docker exec -it test-mariadb mariadb -u seokhan -p -h 127.0.0.1
SHOW DATABASES;
# 원하는 버전 선택 (예: 3.14.0)
wget https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz
tar -xzvf helm-v3.14.0-linux-amd64.tar.gz
cp linux-amd64/helm .
sudo mv helm /usr/local/bin/
chmod +x /usr/local/bin/helm
helm version
docker run -d -p 5000:5000 --restart=always --name registry registry:2
docker tag mariadb:test3 localhost:5000/mariadb:test3
docker push localhost:5000/mariadb:test3
apiVersion: v1
kind: PersistentVolume
metadata:
name: mariadb-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path
hostPath:
path: /mnt/data/mariadb
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mariadb-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-path
volumeName: mariadb-pv
#외부망에서 헬름 chart 가져오기
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm pull bitnami/mariadb --version 18.0.0 --untar
tar czf mariadb-chart.tgz mariadb/
#내부망 노드에 설치
cp mariadb-chart.tgz /home/k8s-master/
tar xzf /home/k8s-master/mariadb-chart.tgz
helm install my-mariadb /home/k8s-master/mariadb -f values.yaml
#에러시 삭제후 재생성
helm uninstall my-mariadb
helm install my-mariadb /home/k8s-master/mariadb -f values.yaml
#기존 릴리즈 있을 시
helm upgrade my-mariadb /home/k8s-masger/mariadb -f values.yaml
#확인
kubectl get pods -w -l app.kubernetes.io/instance=my-mariadb
# values.yaml (한글 주석 적용)
image:
registry: 192.168.2.76:443 # Harbor 레지스트리 주소 (TLS 포트)
repository: project/mariadb # Harbor 프로젝트/레포지토리 경로
tag: test2 # Harbor에 푸시한 이미지 태그
pullPolicy: IfNotPresent # 로컬에 이미지가 있으면 Pull하지 않음
imagePullSecrets:
- name: harbor-credential # 레지스트리 접속용 시크릿 이름
auth:
rootPassword: k8spass # init.sql과 동일한 루트 비밀번호
database: "" # init.sql이 DB를 생성하므로 빈 값
username: "" # init.sql이 사용자 생성하므로 빈 값
password: "" # init.sql이 사용자 비밀번호 설정
primary:
persistence:
enabled: true
existingClaim: mariadb-pvc # 미리 생성한 PVC 이름
mountPath: /bitnami/mariadb # Bitnami 이미지의 기본 데이터 디렉토리
service:
type: NodePort # NodePort 타입으로 외부 노출 (TCP)
port: 3306 # 클러스터 내부 서비스 포트 (컨테이너 포트)
nodePorts:
mysql: 30036 # 노드에서 사용할 외부 포트 (externalPort)
# Liveness Probe: 컨테이너가 살아 있는지 검사
# HTTP GET 프로브는 MariaDB에는 사용할 수 없습니다 (HTTP 서버 아님)
# 대신 mysqladmin ping 명령을 통해 TCP DB 엔진 응답을 확인합니다
livenessProbe:
enabled: true
initialDelaySeconds: 30 # 시작 후 30초 뒤부터 검사 시작
periodSeconds: 10 # 10초마다 검사
exec:
command:
- mysqladmin
- ping
- -h
- 127.0.0.1
# Readiness Probe: 서비스 준비 상태 검사
# HTTP GET 프로브 대신 Exec 또는 TCP 소켓 프로브 사용
readinessProbe:
enabled: true
initialDelaySeconds: 5 # 시작 후 5초 뒤부터 검사 시작
periodSeconds: 5 # 5초마다 검사
exec:
command:
- mysqladmin
- ping
- -h
- 127.0.0.1
ingress:
enabled: true # Ingress 설정 (HTTP 전용)
# MariaDB는 HTTP 서버가 아니므로 일반 HTTP Ingress로는 DB 접속 불가
hostname: mariadb.local # 사용할 도메인 이름
path: / # 루트 경로로 매핑 (HTTP 요청용)
pathType: ImplementationSpecific
annotations:
# HTTP 세션 유지용 쿠키 설정 예시 (웹용)
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "mariadb-session"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-path: "/"
tls: false # TLS 미사용 (웹용 HTTP)
# ───────────────────────────────────────────────────────────────
# NodePort를 통한 TCP 접속 방법:
# 1) NodePort 설정을 사용하여 외부에서 192.168.2.76:30036 으로 접속
# mysql -h 192.168.2.76 -P 30036 -u your_user -p
# 2) HTTP Ingress는 MariaDB 접속 용도로는 사용하지 않습니다 (웹 트래픽만).
# 3) DB 접속은 NodePort 또는 TCP Ingress 설정을 통해야 합니다.
# 1) Deployment (단일 복제본)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-mariadb
spec:
replicas: 1
selector:
matchLabels:
app: my-mariadb
template:
metadata:
labels:
app: my-mariadb
spec:
imagePullSecrets:
- name: harbor-credential # Harbor 이미지 당김용 시크릿
containers:
- name: mariadb
image: 192.168.2.76:443/project/mariadb:test2
env:
- name: MARIADB_ROOT_PASSWORD
value: "k8spass" # 루트 비밀번호
volumeMounts:
- name: data
mountPath: /bitnami/mariadb # 데이터 저장소
ports:
- containerPort: 3306 # MariaDB 내부 포트
# Liveness Probe: mysqladmin ping으로 컨테이너 상태 확인
livenessProbe:
exec:
#mariadb-admin ping으로 명령어 수정 가능
command: ["mysqladmin","ping","-h","127.0.0.1"]
initialDelaySeconds: 30
periodSeconds: 10
# Readiness Probe: DB가 준비되었는지 확인
#mariadb-admin ping으로 명령어 수정 가능
readinessProbe:
exec:
command: ["mysqladmin","ping","-h","127.0.0.1"]
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: data
persistentVolumeClaim:
claimName: mariadb-pvc # 미리 만든 PVC 사용
---
# 2) Service (NodePort으로 외부 노출)
apiVersion: v1
kind: Service
metadata:
name: my-mariadb
spec:
type: NodePort
selector:
app: my-mariadb
ports:
- name: mysql
port: 3306 # 클러스터 내부 서비스 포트
targetPort: 3306 # 컨테이너 포트
nodePort: 30036 # 외부 접속용 포트
---
# 1) Init 스크립트를 담은 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: mariadb-initdb
data:
init.sql: |
-- 1. DB가 없으면 생성
CREATE DATABASE IF NOT EXISTS test_seokhan;
-- 2. 사용자 생성 및 권한 부여
CREATE USER IF NOT EXISTS 'seokhan'@'%' IDENTIFIED BY 'k8spass';
GRANT ALL PRIVILEGES ON test_seokhan.* TO 'seokhan'@'%';
FLUSH PRIVILEGES;
-- 3. 테이블 생성
USE test_seokhan;
CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10, 2)
);
---
# 2) Deployment (단일 복제본)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-mariadb
spec:
replicas: 1
selector:
matchLabels:
app: my-mariadb
template:
metadata:
labels:
app: my-mariadb
spec:
imagePullSecrets:
- name: harbor-credential # Harbor 이미지 당김용 시크릿
containers:
- name: mariadb
image: 192.168.2.76:443/project/mariadb:test2
env:
- name: MARIADB_ROOT_PASSWORD
value: "k8spass" # 루트 비밀번호
volumeMounts:
- name: data
mountPath: /bitnami/mariadb # 데이터 저장소
- name: initdb
mountPath: /docker-entrypoint-initdb.d # init SQL 실행 경로
ports:
- containerPort: 3306 # MariaDB 내부 포트
# Liveness Probe: mysqladmin ping으로 컨테이너 상태 확인
livenessProbe:
exec:
command: ["mysqladmin","ping","-h","127.0.0.1"]
initialDelaySeconds: 30
periodSeconds: 10
# Readiness Probe: DB가 준비되었는지 확인
readinessProbe:
exec:
command: ["mysqladmin","ping","-h","127.0.0.1"]
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: initdb
configMap:
name: mariadb-initdb
- name: data
persistentVolumeClaim:
claimName: mariadb-pvc # 미리 만든 PVC 사용
---
# 3) Service (NodePort으로 외부 노출)
apiVersion: v1
kind: Service
metadata:
name: my-mariadb
spec:
type: NodePort
selector:
app: my-mariadb
ports:
- name: mysql
port: 3306 # 클러스터 내부 서비스 포트
targetPort: 3306 # 컨테이너 포트
nodePort: 30036 # 외부 접속용 포트
#외부접속
mysql -h <NodeIP> -P 30036 -u seokhan -p
#내부접속
mysql -h my-mariadb.default.svc.cluster.local -P 3306 -u seokhan -p
#같은 NAMESPACE가 아닐때
jdbc:mariadb://192.168.2.76:30036/test_seokhan?user=seokhan&password=k8spass
#같은 네임스페이스 일때
jdbc:mariadb://my-mariadb.default.svc.cluster.local:3306/test_seokhan?user=seokhan&password=k8spass// …/test_seokhan ← 접속할 “데이터베이스” 이름
// products 테이블은 이 DB 안에 미리 생성되어 있으므로,
// URL에는 포함되지 않고 아래처럼 SQL 쿼리에서 사용합니다.
//
// 예시:
// Connection conn = DriverManager.getConnection(url);
// Statement stmt = conn.createStatement();
// ResultSet rs = stmt.executeQuery("SELECT * FROM products");
-----------------------------------------------------------------------
my-mariadb.default.svc.cluster.local
└───────┬──────────┘
Service Namespace
name name
Service 이름: my-mariadb
Namespace 이름: default
예시 : <서비스명>.<네임스페이스>.svc.<클러스터도메인>
svc
“service”의 약자
<서비스명>.<네임스페이스>.svc.<클러스터도메인> 형태로 서비스별 DNS 레코드를 관리
cluster.local
기본 클러스터 도메인 이름
클러스터 내 Pod와 서비스가 서로를 이름으로 찾을 때 사용하는 DNS 영역
패키지: MySql.Data (NuGet)
csharp
CopyEdit
using System;
using MySql.Data.MySqlClient;
class Program
{
static void Main()
{
// 1) 커넥션 스트링 구성
var connStr = new MySqlConnectionStringBuilder
{
Server = "my-mariadb.default.svc.cluster.local", // 서비스 호스트
Port = 3306, // 서비스 포트
Database = "test_seokhan", // 접속할 데이터베이스
UserID = "seokhan", // 사용자
Password = "k8spass", // 비밀번호
SslMode = MySqlSslMode.None // 클러스터 내부라면 SSL 해제
}.ConnectionString;
// 2) 연결 및 쿼리 실행
using var conn = new MySqlConnection(connStr);
conn.Open();
Console.WriteLine("Connected!");
// products 테이블 조회 예시
using var cmd = new MySqlCommand("SELECT id, name, price FROM products;", conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine($"ID={reader.GetInt32(0)}, Name={reader.GetString(1)}, Price={reader.GetDecimal(2)}");
}
}
}
패키지: mysql2
js
CopyEdit
// 1) 모듈 설치 (터미널):
// npm install mysql2
const mysql = require('mysql2');
// 2) 커넥션 풀 또는 싱글 커넥션 생성
const connection = mysql.createConnection({
host: 'my-mariadb.default.svc.cluster.local', // 서비스 호스트
port: 3306, // 서비스 포트
user: 'seokhan', // 사용자
password: 'k8spass', // 비밀번호
database: 'test_seokhan', // 접속할 데이터베이스
ssl: false // 내부 클러스터라면 SSL 옵션 해제
});
// 3) 연결 및 쿼리 실행
connection.connect(err => {
if (err) {
return console.error('Connection error:', err);
}
console.log('Connected!');
// products 테이블 조회 예시
connection.query(
'SELECT id, name, price FROM products',
(err, results) => {
if (err) throw err;
console.log('Products:', results);
connection.end();
}
);
});
1.릴리즈 선택 이유
MariaDB 11.3: 단기 릴리스로, GA 이후 약 1년간만 지원됩니다.
https://mariadb.com/kb/en/mariadb-11-3-0-release-notes/
MariaDB 11.4: 장기 지원(LTS) 릴리스로, 2029년 5월까지 보안 및 버그 수정이 제공됩니다.
https://mariadb.org/11-4-lts/
#릴리즈 노트 사이트
https://mariadb.com/kb/en/mariadb-11-4-0-release-notes/
sys 스키마 개선: 새로운 뷰 sys.privileges_by_table_by_level이 추가되어, 사용자 권한을 테이블 및 수준별로 확인할 수 있습니다.
옵티마이저 향상: 내림차순 인덱스를 활용하여 MIN() 및 MAX() 함수의 성능이 개선되었습니다.
11.4 버전으로 설치한다
docker pull docker.io/library/mariadb:11.4
FROM mariadb:11.4
# 환경변수로 root 계정 및 사용자 계정 설정
#ENV MYSQL_ROOT_PASSWORD=rootpass
#ENV MYSQL_DATABASE=mydb
#ENV MYSQL_USER=myuser
#ENV MYSQL_PASSWORD=mypass
# (선택) 초기화 SQL 넣고 싶을 경우
# COPY ./init.sql /docker-entrypoint-initdb.d/
EXPOSE 3306
#이미 명시되어있어서 안넣어도됨
#ENTRYPOINT ["docker-entrypoint.sh"]
#CMD ["mariadbd"]
vi init.sql
-- 필요한 경우 DB 생성
CREATE DATABASE IF NOT EXISTS mydb;
-- 사용자 생성 (이미 존재하면 무시)
CREATE USER IF NOT EXISTS 'myuser'@'%' IDENTIFIED BY 'mypass';
-- 권한 부여
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'%';
-- 권한 적용
FLUSH PRIVILEGES;
-------------------------------
#도커로 확인
docker build -t my-mariadb .
docker run -d --name mariadb \
-e MYSQL_ROOT_PASSWORD=k8spass \
my-mariadb
docker exec -it mariadb bash
#확인
mariadb -uroot -p
SHOW DATABASES;
mkdir -p my-mariadb/templates
cd my-mariadb
apiVersion: v1
kind: PersistentVolume
metadata:
name: mariadb-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path
hostPath:
path: /mnt/data/mariadb
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mariadb-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-path
volumeName: mariadb-pv
### vi Chart.yaml
# my-mariadb/Chart.yaml
apiVersion: v2
name: mariadb
description: A simple MariaDB chart using DockerHub image
type: application
version: 0.1.0
appVersion: "11.4"
image:
registry: docker.io
repository: library/mariadb
tag: "11.4"
pullPolicy: IfNotPresent
imagePullSecrets: []
auth:
rootPassword: k8spass # root 계정 비밀번호
database: mydatabase # 생성할 초기 DB 이름
username: myuser # 생성할 사용자 이름
password: mypass # 사용자 계정 비밀번호
primary:
persistence:
enabled: true
existingClaim: mariadb-pvc
mountPath: /var/lib/mysql # DB 데이터 디렉토리
logMountPath: /var/log/mysql # MariaDB 로그 디렉토리
service:
type: NodePort
port: 3306
nodePorts:
mysql: 30036
livenessProbe:
exec:
command: ["/bin/bash", "-c", 'mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1"']
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["/bin/bash", "-c", 'mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1"']
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 100m
memory: 128Mi
ingress:
enabled: false
hostname: mariadb.local
path: /
pathType: ImplementationSpecific
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "mariadb-session"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-path: "/"
tls: false
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: {{ .Values.primary.service.type }}
ports:
- port: {{ .Values.primary.service.port }}
targetPort: {{ .Values.primary.service.port }}
nodePort: {{ .Values.primary.service.nodePorts.mysql }}
selector:
app: {{ .Release.Name }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: 1
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: mariadb
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.primary.service.port }}
env:
- name: MYSQL_ROOT_PASSWORD
value: "{{ .Values.auth.rootPassword }}"
- name: MYSQL_DATABASE
value: "{{ .Values.auth.database }}"
- name: MYSQL_USER
value: "{{ .Values.auth.username }}"
- name: MYSQL_PASSWORD
value: "{{ .Values.auth.password }}"
volumeMounts:
- name: mariadb-storage
mountPath: {{ .Values.primary.persistence.mountPath }}
- name: mariadb-logs
mountPath: {{ .Values.primary.persistence.logMountPath }}
livenessProbe:
{{- toYaml .Values.primary.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.primary.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.primary.resources | nindent 12 }}
volumes:
- name: mariadb-storage
persistentVolumeClaim:
claimName: {{ .Values.primary.persistence.existingClaim }}
- name: mariadb-logs
persistentVolumeClaim:
claimName: {{ .Values.primary.persistence.existingClaim }}
#로그 기록 시
my.cnf
[mysqld]
log_error = /var/log/mysql/mariadb.log
general_log = 1
general_log_file = /var/log/mysql/general.log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
#헬름으로 설치
helm install mariadb ./my-mariadb
#헬름 변동시
helm upgrade mariadb ./my-mariadb
#내부에서 확인
kubectl exec -it mariadb-79ff4d84b7-wwqm7 -- bash
#삭제하기
helm uninstall mariadb
kubectl delete pvc mariadb-pvc
kubectl delete pv mariadb-pv
rm -rf /mnt/data/mariadb/* # 실제 경로 초기화 주의!
helm install mariadb ./my-mariadb
kubectl delete deployment mariadb
kubectl delete svc mariadb
helm uninstall mariadb --no-hooks
kubectl delete secret -l "owner=helm,name=mariadb"
#삭제 계속 안될시
kubectl delete deployment mariadb --ignore-not-found
kubectl delete svc mariadb --ignore-not-found
kubectl delete pvc mariadb-pvc --ignore-not-found
kubectl delete pv mariadb-pv --ignore-not-found
kubectl delete secret -l "owner=helm,name=mariadb" --ignore-not-found
helm uninstall mariadb --no-hooks || true