Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native deployment #51

Merged
merged 9 commits into from
Aug 11, 2021
Merged

Support native deployment #51

merged 9 commits into from
Aug 11, 2021

Conversation

xxx7xxxx
Copy link
Contributor

Detail

Actually, this is also a refactoring in implementing the new feature:

  1. Simplify interface with a lot of arguments.
  2. Complete service spec.
  3. Simplify complex containers/volumes layout (see reference comparison), moreover, it is more consistent.
  4. Make logs clean and consistent.

And there left something to complete, which of part were commented, and:

  1. Add documents for supporting native deployment.
  2. Add more unit tests.

After them, I found more trivial or significant stuff needed to be improved, which will be opened issues in the next contribution.

Reference

There lists the old and new specs of deployment after injecting the sidecar. The same/unimportant part has been committed. We can see the two init containers have been merged into one, and the volumes and volumeMounts became more consistent. The config of k8s objects are complex enough, it's very important to manage the complexity of them carefully.

old-vets-service.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vets-service
  namespace: spring-petclinic
spec:
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: vets-service
    spec:
      containers:
      - args:
        - -c
        - java -server -Xmx1024m -Xms1024m -Dspring.profiles.active=sit -Djava.security.egd=file:/dev/./urandom  org.springframework.boot.loader.JarLauncher
        command:
        - /bin/sh
        env:
        - name: JAVA_TOOL_OPTIONS
          value: ' -javaagent:/easeagent-volume/easeagent.jar -Deaseagent.log.conf=/easeagent-volume/log4j2.xml '
        image: megaease/spring-petclinic-vets-service:latest
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - sh
              - -c
              - sleep 10
        name: vets-service
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          limits:
            cpu: "2"
            memory: 1Gi
          requests:
            cpu: 200m
            memory: 256Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /application/application-sit.yml
          name: configmap-volume-0
          subPath: application-sit.yml
        - mountPath: /easeagent-volume
          name: easeagent-volume
      - command:
        - /bin/sh
        - -c
        - /opt/easegress/bin/easegress-server -f /easegress-sidecar/eg-sidecar.yaml
        env:
        - name: APPLICATION_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        image: megaease/easegress:server-sidecar
        imagePullPolicy: Always
        name: easemesh-sidecar
        ports:
        - containerPort: 13001
          name: sidecar-ingress
          protocol: TCP
        - containerPort: 13002
          name: sidecar-egress
          protocol: TCP
        - containerPort: 13009
          name: sidecar-eureka
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /easegress-sidecar
          name: sidecar-params-volume
      dnsPolicy: ClusterFirst
      initContainers:
      - command:
        - /bin/sh
        - -c
        - cp -r /easeagent-volume/. /easeagent-share-volume
        image: megaease/easeagent-initializer:latest
        imagePullPolicy: Always
        name: easeagent-initializer
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /easeagent-share-volume
          name: easeagent-volume
      - command:
        - /bin/sh
        - -c
        - |-
          echo name: $POD_NAME >> /opt/eg-sidecar.yaml; echo 'cluster-join-urls: http://easemesh-controlplane-svc.easemesh:2380
          cluster-request-timeout: 10s
          cluster-role: reader
          cluster-name: easemesh-control-plane
          Labels:
            alive-probe: http://localhost:9900/health
            application-port: "8080"
            mesh-service-labels: ""
            mesh-servicename: vets-service
          ' >> /opt/eg-sidecar.yaml; cp -r /opt/. /sidecar-params-volume
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        image: megaease/easegress:server-sidecar
        imagePullPolicy: Always
        name: easegress-sidecar-initializer
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /sidecar-params-volume
          name: sidecar-params-volume
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: application-sit-yml
            path: application-sit.yml
          name: vets-service
        name: configmap-volume-0
      - emptyDir: {}
        name: easeagent-volume
      - emptyDir: {}
        name: sidecar-params-volume

new-vets-service.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vets-service
  namespace: spring-petclinic
spec:
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: vets-service
    spec:
      containers:
      - args:
        - -c
        - java -server -Xmx1024m -Xms1024m -Dspring.profiles.active=sit -Djava.security.egd=file:/dev/./urandom  org.springframework.boot.loader.JarLauncher
        command:
        - /bin/sh
        env:
        - name: JAVA_TOOL_OPTIONS
          value: ' -javaagent:/agent-volume/easeagent.jar -Deaseagent.log.conf=/agent-volume/log4j2.xml '
        image: megaease/spring-petclinic-vets-service:latest
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - sh
              - -c
              - sleep 10
        name: vets-service
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          limits:
            cpu: "2"
            memory: 1Gi
          requests:
            cpu: 200m
            memory: 256Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /application/application-sit.yml
          name: configmap-volume-0
          subPath: application-sit.yml
        - mountPath: /agent-volume
          name: agent-volume
      - command:
        - /bin/sh
        - -c
        - /opt/easegress/bin/easegress-server -f /sidecar-volume/sidecar-config.yaml
        env:
        - name: APPLICATION_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        image: megaease/easegress:server-sidecar
        imagePullPolicy: IfNotPresent
        name: easemesh-sidecar
        ports:
        - containerPort: 13001
          name: sidecar-ingress
          protocol: TCP
        - containerPort: 13002
          name: sidecar-egress
          protocol: TCP
        - containerPort: 13009
          name: sidecar-eureka
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /sidecar-volume
          name: sidecar-volume
      dnsPolicy: ClusterFirst
      initContainers:
      - command:
        - sh
        - -c
        - |-
          set -e
          cp -r /easeagent-volume/* /agent-volume

          echo 'name: vets-service
          cluster-join-urls: http://easemesh-controlplane-svc.easemesh:2380
          cluster-request-timeout: 10s
          cluster-role: reader
          cluster-name: easemesh-control-plane
          labels:
            alive-probe:
            application-port: 8080
            mesh-service-labels:
            mesh-servicename: vets-service
          ' > /sidecar-volume/sidecar-config.yaml
        image: megaease/easeagent-initializer:latest
        imagePullPolicy: Always
        name: initializer
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /agent-volume
          name: agent-volume
        - mountPath: /sidecar-volume
          name: sidecar-volume
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: application-sit-yml
            path: application-sit.yml
          name: vets-service
        name: configmap-volume-0
      - emptyDir: {}
        name: agent-volume
      - emptyDir: {}
        name: sidecar-volume

The diff of them:

diff --git old-vets-service.yaml new-vets-service.yaml
index cf4331e..dcb0781 100644
--- old-vets-service.yaml
+++ new-vets-service.yaml
@@ -18,7 +18,7 @@ spec:
         - /bin/sh
         env:
         - name: JAVA_TOOL_OPTIONS
-          value: ' -javaagent:/easeagent-volume/easeagent.jar -Deaseagent.log.conf=/easeagent-volume/log4j2.xml '
+          value: ' -javaagent:/agent-volume/easeagent.jar -Deaseagent.log.conf=/agent-volume/log4j2.xml '
         image: megaease/spring-petclinic-vets-service:latest
         imagePullPolicy: IfNotPresent
         lifecycle:
@@ -45,12 +45,12 @@ spec:
         - mountPath: /application/application-sit.yml
           name: configmap-volume-0
           subPath: application-sit.yml
-        - mountPath: /easeagent-volume
-          name: easeagent-volume
+        - mountPath: /agent-volume
+          name: agent-volume
       - command:
         - /bin/sh
         - -c
-        - /opt/easegress/bin/easegress-server -f /easegress-sidecar/eg-sidecar.yaml
+        - /opt/easegress/bin/easegress-server -f /sidecar-volume/sidecar-config.yaml
         env:
         - name: APPLICATION_IP
           valueFrom:
@@ -58,7 +58,7 @@ spec:
               apiVersion: v1
               fieldPath: status.podIP
         image: megaease/easegress:server-sidecar
-        imagePullPolicy: Always
+        imagePullPolicy: IfNotPresent
         name: easemesh-sidecar
         ports:
         - containerPort: 13001
@@ -74,52 +74,39 @@ spec:
         terminationMessagePath: /dev/termination-log
         terminationMessagePolicy: File
         volumeMounts:
-        - mountPath: /easegress-sidecar
-          name: sidecar-params-volume
+        - mountPath: /sidecar-volume
+          name: sidecar-volume
       dnsPolicy: ClusterFirst
       initContainers:
       - command:
-        - /bin/sh
-        - -c
-        - cp -r /easeagent-volume/. /easeagent-share-volume
-        image: megaease/easeagent-initializer:latest
-        imagePullPolicy: Always
-        name: easeagent-initializer
-        resources: {}
-        terminationMessagePath: /dev/termination-log
-        terminationMessagePolicy: File
-        volumeMounts:
-        - mountPath: /easeagent-share-volume
-          name: easeagent-volume
-      - command:
-        - /bin/sh
+        - sh
         - -c
         - |-
-          echo name: $POD_NAME >> /opt/eg-sidecar.yaml; echo 'cluster-join-urls: http://easemesh-controlplane-svc.easemesh:2380
+          set -e
+          cp -r /easeagent-volume/* /agent-volume
+
+          echo 'name: vets-service
+          cluster-join-urls: http://easemesh-controlplane-svc.easemesh:2380
           cluster-request-timeout: 10s
           cluster-role: reader
           cluster-name: easemesh-control-plane
-          Labels:
-            alive-probe: http://localhost:9900/health
-            application-port: "8080"
-            mesh-service-labels: ""
+          labels:
+            alive-probe:
+            application-port: 8080
+            mesh-service-labels:
             mesh-servicename: vets-service
-          ' >> /opt/eg-sidecar.yaml; cp -r /opt/. /sidecar-params-volume
-        env:
-        - name: POD_NAME
-          valueFrom:
-            fieldRef:
-              apiVersion: v1
-              fieldPath: metadata.name
-        image: megaease/easegress:server-sidecar
+          ' > /sidecar-volume/sidecar-config.yaml
+        image: megaease/easeagent-initializer:latest
         imagePullPolicy: Always
-        name: easegress-sidecar-initializer
+        name: initializer
         resources: {}
         terminationMessagePath: /dev/termination-log
         terminationMessagePolicy: File
         volumeMounts:
-        - mountPath: /sidecar-params-volume
-          name: sidecar-params-volume
+        - mountPath: /agent-volume
+          name: agent-volume
+        - mountPath: /sidecar-volume
+          name: sidecar-volume
       restartPolicy: Always
       schedulerName: default-scheduler
       securityContext: {}
@@ -133,6 +120,6 @@ spec:
           name: vets-service
         name: configmap-volume-0
       - emptyDir: {}
-        name: easeagent-volume
+        name: agent-volume
       - emptyDir: {}
-        name: sidecar-params-volume
+        name: sidecar-volume

@@ -1,133 +0,0 @@
/*
Copy link
Contributor

@zhao-kun zhao-kun Jul 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you delete the test case? I think it can help us verified the spec, and checking the result of reconciling whether is correct.

"github.com/megaease/easemesh/mesh-operator/pkg/syncer"
"github.com/pkg/errors"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to follow the convention of import section?

r.Log.WithValues("DeploymentID", req.NamespacedName)

deploy := &v1.Deployment{}
err := r.Client.Get(context.TODO(), req.NamespacedName, deploy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ctx is passed by argument, prefer to using it

buff, err := yaml.Marshal(deploy)
if err != nil {
t.Fatalf("mashal failed: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should verify the sidecar was injected correctly

// https://github.com/kubernetes/klog/issues/253
// https://github.com/kubernetes/klog/pull/242
// https://github.com/kubernetes-sigs/controller-runtime/issues/1538
deploy.Spec.Template.ObjectMeta.Labels = sourceDeploySpec.Selector.MatchLabels
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhao-kun
Copy link
Contributor

It's good to refactor about reducing numbers of initializing containers

@xxx7xxxx
Copy link
Contributor Author

Updated as comments along with ImagePullPolicy assigned with IfNotPresent by default

Copy link
Contributor

@zhao-kun zhao-kun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some issues:
scenarios 0:

  1. I had a deployment that hasn't any mesh annotations, and it's applied in K8s
  2. I modified the deployment add some mesh annotations, and apply it again

result:
the original deployment and the new deployment (injected with sidecar) are co-existed ( does it expected?)

scenarios 1:

  1. No other deployments existed in the current namespace.
  2. Deploy a deployment with a mesh annotation.

Result:
An extra deployment **vets-service-5956bc7b4c-hlpkz ** was created and immediately deleted, it's unexpected"
we should avoid the original deployment been created.(could we?)

zhaokun-tm1801: emctl  git:(ly-operator)  ▷  k get po -n spring-petclinic  -w                                       
NAME                            READY   STATUS    RESTARTS   AGE
vets-service-5956bc7b4c-hlpkz   0/1     Pending   0          0s
vets-service-5956bc7b4c-hlpkz   0/1     Pending   0          0s
vets-service-8c64999f9-jk7fs    0/2     Pending   0          0s
vets-service-5956bc7b4c-hlpkz   0/1     ContainerCreating   0          0s
vets-service-8c64999f9-jk7fs    0/2     Pending             0          0s
vets-service-8c64999f9-jk7fs    0/2     Init:0/1            0          0s
vets-service-5956bc7b4c-hlpkz   0/1     ContainerCreating   0          1s
vets-service-8c64999f9-jk7fs    0/2     Init:0/1            0          1s
vets-service-5956bc7b4c-hlpkz   1/1     Running             0          2s
vets-service-8c64999f9-jk7fs    0/2     PodInitializing     0          2s
vets-service-8c64999f9-jk7fs    2/2     Running             0          3s
vets-service-5956bc7b4c-hlpkz   1/1     Terminating         0          3s

scenarios 2:
Repeatedly applying a deployment with mesh annotations, I got a following error outputs:

The Deployment "vets-service" is invalid:
* spec.template.spec.containers[0].volumeMounts[0].name: Not found: "agent-volume"
* spec.template.spec.containers[1].volumeMounts[0].name: Not found: "sidecar-volume"
* spec.template.spec.initContainers[0].volumeMounts[0].name: Not found: "agent-volume"
* spec.template.spec.initContainers[0].volumeMounts[1].name: Not found: "sidecar-volume"                                                 

deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: spring-petclinic
  name: vets-service
  annotations:
    mesh.megaease.com/enable: "true"
    mesh.megaease.com/service-name: "vets-service"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vets-service
  template:
    metadata:
      labels:
        app: vets-service
    spec:
      containers:
        - image: megaease/spring-petclinic-vets-service:latest
          name: vets-service
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command: ["sh", "-c", "sleep 10"]
          command: ["/bin/sh"]
          args: ["-c", "java -server -Xmx1024m -Xms1024m -Dspring.profiles.active=sit -Djava.security.egd=file:/dev/./urandom  org.springframework.boot.loader.JarLauncher"]
          resources:
            limits:
              cpu: 2000m
              memory: 1Gi
            requests:
              cpu: 200m
              memory: 256Mi
          ports:
            - containerPort: 8080
      volumes:
      restartPolicy: Always


service := &meshService{
Name: deploy.Name,
Labels: deploy.Labels,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All deployment‘s labels are registered in the registry, which could interfere with the traffic splitting because traffic splitting only concerns labels that are dedicated to select the next service instance, too many unrelated labels maybe lead to unexpected behavior.

Could we organize the canary labels in the mesh annotations?

@xxx7xxxx
Copy link
Contributor Author

scenarios 0

You seem not to delete the meshdeployment with the same name.

scenarios 1

According to the quick research, it seems we don't have that interface to block the system-level controller to deploy Deployment immediately after it was creating. It seems not to give us the ability to register pre-hook.

scenarios 2

kubectl replace will work fine instead of kubectl apply, the mechanism of two interfaces decides the different results[1]. We add more fields for the original spec, so applying part of spec will cause the Not Found issue.

Could we organize the canary labels in the mesh annotations?

Sure, the example could be:

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: spring-petclinic
  name: vets-service
  annotations:
    mesh.megaease.com/enable: "true"
    mesh.megaease.com/service-name: "vets-service"
    mesh.megaease.com/service-labels: "version=beta,env=production"

It seems harder than structural labels to write and read, but acceptable for me.

[1] https://stackoverflow.com/a/62067419/1705845

@zhao-kun
Copy link
Contributor

FYI: Other mesh products leverage Dynamic Admission Control to modify objects sent to the API server to enforce custom defaults.

@xxx7xxxx
Copy link
Contributor Author

xxx7xxxx commented Aug 1, 2021

Updated with Dynamic Admission Control.

return ignoreResp(req)
}

if req.Kind.Kind != "Deployment" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the implementation is good to me, it should be better if we mutated pod instead of Deployment, which means that we could support Deployment, Statefulset, DaemonSet at the same time in one implementation.

@zhao-kun
Copy link
Contributor

There are some conflicts should be resolved

@xxx7xxxx
Copy link
Contributor Author

Conflict fixed. I think it's cool to support more objects but this PR is big enough, I'd like to support them in my next contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants