You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2021/10/14 07:15:12 UTC

[skywalking-swck] branch master updated: Add Storage CRDs and Controller (#31)

This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-swck.git


The following commit(s) were added to refs/heads/master by this push:
     new 4237536  Add Storage CRDs and Controller (#31)
4237536 is described below

commit 4237536185d45a5d6dd9a6d04e3a980d708e622c
Author: Chord <12...@qq.com>
AuthorDate: Thu Oct 14 15:15:07 2021 +0800

    Add Storage CRDs and Controller (#31)
    
    * Add Storage CRDs and Webhook
    
    Author: esto <12...@qq.com>
    
    * Add StorageController
    
    * fix some issues
    
    * fix lint error
---
 Makefile                                           |   8 +-
 PROJECT                                            |   3 +
 apis/operator/v1alpha1/oapserver_types.go          |  11 +
 apis/operator/v1alpha1/storage_types.go            | 114 +++++
 apis/operator/v1alpha1/storage_webhook.go          | 103 +++++
 apis/operator/v1alpha1/zz_generated.deepcopy.go    | 135 ++++++
 cmd/manager/manager.go                             |  15 +
 .../operator.skywalking.apache.org_oapservers.yaml | 222 ++++++++++
 ...> operator.skywalking.apache.org_storages.yaml} | 187 +++------
 .../operator.skywalking.apache.org_oapservers.yaml | 222 ++++++++++
 .../operator.skywalking.apache.org_storages.yaml}  | 187 +++------
 config/operator/crd/kustomization.yaml             |   3 +
 .../crd/patches/cainjection_in_storages.yaml       |  23 +-
 .../operator/crd/patches/webhook_in_storages.yaml  |  33 +-
 config/operator/rbac/role.yaml                     |  32 ++
 .../operator/rbac/storage_editor_role.yaml         |  40 +-
 .../operator/rbac/storage_viewer_role.yaml         |  36 +-
 config/operator/webhook/manifests.yaml             |  36 ++
 controllers/operator/oapserver_controller.go       |  74 ++++
 controllers/operator/storage_controller.go         | 314 ++++++++++++++
 dist/LICENSE                                       |   3 +-
 go.mod                                             |   1 +
 go.sum                                             |   7 +-
 main.go                                            |  20 +-
 .../manifests/oapserver/templates/deployment.yaml  | 129 +++---
 .../elasticsearch7/templates/configmap.yaml        |  39 +-
 .../storage/elasticsearch7/templates/service.yaml  |  36 +-
 .../elasticsearch7/templates/service_account.yaml  |  24 +-
 .../elasticsearch7/templates/statefulset.yaml      | 107 +++++
 pkg/operator/repo/assets.gen.go                    | 461 +++++++++++++++++----
 30 files changed, 2104 insertions(+), 521 deletions(-)

diff --git a/Makefile b/Makefile
index c0f0ecb..b55e014 100644
--- a/Makefile
+++ b/Makefile
@@ -185,7 +185,7 @@ GO_BINDATA := $(GOBIN)/go-bindata
 $(GO_BINDATA):
 	curl --location --output $(GO_BINDATA) https://github.com/kevinburke/go-bindata/releases/download/v3.21.0/go-bindata-$(OSNAME)-amd64 \
 		&& chmod +x $(GO_BINDATA)
-		
+
 update-templates: $(GO_BINDATA)
 	@echo updating charts
 	-hack/run_update_templates.sh
@@ -201,14 +201,14 @@ RELEASE_SCRIPTS := ./build/package/release.sh
 
 release-binary: release-operator release-adapter
 	${RELEASE_SCRIPTS} -b
-	
+
 release-source:
 	${RELEASE_SCRIPTS} -s
-	
+
 release-sign:
 	${RELEASE_SCRIPTS} -k bin
 	${RELEASE_SCRIPTS} -k src
 
 release: release-binary release-source release-sign
 
-.PHONY: release-manager release-binary release-source release
+.PHONY: release-manager release-binary release-source release
\ No newline at end of file
diff --git a/PROJECT b/PROJECT
index 60b7a6e..b181ccc 100644
--- a/PROJECT
+++ b/PROJECT
@@ -29,4 +29,7 @@ resources:
 - group: operator
   kind: Fetcher
   version: v1alpha1
+- group: operator
+  kind: Storage
+  version: v1alpha1
 version: "2"
diff --git a/apis/operator/v1alpha1/oapserver_types.go b/apis/operator/v1alpha1/oapserver_types.go
index fa16489..91e606b 100644
--- a/apis/operator/v1alpha1/oapserver_types.go
+++ b/apis/operator/v1alpha1/oapserver_types.go
@@ -38,6 +38,9 @@ type OAPServerSpec struct {
 	// Service relevant settings
 	// +kubebuilder:validation:Optional
 	Service Service `json:"service,omitempty"`
+	// StorageConfig relevant settings
+	// +kubebuilder:validation:Optional
+	StorageConfig RelevantStorage `json:"storage,omitempty"`
 }
 
 // OAPServerStatus defines the observed state of OAPServer
@@ -53,6 +56,14 @@ type OAPServerStatus struct {
 	Conditions []appsv1.DeploymentCondition `json:"conditions,omitempty"`
 }
 
+type RelevantStorage struct {
+	// Name relevant settings
+	// +kubebuilder:validation:Required
+	Name string `json:"name,omitempty"`
+	// Storage relevant settings
+	Storage Storage `json:"injectstorage,omitempty"`
+}
+
 // +kubebuilder:object:root=true
 // +kubebuilder:subresource:status
 // +kubebuilder:printcolumn:name="Version",type="string",priority=1,JSONPath=".spec.version",description="The version"
diff --git a/apis/operator/v1alpha1/storage_types.go b/apis/operator/v1alpha1/storage_types.go
new file mode 100644
index 0000000..dc54b4e
--- /dev/null
+++ b/apis/operator/v1alpha1/storage_types.go
@@ -0,0 +1,114 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package v1alpha1
+
+import (
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// StorageSpec defines the desired state of Storage
+type StorageSpec struct {
+	// Type of storage.
+	// +kubebuilder:validation:Required
+	Type string `json:"type,omitempty"`
+	// ConnectType is the way to connect storage(e.g. external,internal).
+	// +kubebuilder:validation:Required
+	ConnectType string `json:"connectType,omitempty"`
+	// Address of external storage address.
+	// +kubebuilder:validation:Optional
+	ConnectAddress string `json:"address,omitempty"`
+	// Version of storage.
+	// +kubebuilder:validation:Required
+	Version string `json:"version,omitempty"`
+	// Image is the storage Docker image to deploy.
+	// +kubebuilder:validation:Optional
+	Image string `json:"image,omitempty"`
+	// Instance is the number of storage.
+	// +kubebuilder:validation:Optional
+	Instances int32 `json:"instances,omitempty"`
+	// Security relevant settings
+	// +kubebuilder:validation:Optional
+	Security SecuritySpec `json:"security,omitempty"`
+	// ServiceName relevant settings
+	ServiceName string `json:"servicename,omitempty"`
+	// Config holds the Storage configuration.
+	Config []corev1.EnvVar `json:"config,omitempty"`
+	//ResourceCnfig relevant settings
+	ResourceCnfig Resource `json:"resource,omitempty"`
+}
+
+// SecuritySpec defines the security setting of Storage
+type SecuritySpec struct {
+	// SSLConfig of  storage .
+	// +kubebuilder:validation:Optional
+	TLS bool `json:"tls,omitempty"`
+	// UserConfig of storage .
+	// +kubebuilder:validation:Optional
+	User UserSpec `json:"user,omitempty"`
+}
+
+// UserSpec defines the user security setting of Storage
+type UserSpec struct {
+	// SecretName of storage user .
+	// +kubebuilder:validation:Optional
+	SecretName string `json:"secretName,omitempty"`
+}
+
+type Resource struct {
+	Limit    string `json:"limit,omitempty"`
+	Requests string `json:"requests,omitempty"`
+}
+
+// StorageStatus defines the observed state of Storage
+type StorageStatus struct {
+	// Represents the latest available observations of the underlying statefulset's current state.
+	// +kubebuilder:validation:Optional
+	Conditions []appsv1.StatefulSetCondition `json:"conditions,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+// +kubebuilder:printcolumn:name="Instances",type="string",JSONPath=".spec.instances",description="The number of expected instance"
+// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".spec.type",description="The type of strorage"
+// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="The version"
+// +kubebuilder:printcolumn:name="ConnectType",type="string",JSONPath=".spec.connectType",description="the way to connect storage"
+
+// Storage is the Schema for the storages API
+type Storage struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   StorageSpec   `json:"spec,omitempty"`
+	Status StorageStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// StorageList contains a list of Storage
+type StorageList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Storage `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&Storage{}, &StorageList{})
+}
diff --git a/apis/operator/v1alpha1/storage_webhook.go b/apis/operator/v1alpha1/storage_webhook.go
new file mode 100644
index 0000000..2275d96
--- /dev/null
+++ b/apis/operator/v1alpha1/storage_webhook.go
@@ -0,0 +1,103 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package v1alpha1
+
+import (
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/util/validation/field"
+	ctrl "sigs.k8s.io/controller-runtime"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+)
+
+// log is for logging in this package.
+var storagelog = logf.Log.WithName("storage-resource")
+
+func (r *Storage) SetupWebhookWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewWebhookManagedBy(mgr).
+		For(r).
+		Complete()
+}
+
+// nolint: lll
+// +kubebuilder:webhook:path=/mutate-operator-skywalking-apache-org-v1alpha1-storage,mutating=true,failurePolicy=fail,groups=operator.skywalking.apache.org,resources=storages,verbs=create;update,versions=v1alpha1,name=mstorage.kb.io
+
+var _ webhook.Defaulter = &Storage{}
+
+// Default implements webhook.Defaulter so a webhook will be registered for the type
+func (r *Storage) Default() {
+	storagelog.Info("default", "name", r.Name)
+	if r.Spec.ConnectType == "internal" {
+		if r.Spec.Image == "" {
+			r.Spec.Image = "docker.elastic.co/elasticsearch/elasticsearch:7.5.1"
+		}
+		if r.Spec.Instances == 0 {
+			r.Spec.Instances = 3
+		}
+	}
+}
+
+// nolint: lll
+// +kubebuilder:webhook:verbs=create;update,path=/validate-operator-skywalking-apache-org-v1alpha1-storage,mutating=false,failurePolicy=fail,groups=operator.skywalking.apache.org,resources=storages,versions=v1alpha1,name=vstorage.kb.io
+
+var _ webhook.Validator = &Storage{}
+
+// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
+func (r *Storage) ValidateCreate() error {
+	storagelog.Info("validate create", "name", r.Name)
+	return r.valid()
+}
+
+// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
+func (r *Storage) ValidateUpdate(old runtime.Object) error {
+	storagelog.Info("validate update", "name", r.Name)
+	return r.valid()
+}
+
+// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
+func (r *Storage) ValidateDelete() error {
+	storagelog.Info("validate delete", "name", r.Name)
+	return nil
+}
+
+func (r *Storage) valid() error {
+	var allErrs field.ErrorList
+	if r.Spec.Type != "elasticsearch7" {
+		storagelog.Info("Invalid Storage Type")
+		err := field.Invalid(field.NewPath("spec").Child("type"),
+			r.Spec.Type,
+			"d. must be elasticsearch or elasticsearch7")
+		allErrs = append(allErrs, err)
+	}
+	if r.Spec.ConnectType != "internal" && r.Spec.ConnectType != "external" {
+		storagelog.Info("Invalid Storage ConnectType")
+		err := field.Invalid(field.NewPath("spec").Child("connecttype"),
+			r.Spec.ConnectType,
+			"d. must be internal or external ")
+		allErrs = append(allErrs, err)
+	}
+	if len(allErrs) != 0 {
+		return apierrors.NewInvalid(
+			schema.GroupKind{Group: r.GroupVersionKind().Group, Kind: r.GroupVersionKind().Kind},
+			r.Name,
+			allErrs)
+	}
+	return nil
+}
diff --git a/apis/operator/v1alpha1/zz_generated.deepcopy.go b/apis/operator/v1alpha1/zz_generated.deepcopy.go
index 27953b1..5a4c9da 100644
--- a/apis/operator/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/operator/v1alpha1/zz_generated.deepcopy.go
@@ -300,6 +300,22 @@ func (in *OAPServerStatus) DeepCopy() *OAPServerStatus {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SecuritySpec) DeepCopyInto(out *SecuritySpec) {
+	*out = *in
+	out.User = in.User
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecuritySpec.
+func (in *SecuritySpec) DeepCopy() *SecuritySpec {
+	if in == nil {
+		return nil
+	}
+	out := new(SecuritySpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Service) DeepCopyInto(out *Service) {
 	*out = *in
 	in.Template.DeepCopyInto(&out.Template)
@@ -342,6 +358,110 @@ func (in *ServiceTemplate) DeepCopy() *ServiceTemplate {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Storage) DeepCopyInto(out *Storage) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage.
+func (in *Storage) DeepCopy() *Storage {
+	if in == nil {
+		return nil
+	}
+	out := new(Storage)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Storage) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *StorageList) DeepCopyInto(out *StorageList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Storage, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageList.
+func (in *StorageList) DeepCopy() *StorageList {
+	if in == nil {
+		return nil
+	}
+	out := new(StorageList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *StorageList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *StorageSpec) DeepCopyInto(out *StorageSpec) {
+	*out = *in
+	out.Security = in.Security
+	if in.Config != nil {
+		in, out := &in.Config, &out.Config
+		*out = make([]corev1.EnvVar, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSpec.
+func (in *StorageSpec) DeepCopy() *StorageSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(StorageSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *StorageStatus) DeepCopyInto(out *StorageStatus) {
+	*out = *in
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]appsv1.StatefulSetCondition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageStatus.
+func (in *StorageStatus) DeepCopy() *StorageStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(StorageStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *UI) DeepCopyInto(out *UI) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
@@ -447,3 +567,18 @@ func (in *UIStatus) DeepCopy() *UIStatus {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UserSpec) DeepCopyInto(out *UserSpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec.
+func (in *UserSpec) DeepCopy() *UserSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(UserSpec)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/cmd/manager/manager.go b/cmd/manager/manager.go
index 5446df3..0d4b09c 100644
--- a/cmd/manager/manager.go
+++ b/cmd/manager/manager.go
@@ -104,6 +104,17 @@ func main() {
 		os.Exit(1)
 	}
 
+	if err = (&operatorcontroller.StorageReconciler{
+		Client:   mgr.GetClient(),
+		Log:      ctrl.Log.WithName("controllers").WithName("Storage"),
+		Scheme:   mgr.GetScheme(),
+		FileRepo: repo.NewRepo("storage"),
+		Recorder: mgr.GetEventRecorderFor("storage-controller"),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "Storage")
+		os.Exit(1)
+	}
+
 	if err = (&operatorcontroller.ConfigMapReconciler{
 		Client:   mgr.GetClient(),
 		Log:      ctrl.Log.WithName("controllers").WithName("ConfigMap"),
@@ -126,6 +137,10 @@ func main() {
 			setupLog.Error(err, "unable to create webhook", "webhook", "Fetcher")
 			os.Exit(1)
 		}
+		if err = (&operatorv1alpha1.Storage{}).SetupWebhookWithManager(mgr); err != nil {
+			setupLog.Error(err, "unable to create webhook", "webhook", "storage")
+			os.Exit(1)
+		}
 		// register a webhook to enable the agent injector,
 		setupLog.Info("registering /mutate-v1-pod webhook")
 		mgr.GetWebhookServer().Register("/mutate-v1-pod",
diff --git a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml b/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
index a46a5fe..bd73b05 100644
--- a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
+++ b/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
@@ -279,6 +279,228 @@ spec:
                       type: string
                   type: object
               type: object
+            storage:
+              description: StorageConfig relevant settings
+              properties:
+                injectstorage:
+                  description: Storage relevant settings
+                  properties:
+                    apiVersion:
+                      description: 'APIVersion defines the versioned schema of this
+                        representation of an object. Servers should convert recognized
+                        schemas to the latest internal value, and may reject unrecognized
+                        values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+                      type: string
+                    kind:
+                      description: 'Kind is a string value representing the REST resource
+                        this object represents. Servers may infer this from the endpoint
+                        the client submits requests to. Cannot be updated. In CamelCase.
+                        More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+                      type: string
+                    metadata:
+                      type: object
+                    spec:
+                      description: StorageSpec defines the desired state of Storage
+                      properties:
+                        address:
+                          description: Address of external storage address.
+                          type: string
+                        config:
+                          description: Config holds the Storage configuration.
+                          items:
+                            description: EnvVar represents an environment variable
+                              present in a Container.
+                            properties:
+                              name:
+                                description: Name of the environment variable. Must
+                                  be a C_IDENTIFIER.
+                                type: string
+                              value:
+                                description: 'Variable references $(VAR_NAME) are
+                                  expanded using the previous defined environment
+                                  variables in the container and any service environment
+                                  variables. If a variable cannot be resolved, the
+                                  reference in the input string will be unchanged.
+                                  The $(VAR_NAME) syntax can be escaped with a double
+                                  $$, ie: $$(VAR_NAME). Escaped references will never
+                                  be expanded, regardless of whether the variable
+                                  exists or not. Defaults to "".'
+                                type: string
+                              valueFrom:
+                                description: Source for the environment variable's
+                                  value. Cannot be used if value is not empty.
+                                properties:
+                                  configMapKeyRef:
+                                    description: Selects a key of a ConfigMap.
+                                    properties:
+                                      key:
+                                        description: The key to select.
+                                        type: string
+                                      name:
+                                        description: 'Name of the referent. More info:
+                                          https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                          TODO: Add other useful fields. apiVersion,
+                                          kind, uid?'
+                                        type: string
+                                      optional:
+                                        description: Specify whether the ConfigMap
+                                          or its key must be defined
+                                        type: boolean
+                                    required:
+                                    - key
+                                    type: object
+                                  fieldRef:
+                                    description: 'Selects a field of the pod: supports
+                                      metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`,
+                                      `metadata.annotations[''<KEY>'']`, spec.nodeName,
+                                      spec.serviceAccountName, status.hostIP, status.podIP,
+                                      status.podIPs.'
+                                    properties:
+                                      apiVersion:
+                                        description: Version of the schema the FieldPath
+                                          is written in terms of, defaults to "v1".
+                                        type: string
+                                      fieldPath:
+                                        description: Path of the field to select in
+                                          the specified API version.
+                                        type: string
+                                    required:
+                                    - fieldPath
+                                    type: object
+                                  resourceFieldRef:
+                                    description: 'Selects a resource of the container:
+                                      only resources limits and requests (limits.cpu,
+                                      limits.memory, limits.ephemeral-storage, requests.cpu,
+                                      requests.memory and requests.ephemeral-storage)
+                                      are currently supported.'
+                                    properties:
+                                      containerName:
+                                        description: 'Container name: required for
+                                          volumes, optional for env vars'
+                                        type: string
+                                      divisor:
+                                        anyOf:
+                                        - type: integer
+                                        - type: string
+                                        description: Specifies the output format of
+                                          the exposed resources, defaults to "1"
+                                        pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                                        x-kubernetes-int-or-string: true
+                                      resource:
+                                        description: 'Required: resource to select'
+                                        type: string
+                                    required:
+                                    - resource
+                                    type: object
+                                  secretKeyRef:
+                                    description: Selects a key of a secret in the
+                                      pod's namespace
+                                    properties:
+                                      key:
+                                        description: The key of the secret to select
+                                          from.  Must be a valid secret key.
+                                        type: string
+                                      name:
+                                        description: 'Name of the referent. More info:
+                                          https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                          TODO: Add other useful fields. apiVersion,
+                                          kind, uid?'
+                                        type: string
+                                      optional:
+                                        description: Specify whether the Secret or
+                                          its key must be defined
+                                        type: boolean
+                                    required:
+                                    - key
+                                    type: object
+                                type: object
+                            required:
+                            - name
+                            type: object
+                          type: array
+                        connectType:
+                          description: ConnectType is the way to connect storage(e.g.
+                            external,internal).
+                          type: string
+                        image:
+                          description: Image is the storage Docker image to deploy.
+                          type: string
+                        instances:
+                          description: Instance is the number of storage.
+                          format: int32
+                          type: integer
+                        resource:
+                          description: ResourceCnfig relevant settings
+                          properties:
+                            limit:
+                              type: string
+                            requests:
+                              type: string
+                          type: object
+                        security:
+                          description: Security relevant settings
+                          properties:
+                            tls:
+                              description: SSLConfig of  storage .
+                              type: boolean
+                            user:
+                              description: UserConfig of storage .
+                              properties:
+                                secretName:
+                                  description: SecretName of storage user .
+                                  type: string
+                              type: object
+                          type: object
+                        servicename:
+                          description: ServiceName relevant settings
+                          type: string
+                        type:
+                          description: Type of storage.
+                          type: string
+                        version:
+                          description: Version of storage.
+                          type: string
+                      type: object
+                    status:
+                      description: StorageStatus defines the observed state of Storage
+                      properties:
+                        conditions:
+                          description: Represents the latest available observations
+                            of the underlying statefulset's current state.
+                          items:
+                            description: StatefulSetCondition describes the state
+                              of a statefulset at a certain point.
+                            properties:
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: A human readable message indicating details
+                                  about the transition.
+                                type: string
+                              reason:
+                                description: The reason for the condition's last transition.
+                                type: string
+                              status:
+                                description: Status of the condition, one of True,
+                                  False, Unknown.
+                                type: string
+                              type:
+                                description: Type of statefulset condition.
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                      type: object
+                  type: object
+                name:
+                  description: Name relevant settings
+                  type: string
+              type: object
             version:
               description: Version of OAP.
               type: string
diff --git a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml b/config/dev/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
similarity index 57%
copy from config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
copy to config/dev/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
index a46a5fe..22c09a8 100644
--- a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
+++ b/config/dev/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
@@ -23,42 +23,37 @@ metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.2.5
   creationTimestamp: null
-  name: oapservers.operator.skywalking.apache.org
+  name: storages.operator.skywalking.apache.org
 spec:
   additionalPrinterColumns:
-  - JSONPath: .spec.version
-    description: The version
-    name: Version
-    priority: 1
-    type: string
   - JSONPath: .spec.instances
     description: The number of expected instance
     name: Instances
     type: string
-  - JSONPath: .status.availableReplicas
-    description: The number of running
-    name: Running
+  - JSONPath: .spec.type
+    description: The type of strorage
+    name: Type
     type: string
-  - JSONPath: .status.address
-    description: The address of OAP server
-    name: Address
+  - JSONPath: .spec.version
+    description: The version
+    name: Version
     type: string
-  - JSONPath: .spec.image
-    name: Image
-    priority: 1
+  - JSONPath: .spec.connectType
+    description: the way to connect storage
+    name: ConnectType
     type: string
   group: operator.skywalking.apache.org
   names:
-    kind: OAPServer
-    listKind: OAPServerList
-    plural: oapservers
-    singular: oapserver
+    kind: Storage
+    listKind: StorageList
+    plural: storages
+    singular: storage
   scope: Namespaced
   subresources:
     status: {}
   validation:
     openAPIV3Schema:
-      description: OAPServer is the Schema for the oapservers API
+      description: Storage is the Schema for the storages API
       properties:
         apiVersion:
           description: 'APIVersion defines the versioned schema of this representation
@@ -73,10 +68,13 @@ spec:
         metadata:
           type: object
         spec:
-          description: OAPServerSpec defines the desired state of OAPServer
+          description: StorageSpec defines the desired state of Storage
           properties:
+            address:
+              description: Address of external storage address.
+              type: string
             config:
-              description: Config holds the OAP server configuration.
+              description: Config holds the Storage configuration.
               items:
                 description: EnvVar represents an environment variable present in
                   a Container.
@@ -178,131 +176,56 @@ spec:
                 - name
                 type: object
               type: array
+            connectType:
+              description: ConnectType is the way to connect storage(e.g. external,internal).
+              type: string
             image:
-              description: Image is the OAP Server Docker image to deploy.
+              description: Image is the storage Docker image to deploy.
               type: string
             instances:
-              description: Count is the number of OAP servers
+              description: Instance is the number of storage.
               format: int32
               type: integer
-            service:
-              description: Service relevant settings
+            resource:
+              description: ResourceCnfig relevant settings
               properties:
-                ingress:
-                  description: Ingress defines the behavior of an ingress
-                  properties:
-                    annotations:
-                      additionalProperties:
-                        type: string
-                      description: Annotations is an unstructured key value map stored
-                        with a resource that may be set by external tools to store
-                        and retrieve arbitrary metadata. They are not queryable and
-                        should be preserved when modifying objects.
-                      type: object
-                    host:
-                      description: Host is the fully qualified domain name of a network
-                        host, as defined by RFC 3986. Note the following deviations
-                        from the "host" part of the URI as defined in RFC 3986
-                      type: string
-                    ingressClassName:
-                      description: IngressClassName is the name of the IngressClass
-                        cluster resource. The associated IngressClass defines which
-                        controller will implement the resource. This replaces the
-                        deprecated `kubernetes.io/ingress.class` annotation. For backwards
-                        compatibility, when that annotation is set, it must be given
-                        precedence over this field. The controller may emit a warning
-                        if the field and annotation have different values. Implementations
-                        of this API should ignore Ingresses without a class specified.
-                        An IngressClass resource may be marked as default, which can
-                        be used to set a default value for this field. For more information,
-                        refer to the IngressClass documentation.
-                      type: string
-                    tls:
-                      description: TLS configuration. Currently the Ingress only supports
-                        a single TLS port, 443. If multiple members of this list specify
-                        different hosts, they will be multiplexed on the same port
-                        according to the hostname specified through the SNI TLS extension,
-                        if the ingress controller fulfilling the ingress supports
-                        SNI.
-                      items:
-                        description: IngressTLS describes the transport layer security
-                          associated with an Ingress.
-                        properties:
-                          hosts:
-                            description: Hosts are a list of hosts included in the
-                              TLS certificate. The values in this list must match
-                              the name/s used in the tlsSecret. Defaults to the wildcard
-                              host setting for the loadbalancer controller fulfilling
-                              this Ingress, if left unspecified.
-                            items:
-                              type: string
-                            type: array
-                            x-kubernetes-list-type: atomic
-                          secretName:
-                            description: SecretName is the name of the secret used
-                              to terminate TLS traffic on port 443. Field is left
-                              optional to allow TLS routing based on SNI hostname
-                              alone. If the SNI host in a listener conflicts with
-                              the "Host" header field used by an IngressRule, the
-                              SNI host is used for termination and value of the Host
-                              header is used for routing.
-                            type: string
-                        type: object
-                      type: array
-                  type: object
-                template:
-                  description: ServiceTemplate defines the behavior of a service.
+                limit:
+                  type: string
+                requests:
+                  type: string
+              type: object
+            security:
+              description: Security relevant settings
+              properties:
+                tls:
+                  description: SSLConfig of  storage .
+                  type: boolean
+                user:
+                  description: UserConfig of storage .
                   properties:
-                    clusterIP:
-                      description: clusterIP is the IP address of the service and
-                        is usually assigned randomly.
-                      type: string
-                    externalIPs:
-                      description: externalIPs is a list of IP addresses for which
-                        nodes in the cluster will also accept traffic for this service.
-                      items:
-                        type: string
-                      type: array
-                    loadBalancerIP:
-                      description: 'Only applies to Service Type: LoadBalancer LoadBalancer
-                        will get created with the IP specified in this field.'
-                      type: string
-                    loadBalancerSourceRanges:
-                      description: If specified and supported by the platform, this
-                        will restrict traffic through the cloud-provider load-balancer
-                        will be restricted to the specified client IPs.
-                      items:
-                        type: string
-                      type: array
-                    type:
-                      description: type determines how the Service is exposed.
+                    secretName:
+                      description: SecretName of storage user .
                       type: string
                   type: object
               type: object
+            servicename:
+              description: ServiceName relevant settings
+              type: string
+            type:
+              description: Type of storage.
+              type: string
             version:
-              description: Version of OAP.
+              description: Version of storage.
               type: string
-          required:
-          - instances
-          - version
           type: object
         status:
-          description: OAPServerStatus defines the observed state of OAPServer
+          description: StorageStatus defines the observed state of Storage
           properties:
-            address:
-              description: Address indicates the entry of OAP server which ingresses
-                data
-              type: string
-            availableReplicas:
-              description: Total number of available pods (ready for at least minReadySeconds)
-                targeted by this deployment.
-              format: int32
-              type: integer
             conditions:
               description: Represents the latest available observations of the underlying
-                deployment's current state.
+                statefulset's current state.
               items:
-                description: DeploymentCondition describes the state of a deployment
+                description: StatefulSetCondition describes the state of a statefulset
                   at a certain point.
                 properties:
                   lastTransitionTime:
@@ -310,10 +233,6 @@ spec:
                       to another.
                     format: date-time
                     type: string
-                  lastUpdateTime:
-                    description: The last time this condition was updated.
-                    format: date-time
-                    type: string
                   message:
                     description: A human readable message indicating details about
                       the transition.
@@ -325,7 +244,7 @@ spec:
                     description: Status of the condition, one of True, False, Unknown.
                     type: string
                   type:
-                    description: Type of deployment condition.
+                    description: Type of statefulset condition.
                     type: string
                 required:
                 - status
diff --git a/config/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml b/config/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
index a46a5fe..bd73b05 100644
--- a/config/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
+++ b/config/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
@@ -279,6 +279,228 @@ spec:
                       type: string
                   type: object
               type: object
+            storage:
+              description: StorageConfig relevant settings
+              properties:
+                injectstorage:
+                  description: Storage relevant settings
+                  properties:
+                    apiVersion:
+                      description: 'APIVersion defines the versioned schema of this
+                        representation of an object. Servers should convert recognized
+                        schemas to the latest internal value, and may reject unrecognized
+                        values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+                      type: string
+                    kind:
+                      description: 'Kind is a string value representing the REST resource
+                        this object represents. Servers may infer this from the endpoint
+                        the client submits requests to. Cannot be updated. In CamelCase.
+                        More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+                      type: string
+                    metadata:
+                      type: object
+                    spec:
+                      description: StorageSpec defines the desired state of Storage
+                      properties:
+                        address:
+                          description: Address of external storage address.
+                          type: string
+                        config:
+                          description: Config holds the Storage configuration.
+                          items:
+                            description: EnvVar represents an environment variable
+                              present in a Container.
+                            properties:
+                              name:
+                                description: Name of the environment variable. Must
+                                  be a C_IDENTIFIER.
+                                type: string
+                              value:
+                                description: 'Variable references $(VAR_NAME) are
+                                  expanded using the previous defined environment
+                                  variables in the container and any service environment
+                                  variables. If a variable cannot be resolved, the
+                                  reference in the input string will be unchanged.
+                                  The $(VAR_NAME) syntax can be escaped with a double
+                                  $$, ie: $$(VAR_NAME). Escaped references will never
+                                  be expanded, regardless of whether the variable
+                                  exists or not. Defaults to "".'
+                                type: string
+                              valueFrom:
+                                description: Source for the environment variable's
+                                  value. Cannot be used if value is not empty.
+                                properties:
+                                  configMapKeyRef:
+                                    description: Selects a key of a ConfigMap.
+                                    properties:
+                                      key:
+                                        description: The key to select.
+                                        type: string
+                                      name:
+                                        description: 'Name of the referent. More info:
+                                          https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                          TODO: Add other useful fields. apiVersion,
+                                          kind, uid?'
+                                        type: string
+                                      optional:
+                                        description: Specify whether the ConfigMap
+                                          or its key must be defined
+                                        type: boolean
+                                    required:
+                                    - key
+                                    type: object
+                                  fieldRef:
+                                    description: 'Selects a field of the pod: supports
+                                      metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`,
+                                      `metadata.annotations[''<KEY>'']`, spec.nodeName,
+                                      spec.serviceAccountName, status.hostIP, status.podIP,
+                                      status.podIPs.'
+                                    properties:
+                                      apiVersion:
+                                        description: Version of the schema the FieldPath
+                                          is written in terms of, defaults to "v1".
+                                        type: string
+                                      fieldPath:
+                                        description: Path of the field to select in
+                                          the specified API version.
+                                        type: string
+                                    required:
+                                    - fieldPath
+                                    type: object
+                                  resourceFieldRef:
+                                    description: 'Selects a resource of the container:
+                                      only resources limits and requests (limits.cpu,
+                                      limits.memory, limits.ephemeral-storage, requests.cpu,
+                                      requests.memory and requests.ephemeral-storage)
+                                      are currently supported.'
+                                    properties:
+                                      containerName:
+                                        description: 'Container name: required for
+                                          volumes, optional for env vars'
+                                        type: string
+                                      divisor:
+                                        anyOf:
+                                        - type: integer
+                                        - type: string
+                                        description: Specifies the output format of
+                                          the exposed resources, defaults to "1"
+                                        pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                                        x-kubernetes-int-or-string: true
+                                      resource:
+                                        description: 'Required: resource to select'
+                                        type: string
+                                    required:
+                                    - resource
+                                    type: object
+                                  secretKeyRef:
+                                    description: Selects a key of a secret in the
+                                      pod's namespace
+                                    properties:
+                                      key:
+                                        description: The key of the secret to select
+                                          from.  Must be a valid secret key.
+                                        type: string
+                                      name:
+                                        description: 'Name of the referent. More info:
+                                          https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                          TODO: Add other useful fields. apiVersion,
+                                          kind, uid?'
+                                        type: string
+                                      optional:
+                                        description: Specify whether the Secret or
+                                          its key must be defined
+                                        type: boolean
+                                    required:
+                                    - key
+                                    type: object
+                                type: object
+                            required:
+                            - name
+                            type: object
+                          type: array
+                        connectType:
+                          description: ConnectType is the way to connect storage(e.g.
+                            external,internal).
+                          type: string
+                        image:
+                          description: Image is the storage Docker image to deploy.
+                          type: string
+                        instances:
+                          description: Instance is the number of storage.
+                          format: int32
+                          type: integer
+                        resource:
+                          description: ResourceCnfig relevant settings
+                          properties:
+                            limit:
+                              type: string
+                            requests:
+                              type: string
+                          type: object
+                        security:
+                          description: Security relevant settings
+                          properties:
+                            tls:
+                              description: SSLConfig of  storage .
+                              type: boolean
+                            user:
+                              description: UserConfig of storage .
+                              properties:
+                                secretName:
+                                  description: SecretName of storage user .
+                                  type: string
+                              type: object
+                          type: object
+                        servicename:
+                          description: ServiceName relevant settings
+                          type: string
+                        type:
+                          description: Type of storage.
+                          type: string
+                        version:
+                          description: Version of storage.
+                          type: string
+                      type: object
+                    status:
+                      description: StorageStatus defines the observed state of Storage
+                      properties:
+                        conditions:
+                          description: Represents the latest available observations
+                            of the underlying statefulset's current state.
+                          items:
+                            description: StatefulSetCondition describes the state
+                              of a statefulset at a certain point.
+                            properties:
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: A human readable message indicating details
+                                  about the transition.
+                                type: string
+                              reason:
+                                description: The reason for the condition's last transition.
+                                type: string
+                              status:
+                                description: Status of the condition, one of True,
+                                  False, Unknown.
+                                type: string
+                              type:
+                                description: Type of statefulset condition.
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                      type: object
+                  type: object
+                name:
+                  description: Name relevant settings
+                  type: string
+              type: object
             version:
               description: Version of OAP.
               type: string
diff --git a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml b/config/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
similarity index 57%
copy from config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
copy to config/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
index a46a5fe..22c09a8 100644
--- a/config/dev/operator/crd/bases/operator.skywalking.apache.org_oapservers.yaml
+++ b/config/operator/crd/bases/operator.skywalking.apache.org_storages.yaml
@@ -23,42 +23,37 @@ metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.2.5
   creationTimestamp: null
-  name: oapservers.operator.skywalking.apache.org
+  name: storages.operator.skywalking.apache.org
 spec:
   additionalPrinterColumns:
-  - JSONPath: .spec.version
-    description: The version
-    name: Version
-    priority: 1
-    type: string
   - JSONPath: .spec.instances
     description: The number of expected instance
     name: Instances
     type: string
-  - JSONPath: .status.availableReplicas
-    description: The number of running
-    name: Running
+  - JSONPath: .spec.type
+    description: The type of strorage
+    name: Type
     type: string
-  - JSONPath: .status.address
-    description: The address of OAP server
-    name: Address
+  - JSONPath: .spec.version
+    description: The version
+    name: Version
     type: string
-  - JSONPath: .spec.image
-    name: Image
-    priority: 1
+  - JSONPath: .spec.connectType
+    description: the way to connect storage
+    name: ConnectType
     type: string
   group: operator.skywalking.apache.org
   names:
-    kind: OAPServer
-    listKind: OAPServerList
-    plural: oapservers
-    singular: oapserver
+    kind: Storage
+    listKind: StorageList
+    plural: storages
+    singular: storage
   scope: Namespaced
   subresources:
     status: {}
   validation:
     openAPIV3Schema:
-      description: OAPServer is the Schema for the oapservers API
+      description: Storage is the Schema for the storages API
       properties:
         apiVersion:
           description: 'APIVersion defines the versioned schema of this representation
@@ -73,10 +68,13 @@ spec:
         metadata:
           type: object
         spec:
-          description: OAPServerSpec defines the desired state of OAPServer
+          description: StorageSpec defines the desired state of Storage
           properties:
+            address:
+              description: Address of external storage address.
+              type: string
             config:
-              description: Config holds the OAP server configuration.
+              description: Config holds the Storage configuration.
               items:
                 description: EnvVar represents an environment variable present in
                   a Container.
@@ -178,131 +176,56 @@ spec:
                 - name
                 type: object
               type: array
+            connectType:
+              description: ConnectType is the way to connect storage(e.g. external,internal).
+              type: string
             image:
-              description: Image is the OAP Server Docker image to deploy.
+              description: Image is the storage Docker image to deploy.
               type: string
             instances:
-              description: Count is the number of OAP servers
+              description: Instance is the number of storage.
               format: int32
               type: integer
-            service:
-              description: Service relevant settings
+            resource:
+              description: ResourceCnfig relevant settings
               properties:
-                ingress:
-                  description: Ingress defines the behavior of an ingress
-                  properties:
-                    annotations:
-                      additionalProperties:
-                        type: string
-                      description: Annotations is an unstructured key value map stored
-                        with a resource that may be set by external tools to store
-                        and retrieve arbitrary metadata. They are not queryable and
-                        should be preserved when modifying objects.
-                      type: object
-                    host:
-                      description: Host is the fully qualified domain name of a network
-                        host, as defined by RFC 3986. Note the following deviations
-                        from the "host" part of the URI as defined in RFC 3986
-                      type: string
-                    ingressClassName:
-                      description: IngressClassName is the name of the IngressClass
-                        cluster resource. The associated IngressClass defines which
-                        controller will implement the resource. This replaces the
-                        deprecated `kubernetes.io/ingress.class` annotation. For backwards
-                        compatibility, when that annotation is set, it must be given
-                        precedence over this field. The controller may emit a warning
-                        if the field and annotation have different values. Implementations
-                        of this API should ignore Ingresses without a class specified.
-                        An IngressClass resource may be marked as default, which can
-                        be used to set a default value for this field. For more information,
-                        refer to the IngressClass documentation.
-                      type: string
-                    tls:
-                      description: TLS configuration. Currently the Ingress only supports
-                        a single TLS port, 443. If multiple members of this list specify
-                        different hosts, they will be multiplexed on the same port
-                        according to the hostname specified through the SNI TLS extension,
-                        if the ingress controller fulfilling the ingress supports
-                        SNI.
-                      items:
-                        description: IngressTLS describes the transport layer security
-                          associated with an Ingress.
-                        properties:
-                          hosts:
-                            description: Hosts are a list of hosts included in the
-                              TLS certificate. The values in this list must match
-                              the name/s used in the tlsSecret. Defaults to the wildcard
-                              host setting for the loadbalancer controller fulfilling
-                              this Ingress, if left unspecified.
-                            items:
-                              type: string
-                            type: array
-                            x-kubernetes-list-type: atomic
-                          secretName:
-                            description: SecretName is the name of the secret used
-                              to terminate TLS traffic on port 443. Field is left
-                              optional to allow TLS routing based on SNI hostname
-                              alone. If the SNI host in a listener conflicts with
-                              the "Host" header field used by an IngressRule, the
-                              SNI host is used for termination and value of the Host
-                              header is used for routing.
-                            type: string
-                        type: object
-                      type: array
-                  type: object
-                template:
-                  description: ServiceTemplate defines the behavior of a service.
+                limit:
+                  type: string
+                requests:
+                  type: string
+              type: object
+            security:
+              description: Security relevant settings
+              properties:
+                tls:
+                  description: SSLConfig of  storage .
+                  type: boolean
+                user:
+                  description: UserConfig of storage .
                   properties:
-                    clusterIP:
-                      description: clusterIP is the IP address of the service and
-                        is usually assigned randomly.
-                      type: string
-                    externalIPs:
-                      description: externalIPs is a list of IP addresses for which
-                        nodes in the cluster will also accept traffic for this service.
-                      items:
-                        type: string
-                      type: array
-                    loadBalancerIP:
-                      description: 'Only applies to Service Type: LoadBalancer LoadBalancer
-                        will get created with the IP specified in this field.'
-                      type: string
-                    loadBalancerSourceRanges:
-                      description: If specified and supported by the platform, this
-                        will restrict traffic through the cloud-provider load-balancer
-                        will be restricted to the specified client IPs.
-                      items:
-                        type: string
-                      type: array
-                    type:
-                      description: type determines how the Service is exposed.
+                    secretName:
+                      description: SecretName of storage user .
                       type: string
                   type: object
               type: object
+            servicename:
+              description: ServiceName relevant settings
+              type: string
+            type:
+              description: Type of storage.
+              type: string
             version:
-              description: Version of OAP.
+              description: Version of storage.
               type: string
-          required:
-          - instances
-          - version
           type: object
         status:
-          description: OAPServerStatus defines the observed state of OAPServer
+          description: StorageStatus defines the observed state of Storage
           properties:
-            address:
-              description: Address indicates the entry of OAP server which ingresses
-                data
-              type: string
-            availableReplicas:
-              description: Total number of available pods (ready for at least minReadySeconds)
-                targeted by this deployment.
-              format: int32
-              type: integer
             conditions:
               description: Represents the latest available observations of the underlying
-                deployment's current state.
+                statefulset's current state.
               items:
-                description: DeploymentCondition describes the state of a deployment
+                description: StatefulSetCondition describes the state of a statefulset
                   at a certain point.
                 properties:
                   lastTransitionTime:
@@ -310,10 +233,6 @@ spec:
                       to another.
                     format: date-time
                     type: string
-                  lastUpdateTime:
-                    description: The last time this condition was updated.
-                    format: date-time
-                    type: string
                   message:
                     description: A human readable message indicating details about
                       the transition.
@@ -325,7 +244,7 @@ spec:
                     description: Status of the condition, one of True, False, Unknown.
                     type: string
                   type:
-                    description: Type of deployment condition.
+                    description: Type of statefulset condition.
                     type: string
                 required:
                 - status
diff --git a/config/operator/crd/kustomization.yaml b/config/operator/crd/kustomization.yaml
index d3a42d6..a63205b 100644
--- a/config/operator/crd/kustomization.yaml
+++ b/config/operator/crd/kustomization.yaml
@@ -22,6 +22,7 @@ resources:
 - bases/operator.skywalking.apache.org_oapservers.yaml
 - bases/operator.skywalking.apache.org_uis.yaml
 - bases/operator.skywalking.apache.org_fetchers.yaml
+- bases/operator.skywalking.apache.org_storages.yaml
 # +kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -31,6 +32,8 @@ patchesStrategicMerge:
 - patches/cainjection_in_uis.yaml
 - patches/webhook_in_fetchers.yaml
 - patches/cainjection_in_fetchers.yaml
+- patches/webhook_in_storages.yaml
+- patches/cainjection_in_storages.yaml
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
 configurations:
diff --git a/PROJECT b/config/operator/crd/patches/cainjection_in_storages.yaml
similarity index 71%
copy from PROJECT
copy to config/operator/crd/patches/cainjection_in_storages.yaml
index 60b7a6e..cb0439d 100644
--- a/PROJECT
+++ b/config/operator/crd/patches/cainjection_in_storages.yaml
@@ -14,19 +14,12 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: storages.operator.skywalking.apache.org
diff --git a/PROJECT b/config/operator/crd/patches/webhook_in_storages.yaml
similarity index 54%
copy from PROJECT
copy to config/operator/crd/patches/webhook_in_storages.yaml
index 60b7a6e..1779747 100644
--- a/PROJECT
+++ b/config/operator/crd/patches/webhook_in_storages.yaml
@@ -14,19 +14,22 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: storages.operator.skywalking.apache.org
+spec:
+  preserveUnknownFields: false
+  conversion:
+    strategy: Webhook
+    webhookClientConfig:
+      # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
+      # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
+      caBundle: Cg==
+      service:
+        namespace: system
+        name: webhook-service
+        path: /convert
diff --git a/config/operator/rbac/role.yaml b/config/operator/rbac/role.yaml
index 94f4c1b..b554d98 100644
--- a/config/operator/rbac/role.yaml
+++ b/config/operator/rbac/role.yaml
@@ -69,6 +69,18 @@ rules:
   - update
   - watch
 - apiGroups:
+  - apps
+  resources:
+  - statefulset
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
   - coordination.k8s.io
   resources:
   - leases
@@ -131,6 +143,26 @@ rules:
 - apiGroups:
   - operator.skywalking.apache.org
   resources:
+  - storages
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - storages/status
+  verbs:
+  - get
+  - patch
+  - update
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
   - uis
   verbs:
   - create
diff --git a/PROJECT b/config/operator/rbac/storage_editor_role.yaml
similarity index 66%
copy from PROJECT
copy to config/operator/rbac/storage_editor_role.yaml
index 60b7a6e..bf6a8fc 100644
--- a/PROJECT
+++ b/config/operator/rbac/storage_editor_role.yaml
@@ -14,19 +14,29 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+# permissions for end users to edit storages.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: storage-editor-role
+rules:
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - storages
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - storages/status
+  verbs:
+  - get
+
diff --git a/PROJECT b/config/operator/rbac/storage_viewer_role.yaml
similarity index 69%
copy from PROJECT
copy to config/operator/rbac/storage_viewer_role.yaml
index 60b7a6e..2308ee3 100644
--- a/PROJECT
+++ b/config/operator/rbac/storage_viewer_role.yaml
@@ -14,19 +14,25 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+# permissions for end users to view storages.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: storage-viewer-role
+rules:
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - storages
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - operator.skywalking.apache.org
+  resources:
+  - storages/status
+  verbs:
+  - get
+
diff --git a/config/operator/webhook/manifests.yaml b/config/operator/webhook/manifests.yaml
index ff62b0d..a3a91a0 100644
--- a/config/operator/webhook/manifests.yaml
+++ b/config/operator/webhook/manifests.yaml
@@ -82,6 +82,24 @@ webhooks:
     service:
       name: webhook-service
       namespace: system
+      path: /mutate-operator-skywalking-apache-org-v1alpha1-storage
+  failurePolicy: Fail
+  name: mstorage.kb.io
+  rules:
+  - apiGroups:
+    - operator.skywalking.apache.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    - UPDATE
+    resources:
+    - storages
+- clientConfig:
+    caBundle: Cg==
+    service:
+      name: webhook-service
+      namespace: system
       path: /mutate-operator-skywalking-apache-org-v1alpha1-ui
   failurePolicy: Fail
   name: mui.kb.io
@@ -144,6 +162,24 @@ webhooks:
     service:
       name: webhook-service
       namespace: system
+      path: /validate-operator-skywalking-apache-org-v1alpha1-storage
+  failurePolicy: Fail
+  name: vstorage.kb.io
+  rules:
+  - apiGroups:
+    - operator.skywalking.apache.org
+    apiVersions:
+    - v1alpha1
+    operations:
+    - CREATE
+    - UPDATE
+    resources:
+    - storages
+- clientConfig:
+    caBundle: Cg==
+    service:
+      name: webhook-service
+      namespace: system
       path: /validate-operator-skywalking-apache-org-v1alpha1-ui
   failurePolicy: Fail
   name: vui.kb.io
diff --git a/controllers/operator/oapserver_controller.go b/controllers/operator/oapserver_controller.go
index 6538cb7..20b38f7 100644
--- a/controllers/operator/oapserver_controller.go
+++ b/controllers/operator/oapserver_controller.go
@@ -20,6 +20,7 @@ package controllers
 import (
 	"context"
 	"fmt"
+	"net/url"
 	"time"
 
 	"github.com/go-logr/logr"
@@ -54,6 +55,8 @@ type OAPServerReconciler struct {
 // +kubebuilder:rbac:groups="",resources=services;serviceaccounts,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;create;update
 // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=*
+// +kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=storages,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=storages/status,verbs=get;update;patch
 
 func (r *OAPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
 	log := r.Log.WithValues("oapserver", req.NamespacedName)
@@ -75,6 +78,9 @@ func (r *OAPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
 		GVK:      operatorv1alpha1.GroupVersion.WithKind("OAPServer"),
 		Recorder: r.Recorder,
 	}
+
+	r.InjectStorage(ctx, log, &oapServer)
+
 	if err := app.ApplyAll(ctx, ff, log); err != nil {
 		return ctrl.Result{}, err
 	}
@@ -120,6 +126,74 @@ func (r *OAPServerReconciler) checkState(ctx context.Context, log logr.Logger, o
 	return errCol.Error()
 }
 
+//InjectStorage Inject Storage
+func (r *OAPServerReconciler) InjectStorage(ctx context.Context, log logr.Logger, oapServer *operatorv1alpha1.OAPServer) {
+	storage := &operatorv1alpha1.Storage{}
+	err := r.Client.Get(ctx, client.ObjectKey{Namespace: oapServer.Namespace, Name: oapServer.Spec.StorageConfig.Name}, storage)
+	if err == nil {
+		r.ConfigStorage(ctx, log, storage, oapServer)
+		log.Info("success inject storage")
+	} else {
+		log.Info("fail inject storage")
+	}
+}
+
+func (r *OAPServerReconciler) ConfigStorage(ctx context.Context, log logr.Logger, s *operatorv1alpha1.Storage, o *operatorv1alpha1.OAPServer) {
+	user, tls := s.Spec.Security.User, s.Spec.Security.TLS
+	SwStorageEsHTTPProtocol := "http"
+	SwEsUser := ""
+	SwEsPassword := ""
+	SwStorageEsSslJksPath := ""
+	SwStorageEsSslJksPass := ""
+	SwStorageEsClusterNodes := ""
+	o.Spec.StorageConfig.Storage = *s
+	if user.SecretName != "" {
+		if user.SecretName == "default" {
+			SwEsUser = "elastic"
+			SwEsPassword = "changeme"
+		} else {
+			usersecret := &core.Secret{}
+			if err := r.Client.Get(ctx, client.ObjectKey{Namespace: s.Namespace, Name: user.SecretName}, usersecret); err != nil && !apierrors.IsNotFound(err) {
+				log.Info("fail get usersecret ")
+			}
+			for k, v := range usersecret.Data {
+				if k == "username" {
+					SwEsUser = string(v)
+				} else if k == "password" {
+					SwEsPassword = string(v)
+				}
+			}
+		}
+	}
+	if tls {
+		SwStorageEsHTTPProtocol = "https"
+		SwStorageEsSslJksPath = "/skywalking/p12/storage.p12"
+		SwStorageEsClusterNodes = "skywalking-storage"
+	} else {
+		SwStorageEsClusterNodes = s.Name + "-" + s.Spec.Type
+	}
+
+	o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE", Value: s.Spec.Type})
+	if user.SecretName != "" {
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_ES_USER", Value: SwEsUser})
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_ES_PASSWORD", Value: SwEsPassword})
+	}
+	if tls {
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_SSL_JKS_PATH", Value: SwStorageEsSslJksPath})
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_SSL_JKS_PASS", Value: SwStorageEsSslJksPass})
+	}
+	if apiequal.Semantic.DeepDerivative(s.Spec.ConnectType, "external") {
+		parseurl, _ := url.Parse(s.Spec.ConnectAddress)
+		SwStorageEsHTTPProtocol = parseurl.Scheme
+		SwStorageEsClusterNodes = parseurl.Host
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_HTTP_PROTOCOL", Value: SwStorageEsHTTPProtocol})
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_CLUSTER_NODES", Value: SwStorageEsClusterNodes})
+	} else {
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_HTTP_PROTOCOL", Value: SwStorageEsHTTPProtocol})
+		o.Spec.Config = append(o.Spec.Config, core.EnvVar{Name: "SW_STORAGE_ES_CLUSTER_NODES", Value: SwStorageEsClusterNodes + ":9200"})
+	}
+}
+
 func (r *OAPServerReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&operatorv1alpha1.OAPServer{}).
diff --git a/controllers/operator/storage_controller.go b/controllers/operator/storage_controller.go
new file mode 100644
index 0000000..ca21e4e
--- /dev/null
+++ b/controllers/operator/storage_controller.go
@@ -0,0 +1,314 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package controllers
+
+import (
+	"bytes"
+	"context"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/pem"
+	"fmt"
+	"time"
+
+	"github.com/go-logr/logr"
+	apps "k8s.io/api/apps/v1"
+	certv1beta1 "k8s.io/api/certificates/v1beta1"
+	core "k8s.io/api/core/v1"
+	apiequal "k8s.io/apimachinery/pkg/api/equality"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	kubeclient "k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/record"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"software.sslmate.com/src/go-pkcs12"
+
+	operatorv1alpha1 "github.com/apache/skywalking-swck/apis/operator/v1alpha1"
+	"github.com/apache/skywalking-swck/pkg/kubernetes"
+)
+
+// StorageReconciler reconciles a Storage object
+type StorageReconciler struct {
+	client.Client
+	Log        logr.Logger
+	Scheme     *runtime.Scheme
+	FileRepo   kubernetes.Repo
+	Recorder   record.EventRecorder
+	RestConfig *rest.Config
+}
+
+// +kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=storages,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=operator.skywalking.apache.org,resources=storages/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=apps,resources=statefulset,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups="",resources=services;serviceaccounts,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;create;update
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=*
+
+func (r *StorageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	log := r.Log.WithValues("Storage", req.NamespacedName)
+	log.Info("=====================reconcile started================================")
+
+	storage := operatorv1alpha1.Storage{}
+	if err := r.Client.Get(ctx, req.NamespacedName, &storage); err != nil {
+		return ctrl.Result{}, client.IgnoreNotFound(err)
+	}
+	if storage.Spec.ConnectType == "external" {
+		return ctrl.Result{RequeueAfter: schedDuration}, nil
+	}
+
+	r.createCert(ctx, log, &storage)
+	r.checkSecurity(ctx, log, &storage)
+
+	ff, err := r.FileRepo.GetFilesRecursive(storage.Spec.Type + "/templates")
+	if err != nil {
+		log.Error(err, "failed to load resource templates")
+		return ctrl.Result{}, err
+	}
+	app := kubernetes.Application{
+		Client:   r.Client,
+		FileRepo: r.FileRepo,
+		CR:       &storage,
+		GVK:      operatorv1alpha1.GroupVersion.WithKind("Storage"),
+		Recorder: r.Recorder,
+	}
+	if err := app.ApplyAll(ctx, ff, log); err != nil {
+		return ctrl.Result{}, err
+	}
+	if err := r.checkState(ctx, log, &storage); err != nil {
+		log.Error(err, "failed to check sub resources state")
+		return ctrl.Result{}, err
+	}
+
+	return ctrl.Result{RequeueAfter: schedDuration}, nil
+}
+
+func (r *StorageReconciler) checkState(ctx context.Context, log logr.Logger, storage *operatorv1alpha1.Storage) error {
+	overlay := operatorv1alpha1.StorageStatus{}
+	statefulset := apps.StatefulSet{}
+	errCol := new(kubernetes.ErrorCollector)
+	object := client.ObjectKey{Namespace: storage.Namespace, Name: storage.Name + "-" + storage.Spec.Type}
+	if err := r.Client.Get(ctx, object, &statefulset); err != nil && !apierrors.IsNotFound(err) {
+		errCol.Collect(fmt.Errorf("failed to get statefulset: %w", err))
+	} else {
+		overlay.Conditions = statefulset.Status.Conditions
+	}
+
+	if apiequal.Semantic.DeepDerivative(overlay, storage.Status) {
+		log.Info("Status keeps the same as before")
+	}
+	storage.Status = overlay
+	storage.Kind = "Storage"
+	if err := kubernetes.ApplyOverlay(storage, &operatorv1alpha1.Storage{Status: overlay}); err != nil {
+		errCol.Collect(fmt.Errorf("failed to apply overlay: %w", err))
+		return errCol.Error()
+	}
+	if err := r.Status().Update(ctx, storage); err != nil {
+		errCol.Collect(fmt.Errorf("failed to update status of es: %w", err))
+	}
+	log.Info("updated Status sub resource")
+	return errCol.Error()
+}
+
+func (r *StorageReconciler) checkSecurity(ctx context.Context, log logr.Logger, s *operatorv1alpha1.Storage) {
+	user, tls := s.Spec.Security.User, s.Spec.Security.TLS
+	if user.SecretName != "" {
+		if user.SecretName == "default" {
+			s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "ELASTIC_USER", Value: "elastic"})
+			s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "ELASTIC_PASSWORD", Value: "changeme"})
+		} else {
+			usersecret := core.Secret{}
+			if err := r.Client.Get(ctx, client.ObjectKey{Namespace: s.Namespace, Name: user.SecretName}, &usersecret); err != nil && !apierrors.IsNotFound(err) {
+				log.Info("fail get usersecret ")
+			}
+			for k, v := range usersecret.Data {
+				if k == "username" {
+					s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "ELASTIC_USER", Value: string(v)})
+				} else if k == "password" {
+					s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "ELASTIC_PASSWORD", Value: string(v)})
+				}
+			}
+		}
+	}
+	if tls {
+		s.Spec.ServiceName = "skywalking-storage"
+	} else {
+		s.Spec.ServiceName = s.Name + "-" + s.Spec.Type
+	}
+	if s.Spec.ResourceCnfig.Limit == "" && s.Spec.ResourceCnfig.Requests == "" {
+		s.Spec.ResourceCnfig.Limit, s.Spec.ResourceCnfig.Requests = "1000m", "100m"
+	}
+	clusterInitialMasterNodes := s.Name + "-elasticsearch7-0" + "," + s.Name + "-elasticsearch7-1"
+	esJavaOptsValue := "-Xms512m -Xmx512m"
+	s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "discovery.seed_hosts", Value: s.Spec.ServiceName})
+	s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "cluster.initial_master_nodes", Value: clusterInitialMasterNodes})
+	s.Spec.Config = append(s.Spec.Config, core.EnvVar{Name: "ES_JAVA_OPTS", Value: esJavaOptsValue})
+}
+
+func (r *StorageReconciler) createCert(ctx context.Context, log logr.Logger, s *operatorv1alpha1.Storage) {
+	clientset, err := kubeclient.NewForConfig(r.RestConfig)
+	if err != nil {
+		return
+	}
+	existSecret, err := clientset.CoreV1().Secrets(s.Namespace).Get(ctx, "skywalking-storage", metav1.GetOptions{})
+	if err != nil {
+		log.Info("fail get skywalking-storage secret")
+		return
+	}
+	if existSecret.Name != "" {
+		_, verifyCert, decodeErr := pkcs12.Decode(existSecret.Data["storage.p12"], "")
+		if decodeErr != nil {
+			log.Info("decode storage.p12 error")
+			return
+		}
+		if time.Until(verifyCert.NotAfter).Hours() < 24 {
+			log.Info("storage cert will expire,the storage cert will re-generate!")
+		} else {
+			return
+		}
+	}
+	key, err := rsa.GenerateKey(rand.Reader, 4096)
+	if err != nil {
+		log.Info("fail generate privatekey")
+		return
+	}
+	subj := pkix.Name{
+		CommonName:         "skywalking-storage",
+		Country:            []string{"CN"},
+		Province:           []string{"ZJ"},
+		Locality:           []string{"HZ"},
+		Organization:       []string{"Skywalking"},
+		OrganizationalUnit: []string{"Skywalking"},
+	}
+	asn1Subj, err := asn1.Marshal(subj.ToRDNSequence())
+	if err != nil {
+		return
+	}
+	template := x509.CertificateRequest{
+		RawSubject:         asn1Subj,
+		SignatureAlgorithm: x509.SHA256WithRSA,
+	}
+	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, key)
+	if err != nil {
+		log.Info("fail create certificaterequest")
+		return
+	}
+	buffer := new(bytes.Buffer)
+	err = pem.Encode(buffer, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
+	if err != nil {
+		log.Info("fail encode CERTIFICATE REQUEST")
+		return
+	}
+	singername := "kubernetes.io/kube-apiserver-client"
+	request := certv1beta1.CertificateSigningRequest{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "CertificateSigningRequest",
+			APIVersion: "certificates.k8s.io/v1beta1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "storage-csr",
+		},
+		Spec: certv1beta1.CertificateSigningRequestSpec{
+			Groups:     []string{"system:authenticated"},
+			Request:    buffer.Bytes(),
+			SignerName: &singername,
+			Usages:     []certv1beta1.KeyUsage{certv1beta1.UsageClientAuth},
+		},
+	}
+	err = clientset.CertificatesV1beta1().CertificateSigningRequests().Delete(ctx, "storage-csr", metav1.DeleteOptions{})
+	if err != nil {
+		log.Info("fail delete csr")
+		return
+	}
+	csr, err := clientset.CertificatesV1beta1().CertificateSigningRequests().Create(ctx, &request, metav1.CreateOptions{})
+	if err != nil {
+		log.Info("fail create csr")
+		return
+	}
+	condition := certv1beta1.CertificateSigningRequestCondition{
+		Type:    "Approved",
+		Reason:  "ApprovedBySkywalkingStorage",
+		Message: "Approved by skywalking storage controller",
+	}
+	csr.Status.Conditions = append(csr.Status.Conditions, condition)
+	updateapproval, err := clientset.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(ctx, csr, metav1.UpdateOptions{})
+	if err != nil {
+		log.Info("fail update approval:", updateapproval)
+		return
+	}
+	for {
+		csr, err = clientset.CertificatesV1beta1().CertificateSigningRequests().Get(ctx, "storage-csr", metav1.GetOptions{})
+		if err != nil {
+			log.Info("fail get storage-csr")
+			return
+		}
+		if csr.Status.Certificate != nil {
+			break
+		}
+	}
+	block, _ := pem.Decode(csr.Status.Certificate)
+	cert, err := x509.ParseCertificate(block.Bytes)
+
+	if err != nil {
+		log.Info("fail parse certificate")
+		return
+	}
+	p12, err := pkcs12.Encode(rand.Reader, key, cert, nil, "")
+
+	if err != nil {
+		log.Info("fail encode pkcs12")
+		return
+	}
+	err = clientset.CoreV1().Secrets(s.Namespace).Delete(ctx, "skywalking-storage", metav1.DeleteOptions{})
+	if err != nil {
+		log.Info("fail delete secret skywalking-storage")
+		return
+	}
+	data := make(map[string][]byte)
+	data["storage.p12"] = p12
+	secret := core.Secret{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Secret",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "skywalking-storage",
+			Namespace: s.Namespace,
+		},
+		Data: data,
+		Type: "Opaque",
+	}
+	storageTLSSecret, err := clientset.CoreV1().Secrets(s.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
+	if err != nil {
+		log.Info("fail create secret skywalking-storage")
+		return
+	}
+	log.Info("success create secret skywalking-storage", storageTLSSecret.Name)
+}
+
+func (r *StorageReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&operatorv1alpha1.Storage{}).
+		Owns(&core.Service{}).
+		Complete(r)
+}
diff --git a/dist/LICENSE b/dist/LICENSE
index cd7adfb..21c667b 100644
--- a/dist/LICENSE
+++ b/dist/LICENSE
@@ -230,4 +230,5 @@ BSD licenses
 The following components are provided under a BSD license. See project link for details.
 The text of each license is also included at licenses/LICENSE-[project].txt.
 
-	json-patch v4.5.0 https://github.com/evanphx/json-patch BSD 3-Clause
\ No newline at end of file
+	json-patch v4.5.0 https://github.com/evanphx/json-patch BSD 3-Clause
+    software.sslmate.com/src/go-pkcs12 v0.0.0 https://github.com/SSLMate/go-pkcs12
diff --git a/go.mod b/go.mod
index c3aa8ce..c068891 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	k8s.io/klog/v2 v2.4.0
 	k8s.io/metrics v0.20.1
 	sigs.k8s.io/controller-runtime v0.7.0
+	software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
 )
 
 replace github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.4.1
diff --git a/go.sum b/go.sum
index 089cdf8..c365456 100644
--- a/go.sum
+++ b/go.sum
@@ -553,8 +553,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -673,6 +674,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -882,3 +885,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
+software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
diff --git a/main.go b/main.go
index 2efbf53..20f2469 100644
--- a/main.go
+++ b/main.go
@@ -28,7 +28,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook"
 
 	operatorv1alpha1 "github.com/apache/skywalking-swck/apis/operator/v1alpha1"
-	controllers "github.com/apache/skywalking-swck/controllers/operator"
 	operatorcontroller "github.com/apache/skywalking-swck/controllers/operator"
 	"github.com/apache/skywalking-swck/pkg/operator/repo"
 	// +kubebuilder:scaffold:imports
@@ -73,7 +72,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	if err = (&controllers.OAPServerReconciler{
+	if err = (&operatorcontroller.OAPServerReconciler{
 		Client:   mgr.GetClient(),
 		Log:      ctrl.Log.WithName("controllers").WithName("OAPServer"),
 		Scheme:   mgr.GetScheme(),
@@ -82,7 +81,7 @@ func main() {
 		setupLog.Error(err, "unable to create controller", "controller", "OAPServer")
 		os.Exit(1)
 	}
-	if err = (&controllers.UIReconciler{
+	if err = (&operatorcontroller.UIReconciler{
 		Client:   mgr.GetClient(),
 		Log:      ctrl.Log.WithName("controllers").WithName("UI"),
 		Scheme:   mgr.GetScheme(),
@@ -114,6 +113,21 @@ func main() {
 		setupLog.Error(err, "unable to create webhook", "webhook", "Fetcher")
 		os.Exit(1)
 	}
+
+	if err = (&operatorcontroller.StorageReconciler{
+		Client:     mgr.GetClient(),
+		Log:        ctrl.Log.WithName("controllers").WithName("Storage"),
+		Scheme:     mgr.GetScheme(),
+		FileRepo:   repo.NewRepo("storage"),
+		RestConfig: mgr.GetConfig(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "Storage")
+		os.Exit(1)
+	}
+	if err = (&operatorv1alpha1.Storage{}).SetupWebhookWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create webhook", "webhook", "storage")
+		os.Exit(1)
+	}
 	// +kubebuilder:scaffold:builder
 
 	// register a webhook to enable the agent injector,
diff --git a/pkg/operator/manifests/oapserver/templates/deployment.yaml b/pkg/operator/manifests/oapserver/templates/deployment.yaml
index 8d32689..1589619 100644
--- a/pkg/operator/manifests/oapserver/templates/deployment.yaml
+++ b/pkg/operator/manifests/oapserver/templates/deployment.yaml
@@ -25,6 +25,7 @@ metadata:
     operator.skywalking.apache.org/oap-server-name: {{ .Name }}
     operator.skywalking.apache.org/application: oapserver
     operator.skywalking.apache.org/component: deployment
+
 spec:
   replicas: {{ .Spec.Instances }}
   minReadySeconds: 5
@@ -44,63 +45,73 @@ spec:
       affinity:
         podAntiAffinity:
           preferredDuringSchedulingIgnoredDuringExecution:
-          - weight: 1
-            podAffinityTerm:
-              topologyKey: kubernetes.io/hostname
-              labelSelector:
-                matchLabels:
-                  app: oap
-                  operator.skywalking.apache.org/oap-server-name: {{ .Name }}
+            - weight: 1
+              podAffinityTerm:
+                topologyKey: kubernetes.io/hostname
+                labelSelector:
+                  matchLabels:
+                    app: oap
+                    operator.skywalking.apache.org/oap-server-name: {{ .Name }}
       containers:
-      - name: oap
-        image: {{ .Spec.Image }}
-        imagePullPolicy: IfNotPresent
-        ports:
-        - containerPort: 11800
-          name: grpc
-        - containerPort: 12800
-          name: rest
-        - containerPort: 1234
-          name: http-monitoring
-        livenessProbe:
-          initialDelaySeconds: 10
-          timeoutSeconds: 10
-          periodSeconds: 30
-          failureThreshold: 10
-          successThreshold: 1
-          exec:
-            command:
-              - /skywalking/bin/swctl
-              - ch
-        readinessProbe:
-          initialDelaySeconds: 10
-          timeoutSeconds: 10
-          periodSeconds: 30
-          failureThreshold: 10
-          successThreshold: 1
-          exec:
-            command:
-              - /skywalking/bin/swctl
-              - ch
-        env:
-        - name: JAVA_OPTS
-          value: -Xmx2048M
-        - name: SW_CLUSTER
-          value: kubernetes
-        - name: SW_CLUSTER_K8S_NAMESPACE
-          value: "{{ .Namespace }}"
-        - name: SW_CLUSTER_K8S_LABEL
-          value: "app=oap,operator.skywalking.apache.org/oap-server-name={{ .Name }}"
-        - name: SKYWALKING_COLLECTOR_UID
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.uid
-        - name: SW_TELEMETRY
-          value: prometheus
-        - name: SW_HEALTH_CHECKER
-          value: default
-        {{range .Spec.Config}}
-        - name: {{ .Name }}
-          value: {{ .Value }}
-        {{end}}
-
+        - name: oap
+          image: {{ .Spec.Image }}
+          imagePullPolicy: IfNotPresent
+          ports:
+            - containerPort: 11800
+              name: grpc
+            - containerPort: 12800
+              name: rest
+            - containerPort: 1234
+              name: http-monitoring
+          livenessProbe:
+            initialDelaySeconds: 10
+            timeoutSeconds: 10
+            periodSeconds: 30
+            failureThreshold: 10
+            successThreshold: 1
+            exec:
+              command:
+                - /skywalking/bin/swctl
+                - ch
+          readinessProbe:
+            initialDelaySeconds: 10
+            timeoutSeconds: 10
+            periodSeconds: 30
+            failureThreshold: 10
+            successThreshold: 1
+            exec:
+              command:
+                - /skywalking/bin/swctl
+                - ch
+          {{if .Spec.StorageConfig.Storage.Spec.Security.TLS}}
+          volumeMounts:
+            - name: cert
+              mountPath: /skywalking/p12
+          {{end}}
+          env:
+            - name: JAVA_OPTS
+              value: -Xmx2048M
+            - name: SW_CLUSTER
+              value: kubernetes
+            - name: SW_CLUSTER_K8S_NAMESPACE
+              value: "{{ .Namespace }}"
+            - name: SW_CLUSTER_K8S_LABEL
+              value: "app=oap,operator.skywalking.apache.org/oap-server-name={{ .Name }}"
+            - name: SKYWALKING_COLLECTOR_UID
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.uid
+            - name: SW_TELEMETRY
+              value: prometheus
+            - name: SW_HEALTH_CHECKER
+              value: default
+          {{range .Spec.Config}}
+          - name: {{ .Name }}
+            value: {{ .Value }}
+          {{end}}
+      {{if .Spec.StorageConfig.Storage.Spec.Security.TLS}}
+      volumes:
+        - name: cert
+          secret:
+            secretName:  "skywalking-storage"
+      {{end}}
diff --git a/config/operator/crd/kustomization.yaml b/pkg/operator/manifests/storage/elasticsearch7/templates/configmap.yaml
similarity index 51%
copy from config/operator/crd/kustomization.yaml
copy to pkg/operator/manifests/storage/elasticsearch7/templates/configmap.yaml
index d3a42d6..664ca82 100644
--- a/config/operator/crd/kustomization.yaml
+++ b/pkg/operator/manifests/storage/elasticsearch7/templates/configmap.yaml
@@ -15,23 +15,26 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# This kustomization.yaml is not intended to be run by itself,
-# since it depends on service name and namespace that are out of this kustomize package.
-# It should be run by config/default
-resources:
-- bases/operator.skywalking.apache.org_oapservers.yaml
-- bases/operator.skywalking.apache.org_uis.yaml
-- bases/operator.skywalking.apache.org_fetchers.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Name }}-config
+  namespace: {{ .Namespace }}
+data:
+  elasticsearch.yml: |
+      network.host: 0.0.0.0
+      {{ if .Spec.Security.User.SecretName }}
+      xpack.security.enabled: true
+      xpack.security.transport.ssl.enabled: true
+      xpack.security.transport.ssl.verification_mode: certificate
+      xpack.security.transport.ssl.keystore.path: storage.p12
+      xpack.security.transport.ssl.truststore.path: storage.p12
+      {{end}}
+      {{ if .Spec.Security.TLS }}
+      xpack.security.http.ssl.enabled: true
+      xpack.security.http.ssl.verification_mode: certificate
+      xpack.security.http.ssl.keystore.path: storage.p12
+      xpack.security.http.ssl.truststore.path: storage.p12
+      {{end}}
 
-patchesStrategicMerge:
-- patches/webhook_in_oapservers.yaml
-- patches/cainjection_in_oapservers.yaml
-- patches/webhook_in_uis.yaml
-- patches/cainjection_in_uis.yaml
-- patches/webhook_in_fetchers.yaml
-- patches/cainjection_in_fetchers.yaml
 
-# the following config is for teaching kustomize how to do kustomization for CRDs.
-configurations:
-- kustomizeconfig.yaml
diff --git a/PROJECT b/pkg/operator/manifests/storage/elasticsearch7/templates/service.yaml
similarity index 62%
copy from PROJECT
copy to pkg/operator/manifests/storage/elasticsearch7/templates/service.yaml
index 60b7a6e..ea83d23 100644
--- a/PROJECT
+++ b/pkg/operator/manifests/storage/elasticsearch7/templates/service.yaml
@@ -14,19 +14,25 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+kind: Service
+apiVersion: v1
+metadata:
+  name:  {{ .Spec.ServiceName }}
+  namespace: {{ .Namespace }}
+  labels:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: service
+spec:
+  clusterIP: None
+  selector:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+  ports:
+    - name: rest
+      port: 9200
+    - name: inter-node
+      port: 9300
+
diff --git a/PROJECT b/pkg/operator/manifests/storage/elasticsearch7/templates/service_account.yaml
similarity index 73%
copy from PROJECT
copy to pkg/operator/manifests/storage/elasticsearch7/templates/service_account.yaml
index 60b7a6e..627a4e4 100644
--- a/PROJECT
+++ b/pkg/operator/manifests/storage/elasticsearch7/templates/service_account.yaml
@@ -14,19 +14,13 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
 
-domain: skywalking.apache.org
-multigroup: true
-repo: github.com/apache/skywalking-swck
-resources:
-- group: operator
-  kind: OAPServer
-  version: v1alpha1
-- group: operator
-  kind: UI
-  version: v1alpha1
-- group: operator
-  kind: Fetcher
-  version: v1alpha1
-version: "2"
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ .Name }}-elasticsearch7
+  namespace: {{ .Namespace }}
+  labels:
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: rbac
diff --git a/pkg/operator/manifests/storage/elasticsearch7/templates/statefulset.yaml b/pkg/operator/manifests/storage/elasticsearch7/templates/statefulset.yaml
new file mode 100644
index 0000000..1712bc4
--- /dev/null
+++ b/pkg/operator/manifests/storage/elasticsearch7/templates/statefulset.yaml
@@ -0,0 +1,107 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: {{ .Name }}-elasticsearch7
+  namespace: {{ .Namespace }}
+  labels:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: statefulset
+spec:
+  serviceName:  {{ .Spec.ServiceName }}
+  replicas: {{ .Spec.Instances }}
+  selector:
+    matchLabels:
+      app: es
+      operator.skywalking.apache.org/es-name: {{ .Name }}
+  template:
+    metadata:
+      labels:
+        app: es
+        operator.skywalking.apache.org/es-name: {{ .Name }}
+        operator.skywalking.apache.org/application: elasticsearch
+        operator.skywalking.apache.org/component: statefulset
+    spec:
+      serviceAccountName: {{ .Name }}-elasticsearch7
+      affinity:
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+              - labelSelector:
+                matchExpressions:
+                  - key: app
+                    operator: In
+                    values:
+                      - "es"
+                topologyKey: kubernetes.io/hostname
+      containers:
+        - name: elasticsearch
+          image: {{ .Spec.Image }}
+          resources:
+            limits:
+              cpu: {{ .Spec.ResourceCnfig.Limit }}
+            requests:
+              cpu: {{ .Spec.ResourceCnfig.Requests }}
+          imagePullPolicy: IfNotPresent
+          ports:
+            - containerPort: 9200
+              name: rest
+              protocol: TCP
+            - containerPort: 9300
+              name: inter-node
+              protocol: TCP
+          volumeMounts:
+            - name: config
+              mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
+              subPath: elasticsearch.yml
+            {{ if .Spec.Security.User.SecretName }}
+            - name: cert
+              mountPath: "/usr/share/elasticsearch/config/storage.p12"
+              subPath: storage.p12
+            {{end}}
+          env:
+            - name: cluster.name
+              value: "{{ .Name }}-skywalking-es"
+            - name: node.name
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: thread_pool.write.queue_size
+              value: "1000"
+            {{ range .Spec.Config }}
+            - name: {{ .Name }}
+              value: {{ .Value }}
+            {{end}}
+          livenessProbe:
+            httpGet:
+              path: /_cluster/health
+              port: http
+      volumes:
+        - name: config
+          configMap:
+            name: {{ .Name }}-config
+            items:
+                - key: elasticsearch.yml
+                  path: elasticsearch.yml
+        {{ if .Spec.Security.User.SecretName }}
+        - name: cert
+          secret:
+            secretName:  "skywalking-storage"
+        {{end}}
\ No newline at end of file
diff --git a/pkg/operator/repo/assets.gen.go b/pkg/operator/repo/assets.gen.go
index ac5813d..1a5d855 100644
--- a/pkg/operator/repo/assets.gen.go
+++ b/pkg/operator/repo/assets.gen.go
@@ -25,10 +25,14 @@
 // injector/templates/configmap.yaml (1.2kB)
 // oapserver/templates/cluster_role.yaml (1.241kB)
 // oapserver/templates/cluster_role_binding.yaml (1.207kB)
-// oapserver/templates/deployment.yaml (3.429kB)
+// oapserver/templates/deployment.yaml (3.933kB)
 // oapserver/templates/ingress.yaml (1.672kB)
 // oapserver/templates/service.yaml (1.782kB)
 // oapserver/templates/service_account.yaml (1.09kB)
+// storage/elasticsearch7/templates/configmap.yaml (1.568kB)
+// storage/elasticsearch7/templates/service.yaml (1.29kB)
+// storage/elasticsearch7/templates/service_account.yaml (1.096kB)
+// storage/elasticsearch7/templates/statefulset.yaml (3.673kB)
 // ui/templates/deployment.yaml (2.604kB)
 // ui/templates/ingress.yaml (1.668kB)
 // ui/templates/service.yaml (1.681kB)
@@ -682,6 +686,7 @@ metadata:
     operator.skywalking.apache.org/oap-server-name: {{ .Name }}
     operator.skywalking.apache.org/application: oapserver
     operator.skywalking.apache.org/component: deployment
+
 spec:
   replicas: {{ .Spec.Instances }}
   minReadySeconds: 5
@@ -701,66 +706,76 @@ spec:
       affinity:
         podAntiAffinity:
           preferredDuringSchedulingIgnoredDuringExecution:
-          - weight: 1
-            podAffinityTerm:
-              topologyKey: kubernetes.io/hostname
-              labelSelector:
-                matchLabels:
-                  app: oap
-                  operator.skywalking.apache.org/oap-server-name: {{ .Name }}
+            - weight: 1
+              podAffinityTerm:
+                topologyKey: kubernetes.io/hostname
+                labelSelector:
+                  matchLabels:
+                    app: oap
+                    operator.skywalking.apache.org/oap-server-name: {{ .Name }}
       containers:
-      - name: oap
-        image: {{ .Spec.Image }}
-        imagePullPolicy: IfNotPresent
-        ports:
-        - containerPort: 11800
-          name: grpc
-        - containerPort: 12800
-          name: rest
-        - containerPort: 1234
-          name: http-monitoring
-        livenessProbe:
-          initialDelaySeconds: 10
-          timeoutSeconds: 10
-          periodSeconds: 30
-          failureThreshold: 10
-          successThreshold: 1
-          exec:
-            command:
-              - /skywalking/bin/swctl
-              - ch
-        readinessProbe:
-          initialDelaySeconds: 10
-          timeoutSeconds: 10
-          periodSeconds: 30
-          failureThreshold: 10
-          successThreshold: 1
-          exec:
-            command:
-              - /skywalking/bin/swctl
-              - ch
-        env:
-        - name: JAVA_OPTS
-          value: -Xmx2048M
-        - name: SW_CLUSTER
-          value: kubernetes
-        - name: SW_CLUSTER_K8S_NAMESPACE
-          value: "{{ .Namespace }}"
-        - name: SW_CLUSTER_K8S_LABEL
-          value: "app=oap,operator.skywalking.apache.org/oap-server-name={{ .Name }}"
-        - name: SKYWALKING_COLLECTOR_UID
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.uid
-        - name: SW_TELEMETRY
-          value: prometheus
-        - name: SW_HEALTH_CHECKER
-          value: default
-        {{range .Spec.Config}}
-        - name: {{ .Name }}
-          value: {{ .Value }}
-        {{end}}
-
+        - name: oap
+          image: {{ .Spec.Image }}
+          imagePullPolicy: IfNotPresent
+          ports:
+            - containerPort: 11800
+              name: grpc
+            - containerPort: 12800
+              name: rest
+            - containerPort: 1234
+              name: http-monitoring
+          livenessProbe:
+            initialDelaySeconds: 10
+            timeoutSeconds: 10
+            periodSeconds: 30
+            failureThreshold: 10
+            successThreshold: 1
+            exec:
+              command:
+                - /skywalking/bin/swctl
+                - ch
+          readinessProbe:
+            initialDelaySeconds: 10
+            timeoutSeconds: 10
+            periodSeconds: 30
+            failureThreshold: 10
+            successThreshold: 1
+            exec:
+              command:
+                - /skywalking/bin/swctl
+                - ch
+          {{if .Spec.StorageConfig.Storage.Spec.Security.TLS}}
+          volumeMounts:
+            - name: cert
+              mountPath: /skywalking/p12
+          {{end}}
+          env:
+            - name: JAVA_OPTS
+              value: -Xmx2048M
+            - name: SW_CLUSTER
+              value: kubernetes
+            - name: SW_CLUSTER_K8S_NAMESPACE
+              value: "{{ .Namespace }}"
+            - name: SW_CLUSTER_K8S_LABEL
+              value: "app=oap,operator.skywalking.apache.org/oap-server-name={{ .Name }}"
+            - name: SKYWALKING_COLLECTOR_UID
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.uid
+            - name: SW_TELEMETRY
+              value: prometheus
+            - name: SW_HEALTH_CHECKER
+              value: default
+          {{range .Spec.Config}}
+          - name: {{ .Name }}
+            value: {{ .Value }}
+          {{end}}
+      {{if .Spec.StorageConfig.Storage.Spec.Security.TLS}}
+      volumes:
+        - name: cert
+          secret:
+            secretName:  "skywalking-storage"
+      {{end}}
 `)
 
 func oapserverTemplatesDeploymentYamlBytes() ([]byte, error) {
@@ -774,7 +789,7 @@ func oapserverTemplatesDeploymentYaml() (*asset, error) {
 	}
 
 	info := bindataFileInfo{name: "oapserver/templates/deployment.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
-	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0x12, 0x5f, 0xe9, 0xad, 0xb3, 0xfa, 0xcb, 0x81, 0xcb, 0x77, 0x13, 0x31, 0x9c, 0xde, 0x1e, 0x64, 0x27, 0x1b, 0xde, 0xa4, 0x1c, 0xd5, 0x78, 0x92, 0x5b, 0x6e, 0xe6, 0x56, 0x16, 0xf1, 0xbb}}
+	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x7a, 0x69, 0xf2, 0xdb, 0x58, 0x23, 0x88, 0xba, 0x1b, 0x8f, 0xa8, 0xb4, 0x6c, 0xc7, 0x2e, 0x93, 0xb3, 0x4b, 0x44, 0xfa, 0x74, 0x13, 0xe9, 0x68, 0x99, 0x45, 0x1a, 0x9f, 0x3b, 0xf4, 0x7}}
 	return a, nil
 }
 
@@ -962,6 +977,284 @@ func oapserverTemplatesService_accountYaml() (*asset, error) {
 	return a, nil
 }
 
+var _storageElasticsearch7TemplatesConfigmapYaml = []byte(`# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Name }}-config
+  namespace: {{ .Namespace }}
+data:
+  elasticsearch.yml: |
+      network.host: 0.0.0.0
+      {{ if .Spec.Security.User.SecretName }}
+      xpack.security.enabled: true
+      xpack.security.transport.ssl.enabled: true
+      xpack.security.transport.ssl.verification_mode: certificate
+      xpack.security.transport.ssl.keystore.path: storage.p12
+      xpack.security.transport.ssl.truststore.path: storage.p12
+      {{end}}
+      {{ if .Spec.Security.TLS }}
+      xpack.security.http.ssl.enabled: true
+      xpack.security.http.ssl.verification_mode: certificate
+      xpack.security.http.ssl.keystore.path: storage.p12
+      xpack.security.http.ssl.truststore.path: storage.p12
+      {{end}}
+
+
+`)
+
+func storageElasticsearch7TemplatesConfigmapYamlBytes() ([]byte, error) {
+	return _storageElasticsearch7TemplatesConfigmapYaml, nil
+}
+
+func storageElasticsearch7TemplatesConfigmapYaml() (*asset, error) {
+	bytes, err := storageElasticsearch7TemplatesConfigmapYamlBytes()
+	if err != nil {
+		return nil, err
+	}
+
+	info := bindataFileInfo{name: "storage/elasticsearch7/templates/configmap.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x66, 0x55, 0xb6, 0xb, 0xa3, 0x32, 0xf8, 0xf1, 0xce, 0xa, 0x98, 0x3c, 0x9e, 0xaa, 0x54, 0x5e, 0x77, 0x33, 0x7d, 0xf8, 0x40, 0xea, 0xd8, 0xfe, 0x83, 0x1e, 0x39, 0xd, 0xf5, 0xbf, 0x91, 0x96}}
+	return a, nil
+}
+
+var _storageElasticsearch7TemplatesServiceYaml = []byte(`# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+kind: Service
+apiVersion: v1
+metadata:
+  name:  {{ .Spec.ServiceName }}
+  namespace: {{ .Namespace }}
+  labels:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: service
+spec:
+  clusterIP: None
+  selector:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+  ports:
+    - name: rest
+      port: 9200
+    - name: inter-node
+      port: 9300
+
+`)
+
+func storageElasticsearch7TemplatesServiceYamlBytes() ([]byte, error) {
+	return _storageElasticsearch7TemplatesServiceYaml, nil
+}
+
+func storageElasticsearch7TemplatesServiceYaml() (*asset, error) {
+	bytes, err := storageElasticsearch7TemplatesServiceYamlBytes()
+	if err != nil {
+		return nil, err
+	}
+
+	info := bindataFileInfo{name: "storage/elasticsearch7/templates/service.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd7, 0x4b, 0x25, 0x25, 0xfa, 0xc0, 0x67, 0x42, 0x44, 0x3c, 0x6e, 0x33, 0xdd, 0x36, 0x96, 0xca, 0x8d, 0x2b, 0xb4, 0xc8, 0x5b, 0x36, 0x51, 0x80, 0x57, 0x53, 0xc8, 0x38, 0x52, 0x9f, 0x52, 0x5e}}
+	return a, nil
+}
+
+var _storageElasticsearch7TemplatesService_accountYaml = []byte(`# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ .Name }}-elasticsearch7
+  namespace: {{ .Namespace }}
+  labels:
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: rbac
+`)
+
+func storageElasticsearch7TemplatesService_accountYamlBytes() ([]byte, error) {
+	return _storageElasticsearch7TemplatesService_accountYaml, nil
+}
+
+func storageElasticsearch7TemplatesService_accountYaml() (*asset, error) {
+	bytes, err := storageElasticsearch7TemplatesService_accountYamlBytes()
+	if err != nil {
+		return nil, err
+	}
+
+	info := bindataFileInfo{name: "storage/elasticsearch7/templates/service_account.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xef, 0xa4, 0x39, 0xae, 0xc8, 0xc4, 0xbb, 0xad, 0xc2, 0x3e, 0x3f, 0xfe, 0x49, 0x58, 0x86, 0xf2, 0x6, 0x5f, 0x56, 0xfa, 0x43, 0x35, 0x51, 0x4f, 0x87, 0xe1, 0xbb, 0xf0, 0x89, 0x71, 0x1a}}
+	return a, nil
+}
+
+var _storageElasticsearch7TemplatesStatefulsetYaml = []byte(`# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: {{ .Name }}-elasticsearch7
+  namespace: {{ .Namespace }}
+  labels:
+    app: es
+    operator.skywalking.apache.org/es-name: {{ .Name }}
+    operator.skywalking.apache.org/application: elasticsearch
+    operator.skywalking.apache.org/component: statefulset
+spec:
+  serviceName:  {{ .Spec.ServiceName }}
+  replicas: {{ .Spec.Instances }}
+  selector:
+    matchLabels:
+      app: es
+      operator.skywalking.apache.org/es-name: {{ .Name }}
+  template:
+    metadata:
+      labels:
+        app: es
+        operator.skywalking.apache.org/es-name: {{ .Name }}
+        operator.skywalking.apache.org/application: elasticsearch
+        operator.skywalking.apache.org/component: statefulset
+    spec:
+      serviceAccountName: {{ .Name }}-elasticsearch7
+      affinity:
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+              - labelSelector:
+                matchExpressions:
+                  - key: app
+                    operator: In
+                    values:
+                      - "es"
+                topologyKey: kubernetes.io/hostname
+      containers:
+        - name: elasticsearch
+          image: {{ .Spec.Image }}
+          resources:
+            limits:
+              cpu: {{ .Spec.ResourceCnfig.Limit }}
+            requests:
+              cpu: {{ .Spec.ResourceCnfig.Requests }}
+          imagePullPolicy: IfNotPresent
+          ports:
+            - containerPort: 9200
+              name: rest
+              protocol: TCP
+            - containerPort: 9300
+              name: inter-node
+              protocol: TCP
+          volumeMounts:
+            - name: config
+              mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
+              subPath: elasticsearch.yml
+            {{ if .Spec.Security.User.SecretName }}
+            - name: cert
+              mountPath: "/usr/share/elasticsearch/config/storage.p12"
+              subPath: storage.p12
+            {{end}}
+          env:
+            - name: cluster.name
+              value: "{{ .Name }}-skywalking-es"
+            - name: node.name
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: thread_pool.write.queue_size
+              value: "1000"
+            {{ range .Spec.Config }}
+            - name: {{ .Name }}
+              value: {{ .Value }}
+            {{end}}
+          livenessProbe:
+            httpGet:
+              path: /_cluster/health
+              port: http
+      volumes:
+        - name: config
+          configMap:
+            name: {{ .Name }}-config
+            items:
+                - key: elasticsearch.yml
+                  path: elasticsearch.yml
+        {{ if .Spec.Security.User.SecretName }}
+        - name: cert
+          secret:
+            secretName:  "skywalking-storage"
+        {{end}}`)
+
+func storageElasticsearch7TemplatesStatefulsetYamlBytes() ([]byte, error) {
+	return _storageElasticsearch7TemplatesStatefulsetYaml, nil
+}
+
+func storageElasticsearch7TemplatesStatefulsetYaml() (*asset, error) {
+	bytes, err := storageElasticsearch7TemplatesStatefulsetYamlBytes()
+	if err != nil {
+		return nil, err
+	}
+
+	info := bindataFileInfo{name: "storage/elasticsearch7/templates/statefulset.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+	a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdc, 0x8b, 0x8, 0x38, 0xc7, 0x1e, 0xc9, 0xab, 0xef, 0x48, 0x64, 0x60, 0xeb, 0x68, 0x7e, 0x39, 0x44, 0x5a, 0x7, 0x47, 0x26, 0xea, 0x9d, 0xcc, 0x65, 0x8f, 0xf5, 0xd0, 0x96, 0x92, 0xaf, 0x55}}
+	return a, nil
+}
+
 var _uiTemplatesDeploymentYaml = []byte(`# Licensed to Apache Software Foundation (ASF) under one or more contributor
 # license agreements. See the NOTICE file distributed with
 # this work for additional information regarding copyright
@@ -1283,22 +1576,26 @@ func AssetNames() []string {
 
 // _bindata is a table, holding each asset generator, mapped to its name.
 var _bindata = map[string]func() (*asset, error){
-	"fetcher/templates/cluster_role.yaml":           fetcherTemplatesCluster_roleYaml,
-	"fetcher/templates/cluster_role_binding.yaml":   fetcherTemplatesCluster_role_bindingYaml,
-	"fetcher/templates/configmap.yaml":              fetcherTemplatesConfigmapYaml,
-	"fetcher/templates/deployment.yaml":             fetcherTemplatesDeploymentYaml,
-	"fetcher/templates/service_account.yaml":        fetcherTemplatesService_accountYaml,
-	"injector/templates/annotations.yaml":           injectorTemplatesAnnotationsYaml,
-	"injector/templates/configmap.yaml":             injectorTemplatesConfigmapYaml,
-	"oapserver/templates/cluster_role.yaml":         oapserverTemplatesCluster_roleYaml,
-	"oapserver/templates/cluster_role_binding.yaml": oapserverTemplatesCluster_role_bindingYaml,
-	"oapserver/templates/deployment.yaml":           oapserverTemplatesDeploymentYaml,
-	"oapserver/templates/ingress.yaml":              oapserverTemplatesIngressYaml,
-	"oapserver/templates/service.yaml":              oapserverTemplatesServiceYaml,
-	"oapserver/templates/service_account.yaml":      oapserverTemplatesService_accountYaml,
-	"ui/templates/deployment.yaml":                  uiTemplatesDeploymentYaml,
-	"ui/templates/ingress.yaml":                     uiTemplatesIngressYaml,
-	"ui/templates/service.yaml":                     uiTemplatesServiceYaml,
+	"fetcher/templates/cluster_role.yaml":                   fetcherTemplatesCluster_roleYaml,
+	"fetcher/templates/cluster_role_binding.yaml":           fetcherTemplatesCluster_role_bindingYaml,
+	"fetcher/templates/configmap.yaml":                      fetcherTemplatesConfigmapYaml,
+	"fetcher/templates/deployment.yaml":                     fetcherTemplatesDeploymentYaml,
+	"fetcher/templates/service_account.yaml":                fetcherTemplatesService_accountYaml,
+	"injector/templates/annotations.yaml":                   injectorTemplatesAnnotationsYaml,
+	"injector/templates/configmap.yaml":                     injectorTemplatesConfigmapYaml,
+	"oapserver/templates/cluster_role.yaml":                 oapserverTemplatesCluster_roleYaml,
+	"oapserver/templates/cluster_role_binding.yaml":         oapserverTemplatesCluster_role_bindingYaml,
+	"oapserver/templates/deployment.yaml":                   oapserverTemplatesDeploymentYaml,
+	"oapserver/templates/ingress.yaml":                      oapserverTemplatesIngressYaml,
+	"oapserver/templates/service.yaml":                      oapserverTemplatesServiceYaml,
+	"oapserver/templates/service_account.yaml":              oapserverTemplatesService_accountYaml,
+	"storage/elasticsearch7/templates/configmap.yaml":       storageElasticsearch7TemplatesConfigmapYaml,
+	"storage/elasticsearch7/templates/service.yaml":         storageElasticsearch7TemplatesServiceYaml,
+	"storage/elasticsearch7/templates/service_account.yaml": storageElasticsearch7TemplatesService_accountYaml,
+	"storage/elasticsearch7/templates/statefulset.yaml":     storageElasticsearch7TemplatesStatefulsetYaml,
+	"ui/templates/deployment.yaml":                          uiTemplatesDeploymentYaml,
+	"ui/templates/ingress.yaml":                             uiTemplatesIngressYaml,
+	"ui/templates/service.yaml":                             uiTemplatesServiceYaml,
 }
 
 // AssetDebug is true if the assets were built with the debug flag enabled.
@@ -1370,6 +1667,16 @@ var _bintree = &bintree{nil, map[string]*bintree{
 			"service_account.yaml":      &bintree{oapserverTemplatesService_accountYaml, map[string]*bintree{}},
 		}},
 	}},
+	"storage": &bintree{nil, map[string]*bintree{
+		"elasticsearch7": &bintree{nil, map[string]*bintree{
+			"templates": &bintree{nil, map[string]*bintree{
+				"configmap.yaml":       &bintree{storageElasticsearch7TemplatesConfigmapYaml, map[string]*bintree{}},
+				"service.yaml":         &bintree{storageElasticsearch7TemplatesServiceYaml, map[string]*bintree{}},
+				"service_account.yaml": &bintree{storageElasticsearch7TemplatesService_accountYaml, map[string]*bintree{}},
+				"statefulset.yaml":     &bintree{storageElasticsearch7TemplatesStatefulsetYaml, map[string]*bintree{}},
+			}},
+		}},
+	}},
 	"ui": &bintree{nil, map[string]*bintree{
 		"templates": &bintree{nil, map[string]*bintree{
 			"deployment.yaml": &bintree{uiTemplatesDeploymentYaml, map[string]*bintree{}},