The File-Share is a fully managed cloud file folder service inside an Azure Storage Account that can be mounted like a standard network drive via SMB and NFS protocols. You can mount the Azure File-Share storage to a virtual machine or Kubernetes Pods as an external drive/storage and use it for various purposes, including data file storage. The File-Share storage can be used as a persistence volume for containers to store shared data and logs.
This post covers the steps to mount the Azure File Share to the SAS Compute and CAS Pods.
The Following Pics describe the Azure ADLS2 File Share Storage mount to SAS Viya Compute and CAS pods. You can configure and mount the Compute and CAS pods to Azure File Share using a step-by-step process.
Select this image to see a larger version.
Mobile users: To view the image, select the "Full" version at the bottom of the page.
The following steps can be used to mount an existing Azure File Share storage to SAS Viya Compute and CAS pods.
With the appropriate access to the Storage Account, you can collect the Storage Account Key and create the AKS secret. The following Azure CLI statement can be used to query the Storage Account Key and create an AKS secret in the K8s Cluster under a specific namespace.
Code:
## Get Storage Account key
STORAGE_KEY=\$(az storage account keys list --resource-group \$AKS_MY_RESOURCE_GROUP --account-name \$AKS_MY_STORAGE_ACCOUNT_NAME --query "[0].value" -o tsv)
## Create AKS Secret to store Storage Account key
kubectl create secret generic azure-secret -n $NS --from-literal=azurestorageaccountname=\$AKS_MY_STORAGE_ACCOUNT_NAME --from-literal=azurestorageaccountkey=\$STORAGE_KEY
The following statement can be used to create a YAML file for an AKS Persistence Volume using the azure-secret name and existing Azure File Share name. The “aksshare” is an existing File Share in a Storage Account. When the YAML statement is deployed, it will use the storage account secret stored in the azure-secret in AKS. The Mount option, user ID, and group ID can be altered as per your requirement. The Uid 1001, and Gid 1001 are the default values for user and group ’sas’.
Code:
## Create a YAML file for PV pointing to an existing Azure File-Share storage (aksshare).
cat > ~/project/deploy/${NS}/site-config/data-access/myazurePV.yaml <<-EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: myazurevol
labels:
usage: myazurevol
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
azureFile:
secretName: azure-secret
secretNamespace: ${NS}
shareName: aksshare
mountOptions:
- dir_mode=0777
- file_mode=0777
- uid=1001
- gid=1001
EOF
The following statement can be used to create a YAML file for an AKS Persistence Volume Claim (PVC) using the file share PV name
Code:
## Create YAML file for Persistence Volume Claim (PVC) using File-Share PV.
cat > ~/project/deploy/${NS}/site-config/data-access/myazurePVC.yaml <<-EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: myazurevol-pvc
namespace: ${NS}
# Set this annotation to NOT let Kubernetes automatically create
# a persistent volume for this volume claim.
annotations:
volume.beta.kubernetes.io/storage-class: ""
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
selector:
# To make sure we match the claim with the exact volume, match the label
matchLabels:
usage: myazurevol
EOF
Code:
kubectl apply -f ~/project/deploy/${NS}/site-config/data-access/myazurePV.yaml
kubectl apply -f ~/project/deploy/${NS}/site-config/data-access/myazurePVC.yaml
kubectl -n ${NS} get pv myazurevol
kubectl -n ${NS} get pvc myazurevol-pvc
Log:
[cloud-user@pdcesx03199 ~]$ kubectl -n ${NS} get pv myazurevol
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
myazurevol 5Gi RWX Retain Bound test/myazurevol-pvc 2m1s
[cloud-user@pdcesx03199 ~]$ kubectl -n ${NS} get pvc myazurevol-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
myazurevol-pvc Bound myazurevol 5Gi RWX 50s
The following statement can be used to create a YAML file for mounting an Azure File Share PVC to the SAS compute pod. In this case, the mount path is listed as /mnt/myazurevol, and the PVC name is myazurevol-pvc. The PVC name must match the PVC name created in the previous step.
Code:
cat > ~/project/deploy/${NS}/site-config/data-access/data-mounts-job.yaml <<-EOF
# General example for adding mounts to SAS containers with a
# PatchTransformer
apiVersion: builtin
kind: PatchTransformer
metadata:
name: data-mounts-job
patch: |-
## Azure FIle Share example - kubernetes will mount these for you
- op: add
path: /template/spec/containers/0/volumeMounts/-
value:
name: myazurevol
mountPath: "/mnt/myazurevol"
- op: add
path: /template/spec/volumes/-
value:
name: myazurevol
persistentVolumeClaim:
claimName: myazurevol-pvc
target:
kind: PodTemplate
annotationSelector: sas.com/sas-access-config=true
## But not all PodTemplates, only those related to SPRE sessions
labelSelector: "sas.com/template-intent=sas-launcher"
EOF
The following statement can be used to create a YAML file for mounting an Azure File Share PVC to the CAS pod.
Code:
cat > ~/project/deploy/${NS}/site-config/data-access/data-mounts-cas.yaml <<-EOF
# General example for adding mounts to CAS workers
# PatchTransformer
apiVersion: builtin
kind: PatchTransformer
metadata:
name: data-mounts-cas
patch: |-
## Azure File Share - kubernetes will mount these for you
- op: add
path: /spec/controllerTemplate/spec/containers/0/volumeMounts/-
value:
name: myazurevol
mountPath: "/mnt/myazurevol"
- op: add
path: /spec/controllerTemplate/spec/volumes/-
value:
name: myazurevol
persistentVolumeClaim:
claimName: myazurevol-pvc
target:
kind: CASDeployment
annotationSelector: sas.com/sas-access-config=true
EOF
Update the kustomization.yaml file under the ~/project/deploy/${NS}/ folder and include the additional YAML files to mount the Azure PVC to SAS Compute and CAS Pods. The following yq4 statement updates the kustomization.yaml file with the additional YAML files.
Code:
## Updates kustomization.yaml to mount AzureFile PVC to SAS Compute Server
[[ $(grep -c "site-config/data-access/data-mounts-job.yaml" ~/project/deploy/${NS}/kustomization.yaml) == 0 ]] && \yq4 eval -i ".transformers += [\"site-config/data-access/data-mounts-job.yaml\"]" ~/project/deploy/${NS}/kustomization.yaml
## Updates kustomization.yaml to mount AzureFile PVC to CAS
[[ $(grep -c "site-config/data-access/data-mounts-cas.yaml" ~/project/deploy/${NS}/kustomization.yaml) == 0 ]] && \yq4 eval -i ".transformers += [\"site-config/data-access/data-mounts-cas.yaml\"]" ~/project/deploy/${NS}/kustomization.yaml
Code:
cd ~/project/deploy/${NS}/
kustomize build -o site.yaml
kubectl -n ${NS} apply -f site.yaml
After applying the updated manifest to the AKS cluster, restart the Compute and CAS pods to include the changes and mount the Azure File Share to the SAS Compute and CAS Pods.
Code:
kubectl -n ${NS} delete pods -l casoperator.sas.com/server=default
kubectl -n ${NS} delete pod --selector='app=sas-compute'
kubectl -n ${NS} delete pod --selector='app=sas-launcher'
Once you have applied the updated manifest to the AKS cluster, you can verify that the SAS Compute and CAS pods are mounted with an Azure File Share PVC. Code:
kubectl get pods --all-namespaces -o=json | jq -c \
'.items[] | {name: .metadata.name, namespace: .metadata.namespace, claimName:.spec.volumes[] | select( has ("persistentVolumeClaim") ).persistentVolumeClaim.claimName }' | grep "myazurevol-pvc"
Log:
[cloud-user@pdcesx03199 gelenv]$ kubectl get pods --all-namespaces -o=json | jq -c \
> '.items[] | {name: .metadata.name, namespace: .metadata.namespace, claimName:.spec.volumes[] | select( has ("persistentVolumeClaim") ).persistentVolumeClaim.claimName }' | grep "myazurevol-pvc"
{"name":"sas-cas-server-default-controller","namespace":"test","claimName":"myazurevol-pvc"}
{"name":"sas-cas-server-default-worker-0","namespace":"test","claimName":"myazurevol-pvc"}
{"name":"sas-cas-server-default-worker-1","namespace":"test","claimName":"myazurevol-pvc"}
{"name":"sas-cas-server-default-worker-2","namespace":"test","claimName":"myazurevol-pvc"}
With an Azure File Share mounted to the SAS Compute pod, you can use the following PATH-based LIBNAME statement from the SAS Compute Server to save and read a SAS dataset onto the Azure File Share location. In this case, the PATH is like an external NFS drive mounted to the SAS Compute Server pod.
Code:
/* # LIBNAME Statement for Azure File Share Location */
libname azshrlib "/mnt/myazurevol/data" ;
data azshrlib.fish_sas ;
set sashelp.fish ;
run;
Proc SQL outobs=20;
select * from azshrlib.fish_sas ;
run;quit;
Log:
........
...............
77
78 libname azshrlib "/mnt/myazurevol/data" ;
NOTE: Libref AZSHRLIB was successfully assigned as follows:
Engine: V9
Physical Name: /mnt/myazurevol/data
79
80 data azshrlib.fish_sas ;
81 set sashelp.fish ;
82 run;
NOTE: There were 159 observations read from the data set SASHELP.FISH.
NOTE: The data set AZSHRLIB.FISH_SAS has 159 observations and 7 variables.
NOTE: DATA statement used (Total process time):
real time 0.15 seconds
cpu time 0.01 seconds
83
84 Proc SQL outobs=20;
85 select * from azshrlib.fish_sas ;
WARNING: Statement terminated early due to OUTOBS=20 option.
86 run;quit;
NOTE: PROC SQL statements are executed immediately; The RUN statement has no effect.
NOTE: The PROCEDURE SQL printed page 2.
NOTE: PROCEDURE SQL used (Total process time):
real time 0.05 seconds
cpu time 0.03 seconds
With an Azure File Share mounted to the CAS pods, you can use the following PATH-based CASLIB and PROC CASUTIL statement to save and load CAS from the Azure File Share location. In this case, the PATH is like an external NFS drive mounted to the CAS pods.
Code:
CAS mySession SESSOPTS=(CASLIB=casuser TIMEOUT=99 LOCALE="en_US" metrics=true);
CASLIB azlib DATASOURCE=(SRCTYPE="PATH") path="/mnt/myazurevol/data" ;
proc casutil incaslib="azlib" outcaslib="azlib";
load data=sashelp.cars casout="cars" replace;
save casdata="cars" casout="cars.sas7bdat" replace;
list files;
quit;
proc casutil incaslib="azlib" outcaslib="azlib";
load casdata="cars.sas7bdat" casout="cars_new" replace;
list tables;
quit;
CAS mySession TERMINATE;
Log:
...........
......
78
79 proc casutil incaslib="azlib" outcaslib="azlib";
NOTE: The UUID 'aee7ccc1-239f-cd4d-b53a-ba456ec77118' is connected using session MYSESSION.
80 load data=sashelp.cars casout="cars" replace;
NOTE: The INCASLIB= option is ignored when using the DATA= option in the LOAD statement.
NOTE: Executing action 'table.addTable'.
NOTE: Action 'table.addTable' used (Total process time):
NOTE: SASHELP.CARS was successfully added to the "AZLIB" caslib as "CARS".
87 save casdata="cars" casout="cars.sas7bdat" replace;
NOTE: Executing action 'table.save'.
NOTE: Cloud Analytic Services saved the file cars.sas7bdat in caslib AZLIB.
NOTE: Action 'table.save' used (Total process time):
NOTE: real time 0.097214 seconds
...........
.......
78 proc casutil incaslib="azlib" outcaslib="azlib";
NOTE: The UUID 'aee7ccc1-239f-cd4d-b53a-ba456ec77118' is connected using session MYSESSION.
93 load casdata="cars.sas7bdat" casout="cars_new" replace;
NOTE: Executing action 'table.loadTable'.
NOTE: Cloud Analytic Services made the file cars.sas7bdat available as table CARS_NEW in caslib azlib.
NOTE: Action 'table.loadTable' used (Total process time):
NOTE: real time 0.069158 seconds
..........
..................
Find more articles from SAS Global Enablement and Learning here.
Visit the Tips & Tricks page for setup guidance, demos, and practical examples that show how Copilot supports your workflows.
The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.