Dynamic Database Storage with the Amazon EBS CSI Driver for Amazon EKS
How to implement dynamic provisioning of Amazon EBS volumes for your self-managed databases in Kubernetes with the EBS CSI Driver Add-On.
About | |
---|---|
✅ AWS experience | 200 - Intermediate |
⏱ Time to complete | 30 minutes |
🧩 Prerequisites | - AWS Account |
📢 Feedback | Any feedback, issues, or just a 👍 / 👎 ? |
⏰ Last Updated | 2023-08-30 |
- Install the latest version of kubectl. To check your version, run:
kubectl version --short
. - Install the latest version of eksctl. To check your version, run:
eksctl info
.
- Authentication: Utilize the pre-configured IAM Role for Service Account (IRSA) specifically designed for the EBS CSI Driver. This role, used within a designated AWS Availability Zone, works with the OpenID Connect (OIDC) endpoint to ensure secure communication between Kubernetes pods and AWS services.
- EBS CSI Driver Add-On Set-Up: Amazon EKS add-ons, such as the EBS CSI Driver Add-On, are curated software add-ons that include the latest security patches and bug fixes. The AWS Management Console notifies you of new versions for an Amazon EKS add-on. If you choose to install the EBS CSI driver manually, you are responsible for keeping it up to date. Note that EBS volumes are bound to a specific AWS Availability Zone and can only be accessed by nodes within the same zone, making it suitable for workloads running nodes within the same availability zone.
- Sample Application Deployment: Build and provision storage for self-managed databases using a sample MySQL database. Utilize custom annotations and parameters for the EBS CSI Driver, specifically the 'ReadWriteOnce' access mode, to instruct the EBS CSI Driver to handle storage provisioning for databases. For more examples, see Examples in the EBS CSI Driver GitHub repository.
Note that if you're still within your initial 12-month AWS Free Tier period, it's important to note that usage of EBS volumes beyond the free tier will result in additional AWS charges.
- First, confirm that you are operating within the correct cluster context. This ensures that any subsequent commands are sent to the intended Kubernetes cluster. You can verify the current context by executing the following command:
1
kubectl config current-context
- Define the
CLUSTER_NAME
environment variable for your EKS cluster. Replace the sample value for clusterregion
.
1
export CLUSTER_NAME=$(aws eks describe-cluster --region us-east-2 --name managednodes-quickstart --query "cluster.name" --output text)
- Define the
CLUSTER_REGION
environment variable for your EKS cluster. Replace the sample value for clusterregion
.
1
export CLUSTER_REGION=$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.arn" --output text | cut -d: -f4)
- Define the
CLUSTER_VPC
environment variable for your EKS cluster.
1
export CLUSTER_VPC=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${CLUSTER_REGION} --query "cluster.resourcesVpcConfig.vpcId" --output text)
- Define the
ACCOUNT_ID
environment variable for the account associated with your EKS cluster.
1
export ACCOUNT_ID=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${CLUSTER_REGION} --query "cluster.arn" --output text | cut -d':' -f5)
ebs-csi-controller-sa
service account is crucial for managing EBS volumes in Kubernetes. Make sure it's correctly set up in the kube-system
namespace on your cluster.1
kubectl get sa ebs-csi-controller-sa -n kube-system -o yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/AmazonEKS_EBS_CSI_DriverRole
creationTimestamp: "2023-08-17T00:20:02Z"
labels:
app.kubernetes.io/component: csi-driver
app.kubernetes.io/managed-by: EKS
app.kubernetes.io/name: aws-ebs-csi-driver
app.kubernetes.io/version: 1.21.0
name: ebs-csi-controller-sa
namespace: kube-system
resourceVersion: "14579"
uid: faab5e29-76bc-4a04-8f73-7906a5621050
ebs-csi-controller-sa
. Note that you must have an OpenID Connect (OIDC) endpoint associated with your cluster before you run this command.1
2
3
4
5
6
7
8
eksctl create iamserviceaccount \
--name ebs-csi-controller-sa \
--namespace kube-system \
--region ${CLUSTER_REGION} \
--cluster ${CLUSTER_NAME} \
--role-name `AmazonEKS_EBS_CSI_DriverRole` \
--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
--approve
1
kubectl get deployment ebs-csi-controller -n kube-system
1
2
NAME READY UP-TO-DATE AVAILABLE AGE
ebs-csi-controller 2/2 2 2 101m
1
2
3
4
5
6
eksctl create addon \
--name aws-ebs-csi-driver \
--cluster ${CLUSTER_NAME} \
--region ${CLUSTER_REGION} \
--service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EBS_CSI_DriverRole \
--force
1
2023-08-19 10:13:14 [ℹ] creating addon
- Create an
.env
file and paste the following contents.
1
2
3
4
MYSQL_ROOT_PASSWORD=my-secret-pw
MYSQL_USER=test-user
MYSQL_PASSWORD=my-secret-user-pw
MYSQL_DATABASE=sample
- In the same directory as the
.env
file you just created, run the following command to create a Kubernetes secret with sensitive credentials.
1
kubectl create secret generic mysql-secrets --from-env-file=.env
ebs-sc
that sets up storage provisioning on AWS EBS, with a binding mode that waits for the first consumer; and a Pod named mysql-ebs
that runs a Linux OS container. It also specifies the EBS CSI Driver as the provisioner (i.e., ebs.csi.aws.com
). Together, these components provide a complete example of defining, claiming, and utilizing persistent storage within a Kubernetes cluster.- Create a Kubernetes manifest called mysql-ebs.yaml and paste the following contents into it.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-ebs
namespace: default
labels:
app.kubernetes.io/team: database
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: mysql-ebs
serviceName: mysql
template:
metadata:
labels:
app.kubernetes.io/component: mysql-ebs
app.kubernetes.io/team: database
spec:
containers:
- name: mysql
image: "public.ecr.aws/docker/library/mysql:5.7"
args:
- "--ignore-db-dir=lost+found"
imagePullPolicy: IfNotPresent
envFrom:
- secretRef:
name: mysql-secrets
ports:
- name: mysql
containerPort: 3306
protocol: TCP
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ebs-sc
resources:
requests:
storage: 30Gi
- Deploy the Kubernetes resources in
mysql-ebs.yaml
:
1
kubectl apply -f mysql-ebs.yaml
StorageClass
to provision an EBS volume based on the specified configuration. The volumeClaimTemplates
in the StatefulSet
define the desired properties of the volume, such as accessModes
, storageClassName
, and resource requests. The EBS CSI driver then binds the volume to the StatefulSet
's Pod, allowing the MySQL container to utilize the persistent storage.1
kubectl exec --stdin mysql-ebs-0 -- bash -c "df -h"
/var/lib/mysql
path. This verifies that the Pod has successfully created and attached the volume to the Linux OS. The expected output should look like this:1
2
3
4
5
6
7
8
9
10
Filesystem Size Used Avail Use% Mounted on
overlay 80G 2.8G 78G 4% /
tmpfs 64M 0 64M 0% /dev
tmpfs 3.8G 0 3.8G 0% /sys/fs/cgroup
shm 64M 0 64M 0% /dev/shm
/dev/nvme0n1p1 80G 2.8G 78G 4% /etc/hosts
/dev/nvme1n1 30G 211M 30G 1% /var/lib/mysql
tmpfs 6.9G 12K 6.9G 1% /run/secrets/[kubernetes.io/serviceaccount](http://kubernetes.io/serviceaccount)
tmpfs 3.8G 0 3.8G 0% /proc/acpi
tmpfs 3.8G 0 3.8G 0% /sys/firmware
1
2
3
4
5
6
7
8
9
10
11
# Delete the MySQL deployment (Pod)
kubectl delete pods mysql-ebs-0
# Delete the PersistentVolumeClaim (PVC)
kubectl delete pvc data-mysql-ebs-0
# Delete the StorageClass
kubectl delete sc ebs-sc
# Delete the EBS CSI Driver Add-On
eksctl delete addon --name aws-ebs-csi-driver --cluster ${CLUSTER_NAME}
Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.