mirror of
https://github.com/funkypenguin/geek-cookbook/
synced 2025-12-13 01:36:23 +00:00
Fix chicken/egg problem in k8s cert-manager guide
Signed-off-by: David Young <davidy@funkypenguin.co.nz>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -46,3 +46,4 @@ ehthumbs.db
|
||||
Thumbs.db
|
||||
.venv
|
||||
venv
|
||||
docs_to_pdf
|
||||
|
||||
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 206 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 126 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 570 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 376 KiB |
@@ -19,7 +19,7 @@ In order for Cert Manager to request/renew certificates, we have to tell it abou
|
||||
### LetsEncrypt Staging
|
||||
|
||||
The ClusterIssuer resource below represents a certificate authority which is able to request certificates for any namespace within the cluster.
|
||||
I save this in my flux repo as `cert-manager/cluster-issuer-letsencrypt-staging.yaml`. I've highlighted the areas you'll need to pay attention to:
|
||||
I save this in my flux repo as `letsencrypt-wildcard-cert/cluster-issuer-letsencrypt-staging.yaml`. I've highlighted the areas you'll need to pay attention to:
|
||||
|
||||
???+ example "ClusterIssuer for LetsEncrypt Staging"
|
||||
```yaml hl_lines="8 15 17-21"
|
||||
@@ -52,7 +52,7 @@ Deploying this issuer YAML into the cluster would provide Cert Manager with the
|
||||
|
||||
### LetsEncrypt Prod
|
||||
|
||||
As you'd imagine, the "prod" version of the LetsEncrypt issues is very similar, and I save this in my flux repo as `cert-manager/cluster-issuer-letsencrypt-prod.yaml`
|
||||
As you'd imagine, the "prod" version of the LetsEncrypt issues is very similar, and I save this in my flux repo as `letsencrypt-wildcard-cert/cluster-issuer-letsencrypt-prod.yaml`
|
||||
|
||||
???+ example "ClusterIssuer for LetsEncrypt Prod"
|
||||
```yaml hl_lines="8 15 17-21"
|
||||
@@ -85,7 +85,7 @@ As you'd imagine, the "prod" version of the LetsEncrypt issues is very similar,
|
||||
|
||||
### How do we know it works?
|
||||
|
||||
We're not quite ready to issue certificates yet, but we can now test whether the Issuers are configured correctly for LetsEncrypt. To check their status, **describe** the ClusterIssuers (i.e., `kubectl describe clusterissuer -n cert-manager letsencrypt-prod`), which (*truncated*) shows something like this:
|
||||
We're not quite ready to issue certificates yet, but we can now test whether the Issuers are configured correctly for LetsEncrypt. To check their status, **describe** the ClusterIssuers (i.e., `kubectl describe clusterissuer letsencrypt-prod`), which (*truncated*) shows something like this:
|
||||
|
||||
```yaml
|
||||
Status:
|
||||
|
||||
@@ -16,7 +16,9 @@ This behaviour can be prohibitive, because (a) we don't want to have to request/
|
||||
To take advantage of the various workarounds available, I find it best to put the certificates into a dedicated namespace, which I name.. `letsencrypt-wildcard-cert`.
|
||||
|
||||
!!! question "Why not the cert-manager namespace?"
|
||||
Because cert-manager is a _controller_, whose job it is to act on resources. I should be able to remove cert-manager entirely (even its namespace) from my cluster, and re-add it, without impacting the resources it acts upon. If the certificates lived in the `cert-manager` namespace, then I wouldn't be able to remove the namespace without also destroying the certificates.
|
||||
Because cert-manager is a _controller_, whose job it is to act on resources. I should be able to remove cert-manager entirely (even its namespace) from my cluster, and re-add it, without impacting the resources it acts upon. If the certificates lived in the `cert-manager` namespace, then I wouldn't be able to remove the namespace without also destroying the certificates.
|
||||
|
||||
Furthermore, we can't deploy ClusterIssuers (a CRD) in the same kustomization which deploys the helmrelease which creates those CRDs in the first place. Flux won't be able to apply the ClusterIssuers until the CRD is created, and so will fail to reconcile.
|
||||
|
||||
## Preparation
|
||||
|
||||
|
||||
257
manuscript/recipes/immich.md
Normal file
257
manuscript/recipes/immich.md
Normal file
@@ -0,0 +1,257 @@
|
||||
---
|
||||
title: Run Immich in Docker Swarm
|
||||
description: How to install your own immich instance using Docker Swarm
|
||||
---
|
||||
|
||||
# Immich in Docker Swarm
|
||||
|
||||
Immich is a promising self-hosted alternative to Google Photos. Its UI and features are clearly heavily inspired by Google Photos, and like [Photoprism][photoprism], Immich uses tensorflow-based machine learning to auto-tag your photos!
|
||||
|
||||
!!! warning "Pre-production warning"
|
||||
The developer makes it abundantly clear that Immich is under heavy development (*although it's covered by "wife-insurance"[^1]*), features and APIs may change, and all your photos may be lost, or (worse) auto-shared with your :dragon_face: mother-in-law! Take due care :wink:
|
||||
|
||||
{ loading=lazy }
|
||||
|
||||
See my detailed review of Immich, as a Google Photos replacement, [here][review/immich]
|
||||
|
||||
## Immich requirements
|
||||
|
||||
!!! summary "Ingredients"
|
||||
Already deployed:
|
||||
|
||||
* [X] [Docker swarm cluster](/docker-swarm/design/) with [persistent shared storage](/docker-swarm/shared-storage-ceph/)
|
||||
* [X] [Traefik](/docker-swarm/traefik/) configured per design
|
||||
|
||||
New:
|
||||
|
||||
* [ ] DNS entry for your Immich instance, pointed to your [keepalived](/docker-swarm/keepalived/) IP
|
||||
|
||||
### Setup data locations
|
||||
|
||||
First, we create a directory to hold the immich docker-compose configuration:
|
||||
|
||||
```bash
|
||||
mkdir /var/data/config/immich
|
||||
```
|
||||
|
||||
Then we setup directories to hold all the various data:
|
||||
|
||||
```bash
|
||||
mkdir -p /var/data/immich/database-dump
|
||||
mkdir -p /var/data/immich/upload
|
||||
mkdir -p /var/data/runtime/immich/database
|
||||
```
|
||||
|
||||
### Setup Immich enviroment
|
||||
|
||||
Create `/var/data/config/immich/immich.env` something like the example below..
|
||||
|
||||
```yaml title="/var/data/config/immich/immich.env"
|
||||
###################################################################################
|
||||
# Database
|
||||
###################################################################################
|
||||
|
||||
# These are for the Immich components
|
||||
DB_HOSTNAME=db
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_DATABASE_NAME=immich
|
||||
|
||||
# These are specific to how the postgres image likes to receive its ENV vars
|
||||
POSTGRES_PASSWORD=postgres
|
||||
#POSTGRES_USER=postgres
|
||||
POSTGRES_DB=immich
|
||||
|
||||
###################################################################################
|
||||
# Redis
|
||||
###################################################################################
|
||||
|
||||
REDIS_HOSTNAME=redis
|
||||
|
||||
# Optional Redis settings:
|
||||
# REDIS_PORT=6379
|
||||
# REDIS_DBINDEX=0
|
||||
# REDIS_PASSWORD=
|
||||
# REDIS_SOCKET=
|
||||
|
||||
|
||||
###################################################################################
|
||||
# JWT SECRET
|
||||
###################################################################################
|
||||
|
||||
JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess # (1)!
|
||||
|
||||
###################################################################################
|
||||
# MAPBOX
|
||||
####################################################################################
|
||||
|
||||
# ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
|
||||
ENABLE_MAPBOX=false
|
||||
MAPBOX_KEY=
|
||||
|
||||
###################################################################################
|
||||
# WEB - Required
|
||||
###################################################################################
|
||||
|
||||
# This is the URL of your vm/server where you host Immich, so that the web frontend
|
||||
# know where can it make the request to.
|
||||
# For example: If your server IP address is 10.1.11.50, the environment variable will
|
||||
# be VITE_SERVER_ENDPOINT=http://10.1.11.50:2283/api
|
||||
# !CAUTION! THERE IS NO FORWARD SLASH AT THE END
|
||||
|
||||
VITE_SERVER_ENDPOINT=https://immich.example.com/api
|
||||
|
||||
|
||||
####################################################################################
|
||||
# WEB - Optional
|
||||
####################################################################################
|
||||
|
||||
# Custom message on the login page, should be written in HTML form.
|
||||
# For example VITE_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"
|
||||
|
||||
VITE_LOGIN_PAGE_MESSAGE=
|
||||
|
||||
NODE_ENV=production
|
||||
```
|
||||
|
||||
1. Yes, this has to be long. At least 20 characters.
|
||||
|
||||
### Immich Docker Swarm config
|
||||
|
||||
Create a docker swarm config file in docker-compose syntax (v3), something like this example:
|
||||
|
||||
--8<-- "premix-cta.md"
|
||||
|
||||
```yaml title="/var/data/config/immich/immich.yml"
|
||||
version: "3.2"
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
volumes:
|
||||
- /var/data/immich/upload:/usr/src/app/upload
|
||||
env_file: /var/data/config/immich/immich.env
|
||||
networks:
|
||||
- internal
|
||||
|
||||
immich-microservices:
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
volumes:
|
||||
- /var/data/immich/upload:/usr/src/app/upload
|
||||
env_file: /var/data/config/immich/immich.env
|
||||
networks:
|
||||
- internal
|
||||
|
||||
immich-machine-learning:
|
||||
image: altran1502/immich-machine-learning:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
volumes:
|
||||
- /var/data/immich/upload:/usr/src/app/upload
|
||||
env_file: /var/data/config/immich/immich.env
|
||||
networks:
|
||||
- internal
|
||||
|
||||
immich-web:
|
||||
image: altran1502/immich-web:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file: /var/data/config/immich/immich.env
|
||||
networks:
|
||||
- internal
|
||||
|
||||
redis:
|
||||
image: redis:6.2
|
||||
networks:
|
||||
- internal
|
||||
|
||||
db:
|
||||
image: postgres:14
|
||||
env_file: /var/data/config/immich/immich.env
|
||||
volumes:
|
||||
- /var/data/runtime/immich/database:/var/lib/postgresql/data
|
||||
networks:
|
||||
- internal
|
||||
|
||||
db-backup:
|
||||
image: postgres:14
|
||||
env_file: /var/data/config/immich/immich-db-backup.env
|
||||
volumes:
|
||||
- /var/data/immich/database-dump:/dump
|
||||
entrypoint: |
|
||||
bash -c 'bash -s <<EOF
|
||||
trap "break;exit" SIGHUP SIGINT SIGTERM
|
||||
sleep 2m
|
||||
while /bin/true; do
|
||||
pg_dump -Fc > /dump/dump_\`date +%d-%m-%Y"_"%H_%M_%S\`.psql
|
||||
ls -tr /dump/dump_*.psql | head -n -"$$BACKUP_NUM_KEEP" | xargs -r rm
|
||||
sleep $$BACKUP_FREQUENCY
|
||||
done
|
||||
EOF'
|
||||
networks:
|
||||
- internal
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: altran1502/immich-proxy:release
|
||||
ports:
|
||||
- 2283:80
|
||||
deploy:
|
||||
replicas: 1
|
||||
labels:
|
||||
# traefik
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=traefik_public
|
||||
|
||||
# traefikv1
|
||||
- traefik.frontend.rule=Host:immich.example.com
|
||||
- traefik.port=80
|
||||
|
||||
# traefikv2
|
||||
- "traefik.http.routers.immich.rule=Host(`immich.example.com`)"
|
||||
- "traefik.http.routers.immich.entrypoints=https"
|
||||
- "traefik.http.services.immich.loadbalancer.server.port=80"
|
||||
networks:
|
||||
- internal
|
||||
- traefik_public
|
||||
|
||||
networks:
|
||||
traefik_public:
|
||||
external: true
|
||||
internal:
|
||||
driver: overlay
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.16.8.0/24
|
||||
```
|
||||
|
||||
--8<-- "reference-networks.md"
|
||||
|
||||
## Launch Immich!
|
||||
|
||||
Launch the Immich stack by running
|
||||
|
||||
```bash
|
||||
docker stack deploy immich -c /var/data/config/immich/immich.yml
|
||||
```
|
||||
|
||||
Now hit the URL you defined in your config, and you should be prompted to create your first (admin) account, after which you can login (*with the details you just created*), and start admin-ing. Install a mobile app, connect using the same credentials, and start backing up all your photos!
|
||||
|
||||
## Summary
|
||||
|
||||
What have we achieved? We have an HTTPS-protected endpoint to target with the native mobile apps, allowing us to backup photos from mobile devices and have them become searchable, shareable, and browseable via a beautiful, Google Photos-esque interface!
|
||||
|
||||
!!! summary "Summary"
|
||||
Created:
|
||||
|
||||
* [X] Photos can be synced from mobile device, or manually uploaded via web UI
|
||||
|
||||
## Setup Immich in < 60s
|
||||
|
||||
Sponsors have access to a [Premix](/premix/) playbook, which will set up Immich in under 60s (*see below*):
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/s-NZjYrNOPg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
--8<-- "recipe-footer.md"
|
||||
|
||||
[^1]: "wife-insurance": When the developer's wife is a primary user of the platform, you can bet he'll be writing quality code! :woman: :material-karate: :man: :bed: :cry:
|
||||
[^2]: There's a [friendly Discord server](https://discord.com/invite/D8JsnBEuKb) for Immich too!
|
||||
@@ -20,7 +20,7 @@ Immich is a promising self-hosted alternative to Google Photos. Its UI and featu
|
||||
|
||||
I'm personally excited about Immich because I've recently been debating how to migrate from Google Photos, in which I'm hitting my 15GB storage limit.
|
||||
|
||||
{ loading=lazy }
|
||||
{ loading=lazy }
|
||||
|
||||
Immich is a bit of an outlier in the self-hosted application space in terms of its maturity.. the [repository](https://github.com/immich-app/immich) currently states that it's **not** production-ready, but it's already got both an Android and iOS app available in the respective app stores.
|
||||
|
||||
@@ -57,7 +57,7 @@ So what I'm looking for is a solution to replace Google Photos - a way for each
|
||||
|
||||
### Install
|
||||
|
||||
Installation was... tricky, but that's because I wanted to install Immich using Docker Swarm, rather than the example docker-compose included with the repo. I'm working on a recipe, and will update this review when done.
|
||||
I've written a recipe to [install Immich in Docker Swarm][immich]. Immich can also be "automatically" installed using the ansible playbook in [Premix](/premix/) 🚀.
|
||||
|
||||
### Web UI
|
||||
|
||||
@@ -156,11 +156,6 @@ Based on how the pre-production development has progressed, and the massive hung
|
||||
|
||||
Please [join me](/#sponsored-projects) in sponsoring [@alextran1502](https://github.com/sponsors/alextran1502), to support this exceptional product!
|
||||
|
||||
### Setup Immich via premix
|
||||
|
||||
I'll have a recipe up for Immich in due course, but if you're wanting to use the pre-prod, development, dont-blame-me version, there's an ansible role in the [Premix](/premix/) playbook which will set it up in under 60s (*see below*):
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/s-NZjYrNOPg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
--8<-- "review-footer.md"
|
||||
|
||||
[^1]: "wife-insurance": When the developer's wife is a primary user of the platform, you can bet he'll be writing quality code! :woman: :material-karate: :man: :bed: :cry:
|
||||
|
||||
@@ -13,7 +13,7 @@ plugins:
|
||||
back_cover: true
|
||||
#cover_title: TITLE TEXT
|
||||
#cover_subtitle: SUBTITLE TEXT
|
||||
# custom_template_path: pdf_templates
|
||||
custom_template_path: with_pdf_template
|
||||
#toc_title: TOC TITLE TEXT
|
||||
#heading_shift: true
|
||||
toc_level: 3
|
||||
|
||||
@@ -97,6 +97,7 @@ nav:
|
||||
- GitLab: recipes/gitlab.md
|
||||
- GitLab Runner: recipes/gitlab-runner.md
|
||||
- Gollum: recipes/gollum.md
|
||||
- Immich: recipes/immich.md
|
||||
- InstaPy: recipes/instapy.md
|
||||
- Jellyfin: recipes/jellyfin.md
|
||||
- Keycloak:
|
||||
@@ -240,7 +241,7 @@ nav:
|
||||
- Design: premix/ansible/design.md
|
||||
# - Swarm: premix/swarm.md
|
||||
# - Kubernetes: premix/kubernetes.md
|
||||
- ✅ Reviews:
|
||||
- ☑️ Reviews:
|
||||
- review/index.md
|
||||
- Immich: review/immich.md
|
||||
- CHANGELOG: recent-changes.md
|
||||
|
||||
@@ -7,4 +7,7 @@ cp -rf manuscript docs_to_pdf
|
||||
find docs_to_pdf -type f -exec gsed -i -e 's/recipe-footer.md/common-links.md/g' {} \;
|
||||
|
||||
# Build PDF from slimmed recipes
|
||||
docker run --rm --name mkdocs-material -e ENABLE_PDF_EXPORT=1 -v ${PWD}/docs_to_pdf:/docs funkypenguin/mkdocs-material build -f mkdocs-pdf-print.yml
|
||||
docker run --rm --name mkdocs-material-build-pdf -e ENABLE_PDF_EXPORT=1\
|
||||
-v ${PWD}/docs_to_pdf:/docs\
|
||||
-v ${PWD}/mkdocs-pdf-print.yml:/mkdocs-pdf-print.yml\
|
||||
funkypenguin/mkdocs-material build -f mkdocs-pdf-print.yml
|
||||
|
||||
3
with_pdf_template/styles.scss
Normal file
3
with_pdf_template/styles.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
* {
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif !important;
|
||||
}
|
||||
Reference in New Issue
Block a user