Stateful Container-EBS
AWS EBS CSI Driver 구성
참조 URL : https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/ebs-csi.html
Amazon EBS CSI(Container Storage Interface) 드라이버에서는 Amazon EKS 클러스터가 영구 볼륨(Persistant Volume, PV)을 위해 Amazon EBS 볼륨의 수명 주기를 관리할 수 있게 해주는 CSI 인터페이스를 제공합니다.
1.IAM 정책 구성
CSI 드라이버는 Kubernetes Pod 세트로 배포됩니다. 이러한 포드에는 볼륨 생성 및 삭제, 클러스터를 구성하는 EC2 worker node에 볼륨 연결과 같은 EBS API 작업을 수행 할 수있는 권한이 있어야합니다.
Amazon EBS CSI(Container Storage Interface) 드라이버는 Amazon EKS 클러스터가 PV (Persistant Volume) 볼륨을 위해 Amazon EBS 볼륨의 Life Cycle 관리할 수 있게 해주는 CSI 인터페이스를 제공합니다.
먼저 정책 JSON 문서를 다운로드하고이 문서에서 IAM 정책을 생성하십시오.
CSI driver를 위한 IAM Policy 샘플 다운로드
IAM Policy 생성
export EBS_CSI_POLICY_NAME="Amazon_EBS_CSI_Driver"
mkdir ${HOME}/environment/ebs_statefulset
cd ${HOME}/environment/ebs_statefulset
curl -sSL -o ebs-csi-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/docs/example-iam-policy.json
export EBS_CNI_POLICY_NAME="Amazon_EBS_CSI_Driver"
aws iam create-policy \
--region ${AWS_REGION} \
--policy-name ${EBS_CNI_POLICY_NAME} \
--policy-document file://${HOME}/environment/ebs_statefulset/ebs-csi-policy.json
export EBS_CNI_POLICY_ARN=$(aws --region ${AWS_REGION} iam list-policies --query 'Policies[?PolicyName==`'$EBS_CNI_POLICY_NAME'`].Arn' --output text)
echo $EBS_CNI_POLICY_ARN
2. Kubernetes 서비스 계정에 대한 IAM 역할 구성
IAM 역할을 Kubernetes 서비스 계정과 연결할 수 있습니다. 그러면 이 서비스 계정은 해당 서비스 계정을 사용하는 모든 포드의 컨테이너에 AWS 권한을 제공 할 수 있습니다. 이 기능을 사용하면 해당 노드의 포드가 AWS API를 호출 할 수 있도록 더 이상 Amazon EKS 노드 IAM 역할에 대한 확장 권한을 제공 할 필요가 없습니다.
eksctl
가 만든 IAM 정책이 포함 된 IAM 역할을 생성하고, 이를 ebs-csi-controller-irsa
CSI 드라이버가 사용할 Kubernetes 서비스 계정과 연결합니다 .
# 앞서서 이 과정을 빈번하게 수행했습니다. 만약 수행했다면 이미 연결되어 있다고 출력될 것입니다.
eksctl utils associate-iam-oidc-provider --region=$AWS_REGION --cluster=eksworkshop --approve
eksctl create iamserviceaccount --cluster eksworkshop \
--name ebs-csi-controller-irsa \
--namespace kube-system \
--attach-policy-arn $EBS_CNI_POLICY_ARN \
--override-existing-serviceaccounts \
--approve
아래와 같이 IAM 대시보드에서 확인 할 수 있습니다.

3. EBS CSI 드라이버 구성 및 배치.
Helm을 통해서 EBS CSI 드라이버를 다운로드 받습니다.
# add the aws-ebs-csi-driver as a helm repo
helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver
# search for the driver
helm search repo aws-ebs-csi-driver
배포를 합니다.
helm upgrade --install aws-ebs-csi-driver \
--namespace kube-system \
--set serviceAccount.controller.create=false \
--set serviceAccount.snapshot.create=false \
--set enableVolumeScheduling=true \
--set enableVolumeResizing=true \
--set enableVolumeSnapshot=true \
--set serviceAccount.snapshot.name=ebs-csi-controller-irsa \
--set serviceAccount.controller.name=ebs-csi-controller-irsa \
aws-ebs-csi-driver/aws-ebs-csi-driver
스토리지 클래스 생성.
클러스터가 사용할 스토리지 클래스를 정의해야 하고, PV 클레임에 대한 기본 스토리지 클래스를 정의해야 합니다.

스토리지 클래스를 정의합니다.
cat << EoF > ${HOME}/environment/ebs_statefulset/mysql-storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: mysql-gp2
provisioner: ebs.csi.aws.com # Amazon EBS CSI driver
parameters:
type: gp2
encrypted: 'true' # EBS volumes will always be encrypted by default
reclaimPolicy: Delete
mountOptions:
- debug
EoF
Storage Class를 생성합니다.
kubectl create -f ${HOME}/environment/ebs_statefulset/mysql-storageclass.yaml
생성된 스토리지 클래스를 확인합니다.
kubectl describe storageclass mysql-gp2
출력 결과를 확인합니다.
whchoi:~/environment/ebs_statefulset $ kubectl describe storageclass mysql-gp2
Name: mysql-gp2
IsDefaultClass: No
Annotations: <none>
Provisioner: ebs.csi.aws.com
Parameters: encrypted=true,type=gp2
AllowVolumeExpansion: <unset>
MountOptions:
debug
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
mysql-gp2 를 storageClassName으로 선언할 것입니다.
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: mysql-gp2
resources:
requests:
storage: 10Gi
ConfigMap 생성
컨피그맵은 key-vlaue Pair로 기밀이 아닌 데이터를 저장하는 데 사용하는 API 오브젝트입니다. 파드는 볼륨 내에서 환경 변수, 커맨드-라인 인수 또는 구성 파일로 컨피그맵을 사용할 수 있습니다.
컨피그맵을 사용하면 컨테이너 이미지에서 환경별 구성을 분리하여, 애플리케이션을 쉽게 이식할 수 있습니다.
1.namespace /configmap 생성
Stateful DB를 생성할 것입니다. 새로운 namespace를 생성합니다.
kubectl create namespace mysql
Configmap을 생성합니다.
cd ${HOME}/environment/ebs_statefulset
cat << EoF > ${HOME}/environment/ebs_statefulset/mysql-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: mysql
labels:
app: mysql
data:
master.cnf: |
# Apply this config only on the leader.
[mysqld]
log-bin
slave.cnf: |
# Apply this config only on followers.
[mysqld]
super-read-only
EoF
Configmap을 생성합니다.
kubectl create -f ${HOME}/environment/ebs_statefulset/mysql-configmap.yaml
MySql을 위한 Service 생성
Staetefulset 멤버를 위한 Headless service 구성과 Client 접속을 위한 구성의 Service 매니페스트를 구성합니다.
cat << EoF > ${HOME}/environment/ebs_statefulset/mysql-services.yaml
# Headless service for stable DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
namespace: mysql
name: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
# Client service for connecting to any MySQL instance for reads.
# For writes, you must instead connect to the leader: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
namespace: mysql
name: mysql-read
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
EoF
mysql, mysql-read를 생성합니다.
kubectl create -f ${HOME}/environment/ebs_statefulset/mysql-services.yaml
StatefuleSet 구성
mysql-statefulset.yml을 기반으로 StatefuleSet을 구성합니다.
cd ${HOME}/environment/ebs_statefulset
wget https://eksworkshop.com/beginner/170_statefulset/statefulset.files/mysql-statefulset.yaml
kubectl apply -f ${HOME}/environment/ebs_statefulset/mysql-statefulset.yaml
정상적으로 생성되었는지 확인합니다.
kubectl -n mysql rollout status statefulset mysql
동적 생성된 PVC를 확인합니다.
kubectl -n mysql get pvc -l app=mysql
아래와 같은 결과를 확인할 수 있습니다.
whchoi:~/environment/ebs_statefulset $ kubectl -n mysql get pvc -l app=mysql
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-mysql-0 Bound pvc-82184a36-d4c4-4371-8deb-a426836af336 10Gi RWO mysql-gp2 2m41s
data-mysql-1 Bound pvc-b492e2aa-1539-447f-a79e-f2316a80dea3 10Gi RWO mysql-gp2 116s
PVC를 아래 명령을 통해 확인합니다.
kubectl -n mysql get pvc -l app=mysql
아래와 같은 출력 결과를 확인 할 수 있습니다.
whchoi98:~/environment/myeks (master) $ kubectl -n mysql get pvc -l app=mysql
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-mysql-0 Bound pvc-c08c7ae2-e3da-46a8-8910-54ac87e0ee20 10Gi RWO mysql-gp2 4m58s
data-mysql-1 Bound pvc-f68bfe62-2ce3-4371-b89a-def75716a106 10Gi RWO mysql-gp2 4m11s
data-mysql-2 Bound pvc-8057ab7e-ffd8-41af-a6cd-72eb178d3e4e 10Gi RWO mysql-gp2 3m16s
EC2 대시보드의 볼륨에서도 동일한 결과를 확인 할 수 있습니다.

SQL Test
1.SQL 테스팅.
다음 명령을 실행하여 mysql-client 를 통 일부 데이터를 mysql-0.mysql 에 보낼 수 있습니다 .
kubectl -n mysql run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello, from mysql-client');
EOF
정상적으로 read되는 지 확인해 봅니다.
kubectl -n mysql run mysql-client --image=mysql:5.7 -it --rm --restart=Never --\
> mysql -h mysql-read -e "SELECT * FROM test.messages"
아래와 같이 출력됩니다.
whchoi:~/environment/ebs_statefulset $ kubectl -n mysql run mysql-client --image=mysql:5.7 -it --rm --restart=Never --\
> mysql -h mysql-read -e "SELECT * FROM test.messages"
+--------------------------+
| message |
+--------------------------+
| hello, from mysql-client |
+--------------------------+
상호간 분산 테스트를 위해서 아래 명령을 통해 확인 해 봅니다.
kubectl -n mysql run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"
2. Container 장애 시험
새로운 Cloud9창에서 아래 명령을 통해 MySQL을 응답하지 않는 상태로 만듭니다.
kubectl -n mysql exec mysql-1 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off
앞서 상호 분산테스트 하였던 Cloud9창에서 어떤 변화가 일어나는지 확인해 봅니다. 아래 명령을 통해 mysql 상태를 확인해 봅니다.
kubectl -n mysql get pod mysql-1
아래와 같은 출력결과를 확인할 수 있습니다.
whchoi:~/environment $ kubectl -n mysql get pod mysql-1
NAME READY STATUS RESTARTS AGE
mysql-1 1/2 Running 0 15m
다시 정상으로 복귀 시키고, 확인해 봅니다.
kubectl -n mysql exec mysql-1 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql
kubectl -n mysql get pod mysql-1
아래와 같은 결과를 확인할 수 있습니다.
whchoi:~/environment $ kubectl -n mysql get pod mysql-1
NAME READY STATUS RESTARTS AGE
mysql-1 2/2 Running 0 17m
3.Pod 삭제 시험
실패한 포드를 시뮬레이션하려면 다음 명령을 사용하여 mysql-1 포드를 삭제합니다.
kubectl -n mysql delete pod mysql-1
앞서 Cloud9 창에서 다시 100에서만 응답되는 것을 확인할 수 있습니다.
StatefulSet 컨트롤러는 실패한 포드를 인식하고 동일한 이름과 링크를 가진 복제본 수를 유지하기 위해 새 포드를 만듭니다.
kubectl -n mysql get pod mysql-1 -w
whchoi:~/environment $ kubectl -n mysql get pod mysql-1 -w
NAME READY STATUS RESTARTS AGE
mysql-1 2/2 Running 0 40s
4.Scaling 시험
이제 스케일링을 시험해 봅니다. replica를 3개까지 확장합니다.
kubectl -n mysql scale statefulset mysql --replicas=3
아래 명령을 통해 확장되는 것을 확인합니다.
kubectl -n mysql rollout status statefulset mysql
다시 앞서 Cloud9 창에서 다시 100,101,102에서 분산해 Read가 일어나는 것을 확인 할 수 있습니다.
새로 배포 된 팔로워 (mysql-2)가 다음 명령으로 동일한 데이터 세트를 가지고 있는지 확인합니다. 동일한 메세지가 출력됩니다.
kubectl -n mysql run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
mysql -h mysql-2.mysql -e "SELECT * FROM test.messages"
이제 다시 축소해 봅니다. 하지만 데이터 또는 PVC를 삭제하지는 않습니다. 수동삭제 해야 합니다.
kubectl -n mysql scale statefulset mysql --replicas=2
kubectl -n mysql get pods -l app=mysql
여전히 PVC가 존재하는 지 확인해 봅니다.
kubectl -n mysql get pvc -l app=mysql
whchoi:~/environment $ kubectl -n mysql get pvc -l app=mysql
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-mysql-0 Bound pvc-82184a36-d4c4-4371-8deb-a426836af336 10Gi RWO mysql-gp2 31m
data-mysql-1 Bound pvc-b492e2aa-1539-447f-a79e-f2316a80dea3 10Gi RWO mysql-gp2 31m
data-mysql-2 Bound pvc-ba84a117-2dc0-455b-ae2a-206466191114 10Gi RWO mysql-gp2 8m35s
Last updated