How to create multi instance in google cloud functions with golang

Issue

I am trying to create multi instance in GCP with cloud function, use golang programing.

I refer tutorial in https://medium.com/google-cloud/using-cloud-scheduler-and-cloud-functions-to-deploy-a-periodic-compute-engine-vm-worker-2b897ef68dc5 then write some customize in my context. Here is my code

package cloudfunctions

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "google.golang.org/api/compute/v1"
)

var ProjectID = ""
var Zone = ""
var Region = ""
var InstanceName = ""
var InstanceType = ""

func DeployInstance(w http.ResponseWriter, r *http.Request) {
    ProjectID = os.Getenv("PROJECT_ID")
    Zone = os.Getenv("ZONE")
    Region = os.Getenv("REGION")

    cs, err := InitComputeService()
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Fatal(err)
    }

    var listInstance = []string{"e2-standard-2", "e2-standard-8", "n2-standard-2", "n2-standard-8", "n1-custom-2-8192", "n1-custom-8-32768", "c2-standard-8" }
    for i:=0; i < 7; i++ {
        InstanceType = listInstance[i]
        InstanceName = "benchmark-"+InstanceType
        
        instance, err := GetInstance(cs)
        if err != nil {
            w.WriteHeader(http.StatusTemporaryRedirect)
            w.Write([]byte(err.Error() + " instance may not exist yet"))
            log.Print(err)
    
            _, err = CreateInstance(cs)
            if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                w.Write([]byte("creating instance " + InstanceName + "in zone: " + Zone))
                startInstance(cs, w)
            }
    
        } else {
            msg := "instance is in intermediate state: " + instance.Status
            w.WriteHeader(http.StatusAccepted)
            w.Write([]byte(msg))
            log.Println(msg)
            
        }
    }
}

func InitComputeService() (*compute.Service, error) {
    ctx := context.Background()
    return compute.NewService(ctx)
}

func GetInstance(computeService *compute.Service) (*compute.Instance, error) {
    return computeService.Instances.Get(ProjectID, Zone, InstanceName).Do()
}


func StartInstance(computeService *compute.Service) (*compute.Operation, error) {
    return computeService.Instances.Start(ProjectID, Zone, InstanceName).Do()
}

// CreateInstance creates a given instance with metadata that logs its information.
func CreateInstance(computeService *compute.Service) (*compute.Operation, error) {

    instance := &compute.Instance{
        Name: InstanceName,
        MachineType: fmt.Sprintf("zones/%s/machineTypes/%s", Zone, InstanceType),
        NetworkInterfaces: []*compute.NetworkInterface{
            {
                Name:       "default",
                Subnetwork: fmt.Sprintf("projects/%s/regions/%s/subnetworks/default", ProjectID, Region),
                AccessConfigs: []*compute.AccessConfig{
                    {
                        Name:        "External NAT",
                        Type:        "ONE_TO_ONE_NAT",
                        NetworkTier: "PREMIUM",
                    },
                },
            },
        },
        Scheduling: &compute.Scheduling{
            Preemptible: true,
        },
        Disks: []*compute.AttachedDisk{
            {
                Boot:       true,         // The first disk must be a boot disk.
                AutoDelete: true,         //Optional
                Mode:       "READ_WRITE", //Mode should be READ_WRITE or READ_ONLY
                Interface:  "SCSI",       //SCSI or NVME - NVME only for SSDs
                InitializeParams: &compute.AttachedDiskInitializeParams{
                    DiskName: "worker-instance-boot-disk",
                    SourceImage: "projects/centos-cloud/global/images/family/centos-7",
                    DiskType:    fmt.Sprintf("projects/%s/zones/%s/diskTypes/pd-ssd", ProjectID, Zone),
                    DiskSizeGb:  200,
                },
            },
        },
    }
    return computeService.Instances.Insert(ProjectID, Zone, instance).Do()
}

// startInstance is a wrapper function for the switch statement
func startInstance(cs *compute.Service, w http.ResponseWriter) {
    operation, err := StartInstance(cs)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Fatal(err)
    }
    w.WriteHeader(http.StatusOK)
    data, _ := operation.MarshalJSON()
    w.Write(data)

}

In above code, I want to create 7 instance with 7 difference setting, specific is instance type and instance name. I test this code in cloud function with DeployInstance is start function. But there is only one instance was created, with name is benchmark-e2-standard-2 and type is e2-standard-2. It output an error with message Error: Infrastructure cannot communicate with function. There was likely a crash or deadlock in the user-provided code. Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging

I visited website but I not find a solution to fix my code. Who can help me why my code not true, how can I fix it. Step by step if possible.
Thanks in advance.

Solution

I was found my answer. Root cause is each instance must have a disk partition, with different name.
So, I change my code with some change, you can see it bellow.

package cloudfunctions

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "google.golang.org/api/compute/v1"
    "time"
)

var ProjectID = ""
var Zone = ""
var Region = ""
var InstanceName = ""
var InstanceType = ""
var IDiskName = ""

func DeployInstance(w http.ResponseWriter, r *http.Request) {
    ProjectID = os.Getenv("PROJECT_ID")
    Zone = os.Getenv("ZONE")
    Region = os.Getenv("REGION")

    var listInstance = []string{"e2-standard-8","e2-standard-2",  "n2-standard-2", "n2-standard-8", "n1-custom-2-8192", "n1-custom-8-32768", "c2-standard-8"}
    for i:=0; i < len(listInstance); i++ {
        cs, err := compute.NewService(context.Background())
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            log.Fatal(err)
        }
        InstanceType = listInstance[i]
        InstanceName = "benchmark-"+InstanceType
        IDiskName = InstanceName+"-boot-disk"

        instance, err := GetInstance(cs)
        if err != nil {
            w.WriteHeader(http.StatusTemporaryRedirect)
            w.Write([]byte(err.Error() + " instance may not exist yet"))

            _, err = CreateInstance(cs)
            if err != nil {
                for {
                    disk, derr := cs.Disks.Get(ProjectID, Zone, IDiskName).Context(context.Background()).Do()
                    log.Print(IDiskName + " is " + disk.Status)
                    time.Sleep(1 * time.Second)
                    if derr != nil {
                        startInstance(cs, w)
                        break
                    }
                }
                
            }
        } else {
            msg := "instance "+ InstanceName +" is in intermediate state: " + instance.Status
            w.WriteHeader(http.StatusAccepted)
            w.Write([]byte(msg))
            log.Println(msg)
        }
    }
}


func GetInstance(computeService *compute.Service) (*compute.Instance, error) {
    return computeService.Instances.Get(ProjectID, Zone, InstanceName).Do()
}


func StartInstance(computeService *compute.Service) (*compute.Operation, error) {
    return computeService.Instances.Start(ProjectID, Zone, InstanceName).Do()
}

// CreateInstance creates a given instance with metadata that logs its information.
func CreateInstance(computeService *compute.Service) (*compute.Operation, error) {
    instance := &compute.Instance{
        Name: InstanceName,
        MachineType: fmt.Sprintf("zones/%s/machineTypes/%s", Zone, InstanceType),
        NetworkInterfaces: []*compute.NetworkInterface{
            {
                Name:       "default",
                Subnetwork: fmt.Sprintf("projects/%s/regions/%s/subnetworks/default", ProjectID, Region),
                AccessConfigs: []*compute.AccessConfig{
                    {
                        Name:        "External NAT",
                        Type:        "ONE_TO_ONE_NAT",
                        NetworkTier: "PREMIUM",
                    },
                },
            },
        },
        Scheduling: &compute.Scheduling{
            Preemptible: true,
        },
        Disks: []*compute.AttachedDisk{
            {
                Boot:       true,         // The first disk must be a boot disk.
                AutoDelete: true,         //Optional
                Mode:       "READ_WRITE", //Mode should be READ_WRITE or READ_ONLY
                Interface:  "SCSI",       //SCSI or NVME - NVME only for SSDs
                InitializeParams: &compute.AttachedDiskInitializeParams{
                    DiskName: IDiskName,
                    SourceImage: "projects/centos-cloud/global/images/family/centos-7",
                    DiskType:    fmt.Sprintf("projects/%s/zones/%s/diskTypes/pd-ssd", ProjectID, Zone),
                    DiskSizeGb:  100,
                },
            },
        },
    }
    return computeService.Instances.Insert(ProjectID, Zone, instance).Do()
}

// startInstance is a wrapper function for the switch statement
func startInstance(cs *compute.Service, w http.ResponseWriter) {
    operation, err := StartInstance(cs)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        log.Fatal(err)
    }
    w.WriteHeader(http.StatusOK)
    data, _ := operation.MarshalJSON()
    w.Write(data)
}

If you have any question about this problem, drop your comment. I hope I can support for you.
Thanks all.

Answered By – sonnh

Answer Checked By – Marilyn (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.