diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-10-04 14:13:31 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-10-04 14:48:31 +0000 |
| commit | e7b84a65084e17d5da64fe1fa716d7a186ed701c (patch) | |
| tree | 61b2c6b140c644c0544b3255aae0069550d963aa /root/blog/self-hosted_virtual_home/text$markdown.md | |
| parent | add mmmfs pdf (diff) | |
| download | mmm-e7b84a65084e17d5da64fe1fa716d7a186ed701c.tar.gz mmm-e7b84a65084e17d5da64fe1fa716d7a186ed701c.zip | |
Split root out of mmm repo
Diffstat (limited to 'root/blog/self-hosted_virtual_home/text$markdown.md')
| -rw-r--r-- | root/blog/self-hosted_virtual_home/text$markdown.md | 224 |
1 files changed, 0 insertions, 224 deletions
diff --git a/root/blog/self-hosted_virtual_home/text$markdown.md b/root/blog/self-hosted_virtual_home/text$markdown.md deleted file mode 100644 index e6b41a1..0000000 --- a/root/blog/self-hosted_virtual_home/text$markdown.md +++ /dev/null @@ -1,224 +0,0 @@ -In this post I'll break down the setup of my self-hosted virtual home: https://s-ol.nu. - -First a quick overview of what this guide will cover: - -- HTTPS server with multiple subdomains and varying backends - - [traefik][traefik] reverse-proxy maintains SSL certificates and serves all requests - - [docker-compose][docker-compose] manages running sites/microservices -- a private-public git server - - access control, management with [gitolite][gitolite] - - [klaus][klaus] web frontend for browsing and cloning public repos - - fine-grained permissions and SSH public-key access -- micro 'CI' setup rebuilds & redeploys docker images when updates are pushed - -**UPDATE (2019-10-03)**: I updated the git hook below to one that supports pushing and building -multiple branches based on the `docker-compose.yml`. - -Most of these projects are very well documented so I won't go into a lot of detail on setting them up. - -# HTTPS Server -To run multiple subdomains from a single machine, the HTTP requests need to be matched according to the 'Host' header. -Most HTTP servers have good facilities for doing this, e.g. apache has vhosts, nginx has server directives etc. - -In the past I had used apache as my main server, which worked well for static content and PHP apps, -but not all applications fit into this scheme too well and configuration is a tad tedious. - -With this latest iteration I am using [traefik][traefik] as a "reverse proxy". -This means traefik doesn't serve anything (not even static content) by itself, -it just delegates requests to one of multiple configured services based on a system of rules. - -It also handles letsencrypt certificate generation and updates out-of-the-box and can be tightly integrated with docker, -so that it automatically reacts to new services being added. - -Traefik can be run at system level, but currently I prefer installing the least system-level applications to have my setup -as self-contained as possible. Therefore I went with this [traefik in docker-compose][traefik-in-docker] setup from kilian.io: - - version: '3.4' - - services: - traefik: - image: traefik:1.5-alpine - restart: always - ports: - - 80:80 - - 443:443 - networks: - - web - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./traefik.toml:/traefik.toml - - ./acme.json:/acme.json - container_name: traefik - - networks: - web: - external: true - -with the small addition of the `:ro` at the end of the docker socket volume, -to prevent attacks on traefik from being able to take over the host docker system (too easily). -In the guide you can find more details, including the `traefik.toml` that I am using almost verbatim. - -# Hosting Sites -With traefik set up, dockerized services can be added and exposed trivially. -For example to start `redirectly`, a tiny link redirect service, this addition suffices: - - redirectly: - image: local/redirectly:master - restart: always - networks: - - web - labels: - - "traefik.frontend.rule=Host:s-ol.nu" - - "traefik.enable=true" - -By setting different subdomains in the frontend-rule section, many different services can be provided. - -The image `local/redirectly:git` in this case is built automatically when a repo is pushed (see below). - -note: if a container doesn't have an EXPOSE directive, or EXPOSEs multiple ports, -you will have to add a `traefik.port` label specifying which port to use. - -# private/public Git Server -While I still have a lot of code on Github, where collaboration is easy, -I prefer to own the infrastructure that I store my private projects on. -I also wanted to have a public web index of some of the projects. - -The git infrastructure itself is mananged by [gitolite][gitolite], which I really enjoy using. - - repo gitolite-admin @all - RW+ = s-ol - C = s-ol - - repo public/.* - R = @all daemon - option writer-is-owner = 1 - - repo ... ... - RW+ = ludopium - -In the first block I grant myself full access to all repos, as well as the right to automatically create repos by -attempting to push/pull from them. - -The second block makes all repos prefixed with `public/` readable by anyone in the gitolite system, -as well as the `git-daemon`, which allows cloning via `git://....` access (port 9418). -The `write-is-owner` option lets me set the git `description` field using `ssh git@git.s-ol.nu desc`. - -I chose the `public/` prefix because it results in all public repos being stored in one directory together -(`/var/lib/gitolite/repositories/public`), where klaus can easily pick them up. - -The klaus web frontend is set up using traefik above like so: - - klaus: - image: hiciu/klaus-dockerfile - restart: always - networks: - - web - volumes: - - /var/lib/gitolite/repositories/public:/srv/git:ro - command: /opt/klaus/venv/bin/uwsgi --wsgi-file /opt/klaus/venv/local/lib/python2.7/site-packages/klaus/contrib/wsgi_autoreload.py --http 0.0.0.0:8080 --processes 1 --threads 2 - environment: - KLAUS_REPOS_ROOT: /srv/git - KLAUS_SITE_NAME: git.s-ol.nu - KAUS_CTAGS_POLICY: tags-and-branches - KLAUS_USE_SMARTHTTP: y - labels: - - "traefik.frontend.rule=Host:git.s-ol.nu" - - "traefik.enable=true" - -I am using the ['autoreload' feature][klaus-autoreload] and the `hiciu/klaus-dockerfile` docker image. -Setting `KLAUS_USE_SMARTHTTP` allows cloning repos via HTTP. - -In the future I would like to modify klaus a bit, for example by showing the README in the root of a project per default -and applying a custom theme. - -# Micro-CI -The last piece of the puzzle is automatically deploying projects whenever they are pushed. -This can be realized using git's `post-receive` hooks and is generally pretty well known. - -I followed this gitolite guide for [storing repo-specific hooks in the gitolite-admin repo][gitolite-hooks]. -It requires a change in the gitolite rc file (on the server), but after that you can configure deployment processes in the conf like this: - - @dockerize = public/redirectly ... - @jekyllify = blog - - repo @dockerize - option hook.post-receive = docker-deploy - - # i actually dont have a jekyll blog anymore but its an easy one as well - repo @jekyllify - option hook.post-receive = jekyll-deploy - -The hooks are stored in the same repo under `local/hooks/repo-specific`. -Here is the `docker-deploy` hook I am using: - - #!/bin/bash - set -e - - while read oldrev newrev refname - do - BRANCH="$(git rev-parse --symbolic --abbrev-ref $refname)" - - # Get project name - PROJECT="$PWD" - PROJECT="${PROJECT#*/repositories/public/}" - PROJECT="${PROJECT#*/repositories/}" - PROJECT="${PROJECT%.git}" - PROJECT="$(echo "$PROJECT" | tr "/ " "-_")" - - # Paths - CHECKOUT_DIR=/tmp/git/$PROJECT - TARGET_DIR=/home/s-ol/aerol - IMAGE_NAME=local/$PROJECT:$BRANCH - - # this one doesn't require python & yq, but it means the container has to run already... - # SERVICES=$(docker ps --filter "ancestor=${IMAGE_NAME}" --format '{{.Label "com.docker.compose.service"}}' \ - # | sort | uniq) - - SERVICES=$(yq -r <"$TARGET_DIR/docker-compose.yml" \ - ".services | to_entries | map(select(.value.image == \"${IMAGE_NAME}\").key) \ - | join(\" \")") - - if [ -z "$SERVICES" ]; then - continue - fi - - mkdir -p "$CHECKOUT_DIR" - GIT_WORK_TREE="$CHECKOUT_DIR" git checkout -q -f $newrev - echo -e "\e[1;32mChecked out '$PROJECT'.\e[00m" - - cd "$CHECKOUT_DIR" - docker build -t "$IMAGE_NAME" . - echo -e "\e[1;32mImage '$IMAGE_NAME' built.\e[00m" - - cd "$TARGET_DIR" - docker-compose up -d $SERVICES - echo -e "\e[1;32mService(s) '$SERVICES' restarted.\e[00m" - done - -It will build a `local/$REPO:$BRANCH` image whenever you push, then run `docker-compose up -d $SERVICES` in `$TARGET_DIR`, -where `$SERVICES` are all the docker-compose services that use the image. If there are none, no image will be built. -For this to work it has to parse the `docker-compose.yaml` file, which means you have to install [`yq`][yq] and `jq`, e.g. on Ubuntu: - - sudo apt-get install jq python3-pip - sudo pip install yq - -If you would like to avoid that, you can use the commented command for `SERVICES=` above, which only relies on docker itself, -the only problem is that you will have to do the first build manually (or re-tag a dummy image) before the first build, -since it can only detect containers that are already running. - ---- - -That's basically it! -If you have questions or comments i'll be happy to hear from you on twitter, github or [mastodon][merveilles]. - -[traefik]: https://traefik.io/ -[docker-compose]: https://docs.docker.com/compose/ -[gitolite]: http://gitolite.com/gitolite/index.html -[klaus]: https://github.com/jonashaag/klaus - -[traefik-in-docker]: https://blog.kilian.io/server-setup/ -[klaus-autoreload]: https://github.com/jonashaag/klaus/wiki/Autoreloader -[gitolite-hooks]: http://gitolite.com/gitolite/cookbook#v36-variation-repo-specific-hooks -[yq]: https://github.com/kislyuk/yq - -[merveilles]: https://merveilles.town/@s_ol |
