Repository: radondb/radondb-mysql-kubernetes
Pull request #840 (2024-03-22 07:38:03)

*: add log pvc for errolog
<!-- Thanks for sending a pull request!

PR Title Format:
1. pkg [, pkg2, pkg3]: what's changed #xxx
2. *: what's changed #xxx

-->

### What type of PR is this?

<!--
Add one of the following types:
/bug
/documentation
/cleanup
/enhancement
-->

### Which issue(s) this PR fixes?

<!--
Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #  add logs to persistence pvc

### What this PR does?

Summary:

### Special notes for your reviewer?



Patch:
diff --git a/api/v1alpha1/mysqlcluster_types.go b/api/v1alpha1/mysqlcluster_types.go
index dd6e361..f77c1d1 100644
--- a/api/v1alpha1/mysqlcluster_types.go
+++ b/api/v1alpha1/mysqlcluster_types.go
@@ -227,6 +227,11 @@ type MysqlOpts struct {
 	// +kubebuilder:validation:Minimum=0
 	// +optional
 	MaxLagSeconds int `json:"maxLagTime,omitempty"`
+
+	// LogfilePVC represents the pvc which save the mysql error log.
+	// Log file to pvc
+	// +optional
+	LogfilePVC *LogPVC `json:"logfilePVC,omitempty"`
 }
 
 // XenonOpts defines the options of xenon container.
@@ -317,11 +322,6 @@ type PodPolicy struct {
 	// +optional
 	// +kubebuilder:default:=false
 	AuditLogTail bool `json:"auditLogTail,omitempty"`
-
-	// ErrorLogTail represents if tail the mysql error log.
-	// +optional
-	// +kubebuilder:default:=false
-	ErrorLogTail bool `json:"errorLogTail,omitempty"`
 }
 
 // Persistence is the desired spec for storing mysql data. Only one of its
@@ -349,6 +349,27 @@ type Persistence struct {
 	Size string `json:"size,omitempty"`
 }
 
+// Persistence is the desired spec for storing mysql data. Only one of its
+// members may be specified.
+type LogPVC struct {
+
+	// AccessModes contains the desired access modes the volume should have.
+	// More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1
+	// +optional
+	// +kubebuilder:default:={"ReadWriteOnce"}
+	AccessModes []corev1.PersistentVolumeAccessMode `json:"accessModes,omitempty"`
+
+	// Name of the StorageClass required by the claim.
+	// More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1
+	// +optional
+	StorageClass *string `json:"storageClass,omitempty"`
+
+	// Size of persistent volume claim.
+	// +optional
+	// +kubebuilder:default:="10Gi"
+	Size string `json:"size,omitempty"`
+}
+
 // bothS3NFS opt
 type BothS3NFSOpt struct {
 	// NFS schedule.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 0fbc2ba..c9134a9 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -174,6 +174,31 @@ func (in *ClusterCondition) DeepCopy() *ClusterCondition {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *LogPVC) DeepCopyInto(out *LogPVC) {
+	*out = *in
+	if in.AccessModes != nil {
+		in, out := &in.AccessModes, &out.AccessModes
+		*out = make([]v1.PersistentVolumeAccessMode, len(*in))
+		copy(*out, *in)
+	}
+	if in.StorageClass != nil {
+		in, out := &in.StorageClass, &out.StorageClass
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogPVC.
+func (in *LogPVC) DeepCopy() *LogPVC {
+	if in == nil {
+		return nil
+	}
+	out := new(LogPVC)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *MetricsOpts) DeepCopyInto(out *MetricsOpts) {
 	*out = *in
@@ -390,6 +415,11 @@ func (in *MysqlOpts) DeepCopyInto(out *MysqlOpts) {
 		}
 	}
 	in.Resources.DeepCopyInto(&out.Resources)
+	if in.LogfilePVC != nil {
+		in, out := &in.LogfilePVC, &out.LogfilePVC
+		*out = new(LogPVC)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MysqlOpts.
diff --git a/api/v1beta1/mysqlcluster_conversion.go b/api/v1beta1/mysqlcluster_conversion.go
index c04950d..27b2100 100644
--- a/api/v1beta1/mysqlcluster_conversion.go
+++ b/api/v1beta1/mysqlcluster_conversion.go
@@ -80,7 +80,17 @@ func Convert_v1alpha1_MysqlClusterSpec_To_v1beta1_MysqlClusterSpec(in *v1alpha1.
 	out.Backup.Resources = in.PodPolicy.ExtraResources
 	out.Log.SlowLogTail = in.PodPolicy.SlowLogTail
 	out.Log.AuditLogTail = in.PodPolicy.AuditLogTail
-	out.Log.ErrorLogTail = in.PodPolicy.ErrorLogTail
+	if in.MysqlOpts.LogfilePVC != nil {
+		out.Log.LogfilePVC = &corev1.PersistentVolumeClaimSpec{}
+		out.Log.LogfilePVC.AccessModes = in.MysqlOpts.LogfilePVC.AccessModes
+		out.Log.LogfilePVC.Resources.Requests = map[corev1.ResourceName]resource.Quantity{
+			corev1.ResourceStorage: resource.MustParse(in.MysqlOpts.LogfilePVC.Size),
+		}
+		out.Log.LogfilePVC.StorageClassName = in.MysqlOpts.LogfilePVC.StorageClass
+	} else {
+		out.Log.LogfilePVC = nil
+	}
+
 	out.Tolerations = in.PodPolicy.Tolerations
 	out.PriorityClassName = in.PodPolicy.PriorityClassName
 	out.Log.BusyboxImage = in.PodPolicy.BusyboxImage
@@ -156,7 +166,17 @@ func Convert_v1beta1_MysqlClusterSpec_To_v1alpha1_MysqlClusterSpec(in *MysqlClus
 	out.MysqlOpts.Image = in.Image
 	out.PodPolicy.SlowLogTail = in.Log.SlowLogTail
 	out.PodPolicy.AuditLogTail = in.Log.AuditLogTail
-	out.PodPolicy.ErrorLogTail = in.Log.ErrorLogTail
+	//out.MysqlOpts.LogfilePVC = (*v1alpha1.LogPVC)(in.Log.LogfilePVC)
+	if in.Log.LogfilePVC != nil {
+		out.MysqlOpts.LogfilePVC = &v1alpha1.LogPVC{}
+		out.MysqlOpts.LogfilePVC.StorageClass = in.Log.LogfilePVC.StorageClassName
+		out.MysqlOpts.LogfilePVC.Size = FormatQuantity(in.Log.LogfilePVC.Resources.Requests[corev1.ResourceStorage])
+		out.MysqlOpts.LogfilePVC.AccessModes = in.Log.LogfilePVC.AccessModes
+	} else {
+		out.MysqlOpts.LogfilePVC = nil
+	}
+
+	out.XenonOpts = v1alpha1.XenonOpts(in.Xenon)
 	out.PodPolicy.BusyboxImage = in.Log.BusyboxImage
 	out.MetricsOpts.Enabled = in.Monitoring.Exporter.Enabled
 	out.PodPolicy.ImagePullPolicy = in.ImagePullPolicy
diff --git a/api/v1beta1/mysqlcluster_types.go b/api/v1beta1/mysqlcluster_types.go
index 013d7bc..780cd35 100644
--- a/api/v1beta1/mysqlcluster_types.go
+++ b/api/v1beta1/mysqlcluster_types.go
@@ -490,11 +490,10 @@ type LogOpts struct {
 	// +kubebuilder:default:=false
 	AuditLogTail bool `json:"auditLogTail,omitempty"`
 
-	// ErrorLogTail represents if tail the mysql error log.
+	// logPvc represents pvc which save the mysql error log.
+	// Log file to pvc
 	// +optional
-	// +kubebuilder:default:=false
-	ErrorLogTail bool `json:"errorLogTail,omitempty"`
-
+	LogfilePVC *corev1.PersistentVolumeClaimSpec `json:"logfilePVC,omitempty"`
 	//Log container resources of a MySQL container.
 	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
 }
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 1fabd94..887cec9 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -279,6 +279,11 @@ func (in *ExporterSpec) DeepCopy() *ExporterSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *LogOpts) DeepCopyInto(out *LogOpts) {
 	*out = *in
+	if in.LogfilePVC != nil {
+		in, out := &in.LogfilePVC, &out.LogfilePVC
+		*out = new(v1.PersistentVolumeClaimSpec)
+		(*in).DeepCopyInto(*out)
+	}
 	in.Resources.DeepCopyInto(&out.Resources)
 }
 
diff --git a/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml b/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml
index ed00f3e..1d1cfa3 100644
--- a/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml
+++ b/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml
@@ -196,6 +196,27 @@ spec:
                     default: false
                     description: InitTokuDB represents if install tokudb engine.
                     type: boolean
+                  logfilePVC:
+                    description: LogfilePVC represents the pvc which save the mysql
+                      error log. Log file to pvc
+                    properties:
+                      accessModes:
+                        default:
+                        - ReadWriteOnce
+                        description: 'AccessModes contains the desired access modes
+                          the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                        items:
+                          type: string
+                        type: array
+                      size:
+                        default: 10Gi
+                        description: Size of persistent volume claim.
+                        type: string
+                      storageClass:
+                        description: 'Name of the StorageClass required by the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                        type: string
+                    type: object
                   maxLagTime:
                     default: 30
                     description: MaxLagSeconds configures the readiness probe of mysqld
@@ -1216,10 +1237,6 @@ spec:
                     default: busybox:1.32
                     description: The busybox image.
                     type: string
-                  errorLogTail:
-                    default: false
-                    description: ErrorLogTail represents if tail the mysql error log.
-                    type: boolean
                   extraResources:
                     default:
                       requests:
@@ -3636,15 +3653,176 @@ spec:
                     default: false
                     description: AuditLogTail represents if tail the mysql audit log.
                     type: boolean
-                  errorLogTail:
-                    default: false
-                    description: ErrorLogTail represents if tail the mysql error log.
-                    type: boolean
                   image:
                     default: busybox:1.32
                     description: To specify the image that will be used for log container.
                       The busybox image.
                     type: string
+                  logfilePVC:
+                    description: logPvc represents pvc which save the mysql error
+                      log. Log file to pvc
+                    properties:
+                      accessModes:
+                        description: 'AccessModes contains the desired access modes
+                          the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                        items:
+                          type: string
+                        type: array
+                      dataSource:
+                        description: 'This field can be used to specify either: *
+                          An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot)
+                          * An existing PVC (PersistentVolumeClaim) If the provisioner
+                          or an external controller can support the specified data
+                          source, it will create a new volume based on the contents
+                          of the specified data source. If the AnyVolumeDataSource
+                          feature gate is enabled, this field will always have the
+                          same contents as the DataSourceRef field.'
+                        properties:
+                          apiGroup:
+                            description: APIGroup is the group for the resource being
+                              referenced. If APIGroup is not specified, the specified
+                              Kind must be in the core API group. For any other third-party
+                              types, APIGroup is required.
+                            type: string
+                          kind:
+                            description: Kind is the type of resource being referenced
+                            type: string
+                          name:
+                            description: Name is the name of resource being referenced
+                            type: string
+                        required:
+                        - kind
+                        - name
+                        type: object
+                      dataSourceRef:
+                        description: 'Specifies the object from which to populate
+                          the volume with data, if a non-empty volume is desired.
+                          This may be any local object from a non-empty API group
+                          (non core object) or a PersistentVolumeClaim object. When
+                          this field is specified, volume binding will only succeed
+                          if the type of the specified object matches some installed
+                          volume populator or dynamic provisioner. This field will
+                          replace the functionality of the DataSource field and as
+                          such if both fields are non-empty, they must have the same
+                          value. For backwards compatibility, both fields (DataSource
+                          and DataSourceRef) will be set to the same value automatically
+                          if one of them is empty and the other is non-empty. There
+                          are two important differences between DataSource and DataSourceRef:
+                          * While DataSource only allows two specific types of objects,
+                          DataSourceRef   allows any non-core object, as well as PersistentVolumeClaim
+                          objects. * While DataSource ignores disallowed values (dropping
+                          them), DataSourceRef   preserves all values, and generates
+                          an error if a disallowed value is   specified. (Alpha) Using
+                          this field requires the AnyVolumeDataSource feature gate
+                          to be enabled.'
+                        properties:
+                          apiGroup:
+                            description: APIGroup is the group for the resource being
+                              referenced. If APIGroup is not specified, the specified
+                              Kind must be in the core API group. For any other third-party
+                              types, APIGroup is required.
+                            type: string
+                          kind:
+                            description: Kind is the type of resource being referenced
+                            type: string
+                          name:
+                            description: Name is the name of resource being referenced
+                            type: string
+                        required:
+                        - kind
+                        - name
+                        type: object
+                      resources:
+                        description: 'Resources represents the minimum resources the
+                          volume should have. If RecoverVolumeExpansionFailure feature
+                          is enabled users are allowed to specify resource requirements
+                          that are lower than previous value but must still be higher
+                          than capacity recorded in the status field of the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                        properties:
+                          limits:
+                            additionalProperties:
+                              anyOf:
+                              - type: integer
+                              - type: string
+                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                              x-kubernetes-int-or-string: true
+                            description: 'Limits describes the maximum amount of compute
+                              resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                            type: object
+                          requests:
+                            additionalProperties:
+                              anyOf:
+                              - type: integer
+                              - type: string
+                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                              x-kubernetes-int-or-string: true
+                            description: 'Requests describes the minimum amount of
+                              compute resources required. If Requests is omitted for
+                              a container, it defaults to Limits if that is explicitly
+                              specified, otherwise to an implementation-defined value.
+                              More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                            type: object
+                        type: object
+                      selector:
+                        description: A label query over volumes to consider for binding.
+                        properties:
+                          matchExpressions:
+                            description: matchExpressions is a list of label selector
+                              requirements. The requirements are ANDed.
+                            items:
+                              description: A label selector requirement is a selector
+                                that contains values, a key, and an operator that
+                                relates the key and values.
+                              properties:
+                                key:
+                                  description: key is the label key that the selector
+                                    applies to.
+                                  type: string
+                                operator:
+                                  description: operator represents a key's relationship
+                                    to a set of values. Valid operators are In, NotIn,
+                                    Exists and DoesNotExist.
+                                  type: string
+                                values:
+                                  description: values is an array of string values.
+                                    If the operator is In or NotIn, the values array
+                                    must be non-empty. If the operator is Exists or
+                                    DoesNotExist, the values array must be empty.
+                                    This array is replaced during a strategic merge
+                                    patch.
+                                  items:
+                                    type: string
+                                  type: array
+                              required:
+                              - key
+                              - operator
+                              type: object
+                            type: array
+                          matchLabels:
+                            additionalProperties:
+                              type: string
+                            description: matchLabels is a map of {key,value} pairs.
+                              A single {key,value} in the matchLabels map is equivalent
+                              to an element of matchExpressions, whose key field is
+                              "key", the operator is "In", and the values array contains
+                              only "value". The requirements are ANDed.
+                            type: object
+                        type: object
+                      storageClassName:
+                        description: 'Name of the StorageClass required by the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                        type: string
+                      volumeMode:
+                        description: volumeMode defines what type of volume is required
+                          by the claim. Value of Filesystem is implied when not included
+                          in claim spec.
+                        type: string
+                      volumeName:
+                        description: VolumeName is the binding reference to the PersistentVolume
+                          backing this claim.
+                        type: string
+                    type: object
                   resources:
                     description: Log container resources of a MySQL container.
                     properties:
diff --git a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml
index 198704c..f5e18fc 100644
--- a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml
+++ b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml
@@ -178,6 +178,27 @@ spec:
                     default: false
                     description: InitTokuDB represents if install tokudb engine.
                     type: boolean
+                  logfilePVC:
+                    description: LogfilePVC represents the pvc which save the mysql
+                      error log. Log file to pvc
+                    properties:
+                      accessModes:
+                        default:
+                        - ReadWriteOnce
+                        description: 'AccessModes contains the desired access modes
+                          the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                        items:
+                          type: string
+                        type: array
+                      size:
+                        default: 10Gi
+                        description: Size of persistent volume claim.
+                        type: string
+                      storageClass:
+                        description: 'Name of the StorageClass required by the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                        type: string
+                    type: object
                   maxLagTime:
                     default: 30
                     description: MaxLagSeconds configures the readiness probe of mysqld
@@ -1198,10 +1219,6 @@ spec:
                     default: busybox:1.32
                     description: The busybox image.
                     type: string
-                  errorLogTail:
-                    default: false
-                    description: ErrorLogTail represents if tail the mysql error log.
-                    type: boolean
                   extraResources:
                     default:
                       requests:
@@ -3618,15 +3635,176 @@ spec:
                     default: false
                     description: AuditLogTail represents if tail the mysql audit log.
                     type: boolean
-                  errorLogTail:
-                    default: false
-                    description: ErrorLogTail represents if tail the mysql error log.
-                    type: boolean
                   image:
                     default: busybox:1.32
                     description: To specify the image that will be used for log container.
                       The busybox image.
                     type: string
+                  logfilePVC:
+                    description: logPvc represents pvc which save the mysql error
+                      log. Log file to pvc
+                    properties:
+                      accessModes:
+                        description: 'AccessModes contains the desired access modes
+                          the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                        items:
+                          type: string
+                        type: array
+                      dataSource:
+                        description: 'This field can be used to specify either: *
+                          An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot)
+                          * An existing PVC (PersistentVolumeClaim) If the provisioner
+                          or an external controller can support the specified data
+                          source, it will create a new volume based on the contents
+                          of the specified data source. If the AnyVolumeDataSource
+                          feature gate is enabled, this field will always have the
+                          same contents as the DataSourceRef field.'
+                        properties:
+                          apiGroup:
+                            description: APIGroup is the group for the resource being
+                              referenced. If APIGroup is not specified, the specified
+                              Kind must be in the core API group. For any other third-party
+                              types, APIGroup is required.
+                            type: string
+                          kind:
+                            description: Kind is the type of resource being referenced
+                            type: string
+                          name:
+                            description: Name is the name of resource being referenced
+                            type: string
+                        required:
+                        - kind
+                        - name
+                        type: object
+                      dataSourceRef:
+                        description: 'Specifies the object from which to populate
+                          the volume with data, if a non-empty volume is desired.
+                          This may be any local object from a non-empty API group
+                          (non core object) or a PersistentVolumeClaim object. When
+                          this field is specified, volume binding will only succeed
+                          if the type of the specified object matches some installed
+                          volume populator or dynamic provisioner. This field will
+                          replace the functionality of the DataSource field and as
+                          such if both fields are non-empty, they must have the same
+                          value. For backwards compatibility, both fields (DataSource
+                          and DataSourceRef) will be set to the same value automatically
+                          if one of them is empty and the other is non-empty. There
+                          are two important differences between DataSource and DataSourceRef:
+                          * While DataSource only allows two specific types of objects,
+                          DataSourceRef   allows any non-core object, as well as PersistentVolumeClaim
+                          objects. * While DataSource ignores disallowed values (dropping
+                          them), DataSourceRef   preserves all values, and generates
+                          an error if a disallowed value is   specified. (Alpha) Using
+                          this field requires the AnyVolumeDataSource feature gate
+                          to be enabled.'
+                        properties:
+                          apiGroup:
+                            description: APIGroup is the group for the resource being
+                              referenced. If APIGroup is not specified, the specified
+                              Kind must be in the core API group. For any other third-party
+                              types, APIGroup is required.
+                            type: string
+                          kind:
+                            description: Kind is the type of resource being referenced
+                            type: string
+                          name:
+                            description: Name is the name of resource being referenced
+                            type: string
+                        required:
+                        - kind
+                        - name
+                        type: object
+                      resources:
+                        description: 'Resources represents the minimum resources the
+                          volume should have. If RecoverVolumeExpansionFailure feature
+                          is enabled users are allowed to specify resource requirements
+                          that are lower than previous value but must still be higher
+                          than capacity recorded in the status field of the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                        properties:
+                          limits:
+                            additionalProperties:
+                              anyOf:
+                              - type: integer
+                              - type: string
+                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                              x-kubernetes-int-or-string: true
+                            description: 'Limits describes the maximum amount of compute
+                              resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                            type: object
+                          requests:
+                            additionalProperties:
+                              anyOf:
+                              - type: integer
+                              - type: string
+                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                              x-kubernetes-int-or-string: true
+                            description: 'Requests describes the minimum amount of
+                              compute resources required. If Requests is omitted for
+                              a container, it defaults to Limits if that is explicitly
+                              specified, otherwise to an implementation-defined value.
+                              More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                            type: object
+                        type: object
+                      selector:
+                        description: A label query over volumes to consider for binding.
+                        properties:
+                          matchExpressions:
+                            description: matchExpressions is a list of label selector
+                              requirements. The requirements are ANDed.
+                            items:
+                              description: A label selector requirement is a selector
+                                that contains values, a key, and an operator that
+                                relates the key and values.
+                              properties:
+                                key:
+                                  description: key is the label key that the selector
+                                    applies to.
+                                  type: string
+                                operator:
+                                  description: operator represents a key's relationship
+                                    to a set of values. Valid operators are In, NotIn,
+                                    Exists and DoesNotExist.
+                                  type: string
+                                values:
+                                  description: values is an array of string values.
+                                    If the operator is In or NotIn, the values array
+                                    must be non-empty. If the operator is Exists or
+                                    DoesNotExist, the values array must be empty.
+                                    This array is replaced during a strategic merge
+                                    patch.
+                                  items:
+                                    type: string
+                                  type: array
+                              required:
+                              - key
+                              - operator
+                              type: object
+                            type: array
+                          matchLabels:
+                            additionalProperties:
+                              type: string
+                            description: matchLabels is a map of {key,value} pairs.
+                              A single {key,value} in the matchLabels map is equivalent
+                              to an element of matchExpressions, whose key field is
+                              "key", the operator is "In", and the values array contains
+                              only "value". The requirements are ANDed.
+                            type: object
+                        type: object
+                      storageClassName:
+                        description: 'Name of the StorageClass required by the claim.
+                          More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                        type: string
+                      volumeMode:
+                        description: volumeMode defines what type of volume is required
+                          by the claim. Value of Filesystem is implied when not included
+                          in claim spec.
+                        type: string
+                      volumeName:
+                        description: VolumeName is the binding reference to the PersistentVolume
+                          backing this claim.
+                        type: string
+                    type: object
                   resources:
                     description: Log container resources of a MySQL container.
                     properties:
diff --git a/mysqlcluster/mysqlcluster.go b/mysqlcluster/mysqlcluster.go
index 2f707eb..fe650bd 100644
--- a/mysqlcluster/mysqlcluster.go
+++ b/mysqlcluster/mysqlcluster.go
@@ -182,7 +182,16 @@ func (c *MysqlCluster) EnsureVolumes() []corev1.Volume {
 			},
 		)
 	}
-
+	if c.Spec.MysqlOpts.LogfilePVC == nil {
+		volumes = append(volumes,
+			corev1.Volume{
+				Name: utils.LogsVolumeName,
+				VolumeSource: corev1.VolumeSource{
+					EmptyDir: &corev1.EmptyDirVolumeSource{},
+				},
+			},
+		)
+	}
 	volumes = append(volumes,
 		corev1.Volume{
 			Name: utils.MysqlConfVolumeName,
@@ -190,12 +199,6 @@ func (c *MysqlCluster) EnsureVolumes() []corev1.Volume {
 				EmptyDir: &corev1.EmptyDirVolumeSource{},
 			},
 		},
-		corev1.Volume{
-			Name: utils.LogsVolumeName,
-			VolumeSource: corev1.VolumeSource{
-				EmptyDir: &corev1.EmptyDirVolumeSource{},
-			},
-		},
 		corev1.Volume{
 			Name: utils.MysqlCMVolumeName,
 			VolumeSource: corev1.VolumeSource{
@@ -321,38 +324,69 @@ func (c *MysqlCluster) EnsureVolumes() []corev1.Volume {
 
 // EnsureVolumeClaimTemplates ensure the volume claim templates.
 func (c *MysqlCluster) EnsureVolumeClaimTemplates(schema *runtime.Scheme) ([]corev1.PersistentVolumeClaim, error) {
-	if !c.Spec.Persistence.Enabled {
+	if !c.Spec.Persistence.Enabled && c.Spec.MysqlOpts.LogfilePVC == nil {
 		return nil, nil
 	}
+	res := []corev1.PersistentVolumeClaim{}
+	if c.Spec.Persistence.Enabled {
+		if c.Spec.Persistence.StorageClass != nil {
+			if *c.Spec.Persistence.StorageClass == "-" {
+				*c.Spec.Persistence.StorageClass = ""
+			}
+		}
+
+		data := corev1.PersistentVolumeClaim{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      utils.DataVolumeName,
+				Namespace: c.Namespace,
+				Labels:    c.GetLabels(),
+			},
+			Spec: corev1.PersistentVolumeClaimSpec{
+				AccessModes: c.Spec.Persistence.AccessModes,
+				Resources: corev1.ResourceRequirements{
+					Requests: corev1.ResourceList{
+						corev1.ResourceStorage: resource.MustParse(c.Spec.Persistence.Size),
+					},
+				},
+				StorageClassName: c.Spec.Persistence.StorageClass,
+			},
+		}
 
-	if c.Spec.Persistence.StorageClass != nil {
-		if *c.Spec.Persistence.StorageClass == "-" {
-			*c.Spec.Persistence.StorageClass = ""
+		if err := controllerutil.SetControllerReference(c.MysqlCluster, &data, schema); err != nil {
+			return nil, fmt.Errorf("failed setting controller reference: %v", err)
 		}
+		res = append(res, data)
 	}
+	if c.Spec.MysqlOpts.LogfilePVC != nil {
+		if c.Spec.MysqlOpts.LogfilePVC.StorageClass != nil {
+			if *c.Spec.MysqlOpts.LogfilePVC.StorageClass == "-" {
+				*c.Spec.MysqlOpts.LogfilePVC.StorageClass = ""
+			}
+		}
 
-	data := corev1.PersistentVolumeClaim{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      utils.DataVolumeName,
-			Namespace: c.Namespace,
-			Labels:    c.GetLabels(),
-		},
-		Spec: corev1.PersistentVolumeClaimSpec{
-			AccessModes: c.Spec.Persistence.AccessModes,
-			Resources: corev1.ResourceRequirements{
-				Requests: corev1.ResourceList{
-					corev1.ResourceStorage: resource.MustParse(c.Spec.Persistence.Size),
+		logdata := corev1.PersistentVolumeClaim{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      utils.LogsVolumeName,
+				Namespace: c.Namespace,
+				Labels:    c.GetLabels(),
+			},
+			Spec: corev1.PersistentVolumeClaimSpec{
+				AccessModes: c.Spec.Persistence.AccessModes,
+				Resources: corev1.ResourceRequirements{
+					Requests: corev1.ResourceList{
+						corev1.ResourceStorage: resource.MustParse(c.Spec.Persistence.Size),
+					},
 				},
+				StorageClassName: c.Spec.Persistence.StorageClass,
 			},
-			StorageClassName: c.Spec.Persistence.StorageClass,
-		},
-	}
+		}
 
-	if err := controllerutil.SetControllerReference(c.MysqlCluster, &data, schema); err != nil {
-		return nil, fmt.Errorf("failed setting controller reference: %v", err)
+		if err := controllerutil.SetControllerReference(c.MysqlCluster, &logdata, schema); err != nil {
+			return nil, fmt.Errorf("failed setting controller reference: %v", err)
+		}
+		res = append(res, logdata)
 	}
-
-	return []corev1.PersistentVolumeClaim{data}, nil
+	return res, nil
 }
 
 // GetNameForResource returns the name of a resource from above
@@ -461,8 +495,11 @@ func (c *MysqlCluster) EnsureMysqlConf() {
 		}
 
 	}
-	if c.Spec.PodPolicy.ErrorLogTail {
-		c.Spec.MysqlOpts.MysqlConf["log-error"] = utils.LogsVolumeMountPath + "/mysql-error.log"
+
+	if _, ok := c.Spec.MysqlOpts.MysqlConf["log_error"]; !ok {
+		if c.Spec.MysqlOpts.LogfilePVC != nil { //has been set logpvc, change log error to file.
+			c.Spec.MysqlOpts.MysqlConf["log-error"] = utils.LogsVolumeMountPath + "/mysql-error.log"
+		}
 	}
 }
 
diff --git a/mysqlcluster/syncer/statefulset.go b/mysqlcluster/syncer/statefulset.go
index d32bd03..cbb8e0e 100644
--- a/mysqlcluster/syncer/statefulset.go
+++ b/mysqlcluster/syncer/statefulset.go
@@ -432,7 +432,7 @@ func (s *StatefulSetSyncer) mutate() error {
 	}
 	s.sfs.Spec.Template.Spec.Tolerations = s.Spec.PodPolicy.Tolerations
 
-	if s.Spec.Persistence.Enabled {
+	if s.Spec.Persistence.Enabled || s.Spec.MysqlOpts.LogfilePVC != nil {
 		if s.sfs.Spec.VolumeClaimTemplates, err = s.EnsureVolumeClaimTemplates(s.cli.Scheme()); err != nil {
 			return err
 		}
@@ -471,10 +471,9 @@ func (s *StatefulSetSyncer) ensurePodSpec() corev1.PodSpec {
 	if s.Spec.PodPolicy.AuditLogTail {
 		containers = append(containers, container.EnsureContainer(utils.ContainerAuditLogName, s.MysqlCluster))
 	}
-	if s.Spec.PodPolicy.ErrorLogTail {
+	if s.Spec.MysqlOpts.LogfilePVC != nil {
 		// 1.create errorLog
 		containers = append(containers, container.EnsureContainer(utils.ContainerErrorLogName, s.MysqlCluster))
-
 	}
 	return corev1.PodSpec{
 		InitContainers:     initContainers,


Test patch:
diff --git a/mysqlcluster/mysqlcluster_test.go b/mysqlcluster/mysqlcluster_test.go
index 1572e97..e9a1a1c 100644
--- a/mysqlcluster/mysqlcluster_test.go
+++ b/mysqlcluster/mysqlcluster_test.go
@@ -257,17 +257,18 @@ func TestGetPodHostName(t *testing.T) {
 func TestEnsureVolumes(t *testing.T) {
 	volume := []corev1.Volume{
 		{
-			Name: utils.MysqlConfVolumeName,
+			Name: utils.LogsVolumeName,
 			VolumeSource: corev1.VolumeSource{
 				EmptyDir: &corev1.EmptyDirVolumeSource{},
 			},
 		},
 		{
-			Name: utils.LogsVolumeName,
+			Name: utils.MysqlConfVolumeName,
 			VolumeSource: corev1.VolumeSource{
 				EmptyDir: &corev1.EmptyDirVolumeSource{},
 			},
 		},
+
 		{
 			Name: utils.MysqlCMVolumeName,
 			VolumeSource: corev1.VolumeSource{