diff --git a/README.md b/README.md index bc36810..dd7a37c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,88 @@ # the task see ()[task.md] -# setup +# deploy -## use ansible runtime container +## python virtualenv Because ansible setups have requirements towards used python versions, libraries etc, -I provide a unified solution everyone that has a contaner runtime like `docker` installed can use. - -This also prevents me from needing to handle python dependencies in conflicting versions. +we use a separate python virtualenv. ```shell -# build the container -docker build -t ansible-runtime:local ansible-runtime +export VENV_PATH='/tmp/venv' +# create the env +$ python -m venv "${VENV_PATH}" -# then run it -docker run -v ${PWD}/ansible:/home/user/workdir:ro --rm -it ansible-runtime:local +# activate it +$ . "${VENV_PATH}/bin/activate" + +# install pip dependencies +pip install -r ansible-runtime/requirements.txt ``` + +## kick off the deployment + +assumptions: +* you have initialized and activated the venv as described above +* the current user has ssh access to `localhost` +* the current user can access the docker socket at `/var/run/docker.sock` + +```shell +$ cd ansible +$ ansible-playbook -i inventory.yaml playbook.yaml +``` + +This deploys three containers and a network. List them: +```shell +# quick and dirty: +docker ps -a | grep userlike +# or with something like this: +docker ps --format json -a | yq -p json -ojson 'select(.Names|test("userlike-.*"))' +``` + +And confirm the routing works as expected: +```shell +curl -k https://localhost:8443/ # shall return one/two backend alternating responses +curl -k https://localhost:8443/one # shall only return "one" responses +curl -k https://localhost:8443/two # shall only return "two" responses + +``` + +## testing +some example queries to test for the given [task](./task.md) +```shell +$ curl -k https://localhost:8443/ +hello my friend, this is backend host "one" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/ +hello my friend, this is backend host "two" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/one +hello my friend, this is backend host "one" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/one +hello my friend, this is backend host "one" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/two +hello my friend, this is backend host "two" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/two +hello my friend, this is backend host "two" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/two/index.html +hello my friend, this is backend host "two" speaking. +have a nice day, mate. +$ curl -k https://localhost:8443/two/index.html.nonexistent + +404 Not Found + +

404 Not Found

+
nginx/1.23.4
+ + +$ curl -k https://localhost:8443/index.html +

503 Service Unavailable

+No server is available to handle this request. + +(venv) [fix@neon +``` \ No newline at end of file diff --git a/ansible-runtime/Dockerfile b/ansible-runtime/Dockerfile deleted file mode 100644 index 7d120ce..0000000 --- a/ansible-runtime/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ - -FROM python:3.11-alpine as base -ARG UID=1000 -ENV UID=${UID} -ARG DOCKER_GID -ENV DOCKER_GID=${DOCKER_GID:-972} - -ADD requirements.txt /requirements.txt - -# RUN --mount=type=cache,target=/var/cache apk update -RUN --mount=type=cache,target=/var/cache pip --cache-dir=/var/cache/pip install -r /requirements.txt - -FROM base as runtime -RUN addgroup -g ${DOCKER_GID} docker && adduser -G docker -Du ${UID} user - -WORKDIR /home/user -USER user -ENTRYPOINT ["/bin/ash"] diff --git a/ansible/haproxy.cfg.template b/ansible/haproxy.cfg.template index b05c804..574099d 100644 --- a/ansible/haproxy.cfg.template +++ b/ansible/haproxy.cfg.template @@ -1,7 +1,10 @@ global log stdout format raw local0 debug - # user haproxy - # group haproxy + user haproxy + group haproxy + # this is to tackle a weird problem I observed + # https://github.com/docker-library/haproxy/issues/180 + fd-hard-limit 10000 daemon defaults @@ -9,23 +12,34 @@ defaults mode http option httplog option dontlognull + maxconn 1000 timeout connect 5000 timeout client 50000 timeout server 50000 frontend http_front - bind *:8443 ssl crt /usr/local/etc/haproxy/tls/certificate.pem + bind :8443 ssl crt /usr/local/etc/haproxy/tls/certificate.pem - acl be_one path_beg /one - acl be_two path_beg /two +{% for hostname in nginx_names %} + acl be_{{ hostname }} path_beg /{{ hostname }} +{% endfor %} +{% for hostname in nginx_names %} + use_backend be_{{ hostname }} if be_{{ hostname }} +{% endfor %} - use_backend be_one if be_one - use_backend be_two if be_two + acl be_round_robin path / + use_backend be_round_robin if be_round_robin {% for hostname in nginx_names %} !be_{{ hostname }} {% endfor %} -backend be_one +{% for hostname in nginx_names %} +backend be_{{ hostname }} mode http - server one http://one:8080/ + http-request set-path %[path,regsub(^/{{ hostname }},"")] + server {{ hostname }} {{ hostname }}:80/ +{% endfor %} -backend be_two +backend be_round_robin mode http - server two http://two:8080/ +{% for hostname in nginx_names %} + http-request set-path %[path,regsub(^/{{ hostname }}/,/)] + server {{ hostname }} {{ hostname }}:80/ +{% endfor %} diff --git a/ansible/haproxy/Dockerfile b/ansible/haproxy/Dockerfile new file mode 100644 index 0000000..3d3521d --- /dev/null +++ b/ansible/haproxy/Dockerfile @@ -0,0 +1,4 @@ +FROM haproxy:lts-alpine + +ADD haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg +# TODO certs diff --git a/ansible/inventory.yaml b/ansible/inventory.yaml index 1aca5de..74c37de 100644 --- a/ansible/inventory.yaml +++ b/ansible/inventory.yaml @@ -1,3 +1,4 @@ docker_host: - localhost: - ansible_host: 127.0.0.1 + hosts: + localhost: + ansible_host: 127.0.0.1 diff --git a/ansible/nginx/Dockerfile b/ansible/nginx/Dockerfile new file mode 100644 index 0000000..213548e --- /dev/null +++ b/ansible/nginx/Dockerfile @@ -0,0 +1,5 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} as base + +COPY init.sh /docker-entrypoint.d/init.sh +RUN chmod 755 /docker-entrypoint.d/init.sh diff --git a/ansible/nginx/init.sh b/ansible/nginx/init.sh new file mode 100644 index 0000000..41798e3 --- /dev/null +++ b/ansible/nginx/init.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -xe + +HOSTNAME=$(cat /etc/hostname) +cat << EOF > /usr/share/nginx/html/index.html +hello my friend, this is backend host "${HOSTNAME}" speaking. +have a nice day, mate. +EOF + +exit 0 \ No newline at end of file diff --git a/ansible/playbook.yaml b/ansible/playbook.yaml index 3969803..dc01c50 100644 --- a/ansible/playbook.yaml +++ b/ansible/playbook.yaml @@ -1,83 +1,118 @@ --- -- name: generate fake cert - hosts: &hosts localhost +- name: userlike deployment + hosts: localhost become: false + vars: + temp_dir: /tmp/userlike + images: + haproxy: haproxy:lts-alpine + nginx: nginx:1.23-alpine + nginx_names: + - one + - two tasks: # adopted from https://docs.ansible.com/ansible/latest/collections/community/crypto/docsite/guide_selfsigned.html - name: ensure local cert dir exists file: - path: /home/user/userlike/haproxy/tls + path: '{{ temp_dir }}/haproxy/tls' state: directory mode: 0755 recurse: yes - name: create private key community.crypto.openssl_privatekey: - path: /home/user/userlike/haproxy/tls/certificate.pem.key + path: '{{ temp_dir }}/haproxy/tls/certificate.key' + size: 2048 + mode: '0644' type: RSA + return_content: true - name: create self-signed certificate community.crypto.x509_certificate: - path: /home/user/userlike/haproxy/tls/certificate.pem - privatekey_path: /home/user/userlike/haproxy/tls/certificate.pem.key + path: '{{ temp_dir }}/haproxy/tls/certificate.crt' + privatekey_path: '{{ temp_dir }}/haproxy/tls/certificate.key' provider: selfsigned + selfsigned_not_after: '+365d' + mode: '0644' -- name: docker preparations - hosts: *hosts - tasks: - - name: set DOCKER_HOST env var - ansible.builtin.shell: - cmd: export DOCKER_HOST=unix:///var/run/docker.sock + - name: concatenate key and certificate + ansible.builtin.copy: + dest: '{{ temp_dir }}/haproxy/tls/certificate.pem' + content: | + {{ lookup('file', '{{ temp_dir }}/haproxy/tls/certificate.key') }} + {{ lookup('file', '{{ temp_dir }}/haproxy/tls/certificate.crt') }} - - name: 'pull image {{ item }}' + - name: remove on-disk certificate remnants + file: + path: '{{ temp_dir }}/haproxy/tls/certificate.{{ item }}' + state: absent + with_items: + - crt + - key + + # docker prepratations + - name: 'pull images {{ item }}' community.docker.docker_image: name: '{{ item }}' state: present source: pull - with_items: - - haproxy:lts-alpine - - nginx:1.23-alpine + with_items: '{{ images.values() }}' - - name: create network + - name: create docker network community.docker.docker_network: name: userlike -- name: run haproxy container - hosts: *hosts - tasks: + # haproxy tasks + - name: ensure local haproxy dir exists + file: + path: '{{ temp_dir }}/haproxy' + state: directory + mode: 0755 + - name: render haproxy config ansible.builtin.template: src: haproxy.cfg.template - dest: /home/user/userlike/haproxy/haproxy.cfg + dest: '{{ temp_dir }}/haproxy/haproxy.cfg' mode: '0644' - - name: build container - community.docker.docker_container: - name: userlike-haproxy:local - build: - path: ./haproxy - - source: build - - name: run container + - name: run haproxy container community.docker.docker_container: name: userlike-haproxy recreate: true detach: true - image: haproxy:lts-alpine + image: '{{ images.haproxy }}' + restart_policy: 'unless-stopped' networks: - name: userlike ports: - - '127.0.0.1:8080:8080' + - '127.0.0.1:8443:8443' volumes: - # has to be host directory - - /tmp/userlike/haproxy:/usr/local/etc/haproxy:ro + - '{{ temp_dir }}/haproxy:/usr/local/etc/haproxy:ro' -# - name: run nginx containers -# hosts: *hosts -# tasks: -# - name: create config -# - name: pull container -# - name: create container -# - name: run container + # nginx tasks + - name: ensure local nginx dir exists + file: + path: '{{ temp_dir }}/nginx' + state: directory + mode: 0755 + + - name: copy nginx init file + copy: + src: nginx/init.sh + dest: '{{ temp_dir }}/nginx/init.sh' + mode: '0755' + + - name: run nginx container + community.docker.docker_container: + name: 'userlike-nginx-{{ item }}' + hostname: '{{ item }}' + recreate: true + detach: true + image: '{{ images.nginx }}' + networks: + - name: userlike + volumes: + - '{{ temp_dir }}/nginx/init.sh://docker-entrypoint.d/init.sh:ro' + with_items: '{{ nginx_names }}' ... \ No newline at end of file