Kitabı oku: «K8s Applications mit MicroK8S auf Raspberry PI», sayfa 5
Kleinster möglicher statischer Webserver
Inspiration:
https://github.com/PierreZ/goStatic
https://hub.docker.com/r/tobilg/mini-webserver
https://devopsdirective.com/posts/2021/04/tiny-container-image/
https://lipanski.com/posts/smallest-docker-image-static-website
https://ashishb.net/tech/docker-101-a-basic-web-server-displaying-hello-world/
Um einen Fallback Backend-Server zu haben (für den Fall dass im Service irgendwo was schief geht) bauen wir uns einen "Under Construction" Service. Dieser Service ist statisch, zeigt ein wenig Information und dient als defaultBackend für alle anderen Services.
Die Projektstruktur ist wie folgt:
.
├── default_ingress.yaml
├── default_latest_container.tar.gz
├── default_svc.yaml
├── default.yaml
├── dockerfile
├── do.sh
├── env.sh
├── html
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── healthz
│ ├── index.html
│ ├── site.webmanifest
│ ├── Slainte.jpeg
│ └── website_under_construction.jpg
├── src
│ └── main.go
└── test
└── test.sh
Das env.sh legt die Variablen fest.
#!/bin/bash
############################################################################################
# $Date: 2021-11-27 00:08:17 +0100 (Sa, 27. Nov 2021) $
# $Revision: 1370 $
# $Author: alfred $
# $HeadURL: https://monitoring.slainte.at/svn/slainte/trunk/k8s/k8s_app/default/env.sh $
# $Id: env.sh 1370 2021-11-26 23:08:17Z alfred $
#
# Bauen und deployen
#
############################################################################################
#shopt -o -s errexit #—Terminates the shell script if a command returns an error code.
shopt -o -s xtrace #—Displays each command before it’s executed.
shopt -o -s nounset #-No Variables without definition
export image="default"
export namespace="default slainte"
#Namespacespezifisch
. ../namespace/${namespace}_env.sh
#
Dieser Webserver (es ist das Default-Backend für alle) wird sowohl in den Namespace "default" als auch in den Namespace "slainte" ausgerollt.
ARG BUILDER_IMAGE=golang:alpine
############################
# STEP 1 build executable binary
############################
FROM ${BUILDER_IMAGE} as builder
# timezone support
ENV TZ=Europe/Vienna
# Timezone support
RUN apk add git \
tzdata && \
cp /usr/share/zoneinfo/${TZ} /etc/localtime &&\
echo $TZ > /etc/timezone
# Create appuser
ENV USER=appuser
ENV UID=10001
# See https://stackoverflow.com/a/55757473/12429735
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"
WORKDIR $GOPATH/src
# Copy the code
COPY src/ /go/src/
# Fetch dependencies.
RUN go mod init main
RUN go mod tidy
RUN go get -d -v
# Build the binary
RUN CGO_ENABLED=0 go build \
-ldflags='-w -s -extldflags "-static"' -a \
-o /go/bin/main .
############################
# STEP 2 build a small image
############################
FROM scratch
#FROM alpine:latest -> Zum testen ist das ganz praktisch
LABEL maintainer="microk8s.raspberry@slainte.at"
LABEL Description="Kleiner statischer Webserver"
# Import from builder.
#COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /etc/localtime /etc/localtime
COPY --from=builder /etc/timezone /etc/timezone
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
# Copy html
COPY --chown=appuser:appuser html/ /html/
# Copy our static executable
COPY --from=builder /go/bin/main /main
EXPOSE 8080
# Use an unprivileged user.
USER appuser:appuser
WORKDIR /html/
# Run the hello binary.
ENTRYPOINT ["/main"]
Das dockerfile ist ein jetzt ein Multistage-Build. Im ersten Schritt wird das go-Programm compiliert und ein paar notwendig Programme installiert. Im zweiten Schritt werden die notwendigen Dateien aus der Stufe 1 sowie die Web-Dateien kopiert.
#!/bin/bash
############################################################################################
# $Date: 2021-11-26 23:02:04 +0100 (Fr, 26. Nov 2021) $
# $Revision: 1361 $
# $Author: alfred $
# $HeadURL: https://monitoring.slainte.at/svn/slainte/trunk/k8s/k8s_app/default/do.sh $
# $Id: do.sh 1361 2021-11-26 22:02:04Z alfred $
#
# Bauen und deployen
#
############################################################################################
#shopt -o -s errexit #—Terminates the shell script if a command returns an error code.
shopt -o -s xtrace #—Displays each command before it’s executed.
shopt -o -s nounset #-No Variables without definition
svn update
source env.sh
docker build --no-cache --force-rm . -t ${image}:latest
#docker save ${image}:latest | gzip > ${image}_latest.tar.gz
docker kill $(docker ps | grep -i ${image}:latest | awk '{print $1 }')
docker container rm -f $(docker container ls -a | grep -i ${image}_latest | awk '{print $1 }')
docker run -d --name ${image}_latest -p 8080:8080 ${image}:latest
sleep 10
docker ps
docker export $(docker ps | grep -i ${image}:latest | awk '{print $1 }') | gzip > ${image}_latest_container.tar.gz
echo "docker exec -u 0 -it ${image}_latest /bin/sh"
#docker exec -u 0 -it ${image}_latest /bin/sh
#docker kill $(docker ps | grep -i ${image}:latest | awk '{print $1 }')
#docker container rm $(docker container ls -a | awk '{print $1 }')
#docker run -it --name ${image}_latest ${image}:latest /bin/bash
#
Das do.sh dient zum lokalen Testen mit docker. Hier wird ein Image erzeugt, und man kann testen, wie sich der Container verhält. Dieses Skript exportier den fertigen Container auch in ein tar-File. Das ist recht praktisch um in Ruhe nachzusehen, welche Dateinen wo gelandet sind.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${image}-depl
namespace: ${namespace}
spec:
replicas: 1
selector:
matchLabels:
app: ${image}-app
minReadySeconds: 60
strategy:
type: Recreate
template:
metadata:
labels:
app: ${image}-app
annotations:
sidecar.istio.io/inject: "false"
spec:
# Verhalten, wenn mehrere Replicas notwendig sind
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ${image}-app
topologyKey: "kubernetes.io/hostname"
# Container Spec
containers:
- name: ${image}
image: ${docker_registry}/${image}:${tag}
imagePullPolicy: Always
env:
- name: image
value: "${image}"
- name: tag
value: "${tag}"
ports:
- containerPort: 8080
name: http
# check for lifetime liveness, restarts if dead
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 30
successThreshold: 1
failureThreshold: 3
# check for initial readiness
readinessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 30
successThreshold: 1
failureThreshold: 3
securityContext:
capabilities:
drop:
- all
add:
- NET_BIND_SERVICE
allowPrivilegeEscalation: false
# Verhalten, beim Beenden
terminationGracePeriodSeconds: 60
restartPolicy: Always
dnsPolicy: ClusterFirst
---
Das default-yaml beschreibt die Verhaltensweise des Services. Nach 60 Sekunden wird geprüft ob der Service bereit und gesund ist (ist in diesem Fall dieselbe Prüfung). Diese Prüfung geschieht alle 30 Sekunden. Weiters werden die Rechte richtig gesetzt. Einerseits hat der User im Container keine Rechte (ist unprivilegiert), so wird nur auch der Pod selbst Rechtemäßig richtig gesetzt (darf nichts, ausser eine Port binden).
---
# Yaml für ${image}:${tag}
---
apiVersion: v1
kind: Service
metadata:
name: ${image}-svc
namespace: ${namespace}
labels:
app: ${image}-app
spec:
ports:
- port: 80
name: http
targetPort: 8080
protocol: TCP
selector:
app: ${image}-app
type: ClusterIP
---
Hier wird der Service beschrieben. Der Service ist nach aussen auf Port 80 erreichbar. Der Pod läuft aber auf Port 8080.
---
# Yaml für ${image}:${tag}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${image}-routes
namespace: ${namespace}
annotations:
kubernetes.io/ingress.class: public
cert-manager.io/cluster-issuer: "${cluster_issuer}"
# https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
# nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-temporary-redirect: "false"
nginx.ingress.kubernetes.io/secure-backends: "true"
nginx.ingress.kubernetes.io/ssl-proxy-headers: "X-Forwarded-Proto: https"
nginx.ingress.kubernetes.io/proxy-body-size: 0m
nginx.ingress.kubernetes.io/proxy-buffering: "off"
# https://github.com/nginxinc/kubernetes-ingress/tree/v1.12.0/examples/ssl-services
# nginx.ingress.kubernetes.io/ssl-services: "default-svc"
# nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- ${host}
secretName: ${secretName}
rules:
- host: ${host}
http:
paths: # https://github.com/google/re2/wiki/Syntax, https://www.regular-expressions.info/refcapture.html
- path: /${image}(/|$)(.*)
pathType: Prefix
backend:
service:
name: ${image}-svc
port:
number: 80
defaultBackend:
service:
name: ${image}-svc
port:
number: 80
---
Hier wird der Ingress beschrieben. Bis zum Ingress-Controller muß der Verkehr gesichert über https gesichert sein. Der Service dahinter kann aber kein https, somit wird das Protokoll am Controller aufgebrochen, und dahinter wird über http kommuniziert. Rewrite führt in der Regel zu Verwirrungen. Siehe auch die Erklärungen in https://kubernetes.github.io/ingress-nginx/examples/rewrite/
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
Und nun kommt der Haupteil. Das Go-Programm um eine einfache Webserver-Funktionalität abzubilden.
// Ausgabe eines einfachen HTML-Files
// Inspiration https://golang.org/doc/articles/wiki/
package main
import (
"fmt"
"strings"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
)
type Page struct {
Title string
Body []byte
}
func loadPage(title string) (*Page, error) {
var xhtml []byte
var err error
title = strings.Replace(title, "default/", "", -1)
title = strings.Replace(title, "default", "", -1)
xhtml, err = ioutil.ReadFile(title)
if err != nil { // wenn was schiefgeht (warum auch immer) wird das index.html gezeigt
log.Println("GET /index.html: " + title )
xhtml, err = ioutil.ReadFile("index.html")
}
//log.Println("RESULT " + title )
return &Page{Title: title, Body: []byte(xhtml)}, err
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/"):]
//log.Println("URL " + title + "\n")
p, _ := loadPage(title)
fmt.Fprintf(w, "%s", p.Body)
}
func logRequest(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
title := r.URL.Path[len("/"):]
title = strings.Replace(title, "default/", "", -1)
title = strings.Replace(title, "default", "", -1)
if title != "healthz" { // Nicht das Log zumüllen.
log.Println(fmt.Sprintf("%q", x))
}
handler.ServeHTTP(w, r)
})
}
func main() {
log.Println("Main Started")
http.HandleFunc("/", viewHandler)
log.Fatal(http.ListenAndServe(":8080", logRequest(http.DefaultServeMux)))
log.Println("Main End")
}
Die Source selbst ist ein einfaches go-Programm. Das Programm zeigt nur die angegebene Datei her. Mehr nicht. Der Aufruf von "/healthz" passiert alle 30 Sekunden. Um das Log nicht vollzumüllen wird dieser Aufruf nicht im Log ausgegeben (alles andere schon).
Wenn ein Aufruf nicht funktioniert (z.b. es gibt die gesuchte Datei nicht), dann wird das "/index.html" hergezeigt. Durch die verschiedenartigen Aufrufe des nginx-Controllers (mal mit dem fixen Teil des Ingress, mal ohne, jenachdem ob man das von aussen aufruft oder ob das ein Nachladen aus dem html ist, zb. das favicon) wird hier hardcoded der default und default/ weggelöscht. Damit ist das Programm nicht mehr wirklich portabel. Vielleicht gibt es in der Zukunft eine bessere Lösung.
Das index.html sieh aus wie folgt:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/transitional.dtd">
<html>
<head>
<meta HTTP-EQUIV=CONTENT-TYPE CONTENT="text/html; charset=utf-8">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<title>K8's Default-Seite</title>
</head>
<body>
<center>
<img src="http://litres.ru/website_under_construction.jpg" alt="">
<p><br/><p>
<p>Bitte verständigen Sie den Siteadmin<br/><p>
</center>
</body>
</html>
Es wird nicht gecached (soweit die Theorie, die Browser und alle Schichten dazwischen halten sich nicht immer daran). Es wird das "website_under_construction.jpg" Bild hergezeigt und ein statischer Text ausgegeben.
Nun testen wir das Ganze am lokalen Rechner.
alfred@bureau:~/svn/trunk/k8s/k8s_app/default$ ./do.sh
+ shopt -o -s nounset
+ svn update
Aktualisiere ».«:
Revision 1377.
+ source env.sh
++ shopt -o -s xtrace
++ shopt -o -s nounset
++ export image=default
++ image=default
++ export 'namespace=default slainte'
++ namespace='default slainte'
++ . ../namespace/default slainte_env.sh
env.sh: Zeile 18: ../namespace/default: Datei oder Verzeichnis nicht gefunden
+ docker build --no-cache --force-rm . -t default:latest
Sending build context to Docker daemon 3.174MB
Step 1/27 : ARG BUILDER_IMAGE=golang:alpine
Step 2/27 : FROM ${BUILDER_IMAGE} as builder
---> 3a38ce03c951
Step 3/27 : ENV TZ=Europe/Vienna
---> Running in 782165bd1a8b
Removing intermediate container 782165bd1a8b
---> 2e2c7a4ea026
Step 4/27 : RUN apk add git tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo $TZ > /etc/timezone
---> Running in 85b32bb185c8
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/7) Installing brotli-libs (1.0.9-r5)
(2/7) Installing nghttp2-libs (1.43.0-r0)
(3/7) Installing libcurl (7.79.1-r0)
(4/7) Installing expat (2.4.1-r0)
(5/7) Installing pcre2 (10.36-r0)
(6/7) Installing git (2.32.0-r0)
(7/7) Installing tzdata (2021e-r0)
Executing busybox-1.33.1-r6.trigger
OK: 22 MiB in 22 packages
Removing intermediate container 85b32bb185c8
---> 3240adcab128
Step 5/27 : ENV USER=appuser
---> Running in 8b8f2c8d3d3b
Removing intermediate container 8b8f2c8d3d3b
---> f2b4866b21ab
Step 6/27 : ENV UID=10001
---> Running in f6feab446106
Removing intermediate container f6feab446106
---> 54a02a4ae4f9
Step 7/27 : RUN adduser --disabled-password --gecos "" --home "/nonexistent" --shell "/sbin/nologin" --no-create-home --uid "${UID}" "${USER}"
---> Running in 05036e634b0c
Removing intermediate container 05036e634b0c
---> e0aa6d53860c
Step 8/27 : WORKDIR $GOPATH/src
---> Running in 962513e1a258
Removing intermediate container 962513e1a258
---> e9c7f169382f
Step 9/27 : COPY src/ /go/src/
---> 383d35179658
Step 10/27 : RUN go mod init main
---> Running in 7de29b1330ae
go: creating new go.mod: module main
go: to add module requirements and sums:
go mod tidy
Removing intermediate container 7de29b1330ae
---> 447d5859e95f
Step 11/27 : RUN go mod tidy
---> Running in ff31b9185dcc
Removing intermediate container ff31b9185dcc
---> d070533a2f6f
Step 12/27 : RUN go get -d -v
---> Running in 1076e9742df7
Removing intermediate container 1076e9742df7
---> cee66da4930a
Step 13/27 : RUN CGO_ENABLED=0 go build -ldflags='-w -s -extldflags "-static"' -a -o /go/bin/main .
---> Running in fda0bfe51009
Removing intermediate container fda0bfe51009
---> 886781406af3
Step 14/27 : FROM scratch
--->
Step 15/27 : LABEL maintainer="microk8s.raspberry@slainte.at"
---> Running in 37be2d43c280
Removing intermediate container 37be2d43c280
---> 74d5145b8a4a
Step 16/27 : LABEL Description="Kleiner statischer Webserver"
---> Running in bea56ecac8d6
Removing intermediate container bea56ecac8d6
---> 8882ec04a7a4
Step 17/27 : COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
---> fc930e8c71cc
Step 18/27 : COPY --from=builder /etc/localtime /etc/localtime
---> 72ab47f2ab4d
Step 19/27 : COPY --from=builder /etc/timezone /etc/timezone
---> 8b6cf086ecc9
Step 20/27 : COPY --from=builder /etc/passwd /etc/passwd
---> a7f34db7d163
Step 21/27 : COPY --from=builder /etc/group /etc/group
---> f5311faa6ef2
Step 22/27 : COPY --chown=appuser:appuser html/ /html/
---> 177ec4c642f2
Step 23/27 : COPY --from=builder /go/bin/main /main
---> 885e4ce994c7
Step 24/27 : EXPOSE 8080
---> Running in b1b0e6258ac8
Removing intermediate container b1b0e6258ac8
---> 5d1baee2add9
Step 25/27 : USER appuser:appuser
---> Running in 3e7a7ed9c791
Removing intermediate container 3e7a7ed9c791
---> 3477baadb6c6
Step 26/27 : WORKDIR /html/
---> Running in fb38b3ed44f9
Removing intermediate container fb38b3ed44f9
---> 99161e3f43d9
Step 27/27 : ENTRYPOINT ["/main"]
---> Running in 60665f656ce7
Removing intermediate container 60665f656ce7
---> f041835dcff7
Successfully built f041835dcff7
Successfully tagged default:latest
++ docker ps
++ grep -i default:latest
++ awk '{print $1 }'
+ docker kill
"docker kill" requires at least 1 argument.
See 'docker kill --help'.
Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
Kill one or more running containers
++ docker container ls -a
++ awk '{print $1 }'
++ grep -i default_latest
+ docker container rm -f 51a36a4f7ed5
51a36a4f7ed5
+ docker run -d --name default_latest -p 8080:8080 default:latest
ac883b89517387a7a674426ea96b82dd59fcf7e2ce513ef18f21890934627f19
+ sleep 10
+ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac883b895173 default:latest "/main" 10 seconds ago Up 10 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp default_latest
041d5fd2b67a portainer/agent:latest "./agent" 5 days ago Up 4 hours 0.0.0.0:9001->9001/tcp, :::9001->9001/tcp portainer_agent
ebb513933fe0 portainer/portainer-ce:latest "/portainer" 5 days ago Up 4 hours 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp, 9000/tcp portainer
+ gzip
++ docker ps
++ grep -i default:latest
++ awk '{print $1 }'
+ docker export ac883b895173
+ echo 'docker exec -u 0 -it default_latest /bin/sh'
docker exec -u 0 -it default_latest /bin/sh
alfred@bureau:~/svn/trunk/k8s/k8s_app/default$
Das Image wird gebaut, und kann im Portainer betrachtet werden.
Abbildung 25: Portainer - Default-Service Container
Der Container startet problemlos.
Abbildung 26: Portainer - Default Service Image Größe
Das Image selbst ist jetzt mit 4,8 MB auch sehr schlank geworden.
Abbildung 27: Portainer - Default Service Image-Layer Größen
Die Zeitzone braucht ca. 2,5KB, was sehr schlank ist.
Die html-Dateien und Bildchen brauchen 528KB.
Die kompilierte go-Datei braucht 4,3MB. Das finde ich recht beachtlich. Denke diesen einfachen Service könnte man mit zb. C einfacher hinbekommen (mehr als GET kann dieser Service ja eh nicht). Wenn mal viel Zeit vorhanden ist, kann man sich das weiter ansehen.
Abbildung 28: Default Webservice
Und wenn man dann diesen Service aufruft, dann sieht das so aus.
Jetzt werden wir den Service am "Entwicklungsrechner" builden, und deployen.
alfred@monitoring:~/dev/default$ mk
+ shopt -o -s nounset
+ namespace=default
+ docker_registry=docker.registry:5000
+ . ./env.sh
++ shopt -o -s xtrace
++ shopt -o -s nounset
++ export image=default
++ image=default
++ export 'namespace=default slainte'
++ namespace='default slainte'
++ . ../namespace/default slainte_env.sh
./env.sh: line 18: ../namespace/default: No such file or directory
+ datum=(`date '+%Y%m%d'`)
++ date +%Y%m%d
++ svn info
++ grep Revision
++ awk '{print $2}'
+ revision=1376
+ tag=20211127-1376
+ svn update
Updating '.':
U do.sh
U src/main.go
Updated to revision 1377.
+ docker build --no-cache . -t docker.registry:5000/default:20211127-1376
Sending build context to Docker daemon 557.1kB
Step 1/27 : ARG BUILDER_IMAGE=golang:alpine
Step 2/27 : FROM ${BUILDER_IMAGE} as builder
---> 975a291cf170
Step 3/27 : ENV TZ=Europe/Vienna
---> Running in 3f12caec82d6
Removing intermediate container 3f12caec82d6
---> d768166f363c
Step 4/27 : RUN apk add git tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo $TZ > /etc/timezone
---> Running in 97dc8787993d
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/aarch64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/aarch64/APKINDEX.tar.gz
(1/7) Installing brotli-libs (1.0.9-r5)
(2/7) Installing nghttp2-libs (1.43.0-r0)
(3/7) Installing libcurl (7.79.1-r0)
(4/7) Installing expat (2.4.1-r0)
(5/7) Installing pcre2 (10.36-r0)
(6/7) Installing git (2.32.0-r0)
(7/7) Installing tzdata (2021e-r0)
Executing busybox-1.33.1-r6.trigger
OK: 22 MiB in 22 packages
Removing intermediate container 97dc8787993d
---> e373bea9d5ac
Step 5/27 : ENV USER=appuser
---> Running in e09441fbcbf5
Removing intermediate container e09441fbcbf5
---> 782881df7fad
Step 6/27 : ENV UID=10001
---> Running in 51e56a86b293
Removing intermediate container 51e56a86b293
---> a49c317f4d3d
Step 7/27 : RUN adduser --disabled-password --gecos "" --home "/nonexistent" --shell "/sbin/nologin" --no-create-home --uid "${UID}" "${USER}"
---> Running in ecdd977ac68f
Removing intermediate container ecdd977ac68f
---> dbc27ad53f47
Step 8/27 : WORKDIR $GOPATH/src
---> Running in 4192b680ffe3
Removing intermediate container 4192b680ffe3
---> 85b3ccb5bdc9
Step 9/27 : COPY src/ /go/src/
---> 42f076c95522
Step 10/27 : RUN go mod init main
---> Running in c315c3253dd4
go: creating new go.mod: module main
go: to add module requirements and sums:
go mod tidy
Removing intermediate container c315c3253dd4
---> e89ddadaf202
Step 11/27 : RUN go mod tidy
---> Running in 46d037445181
Removing intermediate container 46d037445181
---> ae12bb619e5c
Step 12/27 : RUN go get -d -v
---> Running in 07742fd47a31
Removing intermediate container 07742fd47a31
---> 7a84a944fb38
Step 13/27 : RUN CGO_ENABLED=0 go build -ldflags='-w -s -extldflags "-static"' -a -o /go/bin/main .
---> Running in 5be03523c796
Removing intermediate container 5be03523c796
---> 3b4526214505
Step 14/27 : FROM scratch
--->
Step 15/27 : LABEL maintainer="microk8s.raspberry@slainte.at"
---> Running in b85cc7d56c35
Removing intermediate container b85cc7d56c35
---> 18819bc782dc
Step 16/27 : LABEL Description="Kleiner statischer Webserver"
---> Running in bd9e30ad837d
Removing intermediate container bd9e30ad837d
---> 76989ca004c9
Step 17/27 : COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
---> bf390ed23d43
Step 18/27 : COPY --from=builder /etc/localtime /etc/localtime
---> 12a477258813
Step 19/27 : COPY --from=builder /etc/timezone /etc/timezone
---> fe6ff43916ae
Step 20/27 : COPY --from=builder /etc/passwd /etc/passwd
---> df8e003c5608
Step 21/27 : COPY --from=builder /etc/group /etc/group
---> e137cf1f65b3
Step 22/27 : COPY --chown=appuser:appuser html/ /html/
---> 988dcd53ed26
Step 23/27 : COPY --from=builder /go/bin/main /main
---> 7f2287a4e3e8
Step 24/27 : EXPOSE 8080
---> Running in 7667c76d041b
Removing intermediate container 7667c76d041b
---> 23244506a748
Step 25/27 : USER appuser:appuser
---> Running in 966ba5bdda48
Removing intermediate container 966ba5bdda48
---> 2c46e9cc4ae6
Step 26/27 : WORKDIR /html/
---> Running in d183fe447e3c
Removing intermediate container d183fe447e3c
---> 126d56f90f82
Step 27/27 : ENTRYPOINT ["/main"]
---> Running in 065facb8bf39
Removing intermediate container 065facb8bf39
---> be32ef870cef
Successfully built be32ef870cef
Successfully tagged docker.registry:5000/default:20211127-1376
+ docker push docker.registry:5000/default:20211127-1376
The push refers to repository [docker.registry:5000/default]
dac4a02d94a9: Pushed
b1283fda34d3: Pushed
90937b03a9ab: Pushed
400c6d30c005: Pushed
948ebb21a773: Pushed
9cd5dadbfe87: Pushed
a35817905619: Pushed
20211127-1376: digest: sha256:b740a40b5afb5fd3d5b116f26f0c5e9c8beeed224507a8d89722aa164d47e210 size: 1777
+ docker build . -t docker.registry:5000/default:latest
Sending build context to Docker daemon 557.1kB
Step 1/27 : ARG BUILDER_IMAGE=golang:alpine
Step 2/27 : FROM ${BUILDER_IMAGE} as builder
---> 975a291cf170
Step 3/27 : ENV TZ=Europe/Vienna
---> Using cache
---> d768166f363c
Step 4/27 : RUN apk add git tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo $TZ > /etc/timezone
---> Using cache
---> e373bea9d5ac
Step 5/27 : ENV USER=appuser
---> Using cache
---> 782881df7fad
Step 6/27 : ENV UID=10001
---> Using cache
---> a49c317f4d3d
Step 7/27 : RUN adduser --disabled-password --gecos "" --home "/nonexistent" --shell "/sbin/nologin" --no-create-home --uid "${UID}" "${USER}"
---> Using cache
---> dbc27ad53f47
Step 8/27 : WORKDIR $GOPATH/src
---> Using cache
---> 85b3ccb5bdc9
Step 9/27 : COPY src/ /go/src/
---> Using cache
---> 42f076c95522
Step 10/27 : RUN go mod init main
---> Using cache
---> e89ddadaf202
Step 11/27 : RUN go mod tidy
---> Using cache
---> ae12bb619e5c
Step 12/27 : RUN go get -d -v
---> Using cache
---> 7a84a944fb38
Step 13/27 : RUN CGO_ENABLED=0 go build -ldflags='-w -s -extldflags "-static"' -a -o /go/bin/main .
---> Using cache
---> 3b4526214505
Step 14/27 : FROM scratch
--->
Step 15/27 : LABEL maintainer="microk8s.raspberry@slainte.at"
---> Using cache
---> 18819bc782dc
Step 16/27 : LABEL Description="Kleiner statischer Webserver"
---> Using cache
---> 76989ca004c9
Step 17/27 : COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
---> Using cache
---> bf390ed23d43
Step 18/27 : COPY --from=builder /etc/localtime /etc/localtime
---> Using cache
---> 12a477258813
Step 19/27 : COPY --from=builder /etc/timezone /etc/timezone
---> Using cache
---> fe6ff43916ae
Step 20/27 : COPY --from=builder /etc/passwd /etc/passwd
---> Using cache
---> df8e003c5608
Step 21/27 : COPY --from=builder /etc/group /etc/group
---> Using cache
---> e137cf1f65b3