Explorar o código

feat(docs): Implement docker-gen usage #199 (#230)

* feat(docs): Implement docker-gen usage #199

* fix: Add some links and fix some typos

---------

Co-authored-by: Tim Jones <t.jones@timmoth.com>
Matt Foxx hai 1 mes
pai
achega
a2137a040c

+ 124 - 0
Shared.Rcl/wwwroot/raw_docs/docker-gen-guide.md

@@ -0,0 +1,124 @@
+# `docker-gen` User Guide
+
+Use [docker-gen](https://github.com/nginx-proxy/docker-gen) on a target machine to generate RackPeek [**Services**](/docs/resource-levels#services) YAML from existing docker containers.
+
+The generated YAML can be directly used in RackPeek or with the **Import YAML** tool to merge/replace existing services.
+
+# 1. Setup `docker-gen` project
+
+Clone the RackPeek repoistory or copy the [`docker-gen`](https://github.com/Timmoth/RackPeek/tree/main/docs/docker-gen) folder from the repository to a machine.
+
+Create a `.env` file next to the provided `compose.yaml` compose stack. All of your docker-gen config will live in this file.
+
+---
+
+# 2. Configure Docker Daemon Connectivity
+
+docker-gen needs to be able to connect to the Docker Daemon on the target machine in order to generate Service YAML.
+
+## Same Machine
+
+If you created the docker-gen project on the *same machine* Docker is running on then uncomment the volume `- "/var/run/docker.sock:/tmp/docker.sock"` in the `compose.yaml` file.
+
+## Remote Machine
+
+ If you want to generate Services from a *remote machine* Docker is running on your will need a docker **socket-proxy** container running on the target machine, such as [tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy) or [wollomatic/socket-proxy](https://github.com/wollomatic/socket-proxy).
+
+ The socket-proxy container needs **read-only** access to the **containers** api.
+
+ <details>
+  
+ <summary>Example</summary>
+
+ On the remote machine run this compose project:
+
+ ```yaml
+ services:
+  docker-socket-proxy:
+    image: tecnativa/docker-socket-proxy
+    ports: "2375:2375"
+    volumes:
+      - "/var/run/docker.sock:/tmp/docker.sock"
+    environment:
+      - POST=0
+      - CONTAINERS=1
+ ```
+
+ </details>
+
+In the docker-gen `compose.yaml` file uncomment environmental variable `- "DOCKER_HOST=${DOCKER_HOST}"` and in your `.env` set it to `tcp://HOST:IP` of the target machine IE `tcp://192.168.0.100:2375`
+
+---
+
+# 3. Configure Container Filtering
+
+This step is **optional.**
+
+Use [container filters](https://docs.docker.com/engine/reference/commandline/ps/#filter) to restrict which containers docker-gen generates Services from.
+
+Filter should be added to `DOCKER_CONTAINER_FILTERS` in your `.env`. Multiple filters can be used by specifying them as a comma-separated list.
+
+<details>
+
+<summary>Examples</summary>
+
+Only run docker-gen for containers that show up in [homepage](https://gethomepage.dev/):
+
+* filter on label `homepage.name`
+
+```
+DOCKER_CONTAINER_FILTERS=label=homepage.name
+```
+
+Only run docker-gen for containers that are discovered by Traefik and are currently running:
+
+* filter on label `traefik.http.routers`
+* filter on status `running`
+
+```
+DOCKER_CONTAINER_FILTERS=label=traefik.http.routers,status=running
+```
+
+</details>
+
+---
+
+# 4. Add RackPeek Environmental Variables
+
+This step is **optional.**
+
+More RackPeek Service config can be enabled by specifying optional environmental variables in `.env`:
+
+* `RUNS_ON` enables and specifies the name of the [**System**](/docs/resource-levels#systems) the generated Services run on (`runsOn` in YAML)
+* `HOST_IP` enables IP and port in the `network` section of a Service. The IP will always be `HOST_IP` (useful for ports published on the bridge network)
+  * IP and port will only be generated if the container binds to `0.0.0.0` (no IP specified) or specifically binds the ip from `HOST_IP`
+    * docker-gen will first look for ports `80` and `443`. If these ports aren't published then it uses the first published port meeting above conditions
+
+---
+
+# 5. Generate Services
+
+With no further configuration `docker-gen` will output generated Services to stdout. To get clean output from docker compose run:
+
+```bash
+docker compose up --no-log-prefix
+```
+
+Services can also be written to a file by specifying the last line in `command` in your `compose.yaml`. Uncommment `"/etc/docker-gen/templates/services.yaml"` to write to `service.yaml` in the same folder as the `docker-gen` project you created.
+
+---
+
+# Additional Generation Behavior
+
+The `name` for a Service will be parsed from a container in this order:
+
+* label `rackpeek.name`
+* label `homepage.name`
+* label `org.opencontainers.image.title`
+* container name
+
+The `url` for a Service will be parsed from a container in this order:
+
+* label `rackpeek.href`
+* label `homepage.href`
+* label `caddy`

+ 1 - 0
Shared.Rcl/wwwroot/raw_docs/docs-index.json

@@ -3,6 +3,7 @@
   "resource-levels.md",
   "install-guide.md",
   "ansible-generator-guide.md",
+  "docker-gen-guide.md",
   "cli-commands.md",
   "cli-commands-index.md",
   "versioning.md"

+ 23 - 0
docs/docker-gen/compose.yaml

@@ -0,0 +1,23 @@
+services:
+  docker-gen:
+    image: nginxproxy/docker-gen
+    container_name: rackpeek_gen
+    volumes:
+      - "./:/etc/docker-gen/templates"
+      # uncomment to generate from docker daemon on the same machine
+      #- "/var/run/docker.sock:/tmp/docker.sock"
+    environment:
+      # uncomment to generate from remote host (comment to disable)
+      - "DOCKER_HOST=${DOCKER_HOST}"
+
+      # Filter containers to use. Multiple filters are comma separated
+      # https://docs.docker.com/engine/reference/commandline/ps/#filter
+      - "DOCKER_CONTAINER_FILTERS=${DOCKER_CONTAINER_FILTERS}"
+      - "RUNS_ON=${RUNS_ON}"
+      - "HOST_IP=${HOST_IP}"
+    command: [
+      "docker-gen",
+     "/etc/docker-gen/templates/rackpeek.tmpl",
+     # uncomment below line to write to file
+     #"/etc/docker-gen/templates/services.yaml"
+    ] 

+ 77 - 0
docs/docker-gen/rackpeek.tmpl

@@ -0,0 +1,77 @@
+{{/* default rackpeek configuration template */}}
+{{/* Generate Services based on filtered containers */}}
+{{/* Optional RUNS_ON environment variable used for runsOn*/}}
+{{/* If ports are exposed, the first one is used */}}
+{{/* If ports are exposed, the first one is used */}}
+
+{{/* Use index function to access map keys with dots in them https://pkg.go.dev/text/template#hdr-Functions  */}}
+
+{{ range $index, $value := . }}
+
+{{ $addrLen := len $value.Addresses }}
+
+- kind: Service
+{{/* Name priority rackpeek.name -> homepage.name -> org.opencontainers.image.title -> container_name */}}
+{{ if contains $value.Labels "rackpeek.name"  }}
+  name: {{ index $value.Labels "rackpeek.name" }}
+{{ else if contains $value.Labels "homepage.name" }}
+  name: {{ index $value.Labels "homepage.name" }}
+{{ else if contains $value.Labels "org.opencontainers.image.title" }}
+  name: {{ index $value.Labels "org.opencontainers.image.title" }}
+{{ else }}
+  name: {{ $value.Name }}
+{{ end }}
+
+{{/* only include network section if there is a url in labels or published ports with HOST_IP env set */}}
+{{ if or (contains $value.Labels "homepage.href") (contains $value.Labels "rackpeek.href") (contains $value.Labels "caddy") (contains $value.Labels "swag_url") (and ($.Env.HOST_IP) (gt $addrLen 0)) }}
+  network:
+  {{/* URL priority caddy -> homepage.name -> rackpeek.href */}}
+  {{/* cant get url from traefik due to route rule complexity */}}
+  {{ if contains $value.Labels "rackpeek.href" }}
+    url: {{ index $value.Labels "rackpeek.href" }}
+  {{ else if contains $value.Labels "homepage.href" }}
+    url: {{ index $value.Labels "homepage.href" }}
+  {{ else if contains $value.Labels "caddy" }}
+    url: {{ index $value.Labels "caddy" }}
+  {{end }}
+
+{{/* only generate ip:port if HOST_IP env is set */}}
+{{/* -- if rackpeek supports multiple IPs per service in the future this can be modified */}}
+{{ if $.Env.HOST_IP }}
+    {{ $hostIps := split $.Env.HOST_IP "," }}
+    {{ $httpsIsh := where $value.Addresses "Port" "443"}}
+    {{ $httpIsh := where $value.Addresses "Port" "80"}}
+    {{/* try to get ports by known web ports first */}}
+    {{ if or (gt (len $httpsIsh) 0) (gt (len $httpIsh) 0)}}
+        {{ range $i, $address := $value.Addresses }}
+            {{/* only use this if container is binding on all addresses or a known host, and using known web port */}}
+            {{ if and (or (eq $address.HostIP "0.0.0.0") (contains $hostIps $address.HostIP)) (or (eq $address.Port "443") (eq $address.Port "80")) }}
+    ip: {{ index $hostIps 0 }}
+    port: {{ $address.Port }}
+    protocol: TCP 
+    {{ break }}
+            {{ end }}
+        {{ end }}
+
+    {{/* otherwise, use first port */}}
+    {{ else }}
+        {{ range $i, $address := $value.Addresses }}
+            {{/* only use this if container is binding on all addresses or a known host port */}}
+            {{ if or (eq $address.HostIP "0.0.0.0") (contains $hostIps $address.HostIP) }}
+    ip: {{ index $hostIps 0 }}
+    port: {{ $address.Port }}
+    protocol: TCP 
+    {{ break }}
+            {{ end }}
+        {{ end }}
+    {{ end }}
+{{ end }}
+{{/* end network loop */}}
+{{ end }}
+
+{{ if $.Env.RUNS_ON  }}
+  runsOn:
+    - {{ $.Env.RUNS_ON }}
+{{ end }}
+{{/* end containers loop */}}
+{{ end }}