Use a separate controller loop to watch functions change and create a service (#544)

This commit is contained in:
Ta-Ching Chen
2018-04-04 01:17:51 +08:00
committed by GitHub
parent d39944e04d
commit 2a06705320
13 changed files with 186 additions and 108 deletions
+125
View File
@@ -0,0 +1,125 @@
/*
Copyright 2018 The Fission Authors.
Licensed 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 poolmgr
import (
"time"
log "github.com/sirupsen/logrus"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
apiv1 "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/rest"
k8sCache "k8s.io/client-go/tools/cache"
"github.com/fission/fission"
"github.com/fission/fission/crd"
)
func getIstioServiceLabels(fnName string) map[string]string {
return map[string]string{
"functionName": fnName,
}
}
func makeFuncIstioServiceRegister(crdClient *rest.RESTClient,
kubernetesClient *kubernetes.Clientset, fnNamespace string) k8sCache.Controller {
resyncPeriod := 30 * time.Second
lw := k8sCache.NewListWatchFromClient(crdClient, "functions", metav1.NamespaceDefault, fields.Everything())
_, controller := k8sCache.NewInformer(lw, &crd.Function{}, resyncPeriod,
k8sCache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
fn := obj.(*crd.Function)
// Since istio only allows accessing pod through k8s service,
// for the functions with executor type "poolmgr" we need to
// create a service for sending requests to pod in pool.
// Functions with executor type "Newdeploy" is specialized at
// pod starts. In this case, just ignore such functions.
fnExecutorType := fn.Spec.InvokeStrategy.ExecutionStrategy.ExecutorType
if fnExecutorType != fission.ExecutorTypePoolmgr {
return
}
// create a same name service for function
// since istio only allows the traffic to service
sel := map[string]string{
"functionName": fn.Metadata.Name,
"functionUid": string(fn.Metadata.UID),
}
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
// service for accepting user traffic
svc := apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: fnNamespace,
Name: svcName,
Labels: getIstioServiceLabels(fn.Metadata.Name),
},
Spec: apiv1.ServiceSpec{
Type: apiv1.ServiceTypeClusterIP,
Ports: []apiv1.ServicePort{
// Service port name should begin with a recognized prefix, or the traffic will be
// treated as TCP traffic. (https://istio.io/docs/setup/kubernetes/sidecar-injection.html)
// Originally the ports' name are similar to "http-fetch" and "http-specialize".
// But for istio 0.5.1, istio-proxy return unexpected 431 error with such naming.
// https://github.com/istio/istio/issues/928
// Workaround: remove prefix
// TODO: prepend prefix once the bug fixed
{
Name: "fetch",
Protocol: apiv1.ProtocolTCP,
Port: 8000,
TargetPort: intstr.FromInt(8000),
},
{
Name: "specialize",
Protocol: apiv1.ProtocolTCP,
Port: 8888,
TargetPort: intstr.FromInt(8888),
},
},
Selector: sel,
},
}
// create function istio service if it does not exist
_, err := kubernetesClient.CoreV1().Services(fnNamespace).Create(&svc)
if err != nil && !kerrors.IsAlreadyExists(err) {
log.Printf("Error creating function istio service: %v", err)
}
},
DeleteFunc: func(obj interface{}) {
fn := obj.(*crd.Function)
svcName := fission.GetFunctionIstioServiceName(fn.Metadata.Name, fn.Metadata.Namespace)
// delete function istio service
err := kubernetesClient.CoreV1().Services(fnNamespace).Delete(svcName, nil)
if err != nil && !kerrors.IsNotFound(err) {
log.Printf("Error deleting function istio service: %v", err)
}
},
UpdateFunc: func(oldObj, newObj interface{}) {},
})
return controller
}
+7 -14
View File
@@ -28,7 +28,6 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -105,7 +104,8 @@ func MakeGenericPool(
initialReplicas int32,
namespace string,
fsCache *fscache.FunctionServiceCache,
instanceId string) (*GenericPool, error) {
instanceId string,
enableIstio bool) (*GenericPool, error) {
log.Printf("Creating pool for environment %v", env.Metadata)
@@ -122,16 +122,6 @@ func MakeGenericPool(
runtimeImagePullPolicy = "IfNotPresent"
}
enableIstio := false
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
if err != nil {
log.Println("Failed to parse ENABLE_ISTIO")
}
enableIstio = istio
}
// TODO: in general we need to provide the user a way to configure pools. Initial
// replicas, autoscaling params, various timeouts, etc.
gp := &GenericPool{
@@ -479,7 +469,10 @@ func (gp *GenericPool) createPool() error {
// Use long terminationGracePeriodSeconds for connection draining in case that
// pod still runs user functions.
var gracePeriodSeconds int64 = 6 * 60
gracePeriodSeconds := int64(6 * 60)
if gp.env.Spec.TerminationGracePeriod > 0 {
gracePeriodSeconds = gp.env.Spec.TerminationGracePeriod
}
podAnnotation := make(map[string]string)
@@ -750,7 +743,7 @@ func (gp *GenericPool) GetFuncSvc(m *metav1.ObjectMeta) (*fscache.FuncSvc, error
log.Printf("Specialized pod: %v", pod.ObjectMeta.Name)
var svcHost string
if gp.useSvc {
if gp.useSvc && !gp.useIstio {
svcName := fmt.Sprintf("svc-%v", m.Name)
if len(m.UID) > 0 {
svcName = fmt.Sprintf("%s-%v", svcName, m.UID)
+27 -2
View File
@@ -17,11 +17,15 @@ limitations under the License.
package poolmgr
import (
"context"
"log"
"os"
"strconv"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
k8sCache "k8s.io/client-go/tools/cache"
"github.com/fission/fission"
"github.com/fission/fission/crd"
@@ -45,6 +49,9 @@ type (
fsCache *fscache.FunctionServiceCache
instanceId string
requestChannel chan *request
enableIstio bool
istioServiceRegister k8sCache.Controller
}
request struct {
requestType
@@ -61,7 +68,6 @@ type (
func MakeGenericPoolManager(
fissionClient *crd.FissionClient,
kubernetesClient *kubernetes.Clientset,
fissionNamespace string,
functionNamespace string,
fsCache *fscache.FunctionServiceCache,
instanceId string) *GenericPoolManager {
@@ -78,9 +84,28 @@ func MakeGenericPoolManager(
go gpm.service()
go gpm.eagerPoolCreator()
if len(os.Getenv("ENABLE_ISTIO")) > 0 {
istio, err := strconv.ParseBool(os.Getenv("ENABLE_ISTIO"))
if err != nil {
log.Println("Failed to parse ENABLE_ISTIO")
}
gpm.enableIstio = istio
if gpm.enableIstio {
gpm.istioServiceRegister = makeFuncIstioServiceRegister(
gpm.fissionClient.GetCrdClient(), gpm.kubernetesClient, functionNamespace)
}
}
return gpm
}
func (gpm *GenericPoolManager) Run(ctx context.Context) {
if gpm.enableIstio && gpm.istioServiceRegister != nil {
go gpm.istioServiceRegister.Run(ctx.Done())
}
}
func (gpm *GenericPoolManager) service() {
for {
req := <-gpm.requestChannel
@@ -97,7 +122,7 @@ func (gpm *GenericPoolManager) service() {
pool, err = MakeGenericPool(
gpm.fissionClient, gpm.kubernetesClient, req.env, poolsize,
gpm.namespace, gpm.fsCache, gpm.instanceId)
gpm.namespace, gpm.fsCache, gpm.instanceId, gpm.enableIstio)
if err != nil {
req.responseChannel <- &response{error: err}
continue