The Micro in MicroService
From: https://all3dp.com/3d-microprint-laser-sintering-technology-3d-print-tiny-metal-parts/

The Micro in MicroService

MicroServices have been the in thing for a while now. But the question for me is always the size. In my current project, Im doing Operators in Openshift and Kubernetes, and since these in the end are containers and microservices, I want them small.

Im using golang for implementation, and build statically linked executables. So how big is a REST microservice? Are you sitting down? In golang, using a 2 stage docker build, its all of 6.08 Megabytes.

No alt text provided for this image

Checkout, winmachineman size. Incredible. This is fully capable of running in OpenShift.

The trick to this is a two-stage build, for this you need the latest docker. In addition you notic the base image is scratch, this is effectively a empty container. We put the golang executable in it. Here's the Dockerfile:

FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 as builder

RUN go get github.com/glennswest/winmachineman/winmachineman

WORKDIR /go/src/github.com/glennswest/winmachineman/winmachineman

RUN  go get -d -v

RUN  CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-w -s" -o /go/bin/winmachineman



FROM scratch

VOLUME /tmp

WORKDIR /root/

COPY --from=builder /go/bin/winmachineman /go/bin/winmachineman

COPY commit.id commit.id

EXPOSE 8080

ENTRYPOINT ["/go/bin/winmachineman"]

 
  

The code is pretty simple too:

package main

import (
	"net/http"
	"github.com/go-chi/chi"
        "github.com/go-chi/chi/middleware"
         "encoding/json"
         "fmt"
         "log"
)

var router *chi.Mux

func routers() *chi.Mux {
     router.Post("/machines", CreateMachine)
     router.Delete("/machines/{id}", DeleteMachine)
     router.Put("/machines/{id}", UpdateMachine)
     router.Get("/machines",AllMachines)
     router.Get("/healthz",ReadyCheck)
     router.Get("/alivez", AliveCheck)
     router.Get("/", HumanUI)

     return(router)
}

func init() { 
    router = chi.NewRouter() 
    router.Use(middleware.Recoverer)  
    router.Use(middleware.RequestID)
    router.Use(middleware.Logger)
    router.Use(middleware.Recoverer)
    router.Use(middleware.URLFormat)
}

func ReadyCheck(w http.ResponseWriter, r *http.Request) { 
    log.Printf("ReadyCheck %s\n", r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "ready"})
}

func AliveCheck(w http.ResponseWriter, r *http.Request) { 
    log.Printf("ReadyCheck %s\n", r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "alive"})
}

func AllMachines(w http.ResponseWriter, r *http.Request) { 
    log.Printf("AllMachines %s\n", r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "ok"})
}

func HumanUI(w http.ResponseWriter, r *http.Request) { 
    log.Printf("HumanUI %s\n", r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "ok"})
}



// Install a New Machine
func CreateMachine(w http.ResponseWriter, r *http.Request) { 
    log.Printf("CreateMachine: %s\n",r.Body)
    respondwithJSON(w, http.StatusCreated, map[string]string{"message": "successfully created"})
}

// UpdateMachine update a specific machine
func UpdateMachine(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    log.Printf("Update Machine: id: %s - %s\n", id, r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"})

}

// DeleteMachine - Uninstall a node
func DeleteMachine(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    log.Printf("Uninstall Machine: id:%s %s\n", id, r.Body)
    respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"})

}

func main() {
        r := routers()
	http.ListenAndServe(":8080", r)
}

// respondwithError return error message
func respondWithError(w http.ResponseWriter, code int, msg string) {
    respondwithJSON(w, code, map[string]string{"message": msg})
}

// respondwithJSON write json response format
func respondwithJSON(w http.ResponseWriter, code int, payload interface{}) {
    response, _ := json.Marshal(payload)
    fmt.Println(payload)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    w.Write(response)
}

In order to build it:

dhcp-64-117:winmachineman gwest$ cat build.sh

export GIT_COMMIT=$(git rev-parse --short HEAD)

rm -r -f tmp

mkdir tmp

echo $GIT_COMMIT > commit.id

#eval $(minishift docker-env)

docker build --no-cache -t glennswest/winmachineman:$GIT_COMMIT .

docker tag glennswest/winmachineman:$GIT_COMMIT  docker.io/glennswest/winmachineman:$GIT_COMMIT

docker push docker.io/glennswest/winmachineman:$GIT_COMMIT

rm commit.id

dhcp-64-117:winmachineman gwest$ 

Only special magic is I use the git commit id for version management on the images.

To run it:

dhcp-64-117:winmachineman gwest$ cat run.sh

git pull

export GIT_COMMIT=$(git rev-parse --short HEAD)

echo $GIT_COMMIT

export pname=winmachineman

oc delete dc $pname

oc delete is $pname

oc delete sa $pname

oc delete project $pname

sleep 3

oc new-project $pname

oc import-image $pname --from=docker.io/glennswest/$pname:$GIT_COMMIT --confirm

#oc create sa $pname

#oc adm policy add-cluster-role-to-user cluster-admin system:serviceaccount:$pname:default

#oc policy add-role-to-user admin  system:serviceaccount:$pname:default

oc delete  istag/$pname:latest

oc new-app glennswest/$pname:$GIT_COMMIT --token=$(oc sa get-token $pname) 

export defaultdomain=$(oc describe route docker-registry --namespace=default | grep "Requested Host" | cut -d ":" -f 2 | cut -d "." -f 2-)

oc expose svc/winmachineman --hostname=winmachineman.$defaultdomain

 
  

Only magic in the run, is to set the name without the namespace to the running container. This is really cool, and significantly reduces overhead.

The cool news is this trick not only works for golang, but for java and node as well. More to come on hyper-small containers.

Code here: https://github.com/glennswest/winmachineman

要查看或添加评论,请登录

Glenn West的更多文章

  • COBOL - Transformation

    COBOL - Transformation

    Surprisingly there still alot of Cobol code in the world. While there is great resistance to moving, the reality is the…

  • Azure Private DNS for Openshift

    Azure Private DNS for Openshift

    Want to create a openshift cluster, or other application, and need local control of DNS? Azure private dns allows you…

  • Running a container close to bare metal

    Running a container close to bare metal

    Sometimes you want as close to bare metal or bare vm as you can get. Performance and security often drive this.

  • Rapid IoT Development

    Rapid IoT Development

    I have always had a love of hardware and IoT, from my teen years. I started my first IoT project in 2nd grade.

  • Digital Transformation - Modern DB

    Digital Transformation - Modern DB

    Tech Debt also lives in the database! I've done a MRP transformation a few years ago, and one of the shocking things…

  • Stubbing Out a Complex REST API

    Stubbing Out a Complex REST API

    I've started on redfish2esxi, a service designed to allow Metal3 to talk to esxi, and do bare metal provisioning. Since…

  • Metal DB - Using Hardware like a POD

    Metal DB - Using Hardware like a POD

    What if you could schedule and use bare metal hardware as easy as a pod in Kubernetes or OpenShift? Could you really…

  • OpenShift 4.x on your laptop - CRC

    OpenShift 4.x on your laptop - CRC

    OpenShift 4.x is a huge upgrade from OpenShift 3.

  • Fast Development Ideas on OpenShift

    Fast Development Ideas on OpenShift

    I always get inspired when talking with customers. I saw Tabulator this morning, in my daily nodejs update, and really…

    1 条评论
  • NodeJS running on Windows 2019

    NodeJS running on Windows 2019

    Windows 2019 is hot off the press, complete with new container images. Since it's so new, it will take a while for the…

社区洞察

其他会员也浏览了