Kitabı oku: «K8s Applications mit MicroK8S auf Raspberry PI», sayfa 3
cpu: 1
memory: 64Mi
Requests:
cpu: 500m
memory: 24Mi
Liveness: exec [/hello-world] delay=5s timeout=1s period=10s #success=1 #failure=3
Readiness: exec [/hello-world] delay=3s timeout=1s period=3s #success=1 #failure=3
Environment:
FOO: bar
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-vtwl2 (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-vtwl2:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 23s default-scheduler Successfully assigned default/hello-world-6bb7844865-4j5bw to pc5
Warning BackOff 14s (x4 over 19s) kubelet Back-off restarting failed container
Normal Pulled 1s (x3 over 22s) kubelet Container image "docker.registry:5000/hello-world:20211003" already present on machine
Normal Created 1s (x3 over 22s) kubelet Created container hello-world
Normal Started 1s (x3 over 21s) kubelet Started container hello-world
alfred@pc1:/opt/cluster/go$
alfred@pc1:/opt/cluster/go$ kubectl get pod hello-world-6bb7844865-4j5bw
NAME READY STATUS RESTARTS AGE
hello-world-6bb7844865-4j5bw 0/1 CrashLoopBackOff 4 108s
alfred@pc1:/opt/cluster/go$
alfred@pc1:/opt/cluster/go$ kubectl logs hello-world-6bb7844865-4j5bw
hello world
alfred@pc1:/opt/cluster/go$
Das heist, der Service wird ordnungsgemäß installiert, läuft auch einmal und terminiert dann (so ist das Programm). Aus Kubernetes Sicht wird aber über das Deployment versucht den Service wieder herzustellen. Daher wird er permanent restarted. Für das Hello-World Prinzip reicht das. Für einen richtigen Service aber natürlich nicht.
Darum löschen wir den Service auch wieder.
alfred@pc1:/opt/cluster/go$ kubectl delete -f hello-world.yaml
deployment.apps "hello-world" deleted
service "hello-world-service" deleted
alfred@pc1:/opt/cluster/go$
Aber wir haben nun ein funktionierendes Setup. Können auf der Entwicklungsmaschine docker-Container erzeugen, diese in das Repository am Kubernetes Cluster einspielen, und den Service dann auch starten.
Golang IDE
Inspiration:
https://golang.org/doc/articles/wiki/
https://github.com/visualfc/liteide
https://linuxize.com/post/how-to-install-atom-text-editor-on-ubuntu-20-04/
https://www.gophercoders.com/install/atom/configure/
https://snapcraft.io/install/liteide-tpaw/ubuntu
https://elearning.wsldp.com/pcmagazine/install-liteide-on-ubuntu/
Um vernünftig Go Programme entwicklen zu können, braucht man eine vernünftige IDE. Neben IntelliJ (leider nicht in der Community-Edition) gibt es noch LiteIDE sowie den beliebten Editor Atom (wahrscheinlich gibt es noch viele andere). Ich verwenden den Atom-Editor.
Abbildung 10: Atom Editor
Nachdem das Plugin eingerichtet ist, kann man das Projekt bereits editieren.
Damit alles funktioniert muß natürlich go selbst vorhanden sein.
Abbildung 11: Atom Konfiguriert
Man kann aber auch LiteIDE benutzen.
Abbildung 12: LiteIDE
Womit man am liebsten arbeitet, muß jeder für sich selbst entscheiden.
Was auch immer man installiert, meine Rat ist die Applikation nicht über snap zu installieren (wegen des Isolation-Levels), sondern direkt. Die Interaktion zwischen den Snaps (setzen von Variablen usw.) funktioniert sonst unter Umständen nicht wie erwartet.
Am Beispiel von LiteIDE. Siehe auch https://elearning.wsldp.com/pcmagazine/install-liteide-on-ubuntu/
Download der richtigen Datei, entpacken nach /usr/local. Danach wird für den bequement Start eine desktop Datei erzeugt.
[Desktop Entry]
Version=1.0
Name=LiteIDE
Comment="IDE for editing and building projects written in the Go programming language"
GenericName=GoIDE
Exec=/usr/local/liteide/bin/liteide
Terminal=false
Type=Application
Icon=/usr/local/liteide/share/liteide/welcome/images/liteide128.xpm
Categories=Development;
StartupNotify=true
Diese Datei wird dann mit dem Statement
alfred@bureau:~$ cd Schreibtisch/
alfred@bureau:~/Schreibtisch$ ll
insgesamt 480
drwx-----T 2 alfred alfred 4096 Okt 10 09:23 ./
drwx-----T 49 alfred alfred 479232 Okt 10 09:24 ../
-rw-r--r-- 1 alfred alfred 361 Okt 10 09:22 LiteIDE.desktop
alfred@bureau:~/Schreibtisch$ sudo desktop-file-install LiteIDE.desktop
alfred@bureau:~/Schreibtisch$
nach
alfred@bureau:~/Schreibtisch$ ll /usr/share/applications/LiteIDE.desktop
-rw-r--r-- 1 root root 361 Okt 10 09:30 /usr/share/applications/LiteIDE.desktop
alfred@bureau:~/Schreibtisch$
kopiert, und leicht modifiziert. Ab dann kann man im Launcher diese Applikation zu den Favoriten hinzufügen.
Abbildung 13: LiteIDE zu Favoriten hinzufügen
Ab jetzt ist der Start leicht.
Abbildung 14: LiteIDE System Environment
Jetzt muß nur noch das Systemenvironment richtig sein, dann steht dem Entwickeln, testen, Debuggen nichts mehr im Wege.
Lens – the Kubernetes IDE
Inspiration:
https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
https://dev.to/anaisintech/getting-started-with-kubernetes-kubectl-and-microk8s-on-ubuntu-pko
https://microk8s.io/docs/services-and-ports
https://microk8s.io/docs/troubleshooting
Lens ist eine recht vernünftige IDE um mit Kubernetes zu arbeiten. Unter Ubuntu kann das direkt aus dem Software-Repository installiert werden.
Abbildung 15: Lens - Installation unter Ubuntu
Damit das Tool funktionieren kann, brauchen wir auf unserem lokalen Rechner kubectl.
alfred@bureau:~$ snap install kubectl --classic
kubectl version --client
kubectl 1.22.4 aus Canonical✓ installiert
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.4", GitCommit:"b695d79d4f967c403a96986f1750a35eb75e75f1", GitTreeState:"clean", BuildDate:"2021-11-18T02:34:11Z", GoVersion:"go1.16.10", Compiler:"gc", Platform:"linux/amd64"}
alfred@bureau:~$ kubectl cluster-info
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
The connection to the server localhost:8080 was refused - did you specify the right host or port?
alfred@bureau:~$
Nun fehlt noch die Konfig. Die holen wir uns aus dem existierenden Cluster.
alfred@pc1:~/.kube$ microk8s config > config
alfred@pc1:~/.kube$ cat config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREekNDQWZlZ0F3SUJBZ0lVSmdyRlRBbmh6SjBXZThxakdFVFErMDFHYWJRd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0Z6RVZNQk1HQTFVRUF3d01NVEF1TVRVeUxqRTRNeTR4TUI0WERUSXhNVEV5TXpBM016WXpPRm9YRFRNeApNVEV5TVRBM016WXpPRm93RnpFVk1CTUdBMVVFQXd3TU1UQXVNVFV5TGpFNE15NHhNSUlCSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUExYVQ3Y2hkaHUwRzNwc2EvYjdUZHU3UFRoK3l5ek41TmkyRzMKaDBmbUwweUpqOExHL2FGSm9QdW1RRVBNdWYvRFVWUVQrbmFDdjJrdmtjSjFtdlgzdlhBb1hwMHR1S1FLOEFxawp2a1UwNU8rb1pScERJZFQvUUlSSm1Ba3UraHJwWHJscE1TeHRjdUxWZ3hjc0ZpWGU4ZzVxYXVEOGVSMzZudlN1CnJzK3o2RE1rS1lvVzdlYWp4L0loNDBsNkNNY3VVVDNPVnIyTEh0Sm0vM2NaKzZlVEQ3aTNFUjA3TlVmcVk5Y1oKaXcvUzRlSWtUVC9HRjFwZG5mbUtYdnB4NnFKaWVjWDVPanEzM1l5UitBZ3FPdnlBNzVIOWp5SEtqOTdDekdqdgp5ZGJjcmJtYW5SVU1FditlVWVyVDFMeW13SDNFSDk0OUM5VXhkV2pXZTEydVNZaS9YUUlEQVFBQm8xTXdVVEFkCkJnTlZIUTRFRmdRVTZhZjNUSWc3ZjV6N21kYno3NGtCZ2FHVFRMWXdId1lEVlIwakJCZ3dGb0FVNmFmM1RJZzcKZjV6N21kYno3NGtCZ2FHVFRMWXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQwpBUUVBUHppRnhmU29EWjdscGt1TGF0a0VVdy9vUGg0YlIyM2g4bXoyTkpWc0p2NW92bXIvaVA2NXdKa2tKMDVRCnliays0Y0d3TnlFemdQVTBJaFRERnpRVU0vWFRyQlEwZG0zSGFsSHNHVE1iK05Ua3V2REFJQlUzUVhyOWJJYlkKbzVuaUl0SWVRdGR4dVBTbXYwZ2laQ3l6bWpQZTVOcnlRc05ONFQyeFp2UUcrcDhkVnBzWmZhVTd0NERZZlZhbwppTW5KVFJNazdKS3c5M3Y5VDRRcHdsMWthQnlFcjhYUkhWOGxUQUVPa1djOGdlNWx5NlEza1ZGeVAzYUx4djJqCll6K3BxVExncENRQ3JJRlVqcVhpTkdUeUtlQXp1TWlFaWFrclRnODhNZlhaTVdHc0l5NW5GdWt0clpYT0xhL3QKZVo2NEorKzg2SzRrQXRidVdNQUJORjNObEE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
server: https://192.168.0.201:16443
name: microk8s-cluster
contexts:
- context:
cluster: microk8s-cluster
user: admin
name: microk8s
current-context: microk8s
kind: Config
preferences: {}
users:
- name: admin
user:
token: WDZKL1VldzE4aFVNVm03amdmVFR5Z1h2UzNZcTZjeWxNUzVud2ZPcWR5WT0K
alfred@pc1:~/.kube$
Wir kopieren die config auf unseren Rechner in den Ordner ${HOME}/.kube.
alfred@bureau:~/.kube$ ls -lisa
insgesamt 480
1706699 4 drwxr-x--- 2 alfred alfred 4096 Nov 26 11:52 .
1311228 472 drwx-----T 52 alfred alfred 479232 Nov 26 11:52 ..
1705911 4 -rw-rw-r-- 1 alfred alfred 1874 Nov 26 11:51 config
alfred@bureau:~/.kube$
alfred@bureau:~/.kube$ kubectl config get-clusters
NAME
microk8s-cluster
alfred@bureau:~/.kube$
alfred@bureau:~/.kube$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.0.201:16443
CoreDNS is running at https://192.168.0.201:16443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
alfred@bureau:~/.kube$
Nun funktioniert die Kommand-Line-Version. Wir installieren noch die bash-completion (das spart viel Tipparbeit).
alfred@bureau:~$ sudo apt-get install bash-completion
Paketlisten werden gelesen… Fertig
Abhängigkeitsbaum wird aufgebaut… Fertig
Statusinformationen werden eingelesen… Fertig
bash-completion ist schon die neueste Version (1:2.11-2ubuntu1).
bash-completion wurde als manuell installiert festgelegt.
0 aktualisiert, 0 neu installiert, 0 zu entfernen und 0 nicht aktualisiert.
alfred@bureau:~$
alfred@bureau:~$ echo 'source <(kubectl completion bash)' >>~/.bashrc
alfred@bureau:~$ sudo su -l
root@bureau:~# kubectl completion bash >/etc/bash_completion.d/kubectl
root@bureau:~# exit
Abgemeldet
alfred@bureau:~$ echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -F __start_kubectl k' >>~/.bashrc
alfred@bureau:~$
Nun loggen wir uns neu ein. Somit funktioniert der Alias und der Zugriff auf den Cluster von außen.
alfred@bureau:~$ k get all
NAME READY STATUS RESTARTS AGE
pod/webserver-depl-bff4d4675-8v9nz 1/1 Running 2 (179m ago) 18h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.152.183.1 <none> 443/TCP 3d3h
service/webserver-svc ClusterIP 10.152.183.121 <none> 443/TCP,80/TCP 38h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/webserver-depl 1/1 1 1 38h
NAME DESIRED CURRENT READY AGE
replicaset.apps/webserver-depl-cf898f654 0 0 0 38h
replicaset.apps/webserver-depl-98f95758b 0 0 0 38h
replicaset.apps/webserver-depl-646544ddfd 0 0 0 37h
replicaset.apps/webserver-depl-dd8c746d6 0 0 0 37h
replicaset.apps/webserver-depl-86c6d7549f 0 0 0 37h
replicaset.apps/webserver-depl-b485f9695 0 0 0 19h
replicaset.apps/webserver-depl-c8599c9f5 0 0 0 19h
replicaset.apps/webserver-depl-bff4d4675 1 1 1 18h
alfred@bureau:~$
Jetzt können wir den Cluster in der Lens einrichten.
Abbildung 16: Lens - Cluster hinzufügen
Es wird die config aus ${HOME}/.kube/config verwendet.
Abbildung 17: Lens - Cluster Ansicht
Nun ist Lens mit dem Cluster verbunden. Somit kann komfortabel vom eigenen Rechner aus gearbeitet werden.
Alternativ ist natürlich auch das Kubernet Dashboard als Webapplikation vorhanden.
Abbildung 18: Kubernetes Dashboard
Somit kann der Cluster bequem verwaltet und diagnostiziert werden.
Wie sicher das exponieren der API nach aussen ist, weiß ich noch nicht. Im Internet sollten der Cluster durch die Firewall geschützt sein (nur die Ingress-Ports sichtbar).
Generell sollte bei Einsatz im Internet der Firewall große Beachtung zuteil werden. Lokal zu Hause ist die Architektur etwas einfacher.
Abbildung 19: Netzwerk Architektur
Hier ist nur der Ingress nach aussen sichtbar. Alles andere kann von aussen nicht erreicht werden. In Richtung Intranet ist alles erlaubt.
Einfacher WebServer mit Go
Inspiration:
https://golang.org/doc/articles/wiki/
https://www.freecodecamp.org/news/how-to-remove-images-in-docker/
https://docs.docker.com/engine/reference/commandline/image_pull/
Dieses Kapitel beschreibt einen einfachen WebServer mit Go. Das ist ein richtig funktionierender WebService. Dieser Service wird alle Variablen und die Systemzeit des Containers wiedergeben.
Zuerst brauchen wir ein einfaches Programm.
// Ausgabe eines einfachen HTML-Files und ersetzen von Schlüsselwörtern mit konkreten Informationen
// Inspiration https://golang.org/doc/articles/wiki/
// https://www.socketloop.com/tutorials/golang-get-hardware-information-such-as-disk-memory-and-cpu-usage
//
// alfred@monitoring:~/GetInfo$ go get github.com/shirou/gopsutil/...
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/mem"
)
type Page struct {
Title string
Body []byte
}
func loadError(xerr string) (xhtml string) {
var xs string
currentTime := time.Now()
xtext, err := ioutil.ReadFile("error.html")
if err != nil {
log.Fatal("Error File kann nicht geöffnet werden " + err.Error())
}
xs = strings.Replace(string(xtext), "%SYSTIME%", currentTime.Format("2006-01-02 15:04:05 Monday"), -1)
xs = strings.Replace(string(xs), "%SYSTEMFEHLER%", xerr, -1)
return xs
}
func loadPage(title string) (*Page, error) {
var xhtml string
var xtmp string
var xenv string
const GB = 1073741824
filename := title + ".html"
log.Println(filename)
xhtml = ""
currentTime := time.Now()
f, err := os.Open(filename)
if err != nil {
log.Println(filename+" kann nicht geöffnet werden: ", err)
xhtml = loadError(err.Error())
} else {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
xtmp = scanner.Text()
if strings.Contains(xtmp, "%SYSTIME%") {
xtmp = strings.Replace(xtmp, "%SYSTIME%", currentTime.Format("2006-01-02 15:04:05 Monday"), -1)
}
if strings.Contains(xtmp, "%OSENVIRONMENT%") {
xenv = ""
for _, pair := range os.Environ() {
variable := strings.Split(pair, "=")
xenv += variable[0] + "=>" + variable[1] + "<br>"
}
xtmp = strings.Replace(xtmp, "%OSENVIRONMENT%", xenv, -1)
}
if strings.Contains(xtmp, "%DISKUSAGE%") {
diskStat, err := disk.Usage("/")
if err != nil {
log.Println(" Plattenbelegung kann nicht gelesen werden: ", err)
}
xenv = "Pfad:" + diskStat.Path +
"<br>FSTYPE:" + diskStat.Fstype +
"<br>Total disk space:" + fmt.Sprintf("%5.1f", float64(diskStat.Total)/GB) +
" GB<br>Free disk space:" + fmt.Sprintf("%5.1f", float64(diskStat.Free)/GB) +
" GB<br>Used disk space:" + fmt.Sprintf("%5.1f", float64(diskStat.Used)/GB) +
" GB<br>Used GB Prozent:" + fmt.Sprintf("%3.1f", diskStat.UsedPercent) +
"<br>Used Inodes:" + strconv.FormatUint(diskStat.InodesUsed, 10) +
"<br>Used Inodes Prozent:" + fmt.Sprintf("%3.1f", diskStat.InodesUsedPercent)
xtmp = strings.Replace(xtmp, "%DISKUSAGE%", xenv, -1)
}
if strings.Contains(xtmp, "%HOSTINFO%") {
// host or machine kernel, uptime, platform Info
hostStat, err := host.Info()
if err != nil {
log.Println(" Hostinformation kann nicht gelesen werden: ", err)
}
xenv = "Hostname: " + hostStat.Hostname +
"<br>OS: " + hostStat.OS +
"<br>Platform: " + hostStat.Platform +
"<br>Host ID(uuid): " + hostStat.HostID +
"<br>Uptime (sec): " + strconv.FormatUint(hostStat.Uptime, 10) +
"<br>Number of processes running: " + strconv.FormatUint(hostStat.Procs, 10)
xtmp = strings.Replace(xtmp, "%HOSTINFO%", xenv, -1)
}
if strings.Contains(xtmp, "%MEMINFO%") {
runtimeOS := runtime.GOOS
vmStat, err := mem.VirtualMemory()
if err != nil {
log.Println(" Memoryinformation kann nicht gelesen werden: ", err)
}
xenv = "OS : " + runtimeOS +
"<br>Total memory: " + fmt.Sprintf("%5.1f", float64(vmStat.Total)/GB) +
" GB<br>Free memory: " + fmt.Sprintf("%5.1f", float64(vmStat.Free)/GB) +
" GB<br>Used memory: " + fmt.Sprintf("%5.1f", float64(vmStat.Used)/GB) +
" GB<br>Percentage used memory: " + strconv.FormatFloat(vmStat.UsedPercent, 'f', 2, 64)
xtmp = strings.Replace(xtmp, "%MEMINFO%", xenv, -1)
}
xhtml += xtmp
}
if err := scanner.Err(); err != nil {
log.Println(filename+" kann nicht gelesen werden: %s\n", err)
xhtml = loadError(err.Error())
}
}
defer f.Close()
return &Page{Title: title, Body: []byte(xhtml)}, nil
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
fmt.Fprintf(w, "%s", p.Body)
}
func main() {
log.Println("Main Started")
http.HandleFunc("/view/", viewHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
log.Println("Main End")
}
Um die Abhängigkeiten richtig auflösen zu können, braucht go auch ein module-File. Diese Dateien werden vom SDK LiteIDE selbstständig erzeugt.
alfred@monitoring:~/GetInfo$ cat go.mod
module GetInfo
go 1.16
require (
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/shirou/gopsutil v3.21.9+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
)
alfred@monitoring:~/GetInfo$ cat go.sum
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/shirou/gopsutil v3.21.9+incompatible h1:LTLpUnfX81MkHeCtSrwNKZwuW5Id6kCa7/P43NdcNn4=
github.com/shirou/gopsutil v3.21.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
alfred@monitoring:~/GetInfo$
Dieses Programm zeigt nach dem Aufruf von <Link:8080/view/filename> das aufgerufene File <filename> an, und ersetzt gewisse Schlüsselwörter durch konkreten Text. In unserem Beispiel habe ich das File hello.html mit OpenOffice vorbereitet.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="generator" content="LibreOffice 7.1.6.2 (Linux)"/>
<meta name="author" content="Alfred Sabitzer"/>
<meta name="created" content="2021-10-07T16:33:50.696105947"/>
<meta name="changedby" content="Alfred Sabitzer"/>
<meta name="changed" content="2021-10-07T16:36:08.816466992"/>
<style type="text/css">
@page { size: 21cm 29.7cm; margin: 2cm }
p { margin-bottom: 0.25cm; line-height: 115%; background: transparent }
td p { orphans: 0; widows: 0; background: transparent }
</style>
</head>
<body lang="de-AT" link="#000080" vlink="#800000" dir="ltr"><p style="margin-bottom: 0cm; line-height: 100%">
<p style="margin-bottom: 0cm; line-height: 100%"><br/>
</p>
<table width="100%" cellpadding="4" cellspacing="0">
<col width="64*"/>
<col width="64*"/>
<col width="64*"/>
<col width="64*"/>
<tr valign="top">
<td width="25%" style="border-top: 1px solid #000000; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0.1cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
OSENVIRONMENT</p>
</td>
<td width="25%" style="border-top: 1px solid #000000; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0.1cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
DISKUSAGE</p>
</td>
<td width="25%" style="border-top: 1px solid #000000; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0.1cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
HOSTINFO</p>
</td>
<td width="25%" style="border: 1px solid #000000; padding: 0.1cm"><p>
MEMINFO</p>
</td>
</tr>
<tr valign="top">
<td width="25%" style="border-top: none; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
%OSENVIRONMENT%
</p>
</td>
<td width="25%" style="border-top: none; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
%DISKUSAGE%
</p>
</td>
<td width="25%" style="border-top: none; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; padding-top: 0cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm"><p>
%HOSTINFO%</p>
</td>
<td width="25%" style="border-top: none; border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; padding-top: 0cm; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm"><p>
%MEMINFO%</p>
</td>
</tr>
</table>
<p style="margin-bottom: 0cm; line-height: 100%"><br/>
</p>
<p style="margin-bottom: 0cm; line-height: 100%">Es ist <span style="background: #c0c0c0"><sdfield type=DATETIME sdval="44476,6908896088" sdnum="3079;3079;T. MMMM JJJJ">%SYSTIME%</sdfield></span></p>
</body>
</html>
Nun kompilieren wir das File.
alfred@monitoring:~/GetInfo$ go build GetInfo.go
go: downloading github.com/shirou/gopsutil v3.21.9+incompatible
go: extracting github.com/shirou/gopsutil v3.21.9+incompatible
go: downloading golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac
go: extracting golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac
go: finding github.com/shirou/gopsutil v3.21.9+incompatible
go: finding golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac
alfred@monitoring:~/GetInfo$ ll
total 7716
drwxrwxr-x 2 alfred alfred 4096 Oct 15 20:30 ./
drwxr-xr-x 22 alfred alfred 4096 Oct 11 07:30 ../
-rwxrwxr-x 1 alfred alfred 8009715 Oct 15 20:30 GetInfo*
-rw------- 1 alfred alfred 4473 Oct 10 21:34 GetInfo.go
-rw------- 1 alfred alfred 81 Oct 10 21:48 dockerfile
-rw------- 1 alfred alfred 1434 Oct 10 21:19 error.html
-rw-rw-r-- 1 alfred alfred 0 Oct 11 07:28 getinfo.tar
-rw------- 1 alfred alfred 263 Oct 10 10:04 go.mod
-rw------- 1 alfred alfred 1314 Oct 10 10:04 go.sum
-rw------- 1 alfred alfred 3180 Oct 10 21:32 hello.html
alfred@monitoring:~/GetInfo$ ./GetInfo
Ein kurzer Test auf http://monitoring:8080/view/hello ergibt
Abbildung 20: GetInfo Ausgabe
Das heist das Programm funktioniert. Die Logausgabe sieht aus wie folgt:
alfred@monitoring:~/GetInfo$ ./GetInfo
2021/10/10 21:42:01 Main Started
2021/10/10 21:42:16 hello.html
^C
alfred@monitoring:~/GetInfo$
Jetzt brauchen wir noch ein Docker-File
alfred@monitoring:~/GetInfo$ cat dockerfile
# syntax=docker/dockerfile:1
# Alpine is chosen for its small footprint
# compared to Ubuntu
FROM golang:1.16-alpine
WORKDIR /app
# Download necessary Go modules
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY *.go ./
COPY hello.html ./
COPY error.html ./
RUN go build -o /GetInfo
EXPOSE 8080
CMD [ "/GetInfo" ]
alfred@monitoring:~/GetInfo$
um den Container zu bauen.
alfred@monitoring:~/GetInfo$ docker build . -t docker.registry:5000/getinfo:20211015
Sending build context to Docker daemon 16.38kB
Step 1/11 : FROM golang:1.16-alpine
1.16-alpine: Pulling from library/golang
552d1f2373af: Pull complete
eb74239f2a0c: Pull complete
2a8ec004bf6b: Pull complete
12f1fa1a3b20: Pull complete
5c12166b7f9d: Pull complete
Digest: sha256:05df7ff684a2cb06aa207be14a78918cbc3285ed3b965974979e575d59de1c11
Status: Downloaded newer image for golang:1.16-alpine
---> d2f4346f5656
Step 2/11 : WORKDIR /app
---> Running in 9b6c3b098321
Removing intermediate container 9b6c3b098321
---> 738bd59f63db
Step 3/11 : COPY go.mod ./
---> cb6306d5fdf2
Step 4/11 : COPY go.sum ./
---> f7eeef9ebda2
Step 5/11 : RUN go mod download
---> Running in efbe89b22883
Removing intermediate container efbe89b22883
---> cb738e90d4d7
Step 6/11 : COPY *.go ./
---> c5ab2f4e9e5c
Step 7/11 : COPY hello.html ./
---> 8e6738e25799
Step 8/11 : COPY error.html ./
---> 5c9506b2dc3f
Step 9/11 : RUN go build -o /GetInfo.go
---> Running in 3ad675aeb626
Removing intermediate container 3ad675aeb626
---> 0abfe5a9d849
Step 10/11 : EXPOSE 8080
---> Running in 80f728420378
Removing intermediate container 80f728420378
---> b9c5a76c55d2
Step 11/11 : CMD [ "/GetInfo" ]
---> Running in 71d6f27934d2
Removing intermediate container 71d6f27934d2
---> 4fae231152c2
Successfully built 4fae231152c2
Successfully tagged docker.registry:5000/getinfo:20211015
alfred@monitoring:~/GetInfo$
alfred@monitoring:~/GetInfo$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.registry:5000/getinfo 20211015 4fae231152c2 39 seconds ago 317MB
golang 1.16-alpine d2f4346f5656 2 days ago 296MB
alfred@monitoring:~/GetInfo$
Nun starten wir den Container im Vordergrund um den Output zu testen.
alfred@monitoring:~/GetInfo$ docker run --publish 8080:8080 docker.registry:5000/getinfo:20211015
2021/10/15 19:22:13 Main Started
2021/10/15 19:22:40 hello.html
Der Output im Browser ist wie folgt:
Abbildung 21: Docker Test Output
Der Container ist am erwarteten Port erreichbar. Die Anzeige der Werte sind jetzt aus Sicht des Containers, wie man an den Variablen und am Environment sehen kann.
Wir überprüfen die Struktur des gebauten docker-files.
alfred@monitoring:~/GetInfo$ docker save docker.registry:5000/getinfo:20211015 > getinfo.tar
alfred@monitoring:~/GetInfo$ tar -tvf getinfo.tar
-rw-r--r-- 0/0 7059 2021-10-15 21:22 0fca605bf404203fe0e1d41e306687bf70b8cdb4392c41363af0f6712ae28959.json
drwxr-xr-x 0/0 0 2021-10-15 21:22 11ada22ca4865de4368da5b2cdea81c34f3fc62b60e62f00329f839e1e51a431/
-rw-r--r-- 0/0 3 2021-10-15 21:22 11ada22ca4865de4368da5b2cdea81c34f3fc62b60e62f00329f839e1e51a431/VERSION
-rw-r--r-- 0/0 406 2021-10-15 21:22 11ada22ca4865de4368da5b2cdea81c34f3fc62b60e62f00329f839e1e51a431/json
-rw-r--r-- 0/0 5605376 2021-10-15 21:22 11ada22ca4865de4368da5b2cdea81c34f3fc62b60e62f00329f839e1e51a431/layer.tar
drwxr-xr-x 0/0 0 2021-10-15 21:22 1a41da37655e1ae130db0ff268801f26989f408ad4e24fee81b18252883fe835/
-rw-r--r-- 0/0 3 2021-10-15 21:22 1a41da37655e1ae130db0ff268801f26989f408ad4e24fee81b18252883fe835/VERSION