diff --git a/.gitignore b/.gitignore index 3a83dd6..9c5aa2f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ ehthumbs.db Thumbs.db .venv venv +docs_to_pdf diff --git a/manuscript/images/reviews/immich-web.jpg b/manuscript/images/immich.jpg similarity index 100% rename from manuscript/images/reviews/immich-web.jpg rename to manuscript/images/immich.jpg diff --git a/manuscript/images/reviews/immich-mobile-browse-photos.jpeg b/manuscript/images/reviews/immich-mobile-browse-photos.jpeg deleted file mode 100644 index 7871ed6..0000000 Binary files a/manuscript/images/reviews/immich-mobile-browse-photos.jpeg and /dev/null differ diff --git a/manuscript/images/reviews/immich-mobile-save-photos.png b/manuscript/images/reviews/immich-mobile-save-photos.png deleted file mode 100644 index 7b3d1ed..0000000 Binary files a/manuscript/images/reviews/immich-mobile-save-photos.png and /dev/null differ diff --git a/manuscript/images/reviews/immich-mobile-search-photos.jpeg b/manuscript/images/reviews/immich-mobile-search-photos.jpeg deleted file mode 100644 index e43fb89..0000000 Binary files a/manuscript/images/reviews/immich-mobile-search-photos.jpeg and /dev/null differ diff --git a/manuscript/kubernetes/ssl-certificates/letsencrypt-issuers.md b/manuscript/kubernetes/ssl-certificates/letsencrypt-issuers.md index fee50fd..689084a 100644 --- a/manuscript/kubernetes/ssl-certificates/letsencrypt-issuers.md +++ b/manuscript/kubernetes/ssl-certificates/letsencrypt-issuers.md @@ -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: diff --git a/manuscript/kubernetes/ssl-certificates/wildcard-certificate.md b/manuscript/kubernetes/ssl-certificates/wildcard-certificate.md index 1f23dff..10347a7 100644 --- a/manuscript/kubernetes/ssl-certificates/wildcard-certificate.md +++ b/manuscript/kubernetes/ssl-certificates/wildcard-certificate.md @@ -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 diff --git a/manuscript/recipes/immich.md b/manuscript/recipes/immich.md new file mode 100644 index 0000000..e9e192d --- /dev/null +++ b/manuscript/recipes/immich.md @@ -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: + +![Immich Screenshot](/images/immich.jpg){ 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.

Email: demo@demo.de
Password: demo" + +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 < /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*): + + +--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! \ No newline at end of file diff --git a/manuscript/review/immich.md b/manuscript/review/immich.md index 4f0d71c..6c65435 100644 --- a/manuscript/review/immich.md +++ b/manuscript/review/immich.md @@ -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. -![Immich Screenshot](/images/reviews/immich-web.jpg){ loading=lazy } +![Immich Screenshot](/images/immich.jpg){ 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*): - - --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: diff --git a/mkdocs-pdf-print.yml b/mkdocs-pdf-print.yml index 729ba85..327e9fe 100644 --- a/mkdocs-pdf-print.yml +++ b/mkdocs-pdf-print.yml @@ -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 diff --git a/mkdocs.yml b/mkdocs.yml index 165dcf6..7c6aa21 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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 diff --git a/scripts/build-pdf.sh b/scripts/build-pdf.sh index 8d41d8b..0d2f1f2 100755 --- a/scripts/build-pdf.sh +++ b/scripts/build-pdf.sh @@ -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 diff --git a/with_pdf_template/styles.scss b/with_pdf_template/styles.scss new file mode 100644 index 0000000..0212d79 --- /dev/null +++ b/with_pdf_template/styles.scss @@ -0,0 +1,3 @@ +* { + font-family: Verdana, Geneva, Tahoma, sans-serif !important; +} \ No newline at end of file