Files
toallab-automation/roles/nfs_provisioner/tasks/main.yml

395 lines
13 KiB
YAML

---
# Deploy nfs-subdir-external-provisioner on OpenShift, backed by either:
# (a) an external NFS server (set nfs_provisioner_external_server / nfs_provisioner_external_path)
# (b) an in-cluster NFS server pod backed by an LVMS RWO PVC (default)
#
# Architecture (in-cluster mode):
# - NFS server StatefulSet: backs exports with an LVMS RWO PVC
# - Service: exposes NFS server at a stable ClusterIP
# - nfs-subdir-external-provisioner: creates PVs on-demand under the export path
# - StorageClass: "nfs-client" with ReadWriteMany support
#
# Architecture (external mode, nfs_provisioner_external_server != ""):
# - In-cluster NFS server is NOT deployed
# - nfs-subdir-external-provisioner points directly at the external NFS share
# - StorageClass: "nfs-client" with ReadWriteMany support
#
# The in-cluster NFS server requires privileged SCC on OpenShift (kernel NFS).
# All tasks are idempotent (kubernetes.core.k8s state: present).
# ------------------------------------------------------------------
# Step 1: Namespace and RBAC
# ------------------------------------------------------------------
- name: Create NFS provisioner namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ nfs_provisioner_namespace }}"
- name: Create NFS server ServiceAccount
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-server
namespace: "{{ nfs_provisioner_namespace }}"
when: nfs_provisioner_external_server | length == 0
- name: Create NFS provisioner ServiceAccount
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
- name: Create ClusterRole to use privileged SCC (NFS server)
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-server-scc
rules:
- apiGroups: [security.openshift.io]
resources: [securitycontextconstraints]
verbs: [use]
resourceNames: [privileged]
when: nfs_provisioner_external_server | length == 0
- name: Bind privileged SCC ClusterRole to NFS server ServiceAccount
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nfs-server-scc
subjects:
- kind: ServiceAccount
name: nfs-server
namespace: "{{ nfs_provisioner_namespace }}"
roleRef:
kind: ClusterRole
name: nfs-server-scc
apiGroup: rbac.authorization.k8s.io
when: nfs_provisioner_external_server | length == 0
- name: Create ClusterRole to use hostmount-anyuid SCC (NFS provisioner)
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-scc
rules:
- apiGroups: [security.openshift.io]
resources: [securitycontextconstraints]
verbs: [use]
resourceNames: [hostmount-anyuid]
- name: Bind hostmount-anyuid SCC ClusterRole to NFS provisioner ServiceAccount
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nfs-provisioner-scc
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
roleRef:
kind: ClusterRole
name: nfs-provisioner-scc
apiGroup: rbac.authorization.k8s.io
- name: Create ClusterRole for NFS provisioner
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
rules:
- apiGroups: [""]
resources: [persistentvolumes]
verbs: [get, list, watch, create, delete]
- apiGroups: [""]
resources: [persistentvolumeclaims]
verbs: [get, list, watch, update]
- apiGroups: [storage.k8s.io]
resources: [storageclasses]
verbs: [get, list, watch]
- apiGroups: [""]
resources: [events]
verbs: [create, update, patch]
- name: Bind ClusterRole to NFS provisioner ServiceAccount
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
- name: Create Role for leader election
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader-locking-nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
rules:
- apiGroups: [""]
resources: [endpoints]
verbs: [get, list, watch, create, update, patch]
- name: Bind leader election Role
kubernetes.core.k8s:
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: leader-locking-nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io
# ------------------------------------------------------------------
# Step 2: NFS server backing storage and StatefulSet (in-cluster mode only)
# ------------------------------------------------------------------
- name: Create NFS server backing PVC
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-server-data
namespace: "{{ nfs_provisioner_namespace }}"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "{{ nfs_provisioner_storage_size }}"
storageClassName: "{{ nfs_provisioner_storage_class }}"
when: nfs_provisioner_external_server | length == 0
- name: Deploy NFS server StatefulSet
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nfs-server
namespace: "{{ nfs_provisioner_namespace }}"
spec:
replicas: 1
selector:
matchLabels:
app: nfs-server
serviceName: nfs-server
template:
metadata:
labels:
app: nfs-server
spec:
serviceAccountName: nfs-server
containers:
- name: nfs-server
image: "{{ nfs_provisioner_server_image }}"
ports:
- name: nfs
containerPort: 2049
- name: mountd
containerPort: 20048
- name: rpcbind
containerPort: 111
securityContext:
privileged: true
volumeMounts:
- name: nfs-data
mountPath: "{{ nfs_provisioner_export_path }}"
volumes:
- name: nfs-data
persistentVolumeClaim:
claimName: nfs-server-data
when: nfs_provisioner_external_server | length == 0
- name: Create NFS server Service
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: nfs-server
namespace: "{{ nfs_provisioner_namespace }}"
spec:
selector:
app: nfs-server
ports:
- name: nfs
port: 2049
- name: mountd
port: 20048
- name: rpcbind
port: 111
when: nfs_provisioner_external_server | length == 0
# ------------------------------------------------------------------
# Step 3: Wait for in-cluster NFS server to be ready (in-cluster mode only)
# ------------------------------------------------------------------
- name: Wait for NFS server to be ready
kubernetes.core.k8s_info:
api_version: apps/v1
kind: StatefulSet
namespace: "{{ nfs_provisioner_namespace }}"
name: nfs-server
register: __nfs_provisioner_server_status
until: >-
__nfs_provisioner_server_status.resources | length > 0 and
(__nfs_provisioner_server_status.resources[0].status.readyReplicas | default(0)) >= 1
retries: "{{ __nfs_provisioner_wait_retries }}"
delay: 10
when: nfs_provisioner_external_server | length == 0
# ------------------------------------------------------------------
# Step 4: Resolve NFS server address, then deploy nfs-subdir-external-provisioner
# ------------------------------------------------------------------
- name: Set NFS server address (external)
ansible.builtin.set_fact:
__nfs_provisioner_server_addr: "{{ nfs_provisioner_external_server }}"
__nfs_provisioner_server_path: "{{ nfs_provisioner_external_path }}"
when: nfs_provisioner_external_server | length > 0
- name: Retrieve in-cluster NFS server ClusterIP
kubernetes.core.k8s_info:
api_version: v1
kind: Service
namespace: "{{ nfs_provisioner_namespace }}"
name: nfs-server
register: __nfs_provisioner_svc
when: nfs_provisioner_external_server | length == 0
- name: Set NFS server address (in-cluster)
ansible.builtin.set_fact:
__nfs_provisioner_server_addr: "{{ __nfs_provisioner_svc.resources[0].spec.clusterIP }}"
__nfs_provisioner_server_path: "{{ nfs_provisioner_export_path }}"
when: nfs_provisioner_external_server | length == 0
- name: Deploy nfs-subdir-external-provisioner
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-provisioner
namespace: "{{ nfs_provisioner_namespace }}"
spec:
replicas: 1
selector:
matchLabels:
app: nfs-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccountName: nfs-provisioner
containers:
- name: nfs-provisioner
image: "{{ nfs_provisioner_image }}"
env:
- name: PROVISIONER_NAME
value: "{{ nfs_provisioner_name }}"
- name: NFS_SERVER
value: "{{ __nfs_provisioner_server_addr }}"
- name: NFS_PATH
value: "{{ __nfs_provisioner_server_path }}"
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
volumes:
- name: nfs-client-root
nfs:
server: "{{ __nfs_provisioner_server_addr }}"
path: "{{ __nfs_provisioner_server_path }}"
# ------------------------------------------------------------------
# Step 5: Create StorageClass
# ------------------------------------------------------------------
- name: Create NFS StorageClass
kubernetes.core.k8s:
state: present
definition:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: "{{ nfs_provisioner_storage_class_name }}"
provisioner: "{{ nfs_provisioner_name }}"
parameters:
archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
# ------------------------------------------------------------------
# Step 6: Wait for provisioner to be ready
# ------------------------------------------------------------------
- name: Wait for NFS provisioner deployment to be ready
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
namespace: "{{ nfs_provisioner_namespace }}"
name: nfs-provisioner
register: __nfs_provisioner_deploy_status
until: >-
__nfs_provisioner_deploy_status.resources | length > 0 and
(__nfs_provisioner_deploy_status.resources[0].status.readyReplicas | default(0)) >= 1
retries: "{{ __nfs_provisioner_wait_retries }}"
delay: 10
- name: Display NFS provisioner summary
ansible.builtin.debug:
msg:
- "NFS provisioner deployment complete!"
- " Namespace : {{ nfs_provisioner_namespace }}"
- " NFS server : {{ __nfs_provisioner_server_addr }}:{{ __nfs_provisioner_server_path }}"
- " Mode : {{ 'external' if nfs_provisioner_external_server | length > 0 else 'in-cluster (LVMS-backed)' }}"
- " StorageClass : {{ nfs_provisioner_storage_class_name }} (ReadWriteMany)"