395 lines
13 KiB
YAML
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)"
|