Docker Image Static Site Builder
Docker Image Static Site Builder
- Create the website building directory,
srcandhtmldirectories:
mkdir -p ~/metalinux_docs/src/html
- Make the
index.htmlfile:
cat <<EOF | tee ~/metalinux_docs/src/html/index.html
<!DOCTYPE html>
<html>
<body>
<h1>Hello!</h1>
<img src="metalinux-1.png" />
<h1>Test</h1>
<img src="metalinux-1.png" />
<p>Test</p>
</body>
</html>
EOF
-
Add pictures of your choice into
~/metalinux_docs/src/html/ -
Create the
Dockerfile:
cat <<EOF | tee ~/metalinux_docs/Dockerfile
FROM nginx:1.29.1-alpine
COPY src/html /usr/share/nginx/html
EXPOSE 80/tcp
# Documentation
# CMD ["nginx", "-g", "daemon off;"]
EOF
- Change directory into the
~/metalinux_docs/src/html/directory:
cd ~/metalinux_docs/
- Build the Docker Image:
docker build -t metalinux-docs .
- Check the image was build:
docker images
- Check that the image can be ran as a container:
docker run -d -p 80:80 <image_id>
-
Go to
localhostin the browser. -
Create the
.dockerignorefile:
cat <<EOF | tee ~/metalinux_docs/.dockerignore
**/file_name
EOF
- Login to Docker:
docker login
- Push the image to DockerHub:
docker tag metalinux-docs:latest metalllinux/metalinux-docs:latest
docker push metalllinux/metalinux-docs:latest
-
Install
dockeron the Master Node. -
On the Master Node, log into
docker:
docker login
- Copy the credential into Kubernetes:
kubectl create secret generic regcred \
--from-file=.dockerconfigjson=~/.docker/config.json \
--type=kubernetes.io/dockerconfigjson
- Create a manifests directory if there is not one already:
mkdir -p ~/manifests
- Declaratively it can be done like so:
cat <<EOF | tee ~/manifests/metalinux_docs-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: dockerhub-secret
namespace: metalinux-docs
data:
.dockerconfigjson: ewoJImF1dGhzIjogewoJCSJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOiB7CgkJCSJhdXRoIjogImJXVjBZV3hzYkdsdWRYZzZaR05yY2w5d1lYUmZTRVpCVnpjelYyWkhaRXRzZG1WV0xXNXRUM2gxTjBwdVVYQloiCgkJfSwKCQkiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vdjEvYWNjZXNzLXRva2VuIjogewoJCQkiYXV0aCI6ICJiV1YwWVd4c2JHbHVkWGc2WlhsS2FHSkhZMmxQYVVwVFZYcEpNVTVwU1hOSmJsSTFZME5KTmtscmNGaFdRMGx6U1cxMGNGcERTVFpKYm1oWllUTkNRMlJFVG5sV00wMTVVbmt4TVZscWJITmpSWEJ1WTFOS09TNWxlVXB2WkVoU2QyTjZiM1pNTW1neFdXazFhMkl5VG5KYVdFbDFXVEk1ZEVscWNEZEpiVlowV1Zkc2MwbHFiMmxoUnpreldWaEthMHh0UlhWa2JVWjFXa2RXZVdReVJuTlJTRUo1WWpOU2RtSnRNV2hoVjNkMVdUSTVkRWxwZDJsak1sWjZZekpzZG1Kc09YQmFRMGsyU1dwT2FVMXRXWGxQVkUxNVRGUmthVTF0U1hST1JHUnNUV2t3TlU1SFZtcE1WRUV4VDBSc2FWcEVSbTFQVkd4b1dXbEpjMGx1VG5aa1dFcHFXbE5KTmtsdFJqRmtSMmQzU1dsM2FXUllUbXhqYlRWb1lsZFZhVTlwU25SYVdGSm9Za2Q0YzJGWE5URmxRMGx6U1c1V01XRlhVV2xQYVVwcFQxUkdhRTlFWXpOTlV6QXdXbGRWTUV4VVVYcE9SRkYwV1cxU2JVMXBNV3hhYWsxNlRWZFpkMWxxV1hkWmJWbHBabE4zYVdGWVRucEphbTlwWVVoU01HTklUVFpNZVRsellqSmtjR0pwTld0aU1rNXlXbGhKZFZreU9YUk1lVWx6U1c1T01WbHBTVFpKYlVZeFpFZG5kMlpIU1RWTlYwVTBUbnBqZUV4VVVteGFWRkYwVGtSTk1FNURNV2xhUjFsNVRGZFdiVTE2VFhoYWFrSnBUbXBDYVZwcFNYTkpiVVl4V2tOSk5sZDVTbTlrU0ZKM1kzcHZka3d5YURGWmFUVnJZakpPY2xwWVNYVlpNamwwU1dsM2FXRklVakJqU0UwMlRIazVhMkl5VG5KYVdFbDBZMGhLZGxwRE5URmplVFZvWkZoU2IwMUROV3BpTWpCMlpGaE9iR050YkhWYWJUaHBXRk4zYVdGWFJqQkphbTk0VG5wVk1VMXFXWGxOZWxVd1RFTktiR1ZJUVdsUGFrVXpUbFJWZVU1cVZUVk9WRkZ6U1c1T2FtSXpRbXhKYW05cFlqTkNiR0p0Ykd0SlJ6bHRXbTE0Y0dKdFZtWlpWMDVxV2xoT2VrbHBkMmxaV0hCM1NXcHZhVlJFVWpKTlIxSjBZa1UxUTJOR2JGWmhhMlJJV1ZkSmQxRjZTa3RrUjJSVldqRm9lVTFXUmpaT1IxRnBabEV1VW10UGRURmpSMFJEYWxKQlRubFhhMGhaT0dwRFlWQk1VV3hNY2tkSmJtTnhkVVpFVDI4MmIzTTRSRlpmZWxKaWMyWjFVbmxwYWxseFkyaDJURmxQVFZWTFJrZEpUekU0UkVjelZEaGZaRXd5ZW5oS2VHUlJaazFZWmpSTlpsQjNXa0ZDV2tnMk9HRkNRbm8wYm0xUmIyZDNOVGxuUkRCQlIyZ3pNbWhDWjFaVVRHNDVPSEY1Ym5CblVrMVpRVlJmYXpRdFdHZDVkakpuVGxsS1FqRjRNRGgxUTNGZlRtOXlla1pUVVdkV05ERk9jMFYwTTFGYVNtZFhlbTlET1ZsaWMzZHJZbWRRVlROWVUyRlJNMEp2WWw5d2JqQnhaa1J5VDFCQ2NsWmxhRlY0TlVacE5DMXNkblJYWkZOT1YycE5UVU4wVXpGNlMwWnJVbWhIUjJ0Mk9FMXVRelpzTlV4dGJIQTRVRmxRUXpGSmNHWkJSbXRSYlZwR2RVeDFORko0ZUcxVGJrMVRSR3h5UVZKRU1rcHJiVW96YmpWNFMyMURlSFJ2T1dkT1JHMDFPR3hUVjFkdGRXdDBUVGxaY0RSbU9WRTNSbmRCIgoJCX0sCgkJImh0dHBzOi8vaW5kZXguZG9ja2VyLmlvL3YxL3JlZnJlc2gtdG9rZW4iOiB7CgkJCSJhdXRoIjogImJXVjBZV3hzYkdsdWRYZzZkakV1VFZGbVQwVnhkVkV4YjBaNFkweDVkbmw1Y21kUVFrbGtVRk5UVUc1U05VMVJkMmhhUWxOcVpEUnZTbVpXWm1kZmVXUmpibUpmWWxCak9HRm1UbUZyYUdvd1VtaHBjemhLTFV0WVpXNHpTbGh5YTFaSlVtcFZMaTVNTkhZd1pHMXNUa0p3V1ZWcVIwZGhZakJETWtwMFoxUm5XSEl4VVhvMFpBPT0iCgkJfQoJfQp9
type: kubernetes.io/dockerconfigjson
EOF
- The
.docker/config.jsonfile has to be base64 encoded:
cat ~/.docker/config.json | base64 | tr -d '\n'
- Apply the manifest:
kubectl apply -f ~/manifests/metalinux_docs-secret.yaml
- Output the secret with this command:
kubectl get secret dockerhub-secret -n metalinux-docs --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode
- Create the pod that uses my secret:
cat <<EOF | tee ~/manifests/metalinux_docs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: metalinux-docs
namespace: metalinux-docs
spec:
replicas: 2
selector:
matchLabels:
app: metalinux-docs
spec:
containers:
- name: metalinux-docs-container
image: metalllinux/metalinux-docs:latest
ports:
- containerPort: 80
imagePullSecrets:
- name: dockerhub-secret
EOF
- Create the service for the
metalinux-docspod:
cat <<EOF | tee ~/manifests/metalinux_docs-service.yaml
apiVersion: v1
kind: Service
metadata:
name: metalinux-docs-service
namespace: metalinux-docs
spec:
type: LoadBalancer
selector:
app: metalinux-docs
ports:
- port: 80
targetPort: 80
EOF
- Apply the service manifest:
kubectl apply -f ~/manifests/metalinux_docs-service.yaml
-
Follow Cloudflare’s instructions until you generate the tunnel token: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deployment-guides/kubernetes/
-
Convert the token into base64:
cat token | base64 -w 0
- Create the tunnel token manifest for Cloudflare:
cat <<EOF | tee ~/manifests/metalinux_docs-cloudflare-tunnel-secret.yaml
apiVersion: v1
data:
token: ZXlKaElqb2lNVEF6WVRJMU9UZGpNVGhsTnpWaU1URTNPVEpqWXpFNU1qUTNPV1U0TnpRaUxDSjBJam9pTkRFME1XRXpOV1V0TVRRME5pMDBNVEl3TFdFeE5XRXROVE01T1dVd016RXpZMlZtSWl3aWN5STZJazVVWnpWWmFrcHFUWHBaZEU1VVNYcFplVEF3V1hwSmVVeFVaM2RPYW1OMFdWUnJlazVFUVRWT1ZGVjZUV3BKTVNKOQo=
kind: Secret
metadata:
name: cloudflare-tunnel-secret
namespace: metalinux-docs
type: Opaque
EOF
- Generate the secret:
kubectl apply -f ~/manifests/metalinux_docs-cloudflare-tunnel-secret.yaml
- Make the Cloudflare Tunnel Pod:
cat <<EOF | tee ~/manifests/metalinux_docs-cloudflare-tunnel-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflared-deployment
namespace: metalinux-docs
spec:
replicas: 2
selector:
matchLabels:
pod: cloudflared
template:
metadata:
labels:
pod: cloudflared
spec:
securityContext:
sysctls:
# Allows ICMP traffic (ping, traceroute) to resources behind cloudflared.
- name: net.ipv4.ping_group_range
value: "65532 65532"
containers:
- image: cloudflare/cloudflared:latest
name: cloudflared
env:
# Defines an environment variable for the tunnel token.
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-tunnel-secret
key: token
command:
# Configures tunnel run parameters
- cloudflared
- tunnel
- --no-autoupdate
- --loglevel
- debug
- --metrics
- 0.0.0.0:2000
- run
livenessProbe:
httpGet:
# Cloudflared has a /ready endpoint which returns 200 if and only if
# it has an active connection to Cloudflare's network.
path: /ready
port: 2000
failureThreshold: 1
initialDelaySeconds: 10
periodSeconds: 10
EOF
- Create the Cloudflare Tunnel Pod:
kubectl apply -f ~/manifests/metalinux_docs-cloudflare-tunnel-pod.yaml
-
Add a tunnel route. Go to the
Zero Trust Home–>Networks–>Tunnels. Press the hamburger menu and selectConfigure. Then navigate toPublic hostnamesand then clickAdd a public hostname. -
For
Domain, I puthowards-linux-journey-notes.wiki -
For
Service, I putmetalinux-docs-serviceand set theTypetoHTTPS -
Go to Cloudflare’s main page and add your nameserver.