✔️ 데이터와 관련한 인력을 포함시켜야함 : DBA, 데이터 서비스의 백업에 관심 있는 사람
✔️ 데이터와 관련된 다양한 데이터 유형, 도메인 및 경계를 식별해야함 : 데이터베이스 관리자, 스토리지 엔지니어만 사일로 방식(기업 내부에서 발생한 데이터가 각자 분리되어 간극이 생긴 상태) 처리되는 것이 아니라 전체 팀이 더 적합한 조치를 취할 수 있음
✔️ Data Management Body of Knowledge에 따르면 데이터 관리는 "데이터 및 정보 자산의 가치를 제어,보호,제공 및 향상시키는 계획, 정책, 프로그램 및 관행을 개발, 실행 및 감독하는 것"
데이터는 비즈니스의 가장 중요한 측면
데이터 품질은 매우 중요
적시에 적절한 데이터를 액세스해야 함
데이터 관리는 DevOps의 조력자가 되어야 함 : 데이터 관리 요구 사항을 주기에 포함시켜 데이터의 가용성 뿐 아니라 데이터 포인트에 대한 다른 중요 정책 기반 보호와 함께 완전히 테스트된 복구 모델도 포함시켜야함
✔️ DevOps와 DataOps 모두 기술 개발 및 운영의 모범 사례를 적용하여 품질을 개선하고, 속도를 높이고, 보안 위협을 줄이고, 고객을 만족시키고, 숙련된 전문가에게 의미있고 도전적인 업무 제공
✔️ DevOps와 DataOps는 가능한 한 많은 프로세스를 자동화하여 제품 제공을 가속화하는 것을 목표로 함
✔️ DataOps의 목표는 탄력적인 데이터 파이프라인과 데이터 분석을 통한 신뢰할 수 있는 인사이트를 주는 것
✔️ DataOps에 중점을 두는 일반적인 상위 영역은 머신러닝, 빅데이터, 인공지능을 포함한 데이터 분석
✔️ 이 섹션에서는 머신러닝이나 인공지능을 다루지 않고, 데이터 보호 관점에서의 데이터 보호에 초첨
✔️ 데이터와 관련된 핵심 영역
정확성 :프로덕션 데이터가 정확한지 확인, 백업 형태의 데이터도 잘 작동하는지 확인, 복구에 대해 테스트하여 장애나 사유가 발생할 경우 빠르게 가동할 수 있는지
일관성 :데이터 서비스가 여러 위치에 걸쳐 있는 경우, 프로덕션의 모든 데이터 위치에서 일관성을 유지하여 정확한 데이터를 얻을 수 있어야함, 특히 백업과 복제본 등을 위해 데이터의 깨끗한 사본을 생성할 수 있도록 일관성을 보장해야함
보안 : 적절한 사람만 데이터에 액세스할 수 있도록 하는 것이 가장 중요하며, 데이터 보호로 이어져 필요한 사람만 백업에 액세스하고 복원할 수 있도록 해야 함
더 나은 데이터 = 더 나은 의사 결정
✔️ 데이터베이스는 우리 환경에서 가장 흔히 접하는 데이터 서비스
✔️ 올바른 데이터 서비스를 선택하는 것은 애플리케이션 성능과 확장성 측면에서 매우 중요
✔️ 간단한 Key-value 방식을 사용하여 데이터를 저장하는 비관계형 데이터베이스의 한 유형
✔️ Key가 고유 식별자 역할을 하는 key-value 쌍의 모음으로 데이터 저장
✔️ key와 value는 단순한 객체부터 복잡한 객체까지 어떤 형태도 가능
✔️ 고도의 파티셔닝이 가능하며 다른 유형의 데이터베이스에서는 달성할 수 없는 규모로 수평 확장 가능
✔️ Ex. Redis
인메모리 데이터 구조 저장소로, 분산형 인메모리 Key-value 데이터베이스, 캐시 및 메세지 브로커로 사용되며 내구성을 옵션으로 선택할 수 있음
string, list, map, 집합, 정렬된 집합, HyperLogLogs, bitmaps, streams, spartial index 등 다양한 종류의 추상 데이터 구조 지원
✔️ 데이터베이스가 빠르지만 공간에 제약 존재
✔️ 쿼리나 조인이 없어 데이터 모델링 옵션이 매우 제한적
⚡️ Best for: 캐싱, Pub/Sub, 리더보드, 쇼핑 카트 => 일반적으로 다른 영구 데이터 레이어 위에 캐시로 사용됨

✔️ 데이터 저장소를 여러 서버 또는 데이터베이스 노드에 분산할 수 있는 유연한 컬럼으로 구성하고, 다차원 매핑을 사용하여 컬럼, 행, 타임스탬프로 데이터를 참조하는 NoSQL 데이터베이스
✔️ Ex. Cassandra
무료 오픈소스, 분산형, 광역 열 저장소, NoSQL 데이터베이스 관리 시스템
여러 상품 서버에서 대량의 데이터를 처리하도록 설계되어 단일 장애 지점 없이 고가용성 제공
✔️ 스키마가 없어 비정형 데이터를 처리할 수 없지만 일부 워크로드에는 이점으로 작용할 수 있음
⚡️ Best for: 시계열, 과거 기록, 많은 쓰기 및 적은 읽기

✔️ 문서 지향 데이터베이스 또는 문서 저장소라고도 하며, 문서에 정보를 저장하는 데이터베이스
✔️ Ex. MongoDB
소스 사용이 가능한 크로스 플랫폼 문서 지향 데이터베이스 프로그램
NoSQL 데이터베이스 프로그램으로 분류되는 MongoDB는 선택적 스키마와 함께 JSON과 유사한 문서 사용
✔️ NoSQL 문서 데이터베이스를 사용하면 복잡한 SQL 코드를 사용하지 않고도 간단한 데이터 저장 가능
✔️ 안정성 저하 없이 빠르게 저장 가능
⚡️ Best for: 대부분의 애플리케이션, 게임, 사물 인터넷

✔️ 데이터의 관계형 모델에 기반한 디지털 데이터베이스
✔️ 많은 관계형 데이터베이스 시스템에는 데이터베이스를 쿼리하고 유지 관리하기 위해 SQL을 사용할 수 있는 옵션 존재
✔️ Ex. MySQL, Maria DB 등
✔️ ACID(원자성, 일관성, 격리성, 내구성)은 데이터 유효성을 보장하기 위한 데이터베이스 트랜잭션의 일련의 속성
트랜잭션이란 데이터베이스 맥락에서 ACID 속성을 충족하는 일련의 데이터베이스 작업
ex. 한 은행 계좌에서 다른 은행 계좌로 자금을 이체하는 경우, 한 계좌에서 인출하고 다른 계좌에 입금하는 등 여러가지 변경 사항이 포함되더라도 이는 단일 트랜잭션에 해당
⚡️ Best for: 대부분의 애플리케이션에 주로 사용 (최고X 많이 사용되었다는 의미)

✔️ 테이블이나 문서 대신 노드와 관계를 저장
✔️ 데이터는 사전 정의된 모델에 제한되지 않고 저장되므로 매우 유연한 방식으로 데이터를 사고하고 사용 가능
✔️ Ex. Neo4j
⚡️ Best for: 그래프, 지식 그래프, 추천 엔진
✔️ 데이터 콘텐츠 검색 전용으로 사용되는 비관계형 데이터베이스의 한 유형
✔️ 인덱스를 사용해 데이터 간에 유사한 특성을 분류하고 검색 기능을 용이하게 함
✔️ Ex. Elasticsearch
Lucence 라이브러리를 기반으로 하는 검색 엔진
HTTP 웹 인터페이스와 스키마가 없는 JSON 문서를 갖춘 분산형 멀티테넌트 지원 전체 텍스트 검색 엔진 제공
⚡️ Best for: 검색 엔진, Typeahead, 로그 검색
✔️ 단일 통합 백엔드에 대해 여러 데이터 모델을 지원하도록 설계된 데이터베이스 관리 시스템
✔️ 대부분의 데이터베이스 관리 시스템은 데이터를 구성, 저장 및 조작하는 방법을 결정하는 단일 데이터 모델 중심으로 구성
✔️ 문서, 그래프, 관계형, Key-value 모델은 Multi-model 데이터베이스에서 지원할 수 있는 데이터 모델의 예시
✔️ Ex. Fauna
⚡️ Best for: 데이터 모델 선택에 얽매이지 않는 경우, ACID 준수, 빠름, 프로비저닝 오버헤드 없음, 데이터를 어떻게 소비하고 클라우드에 무거운 작업을 맡기고 싶은 경우
✔️ 인프라 장애에 대한 복원력을 확보해야할 뿐 아니라 장애 시나리오가 발생할 경우 안전한 위치에 중요한 데이터의 사본 필요
✔️ 정보 기술에서 백업(또는 데이터 백업)은 데이터 손실이 발생한 후 원본을 복원하는 데 사용할 수 있도록 컴퓨터 데이터의 복사본을 다른 곳에 저장하는 것 또는 이러한 과정
✔️ 데이터를 보호해야 하는 이유

✔️ 데이터의 첫 번째 복사본 또는 백업은 가능한 한 프로덕션 시스템에 가깝게 저장
✔️ 이후 다른 집, 건물, 데이터 센터 또는 퍼블릭 클라우드 등 두 번째 위치로 데이터 사본
✔️ 데이터 복사본 3개 중 2개의 사본을 서로 다른 media type에 로컬로 저장하고, 나머지 1개는 offsite에 원격으로 저장되어야함
✔️ 일부 데이터를 보호하는 시나리오: 로컬 컴퓨터에서 일부 파일 보호
✔️ 집에 로컬로 있는 NAS (저장)장치뿐만 아니라 클라우드의 Object Storage 버킷에도 보호되도록 하고 싶음
✔️ 백업을 암호화, 중복 제거 및 압축하는 동시에 여러 위치로 백업을 보낼 수 있는 오픈 소스 백업 도구인 Kopia(KopiaUI-Setup-0.10.6.exe) 도구 사용

✔️ https://github.com/kopia/kopia/releases 에서 KopiaUI-Setup-0.10.6.exe다운로드

✔️ 로컬 NAS 네트워크 설정


네트워크 연결이 안되는오류발생
참고해서 해결
https://kb.synology.com/ko-kr/DSM/help/DSM/Tutorial/store_with_windows?version=6
https://kb.synology.com/ko-kr/DSM/tutorial/How_to_map_shared_folder_Windows_PC
https://kb.synology.com/ko-kr/DSM/tutorial/What_can_I_do_if_my_Synology_NAS_does_not_show_up_in_Network_in_Windows_File_Explorer
✔️ 로컬 NAS 장치를 사용해 레포지토리 설정하고 SMB를 사용하여 작업 수행 (NFS 사용할 수도 있음)
NAS IP 주소 설정
Kopia 메인에서 Filesystem 선택 > Directory Path \\192.168.169.109\90daysofdevops 입력

🗝️ NAS(Network Attached Storage): 중앙 집중식 파일 서버로, Wi-Fi 또는 이더넷 케이블을 통해 TCP/IP 네트워크에서 파일을 저장하고 공유할 수 있음
🗝️ NFS & SMB: 원격 파일 공유 프로토콜 서버로, 클라이언트 컴퓨터들이 원격 서버에 있는 파일에 접근하고 로컬 파일처럼 읽고 쓸 수 있게 됨.
리눅스-리눅스의 경우 NFS이고,윈도우-윈도우 또는 리눅스-윈도우의 경우 SMB방식 사용

✔️ 비밀번호 정의
해당 비밀번호는 레포지토리 콘텐츠를 암호화하는데 사용됨
레파지토리 구성 완료
✔️ 임시 스냅샷을 트리거하여 레포지토리에 데이터 쓰기 가능
✔️ 스냅샷할 대상의 경로를 입력해 90DaysOfDevOps 폴더의 복사본 생성
✔️ 스냅샷 보존 정의 가능
✔️ 제외하려는 파일이나 파일 형식이 있을 경우 제외
✔️ scheduling을 포함한 다양한 설정을 원하는 경우 정의 가능
✔️ 스냅샷을 선택하면 데이터가 레포지토리에 기록
⚡️ 로컬 및 오프사이트의 복사본을 오브젝트 스토리지에 저장하는 것이 목표
✔️ 데이터 전송을 위해 오브젝트 스토리지로 Google Cloud Storage 선택
✔️ 레포지토리에 대한 구성 교체: 새 레포지토리 추가(생성)
90daysofdevops"C:\Program Files\KopiaUI\resources\server\kopia.exe" --config-file=C:\Users\micha\AppData\Roaming\kopia\repository.config repository create gcs --bucket 90daysofdevops
✔️ 스냅샷을 생성하여 새로 생성한 레포지토리로 전송
"C:\Program Files\KopiaUI\resources\server\kopia.exe" --config-file=C:\Users\micha\AppData\Roaming\kopia\repository.config kopia snapshot create "C:\Users\micha\demo\90DaysOfDevOps"
✔️ 구글 클라우드 스토리지에서 90daysofdevops 저장 확인 가능
✔️ 새로운 위치로 사본 복원
"C:\Program Files\KopiaUI\resources\server\kopia.exe" --config-file=C:\Users\micha\AppData\Roaming\kopia\repository.config snapshot list
"C:\Program Files\KopiaUI\resources\server\kopia.exe" --config-file=C:\Users\micha\AppData\Roaming\kopia\repository.config mount all Z:
또는
kopia snapshot restore kdbd9dff738996cfe7bcf99b45314e193
⚡️ 지난 섹션까지는 로컬 NSA와 클라우드 기반 오브젝트 스토리지(GCS)에 데이터 백업(Kopia 사용)
✔️ Minikube 클러스터와 몇 가지 애드온 활용
volumesnapshots와 csi-hostpath-driver 사용minikube start --addons volumesnapshots,csi-hostpath-driver --apiserver-port=6443 --container-runtime=containerd -p 90daysofdevops --kubernetes-version=1.21.2
✔️ volumesnapshotsclass 리소스에 Kasten K10 annotation 추가
kubectl annotate volumesnapshotclass csi-hostpath-snapclass \
k10.kasten.io/is-snapshot-class=true
✔️ 기본 저장 클래스를 hostpath로 변경
kubectl patch storageclass csi-hostpath-sc -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
✔️ Kasten Helm 레포지토리 추가
helm repo add kasten https://charts.kasten.io/
✔️ 네임스페이스를 생성하고 K10 배포
helm install k10 kasten/k10 --namespace=kasten-io --set auth.tokenAuth.enabled=true --set injectKanisterSidecar.enabled=true --set-string injectKanisterSidecar.namespaceSelector.matchLabels.k10/injectKanisterSidecar=true --create-namespace
✔️ pod 생성 확인
kubectl get pods =n kasten-io -w
✔️ 포트포워딩을 통해 K10 대시보드 접속 후 새 터미널에 아래 명령어 실행
kubectl --namespace kasten-io port-forward service/gateway 8080:8000
http://127.0.0.1:8080/k10/#/ 입력✔️ 대시보드 인증을 위해 토큰 정보 확인
TOKEN_NAME=$(kubectl get secret --namespace kasten-io|grep k10-k10-token | cut -d " " -f 1)
TOKEN=$(kubectl get secret --namespace kasten-io $TOKEN_NAME -o jsonpath="{.data.token}" | base64 --decode)
echo "Token value: "
echo $TOKEN
✔️ 브라우저 접속
✔️ Kubernetes 섹션에서 사용한 stateful 애플리케이션 사용
kubectl get all -n pacman 명령어로 pacman 네임스페이스에 존재하는 모든 pod 확인
kubectl port-forward svc/pacman 9090:80 -n pacman 명령어로 프론트엔드를 포트포워딩
브라우저에 http://localhost:9090으로 접속
해당 게임을 실행하여 점수 기록
⚡️ 데이터베이스의 데이터를 포함하여 전체 애플리케이션 보호 목표
✔️ Kasten K10 대시보드에 Kubernetes 클러스터에 이전에 생성한 pacman 애플리케이션 추가
✔️ 클러스터에 수동 스토리지 스냅샷을 생성한 다음 고득점 데이터에 불량 데이터를 추가하는 실수 상황 시뮬레이션
수동 스냅샷 옵션 사용
대시보드로 돌아가면 실행중인 작업에 대한 상태 보고서가 표시되며 완료되면 성공적으로 표시
✔️ 규범적인 잘못된 변경 추가 > 미션 크리티컬 데이터에 치명적인 변경 수행 가능
✔️ 대시보드에 복원할 수 있는 복원 지점 확인 가능
✔️ resotre(복원)을 선택하여 해당 애플리케이션에 연결된 모든 스냅샷과 내보내기 조회 가능
✔️ 복원을 선택하면 사이드 창이 나타나며 기본 설정을 유지하고 복원 선택
✔️ 복원할 것인지 확인
✔️ 대시보드로 돌아가서 복원 진행 상황 확인 가능
✔️ pacman으로 포트포워딩을 다시해 정상적으로 복원
⚡️ 데이터 서비스의 경우 애플리케이션 일관성과 관련하여 일관성을 관리하는 방법 고려해야함. 애플리케이션 데이터를 일관되게 보호하는 것과 관련된 요구 사항에 대해 살펴볼 것
✔️ Kanister는 Kasten이 만든 오픈소스 프로젝트로, Kubernetes에서 애플리케이션 데이터를 관리(백업 및 복원)할 수 있게 해줌
✔️ Kanister를 Helm 애플리케이션으로 Kubernetes 클러스터에 배포 가능
✔️ Kanister는 Kubernetes 커스텀 리소스를 사용하며, Kanister 배포시 설치되는 주로 커스텀 리소스를 아래와 같음
Profile: 백업을 저장하고 복구할 대상의 위치. 일반적으로 오브젝트 스토리지
Bluprint: 데이터베이스를 백업 및 복구하기 위해 수행해야하는 단계가 blueprint에 유지되어야 함
ActionSet: 대상 백업을 Profile로 이동하고 복원 작업을 수행하는 동작
⚡️ Kanister를 사용해 애플리케이션 데이터를 보호하기 위해 취하는 워크플로우
✔️ Helm을 사용해 Controller를 Kubernetes 클러스터에 배포 > Kanister를 해당 네임스페이스 내에 위치시킴(Blueprint 사용 가능) > Database Workload

✔️ ActionSet 생성

✔️ ActionSet을 사용해 특정 데이터 서비스에 대해 Blueprint에 정의된 액션 실행

✔️ ActionSet은 차례로 Kanister 함수(KubeExec, KubeTask, 리소스 라이프사이클)를 사용해 백업을 대상 레포지토리(Profile)로 push

✔️ 해당 작업이 완료 또는 실패하면 해당 상태가 ActionSet에 업데이트

⚡️ Minikube 클러스터를 사용해 애플리케이션 백업 수행
✔️ 아래 Helm 명령으로 Kubernetes 클러스터에 kanister 이미지 버전 0.75.0 설치
helm install kanister --namespace kanister kanister/kanister-operator --set image.tag=0.75.0 --create-namespace
✔️ kubectl get pods -n kanister 명령어를 사용해 kanister 네임스페이스의 pod 실행 확인
✔️ kubectl get customresourcedefinitions.apiextensions.k8s.io로 쿠버네티스 내 사용자 정의 리소스를 정의하기 위해 사용되는 확장 기능 조회
✔️ Helm을 통해 MySQL 배포
APP_NAME=my-production-app
kubectl create ns ${APP_NAME}
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install mysql-store bitnami/mysql --set primary.persistence.size=1Gi,volumePermissions.enabled=true --namespace=${APP_NAME}
kubectl get pods -n ${APP_NAME} -w

✔️ 초기 데이터로 MySQL 데이터베이스를 채우고 아래 명령어 실행
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace ${APP_NAME} mysql-store -o jsonpath="{.data.mysql-root-password}" | base64 --decode)
MYSQL_HOST=mysql-store.${APP_NAME}.svc.cluster.local
MYSQL_EXEC="mysql -h ${MYSQL_HOST} -u root --password=${MYSQL_ROOT_PASSWORD} -DmyImportantData -t"
echo MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
✔️ 클라이언트 역할을 할 다른 컨테이너 이미지 실행
APP_NAME=my-production-app
kubectl run mysql-client --rm --env APP_NS=${APP_NAME} --env MYSQL_EXEC="${MYSQL_EXEC}" --env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} --env MYSQL_HOST=${MYSQL_HOST} --namespace ${APP_NAME} --tty -i --restart='Never' --image docker.io/bitnami/mysql:latest --command -- bash
💫 만약 기존 MySQL 클라이언트 pod가 실행 중인 경우 아래 명령어로 삭제 후 진행
kubectl delete pod -n ${APP_NAME} mysql-client
✔️ 데이터 추가
echo "create database myImportantData;" | mysql -h ${MYSQL_HOST} -u root --password=${MYSQL_ROOT_PASSWORD}
MYSQL_EXEC="mysql -h ${MYSQL_HOST} -u root --password=${MYSQL_ROOT_PASSWORD} -DmyImportantData -t"
echo "drop table Accounts" | ${MYSQL_EXEC}
echo "create table if not exists Accounts(name text, balance integer); insert into Accounts values('nick', 0);" | ${MYSQL_EXEC}
echo "insert into Accounts values('albert', 112);" | ${MYSQL_EXEC}
echo "insert into Accounts values('alfred', 358);" | ${MYSQL_EXEC}
echo "insert into Accounts values('beatrice', 1321);" | ${MYSQL_EXEC}
echo "insert into Accounts values('bartholomew', 34);" | ${MYSQL_EXEC}
echo "insert into Accounts values('edward', 5589);" | ${MYSQL_EXEC}
echo "insert into Accounts values('edwin', 144);" | ${MYSQL_EXEC}
echo "insert into Accounts values('edwina', 233);" | ${MYSQL_EXEC}
echo "insert into Accounts values('rastapopoulos', 377);" | ${MYSQL_EXEC}
echo "select * from Accounts;" | ${MYSQL_EXEC}
exit
✔️ 데이터 조회

✔️ Kanister는 Blueprint와 두 유틸리티를 통해 오브젝트 스토리지 공급자와 상호작용할 수 있는 CLI인 kanctl과 또 다른 유틸리티인 kando 제공
✔️ Profile 대상과 복원 위치로 사용할 AWS S3 버킷 생성
✔️ 아래 코드를 통해 kanctl과 환경 변수를 사용해 kanister Profile 생성
kanctl create profile s3compliant --access-key $ACCESS_KEY --secret-key $SECRET_KEY --bucket $BUCKET --region eu-west-2 --namespace my-production-app

✔️ 사용할 Blueprint yaml 파일
apiVersion: cr.kanister.io/v1alpha1
kind: Blueprint
metadata:
name: mysql-blueprint
actions:
backup:
outputArtifacts:
mysqlCloudDump:
keyValue:
s3path: "{{ .Phases.dumpToObjectStore.Output.s3path }}"
phases:
- func: KubeTask
name: dumpToObjectStore
objects:
mysqlSecret:
kind: Secret
name: '{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}'
namespace: '{{ .StatefulSet.Namespace }}'
args:
image: ghcr.io/kanisterio/mysql-sidecar:0.75.0
namespace: "{{ .StatefulSet.Namespace }}"
command:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
s3_path="/mysql-backups/{{ .StatefulSet.Namespace }}/{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}/{{ toDate "2006-01-02T15:04:05.999999999Z07:00" .Time | date "2006-01-02T15-04-05" }}/dump.sql.gz"
root_password="{{ index .Phases.dumpToObjectStore.Secrets.mysqlSecret.Data "mysql-root-password" | toString }}"
mysqldump --column-statistics=0 -u root --password=${root_password} -h {{ index .Object.metadata.labels "app.kubernetes.io/instance" }} --single-transaction --all-databases | gzip - | kando location push --profile '{{ toJson .Profile }}' --path ${s3_path} -
kando output s3path ${s3_path}
restore:
inputArtifactNames:
- mysqlCloudDump
phases:
- func: KubeTask
name: restoreFromBlobStore
objects:
mysqlSecret:
kind: Secret
name: '{{ index .Object.metadata.labels "app.kubernetes.io/instance" }}'
namespace: '{{ .StatefulSet.Namespace }}'
args:
image: ghcr.io/kanisterio/mysql-sidecar:0.75.0
namespace: "{{ .StatefulSet.Namespace }}"
command:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
s3_path="{{ .ArtifactsIn.mysqlCloudDump.KeyValue.s3path }}"
root_password="{{ index .Phases.restoreFromBlobStore.Secrets.mysqlSecret.Data "mysql-root-password" | toString }}"
kando location pull --profile '{{ toJson .Profile }}' --path ${s3_path} - | gunzip | mysql -u root --password=${root_password} -h {{ index .Object.metadata.labels "app.kubernetes.io/instance" }}
delete:
inputArtifactNames:
- mysqlCloudDump
phases:
- func: KubeTask
name: deleteFromBlobStore
args:
image: ghcr.io/kanisterio/mysql-sidecar:0.75.0
namespace: "{{ .Namespace.Name }}"
command:
- bash
- -o
- errexit
- -o
- pipefail
- -c
- |
s3_path="{{ .ArtifactsIn.mysqlCloudDump.KeyValue.s3path }}"
kando location delete --profile '{{ toJson .Profile }}' --path ${s3_path}
✔️ kubectl create -f mysql-blueprint.yml -n kanister 명령으로 해당 리소스 생성
⚡️ 애플리케이션의 백업을 정의하는 ActionSet을 사용해 MySQL 데이터 백업 수행
✔️ 컨트롤러와 동일한 네임스페이스에 ActionSet 생성
✔️ kubectl get porfiles.cr.kanister.io -n my-production-app 명령으로 이전에 생성한 Profile 표시. 여기에 여러 Profile을 구성할 수 있으므로 다른 ActionSet에 특정 Profile 사용 가능
✔️ kanctl을 사용해 ActionSet 생성
my-production-app 네임스페이스의 statefulset, MySQL 애플리케이션에 들어가기 위한 secret 정의kanctl create actionset --action backup --namespace kanister --blueprint mysql-blueprint --statefulset my-production-app/mysql-store --profile my-production-app/s3-profile-dc5zm --secrets mysql=my-production-app/mysql-store
✔️ kubectl --namespace kanister describe actionset backup-qpnqv 명령어로 ActionSet의 상태 확인
✔️ AWS S3 버킷에 데이터가 있는지 확인 가능
⚡️ 복원하기 전 약간의 손상을 입혀야 함. ex) 테이블 drop,
✔️ MySQL pod에 연결
APP_NAME=my-production-app
kubectl run mysql-client --rm --env APP_NS=${APP_NAME} --env MYSQL_EXEC="${MYSQL_EXEC}" --env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} --env MYSQL_HOST=${MYSQL_HOST} --namespace ${APP_NAME} --tty -i --restart='Never' --image docker.io/bitnami/mysql:latest --command -- bash
✔️ 중요 데이터 DB는 echo "SHOW DATABASES;" | ${mysql_exec}로 조회 가능
✔️ drop 하기 위해 echo "DROP DATABASE myImportantData;" | ${mysql_exec}
⚡️ 복원
✔️ kubectl get actionset -n kanister 명령어로 앞서 가져온 ActionSet 이름을 찾아 중요한 데이터 다시 가져올 수 있음
✔️ 데이터 복원을 위한 복원 ActionSet 생성
kanctl create actionset -n kanister --action restore --from "backup-qpnqv"
✔️ 아래 명령어를 사용해 데이터베이스에 연결하면 데이터가 복구되어있는지 확인
APP_NAME=my-production-app
kubectl run mysql-client --rm --env APP_NS=${APP_NAME} --env MYSQL_EXEC="${MYSQL_EXEC}" --env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} --env MYSQL_HOST=${MYSQL_HOST} --namespace ${APP_NAME} --tty -i --restart='Never' --image docker.io/bitnami/mysql:latest --command -- bash
✔️ MySQL 클라이언트에서 echo "SHOW DATABASES;" | ${MYSQL_EXEC} 명령어를 실행해 데이터베이스 복원 확인 가능
"select * from Accounts;" | ${MYSQL_EXEC} 명령어를 실행하여 데이터베이스의 내용 확인 가능하며 데이터 복원 완료
⚡️ 재해 발생 시 가능한 한 빨리 가동하거나 최소한 복구 시간 목표를 거의 0에 가깝게 설정해야함
⚡️ 전체 애플리케이션 스택을 standby 환경으로 복제하는 작업을 자동화할 때 대규모로 달성 가능 > 클라우드 리전, 클라우드 제공업체 간 또는 온프레미스와 클라우드 인프라 간의 빠른 페일오버 수행 가능
⚡️ Kasten K10은 실행 중인 Kubernetes 클러스터에 문제가 발생하는 경우 카탈로그 데이터를 복제하여 새 클러스터에서 사용할 수 있도록 하는 기능을 내장하고 있음
⚡️ 백업을 위한 대상 위치로 오브젝트 스토리지 버킷 추가
⚡️ 오프사이트 위치 역할을 할 뿐 아니라 복구할 재해 복구 소스 데이터로도 활용 가능
✔️ 지난 세션에서 Kanister 데모를 위해 생성한 S3 버킷 사용
✔️ 포트 포워딩을 통해 K10 대시보드에 접속하고 새 터미널을 열어 포트 포워딩
kubectl --namespace kasten-io port-forward service/gateway 8080:8000
✔️ Kasten 대시보드 인증
브라우저에 http://127.0.0.1:8080/k10/#/ 입력
대시보드로 인증하려면 토큰 필요
TOKEN_NAME=$(kubectl get secret --namespace kasten-io|grep k10-k10-token | cut -d " " -f 1)
TOKEN=$(kubectl get secret --namespace kasten-io $TOKEN_NAME -o jsonpath="{.data.token}" | base64 --decode)
echo "Token value: "
echo $TOKEN
위 토큰을 가져와 브라우저에 이메일과 회사 이름 입력
Kasten K10 대시보드 액세스 가능
✔️ Profile 생성
대시보드에서 location profile을 추가하고 페이지 상단의 Settings>New Profile 선택
location profile의 위치를 선택할 수 있으며 Amazon S3 선택 후 AWS Access Key, AWS Secret, Region, Bucket 설정
"Enable Immutable Backups" 선택해 생성
⚡️ 이전 세션에서는 pacman 애플리케이션의 임시 스냅샷만 만들었으므로 애플리케이션 백업을 새로 생성한 오브젝트 스토리지 위치로 전송하는 백업 정책 만들어야함
✔️ 대시보드에서 Policies 선택해 "Create New Policy" 클릭
정책의 Name과 Comments 지정
Action은 Snapshot으로 설정
Backup Frequency는 On Demand로 설정
Enable Backups via Snapshot Exports를 활성화시켜 데이터를 lcoation profile로 export. location profile이 여러 개 있는 경우 백업을 전송할 위치 선택 가능
Select Application은 By Name, Select Application Resources는 All Resources로 설정
Create Policy로 정책 생성 완료
✔️ 생성된 정책의 맨 아래 Show import details는 standby 클러스터로 import 하기 위해 필요한 문자열로 안전한 곳에 복사할 것
✔️ 데이터 백업 및 export 성공적으로 완료한 경우

✔️ 새 클러스터 생성
minikube start --addons volumesnapshots,csi-hostpath-driver --apiserver-port=6443 --container-runtime=containerd -p standby --kubernetes-version=1.21.2
✔️ 클러스터에 Kasten K10 배포
helm install k10 kasten/k10 --namespace=kasten-io --set auth.tokenAuth.enabled=true --set injectKanisterSidecar.enabled=true --set-string injectKanisterSidecar.namespaceSelector.matchLabels.k10/injectKanisterSidecar=true --create-namespace
✔️ Minikube를 사용하고 있기에 import 정책을 실행할 때 애플리케이션이 실행되며, 이 standby 클러스터에서 스토리지 클래스가 동일하다는 점이 중요 => 이동성과 변환
✔️ 포트포워딩 및 대시보드 액세스
kubectl --namespace kasten-io port-forward service/gateway 8080:8000 명령어로 포트포워딩
브라우저에 http://127.0.0.1:8080/k10/#/를 입력해 접근
대시보드 인증을 위한 토큰 생성
TOKEN_NAME=$(kubectl get secret --namespace kasten-io|grep k10-k10-token | cut -d " " -f 1)
TOKEN=$(kubectl get secret --namespace kasten-io $TOKEN_NAME -o jsonpath="{.data.token}" | base64 --decode)
echo "Token value: "
echo $TOKEN
⚡️ standby 클러스터에서 import 정책을 생성하고 개체 스토리지 백업에 연결해 어떤 모양과 방식으로 가져올지 결정 가능
✔️ K10 대시보드에서 location profile 추가 후 다크 모드를 표시하여 production system과 DR standby location의 차이 나타냄
✔️ 대시보드에서 Policy 정책탭으로 들어가 새로운 policy 생성
✔️ New Policy를 통해 새로운 import 정책 생성
policy 생성 시 "Restore After Import" 설정이 있어 import 후 복원을 원하는 경우 standby 클러스터로 복원됨 > 다음 세션에서 다룰 것
위에서 설정한대로 동일하게 설정
✔️ 대시보드로 돌아가 Applications 탭으로 이동하면 "Removed" 아래에서 restore 클릭으로 복원 가능
복원 지점을 확인할 수 있으며 이것은 기본 클러스터에서 pacman 애플리케이션에 대해 실행한 백업 작업
다음 세션에서 다루기 위해 기본값 변경하지 않고 복원
✔️ kubectl config get-contexts 명령어를 통해 standby cluster를 확인할 수 있으며, kubectl get pods -n pacman 명령어를 통해 실행 중인 애플리케이션이 존재 확인 가능
✔️ kubectl port-forward svc/pacman 9090:80 -n pacman 명령어로 포트 포워딩
⚡️ 데이터와 애플리케이션의 이동성: 워크로드, 애플리케이션 및 데이터의 위치 이동
⚡️ 워크로드를 가지고 한 클러스터에서 다른 클러스터로 Kubernetes 워크로드 이동 방식 살펴볼 것
✔️ 현재 Kubernetes 클러스터로는 수요를 감당할 수 없고 비용이 비싸기 때문에 프로덕션 Kubernetes 클러스터를 다른 퍼블릭 클라우드에 위치한 재해 복구 위치로 이전하여 확장 기능을 제공하면서 더욱 저렴한 요금으로 운영할 수 있도록
✔️ 현재 Pacman 애플리케이션에 MongoDB가 있고 느린 스토리지에서 실행되고 있는데, 더 빠른 스토리지 계층으로 옮기고 싶음
✔️ 새로운 위치에서 사용 가능한 pod의 수를 늘리고 싶음
✔️ 실제로는 재해 백업 Kubernetes 클러스터에 임포트한 상태
✔️ kubectl delete ns pacman 명령어를 통해 지난 세션에서 수행한 백업 작업 제거
✔️ Kasten K10 대시보드의 Application > Removed 선택
사용 가능한 복원 지점 목록 표시
미션 크리티컬 데이터가 포함되어 있으므로 사용 가능한 지점 선택
✔️ 재해 복구 프로세스 작업시 모든 것을 기본값으로 두었는데, 애플리케이션을 변환해야하는 재해 복구 프로세스가 있는 경우 추가 복원 옵션 사용 가능 : 스토리지와 복제본 수 변경
"Apply transforms to restored resources" 옵션 선택
"Add New Transform" 선택
Reset Form의 두가지 기본 제공 예제가 요구 사항에 필요
첫번째(Change StorageClass)는 기본 클러스터에서는 csi-hostpath-sc라는 스토리지 클래스를 사용하지만, 새 클러스터에서는 standard를 사용하고자 하는 것
두번째(Scale Deployment)는 pacman 프론트엔드 배포를 5로 확장하는 것
위 방식으로 두 가지 transform 확인 가능 > Restore 버튼 클릭
✔️ 터미널로 돌아가 클러스터를 살펴보면 pacman pod의 개수가 5이고 스토리지 클래스가 standard임을 확인할 수 있음
✔️ API를 활용하여 작업 중 일부를 자동화하는 옵션 존재 => UI 전체에 걸쳐 일부 이동 경로가 자동화에 API를 활용할 수 있는 명령 집합 제공
✔️ Kasten K10에서 주목할 점은 배포시 Kubernetes 클러스터 내부에 배포된 다음 Kubernetes API를 통해 호출할 수 있다는 것
✔️ DevSecOps란 개발, 보안 및 운영 팀을 하나로 모아 안전한 소프트웨어 애플리케이션을 구축하고 유지 관리하는 것을 목표로 하는 소프트웨어 개발 접근 방식
✔️ 소프트웨어 업데이트와 기능을 더 빠르고 자주 제공하는 것을 목표로하는 지속적인 통합, 지속적인 제공 및 지속적인 배포가 원칙
✔️ 보안이 처음부터 소프트웨어 개발 수명 주기(SDLC)에 내장되어 있음
✔️ DevOps : 소프트웨어 릴리스의 속도, 안정성 및 품질 향상을 목표로 함
✔️ DevSecOps : 처음부터 소프트웨어 개발 프로세스에 보안 조치 구축

✔️ 자동화된 보안이란 사람의 개입 없이 보안 작업을 수행하는 기술을 사용하는 것 => 보안 프로세스를 보다 효율적이고 효과적으로 만들고 보안 인력의 업무량을 줄여줌
✔️ 컨테이너화와 마이크로서비스를 통해 구현된 규모와 동적 인프라가 등장하면서 정적 보안 정책만 가질 수는 없음 => 자동화된 보안 필요
✔️ 환경 및 데이터 보안
환경 표준화 및 자동화 : 각 서비스에는 무단 연결 및 액세스를 최소화할 수 있는 최소한의 권한이 존재해야함
사용자 ID 및 액세스 제어 기능 중앙 집중화 : 인증은 여러 지점에서 시작되므로 마이크로 서비스 보안을 위해서는 엄격한 액세스 제어 및 중앙 집중식 인증 메커니즘이 필요
마이크로서비스를 실행하는 컨테이너를 서로 및 네트워크로부터 격리 : 전송 중인 데이터와 미사용 데이터 모두 포함
앱과 서비스 간 데이터 암호화 : 통합 보안 기능을 갖춘 컨테이너 오케스트레이션 플랫폼은 무단 액세스 가능성을 최소화하는데 도움이 됨
보안 API 게이트웨이 도입 : 보안 API는 승인 및 라우팅 가시성을 높이며, 노출되는 API를 줄임으로써 외부로부터의 공격을 줄일 수 있음
✔️ 사이버 보안은 디지털 공격, 도난, 손상으로부터 컴퓨터 시스템과 네트워크를 보호하는 관행
✔️ DevSecOps는 개발, 보안 및 운영 방식의 조합
✔️ 사이버 보안과 DevSecOps의 차이점
초점: 사이버 보안은 주로 외부 위협으로부터 시스템을 보호하는 데 중점을 두는 반면, DevSecOps는 보안을 개발 프로세스에 통합하는 데 중점
범위: 사이버 보안은 네트워크 보안, 데이터 보안, 애플리케이션 보안 등을 포함하여 광범위한 반면, DevSecOps는 그 중 소프트웨어 개발 및 배포의 보안을 향상시키는 데 중점
접근 방식: 사이버 보안은 일반적으로 개발 프로세스가 완료된 후 보안 조치를 구현하는 작업을 포함하는 반면, DevSecOps는 처음부터 보안을 개발 프로세스에 통합하는 작업
협업: 사이버 보안에는 IT 팀과 보안팀 간의 협업이 포함되지만, DevSecOps는 개발, 보안 및 운영 팀 간의 협업이 포함됨
✔️ Attacker의 모든 공격이 동일하지 않고 동기도 동일하지 않지만, 데이터를 공격할 가능성이 높음
✔️ Attack Maps는 컴퓨터 네트워크에 대한 공격을 시각적으로 표현한 것으로, 공격의 단계, 공격자가 사용하는 도구 및 기술, 네트워크 진입 및 퇴출 지점을 시각화 => 공격의 세부 정보를 분석하고 취약점을 식별할 수 있어 향후 공격에 대한 방어 계획을 세울 수 있음
✔️ 홈 네트워크 또는 비즈니스에 대한 Attack Maps 생성
사용 중인 모든 통신 흐름과 기술을 포함하여 앱의 그래픽 표현을 나타냄
잠재적인 취약점 및 공격 영역 목록
앱 내 각 연결/상호작용에 대한 기밀성, 무결성 및 가용성 고려
공격/취약점 매핑
✔️ 아래 맵을 통해 애플리케이션이 데이터를 저장하거나 잘못된 데이터를 저장하는 것을 방지하기 위해 서비스 거부 또는 일부 악의적인 내부 공격과 S3 버킷에 대한 액세스가 존재한다고 고려
✔️ 아래 Attack Maps를 테스트하고 피드백을 제공하여 보안이 강화되도록 해야 함 => Continuous Response(지속적 응답)
✔️ 보안 상태 개선
양호 : 공격을 줄이기 위해 소프트웨어에 내장되어야 하는 보안 설계 제약 조건과 제어 식별
더 좋음 : 소프트웨어 주기 후반에 발견되는 문제에 대한 우선순위를 지정하고 보안 구축
최고 : 문제 감지, 단위 테스트, 보안 테스트, 블랙박스 테스트를 위한 스크립트 배포에 자동화 구축

✔️ Red Team은 공격자의 역할, Blue Team은 방어자의 역할을 하여 조직의 보안 강화
Red Team: 코드나 인프라의 취약점을 찾고 사이버 보안 방어를 돌파하려고 시도
Blue Team: 이러한 공격을 방어하고 사고 발생 시 대응
✔️ 이점
취약점 확인
네트워크 보안 강화
공격 탐지 및 격리 경험 쌓기
상세한 대응 계획 수립
회사 전반의 보안 인식 제고
✔️ Red Team
NIST "잠재적인 적의 공격이나 기업의 보안 태세에 대한 악용 능력을 모방하도록 승인되고 조직된 그룹"
목표 : 소프트웨어 개발을 이해하여 응용 프로그램의 약점을 식별한 다음 프로그램을 작성하여 액세스하고 악용하는 것
✔️ Blue Team
NIST "모의 공격자 그룹에 맞서 보안 태세를 유지함으로써 기업의 정보 시스템 사용을 방어하는 역할을 담당하는 그룹"
현재 비즈니스의 보안 태세를 분석한 후 외부 공격을 막기 위해 개선 조치 취함
모니터링 도중 공격이 발생할 경우 이에 대응하는 지속적인 모니터링에 집중
✔️ 사이버 보안의 중요성이 커지면서 red team과 blue team 이상의 보안 필요
✔️ Yellow Team : 보안 시스템과 애플리케이션을 개발하는 빌더, 소프트웨어 엔지니어 및 개발자
✔️ 레드, 블루, 옐로우 팀이 도입되면서 공격자, 방버자, 코더 간의 기술을 혼합하는데 전념하는 보조 컬러 팁을 가질 수 있음
퍼플팀 : Blue team과 Red team 선택. 방어와 공격을 통합하고 팀 간에 협력하고 지식을 고유하면 전반적으로 더 나은 자세를 제공할 수 있음
그린팀 : Green team은 blue team으로부터 통찰력을 얻고, Yellow team과 긴밀히 협력하여 보다 효율적으로 작업
오랜지팀 : Orange team은 Red team과 협력하여 배운 내용을 Yellow team에 전달하여 코드에 더 나은 부안 구축
오픈 소스는 사람들이 자유롭게 사용, 수정, 공유할 수 있는 공개 도메인의 소프트웨어✔️ 오픈 소스의 채택이 급증한 이유는 사내에서 개발된 독점 코드를 강화하는 속도가 빨라 시장 출시 기간을 단축할 수 있기 때문
✔️ 오픈 소스 보안은 오픈 소스 소프트웨어를 사용하는 컴퓨터 시스템과 네트워크의 안전과 보안을 보장하는 관행
✔️ 오픈 소스 소프트웨어는 무료로 사용할 수 있어 널리 사용할 수 있고 보안 향상에 도움이 되지만, 책임감 있게 사용하고 모든 취약점을 해결하여 보안을 유지하는 것이 중요
✔️ Assess(평가) : 프로젝트 상태를 살펴보고, 저장소가 얼마나 활동적이며, 관리자의 반응이 얼마나 되는가
✔️ Adopt(채택) : 소프트웨어 내에서 또는 자체 스택 내의 독립형 앱으로 이를 수행하는 경우 보안 고려 사항에 대한 사내 정책 채택
✔️ Act(행동) : 보안을 유지하고 개선하기 위해 조치를 취해야 함
✔️ Log4j는 Java 내에서 로깅을 위해 사용하는 일반적인 라이브러리로, 취약점은 수백만개의 Java 기반 응용 프로그램에 영향을 미침
✔️ 악의적인 공격자는 애플리케이션 내에서 해당 취약점을 이용하여 시스템에 액세스 가능
수백만 개의 응용 프로그램이 이 패키지에 사용됨
악의적인 행위자는 이를 활용하여 환경을 액세스하거나 악성 코드를 심을 수 있음
✔️ Github에 코드를 복사 붙여넣기 하는 것은 매우 쉽지만, 아래 사항을 고려해야 함
코드에 포함된 패키지 전체를 확인하는가
사용자/유지 관리자를 신뢰하는가
코드 입력의 유효성을 검사하는가
하드코딩된 secret과 환경 또는 secret 관리를 하는가
검증 없이 코드를 신뢰하는가
공개 저장소에 secret이 포함되어있는가
✔️ OWASP : 소프트웨어 보안을 향상시키기 위해 노력하는 비영리 재단
✔️ 환경: 로컬 VirtualBox VM에 배포 + vagrantfile 사용 (아래 애플리케이션 사용)
https://github.com/devsecops/bootcamp/blob/master/Week-2/README.md
✔️ vagrantfile 생성
vagrantfile
Vagrant.configure("2") do |config|
config.vm.box = "centos/7"
config.vm.provider :virtualbox do |v|
v.memory = 8096
v.cpus = 4
end
end
✔️ 가상머신 시작 및 vagrantfile 연결
vagrant up
vagrant ssh
✔️ 사용할 db 설치 및 시작
sudo yum -y install mariadb mariadb-server mariadb-devel
sudo systemctl start mariadb.service
✔️ 필요한 패키지 설치: links, epel-release, node js
sudo yum -y install links
sudo yum install --assumeyes epel-release
sudo yum install --assumeyes nodejs
✔️ RVM(Ruby) 설치
curl -L https://get.rvm.io | bash -s stable
source /home/vagrant/.rvm/scripts/rvm
rvm install ruby-2.7
✔️ Ruby on Rails 애플리케이션을 위한 패키지 설치: Rails gen, git, epel 저장소, redis db
gem install rails
sudo yum install git
sudo yum install epel-release
sudo yum install redis
✔️ 애플리케이션 생성: myapp이라는 Rails 애플리케이션
rails new myapp --skip-turbolinks --skip-spring --skip-test-unit -d mysql
cd myapp
bundle exec rake db:create
bundle exec rake db:migrate
✔️ 애플리케이션 실행
bundle exec rails server -b 0.0.0.0
✔️ 기본 모델 구축
bundle exec rails generate scaffold Bootcamp name:string description:text dates:string
bundle exec rake db:migrate
root bootcamps#index
<p>
<strong>Description:</strong>
<%=raw @bootcamp.description %>
</p>
🚫 cross-site scripting (XSS) 발생 가능
XSS란 악의적인 사용자가 웹 페이지에 악성 스크립트를 삽입해 해당 스크립트가 다른 사용자들에게 실행되도록 하는 공격하는 보안 취약점
이를 사용해 다른 사용자의 신원 데이터(쿠키, 세션 토큰 등)를 훔침
✔️ 검색 기능 만들기 : XSS 방지를 위한 테스트
app/controllers/bootcamps_controller.rb의 index 메소드에 로직 추가
def index
@bootcamps = Bootcamp.all
if params[:search].to_s != ''
@bootcamps = Bootcamp.where("name LIKE '%#{params[:search]}%'")
else
@bootcamps = Bootcamp.all
end
end
app/views/bootcamps/index.html.erb에 검색 필드 추가
<h1>Search</h1>
<%= form_tag(bootcamps_path, method: "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Bootcamps" %>
<%= submit_tag "Search Bootcamps"%>
<% end %>
<h1>Listing Bootcamps</h1>
Parameterized Queries 또는 Prepared Statements와 같은 방법 사용해 안전하게 쿼리 작성해야함✔️ 해당 기법은 주로 SQL Injection 공격을 방지하기 위해 사용
✔️ 개념: 사용자 입력을 쿼리로 전송하는 것이 아니라 문자열 인수로 전달
✔️ 사용자가 DB Query 수행 권한을 가지지 않기에 SQL Injection 공격 예방 가능
ex)
Parameterized Query
public Item findOneByName(String name) {
return em.createQuery("select i from Item i where i.name = :name", Item.class)
.setParameter("name", name)
.getSingleResult();
}
✔️ 개념: 일반 MySQL statement와 달리 placeholder ?를 사용해서 문자열로 변환하여 parameter를 넘김
✔️ 장점
속도: 같은 형태의 value만 다른 반복적인 query의 경우 속도 빠름 / Query의 compile time을 아낄 수 있음
보안: SQL Injection에 대응 가능 / Query Template을 보내는 프로토콜과 Value를 보내는 프로토콜이 다름 / 하나를 변조해서 같은 SQL문 조작 불가능
✔️ 단점
✔️ 일반 MySQL과 Prepared Statement
| 구분 | Statement | Prepared Statement |
|---|---|---|
| Query문 파싱 | 매번 | 1번만 |
| Protocol | Text Protocol | Binary Protocol |
| WARNING, ERROR | 지원O | 지원X |
| 프로토콜 전송 | 1회 | 2회(최초), 1회(이후) |
| SQL Injection | 노출 | 방어 |
ex)
일반 MySQL
SELECT * FROM users WHERE username = 'bob' AND password = 'password';
Prepared Statement
PREPARE statement FROM 'SELECT * FROM users WHERE username = ? AND password = ?';
EXECUTE statement USING @username, @password;