Version 1.0 (#2)

* Adds @rhjensen79 k8s example

* Instructions in README instead of Makefile

* Adds optional TAILSCALE_HOSTNAME instead of hardcoding in tailscale.sh

* TAILSCALE_STATE_ARG env variable, to enable stateful-example which reuses the same ip between deployments

* Fix tailscale.sh for proper `tailscale logout` on container SIGTERM

* Adds github action to build image

* all docker images are in the images folder. Instead of repeating in each example

Co-authored-by: Robert Jensen <robert@robert-jensen.dk>
This commit is contained in:
Louis-Philippe Asselin 2022-09-01 14:51:23 -04:00 committed by GitHub
parent 39dc4c1692
commit 7c146ab113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 201 additions and 43 deletions

3
.envrc_template Normal file
View file

@ -0,0 +1,3 @@
# Rename to .envrc
export TAILSCALE_AUTH_KEY=""
# export TAILSCALE_HOSTNAME=""

71
.github/workflows/build.yaml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Build Containers
# Controls when the workflow will run
on:
workflow_dispatch:
push:
branches:
- "main"
- "dev"
- "v*.*.*"
paths:
- "k8s/**"
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
steps:
# Get the repositery's code
- name: Checkout
uses: actions/checkout@v2
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Login to the Container registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
flavor: |
latest=true
- name: Build and push
uses: docker/build-push-action@v2
with:
context: images/tailscale/.
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.envrc

View file

@ -1,18 +1,59 @@
# Tailscale in Docker without elevated privileges # Tailscale in Docker without elevated privileges
See associated blog post: https://asselin.engineer/tailscale-docker See associated blog post: <https://asselin.engineer/tailscale-docker>
**Replace TAILSCALE_AUTH_KEY in `*/tailscale/start.sh` with your own**: https://login.tailscale.com/admin/settings/keys **Set the TAILSCALE_AUTH_KEY with your own ephemeral auth key**: <https://login.tailscale.com/admin/settings/keys>
## simple-example The `Makefile` contains all commands to launch the various examples. Refer to it to understand which commands are used.
## docker-compose
By default, no state is saved. The nodes are removed from the network when the tailscale container is terminated. This means the ip address is never the same.
The `stateful-example` does save the tailscale node state to a docker volume.
Usage:
````bash
export TAILSCALE_AUTH_KEY="your-key"
# set which project is used
export PROJECT_DIRECTORY="docker-compose/simple-example"
# Sart with rebuild if necessary:
docker-compose --project-directory=${PROJECT_DIRECTORY} up -d --build
# Show logs and tail (follow):
docker-compose --project-directory=${PROJECT_DIRECTORY} logs --follow
# Stop:
docker-compose --project-directory=${PROJECT_DIRECTORY} down
````
### simple-example
As explained in the blog post, uses a docker-compose service to add the container in the VPN. As explained in the blog post, uses a docker-compose service to add the container in the VPN.
## complex-example ### complex-example
Not complex but more complex than the simple-example. Not complex but more complex than the simple-example.
A nginx layer is added. It manages two services in independent containers at locations `/service-one` and `/service-two`. A nginx layer is added. It manages two services in independent containers at urls `/service-one` and `/service-two`.
## TODO ### stateful-example
- force reuse hostname in tailscale instead of adding suffix. Example: first container is assigned `hostname`. Then, if container is recreated, Tailscale assigns `hostname-1`. Possibly helpful [info](https://tailscale.com/kb/1111/ephemeral-nodes/#can-i-create-an-ephemeral-node-without-an-auth-key). Same as simple-example but uses a volume to save state. The goal is to be able to reuse the same tailscale hostname _and ip address_.
Useful in situations where the tailscale magic DNS cannot be used.
## K8S
Same as the simple-example but on kubernetes.
Requirements:
- [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installing-with-a-package-manager)
- [Kubectl](https://kubernetes.io/docs/tasks/tools/)
Usage:
````bash
# Create cluster
kind create cluster --name tailscale
kubectl get nodes
# Deploy tailscale and demo webpage:
kubectl apply -f k8s/simple-example/deployment.yaml
# Delete cluster:
kind delete cluster --name tailscale
````

View file

@ -1,4 +0,0 @@
FROM tailscale/tailscale:v1.29
COPY start.sh /usr/bin/start.sh
RUN chmod +x /usr/bin/start.sh
CMD "start.sh"

View file

@ -1,9 +0,0 @@
#!/bin/ash
echo "Starting TS daemon"
tailscaled --tun=userspace-networking &
PID=$!
until tailscale up --authkey=${TAILSCALE_AUTH_KEY} --hostname=complex-example; do
sleep 0.1
done
tailscale status
wait ${PID}

View file

@ -2,12 +2,14 @@ version: "3.9"
services: services:
tailscale: tailscale:
build: build:
context: ./tailscale context: images/tailscale
environment: environment:
- TAILSCALE_AUTH_KEY TAILSCALE_AUTH_KEY: ${TAILSCALE_AUTH_KEY:?err}
TAILSCALE_HOSTNAME: ${TAILSCALE_HOSTNAME:-tailscale-docker-complex-example}
TAILSCALE_STATE_ARG: "mem:"
nginx: nginx:
build: build:
context: ./nginx context: images/nginx
depends_on: depends_on:
- service-one - service-one
- service-two - service-two

View file

@ -0,0 +1,12 @@
version: "3.9"
services:
tailscale:
build:
context: ../../images/tailscale
environment:
TAILSCALE_AUTH_KEY: ${TAILSCALE_AUTH_KEY:?err}
TAILSCALE_HOSTNAME: ${TAILSCALE_HOSTNAME:-tailscale-docker-simple-example}
TAILSCALE_STATE_ARG: "mem:"
some-service-1:
image: nginxdemos/hello
network_mode: "service:tailscale"

View file

@ -0,0 +1,15 @@
version: "3.9"
services:
tailscale:
build:
context: ../../images/tailscale
environment:
TAILSCALE_AUTH_KEY: ${TAILSCALE_AUTH_KEY:?err}
TAILSCALE_HOSTNAME: ${TAILSCALE_HOSTNAME:-tailscale-docker-stateful-example}
TAILSCALE_STATE_ARG: "/var/lib/tailscale_state/tailscale.state" # a file
volumes:
# a volume is used but it could be a local directory.
- /var/lib/tailscale_state/
some-service-1:
image: nginxdemos/hello
network_mode: "service:tailscale"

View file

@ -1,4 +1,4 @@
FROM tailscale/tailscale:v1.29 FROM tailscale/tailscale:v1.30
COPY start.sh /usr/bin/start.sh COPY start.sh /usr/bin/start.sh
RUN chmod +x /usr/bin/start.sh RUN chmod +x /usr/bin/start.sh
CMD "start.sh" CMD "start.sh"

12
images/tailscale/start.sh Normal file
View file

@ -0,0 +1,12 @@
#!/bin/ash
trap 'kill -TERM $PID' TERM INT
echo "Starting Tailscale daemon"
# -state=mem: will logout and remove ephemeral node from network immediately after ending.
tailscaled --tun=userspace-networking --state=${TAILSCALE_STATE_ARG} &
PID=$!
until tailscale up --authkey="${TAILSCALE_AUTH_KEY}" --hostname="${TAILSCALE_HOSTNAME}"; do
sleep 0.1
done
tailscale status
wait ${PID}
wait ${PID}

View file

@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: tailscale
spec:
selector:
matchLabels:
app: tailscale
template:
metadata:
labels:
app: tailscale
spec:
containers:
- name: tailscale
image: ghcr.io/lpasselin/tailscale-docker:latest
env:
- name: TAILSCALE_AUTH_KEY
value: "${TAILSCALE_AUTH_KEY:-err}"
- name: TAILSCALE_HOSTNAME
value: "tailscale-docker-k8s-simple"
- name: TAILSCALE_STATE_ARG
value: "mem:"
resources:
limits:
memory: "128Mi"
cpu: "500m"
- name: nginx
image: nginxdemos/hello
resources:
limits:
memory: "128Mi"
cpu: "500m"

View file

@ -1,10 +0,0 @@
version: "3.9"
services:
tailscale:
build:
context: ./tailscale
environment:
- TAILSCALE_AUTH_KEY
some-service-1:
image: nginxdemos/hello
network_mode: "service:tailscale"

View file

@ -1,9 +0,0 @@
#!/bin/ash
echo "Starting TS daemon"
tailscaled --tun=userspace-networking &
PID=$!
until tailscale up --authkey=${TAILSCALE_AUTH_KEY} --hostname=complex-example; do
sleep 0.1
done
tailscale status
wait ${PID}