Pre-pull images for updates

The update flow used to delete the active function before
synchronously pulling the next and starting it. That meant
functions would always face downtime during the pull.

This changes the order to pre-pull and reduce any down time.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd)
2020-12-31 18:37:31 +00:00
committed by Alex Ellis
parent 87f993847c
commit c4936133f6
2 changed files with 48 additions and 11 deletions

View File

@ -9,6 +9,7 @@ import (
"net/http"
"os"
"path"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
@ -68,7 +69,11 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
}
}
func deploy(ctx context.Context, req types.FunctionDeployment, client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) error {
// prepull is an optimization which means an image can be pulled before a deployment
// request, since a deployment request first deletes the active function before
// trying to deploy a new one.
func prepull(ctx context.Context, req types.FunctionDeployment, client *containerd.Client, alwaysPull bool) error {
start := time.Now()
r, err := reference.ParseNormalizedNamed(req.Image)
if err != nil {
return err
@ -87,7 +92,33 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container
}
size, _ := image.Size(ctx)
log.Printf("Deploy %s size: %d\n", image.Name(), size)
log.Printf("[Prepull] Deploy %s size: %d, took: %fs\n", image.Name(), size, time.Since(start).Seconds())
return nil
}
func deploy(ctx context.Context, req types.FunctionDeployment, client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) error {
start := time.Now()
r, err := reference.ParseNormalizedNamed(req.Image)
if err != nil {
return err
}
imgRef := reference.TagNameOnly(r).String()
snapshotter := ""
if val, ok := os.LookupEnv("snapshotter"); ok {
snapshotter = val
}
image, err := service.PrepareImage(ctx, client, imgRef, snapshotter, alwaysPull)
if err != nil {
return errors.Wrapf(err, "unable to pull image %s", imgRef)
}
size, _ := image.Size(ctx)
log.Printf("[deploy] Deploy %s size: %d, took: %fs\n", image.Name(), size, time.Since(start).Seconds())
envs := prepareEnv(req.EnvProcess, req.EnvVars)
mounts := getMounts()

View File

@ -56,6 +56,12 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
}
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
if err := prepull(ctx, req, client, alwaysPull); err != nil {
log.Printf("[Update] error with pre-pull: %s, %s\n", name, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if function.replicas != 0 {
err = cninetwork.DeleteCNINetwork(ctx, cni, client, name)
if err != nil {
@ -63,19 +69,19 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
}
}
containerErr := service.Remove(ctx, client, name)
if containerErr != nil {
log.Printf("[Update] error removing %s, %s\n", name, containerErr)
http.Error(w, containerErr.Error(), http.StatusInternalServerError)
if err := service.Remove(ctx, client, name); err != nil {
log.Printf("[Update] error removing %s, %s\n", name, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull)
if deployErr != nil {
log.Printf("[Update] error deploying %s, error: %s\n", name, deployErr)
http.Error(w, deployErr.Error(), http.StatusBadRequest)
// The pull has already been done in prepull, so we can force this pull to "false"
pull := false
if err := deploy(ctx, req, client, cni, secretMountPath, pull); err != nil {
log.Printf("[Update] error deploying %s, error: %s\n", name, err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
}