From daedf91fb5a8204a4ce94f49a5e7a65a9d251e62 Mon Sep 17 00:00:00 2001 From: Vojtech Mares Date: Mon, 19 Feb 2024 22:44:27 +0100 Subject: [PATCH] feat: add demo apps and workflows to build --- .github/workflows/build-apps.yml | 160 +++++++++++++++++++++++++++++++ apps/blue-green/Dockerfile | 19 ++++ apps/blue-green/go.mod | 5 + apps/blue-green/go.sum | 2 + apps/blue-green/main.go | 81 ++++++++++++++++ apps/hello-world/Dockerfile | 17 ++++ apps/hello-world/go.mod | 5 + apps/hello-world/go.sum | 2 + apps/hello-world/main.go | 65 +++++++++++++ apps/probes/Dockerfile | 17 ++++ apps/probes/go.mod | 4 + apps/probes/go.sum | 2 + apps/probes/main.go | 38 ++++++++ 13 files changed, 417 insertions(+) create mode 100644 .github/workflows/build-apps.yml create mode 100644 apps/blue-green/Dockerfile create mode 100644 apps/blue-green/go.mod create mode 100644 apps/blue-green/go.sum create mode 100644 apps/blue-green/main.go create mode 100644 apps/hello-world/Dockerfile create mode 100644 apps/hello-world/go.mod create mode 100644 apps/hello-world/go.sum create mode 100644 apps/hello-world/main.go create mode 100644 apps/probes/Dockerfile create mode 100644 apps/probes/go.mod create mode 100644 apps/probes/go.sum create mode 100644 apps/probes/main.go diff --git a/.github/workflows/build-apps.yml b/.github/workflows/build-apps.yml new file mode 100644 index 0000000..a7482eb --- /dev/null +++ b/.github/workflows/build-apps.yml @@ -0,0 +1,160 @@ +name: Build demo apps + +on: + push: + branches: + - main + paths: + - 'apps/**' + +jobs: + hello-world: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: setup qemu + uses: docker/setup-qemu-action@v3 + + - name: setup docker + uses: docker/setup-buildx-action@v3 + + - name: login to ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: docker metadata + uses: docker/metadata-action@v5 + id: hello_world_meta + with: + images: | + ghcr.io/${{ github.repository }}/apps/hello-world + tags: | + type=sha,format=long + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} + + - name: build and push + uses: docker/build-push-action@v5 + with: + context: /apps/hello-world + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.hello_world_meta.outputs.tags }} + labels: ${{ steps.hello_world_meta.outputs.labels }} + + probes: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: setup qemu + uses: docker/setup-qemu-action@v3 + + - name: setup docker + uses: docker/setup-buildx-action@v3 + + - name: login to ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: docker metadata + uses: docker/metadata-action@v5 + id: probe_meta + with: + images: | + ghcr.io/${{ github.repository }}/apps/probes + tags: | + type=sha,format=long + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} + + - name: build and push + uses: docker/build-push-action@v5 + with: + context: /apps/probes + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.probe_meta.outputs.tags }} + labels: ${{ steps.probe_meta.outputs.labels }} + + blue-green: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: setup qemu + uses: docker/setup-qemu-action@v3 + + - name: setup docker + uses: docker/setup-buildx-action@v3 + + - name: login to ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: docker metadata (blue) + uses: docker/metadata-action@v5 + id: blue_meta + with: + + images: | + ghcr.io/${{ github.repository }}/apps/blue-green + tags: | + type=sha,format=long + type=raw,value=blue + + - name: docker metadata (green) + uses: docker/metadata-action@v5 + id: green_meta + with: + images: | + ghcr.io/${{ github.repository }}/apps/blue-green + tags: | + type=sha,format=long + type=raw,value=green + + - name: build and push (blue) + uses: docker/build-push-action@v5 + with: + context: /apps/blue-green + platforms: linux/amd64,linux/arm64 + build-args: | + IS_BLUE=true + push: true + tags: ${{ steps.green_meta.outputs.tags }} + labels: ${{ steps.green_meta.outputs.labels }} + + - name: build and push (green) + uses: docker/build-push-action@v5 + with: + context: /apps/blue-green + platforms: linux/amd64,linux/arm64 + build-args: | + IS_BLUE=false + push: true + tags: ${{ steps.green_meta.outputs.tags }} + labels: ${{ steps.green_meta.outputs.labels }} + diff --git a/apps/blue-green/Dockerfile b/apps/blue-green/Dockerfile new file mode 100644 index 0000000..82d9c0a --- /dev/null +++ b/apps/blue-green/Dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.22 as builder + +ARG IS_BLUE=true + +WORKDIR /go/src/app + +COPY . . + +RUN go mod download + +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-X main.IsBlue=${IS_BLUE}" -a -installsuffix cgo -o blue-green . + +FROM debian:bookwork-slim as runtime + +WORKDIR /app + +COPY --from=builder /go/src/app/blue-green . + +CMD [ "/app/blue-green" ] diff --git a/apps/blue-green/go.mod b/apps/blue-green/go.mod new file mode 100644 index 0000000..8c20464 --- /dev/null +++ b/apps/blue-green/go.mod @@ -0,0 +1,5 @@ +module github.com/vojtechmares/kubernetes-training/apps/blue-green + +go 1.22.0 + +require github.com/google/uuid v1.6.0 // indirect diff --git a/apps/blue-green/go.sum b/apps/blue-green/go.sum new file mode 100644 index 0000000..7790d7c --- /dev/null +++ b/apps/blue-green/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/apps/blue-green/main.go b/apps/blue-green/main.go new file mode 100644 index 0000000..31dd9e0 --- /dev/null +++ b/apps/blue-green/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "strconv" + "text/template" + + "github.com/google/uuid" +) + +var IsBlue string = "true" + +const html = ` + + + + Hello, Kubernetes! | {{ .Hostname }} + + + +
+

Hello, Kubernetes! 👋

+
hostname: {{ .Hostname }}
+
instanceID: {{ .InstanceID }}
+
+ + +` + +func main() { + instanceID := uuid.New().String() + hostname, err := os.Hostname() + if err != nil { + log.Fatal(err) + } + + var hexColor string = "#15803d" // green + + isBlue, err := strconv.ParseBool(IsBlue) + if err != nil { + log.Fatal(err) + } + + if isBlue { + hexColor = "#1d4ed8" + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tpl, err := template.New("index.html").Parse(html) + if err != nil { + log.Println(err) + w.Write([]byte(fmt.Sprintf("Error: %s", err))) + return + } + + err = tpl.Execute(w, map[string]string{"Hostname": hostname, "InstanceID": instanceID, "Color": hexColor}) + if err != nil { + log.Println(err) + w.Write([]byte(fmt.Sprintf("Error: %s", err))) + return + } + + w.Header().Set("Content-Type", "text/html") + }) + + log.Println("Starting server on :8080") + http.ListenAndServe(":8080", nil) +} diff --git a/apps/hello-world/Dockerfile b/apps/hello-world/Dockerfile new file mode 100644 index 0000000..46a69c7 --- /dev/null +++ b/apps/hello-world/Dockerfile @@ -0,0 +1,17 @@ +FROM golang:1.22 as builder + +WORKDIR /go/src/app + +COPY . . + +RUN go mod download + +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o hello-world . + +FROM debian:bookwork-slim as runtime + +WORKDIR /app + +COPY --from=builder /go/src/app/hello-world . + +CMD [ "/app/hello-world" ] diff --git a/apps/hello-world/go.mod b/apps/hello-world/go.mod new file mode 100644 index 0000000..200de34 --- /dev/null +++ b/apps/hello-world/go.mod @@ -0,0 +1,5 @@ +module github.com/vojtechmares/kubernetes-training/apps/probes + +go 1.22.0 + +require github.com/google/uuid v1.6.0 diff --git a/apps/hello-world/go.sum b/apps/hello-world/go.sum new file mode 100644 index 0000000..7790d7c --- /dev/null +++ b/apps/hello-world/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/apps/hello-world/main.go b/apps/hello-world/main.go new file mode 100644 index 0000000..f40bc04 --- /dev/null +++ b/apps/hello-world/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "text/template" + + "github.com/google/uuid" +) + +const html = ` + + + + Hello, Kubernetes! | {{ .Hostname }} + + + +
+

Hello, Kubernetes! 👋

+
hostname: {{ .Hostname }}
+
instanceID: {{ .InstanceID }}
+
+ + +` + +func main() { + instanceID := uuid.New().String() + hostname, err := os.Hostname() + if err != nil { + log.Fatal(err) + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tpl, err := template.New("index.html").Parse(html) + if err != nil { + log.Println(err) + w.Write([]byte(fmt.Sprintf("Error: %s", err))) + return + } + + err = tpl.Execute(w, map[string]string{"Hostname": hostname, "InstanceID": instanceID}) + if err != nil { + log.Println(err) + w.Write([]byte(fmt.Sprintf("Error: %s", err))) + return + } + + w.Header().Set("Content-Type", "text/html") + }) + + log.Println("Starting server on :8080") + http.ListenAndServe(":8080", nil) +} diff --git a/apps/probes/Dockerfile b/apps/probes/Dockerfile new file mode 100644 index 0000000..5b1642b --- /dev/null +++ b/apps/probes/Dockerfile @@ -0,0 +1,17 @@ +FROM golang:1.22 as builder + +WORKDIR /go/src/app + +COPY . . + +RUN go mod download + +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o probes . + +FROM debian:bookwork-slim as runtime + +WORKDIR /app + +COPY --from=builder /go/src/app/probes . + +CMD [ "/app/probes" ] diff --git a/apps/probes/go.mod b/apps/probes/go.mod new file mode 100644 index 0000000..ed98654 --- /dev/null +++ b/apps/probes/go.mod @@ -0,0 +1,4 @@ +module github.com/vojtechmares/kubernetes-training/apps/probes + +go 1.22.0 + diff --git a/apps/probes/go.sum b/apps/probes/go.sum new file mode 100644 index 0000000..7790d7c --- /dev/null +++ b/apps/probes/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/apps/probes/main.go b/apps/probes/main.go new file mode 100644 index 0000000..7a3b8c8 --- /dev/null +++ b/apps/probes/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "log" + "net/http" + "time" +) + +func main() { + startTime := time.Now() + + http.HandleFunc("/startupz", func(w http.ResponseWriter, r *http.Request) { + if time.Since(startTime) < 30*time.Second { + http.Error(w, "Server is starting up", http.StatusServiceUnavailable) + return + } + + w.Write([]byte("OK")) + w.WriteHeader(http.StatusOK) + }) + + http.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + w.WriteHeader(http.StatusOK) + }) + + http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + w.WriteHeader(http.StatusOK) + }) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + }) + + log.Println("Starting server on :8080") + http.ListenAndServe(":8080", nil) +}