Skip to content

Commit

Permalink
Merge pull request #138 from open-zaak/feature/k8s-deploy
Browse files Browse the repository at this point in the history
Add the deployment tooling for a Kubernetes deployment
  • Loading branch information
joeribekker committed Nov 8, 2019
2 parents 3e021a4 + 893758f commit 474bc3a
Show file tree
Hide file tree
Showing 63 changed files with 2,217 additions and 10 deletions.
5 changes: 4 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Open Zaak
:Keywords: zaken, zaakgericht werken, zaken-api, catalogi-api, besluiten-api, documenten-api
:PythonVersion: 3.7

|build-status| |coverage| |black|
|build-status| |coverage| |black| |docker|

API's voor Zaakgericht werken

Expand Down Expand Up @@ -73,3 +73,6 @@ Licensed under the EUPL_

.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black

.. |docker| image:: https://images.microbadger.com/badges/image/openzaak/open-zaak.svg
:target: https://microbadger.com/images/openzaak/open-zaak
18 changes: 9 additions & 9 deletions bin/docker_start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ set -ex

# Wait for the database container
# See: https://docs.docker.com/compose/startup-order/
db_host=${DB_HOST:-db}
db_user=${DB_USER:-postgres}
db_password=${DB_PASSWORD}
db_port=${DB_PORT:-5432}
export PGHOST=${DB_HOST:-db}
export PGPORT=${DB_PORT:-5432}

fixtures_dir=${FIXTURES_DIR:-/app/fixtures}

uwsgi_port=${UWSGI_PORT:-8000}
uwsgi_processes=${UWSGI_PROCESSES:-2}
uwsgi_threads=${UWSGI_THREADS:-2}

until PGPORT=$db_port PGPASSWORD=$db_password psql -h "$db_host" -U "$db_user" -c '\q'; do
until pg_isready; do
>&2 echo "Waiting for database connection..."
sleep 1
done
Expand Down Expand Up @@ -43,7 +43,7 @@ uwsgi \
--static-map /static=/app/static \
--static-map /media=/app/media \
--chdir src \
--processes 2 \
--threads 2 \
--buffer-size=32768
# processes & threads are needed for concurrency without nginx sitting inbetween
--enable-threads \
--processes $uwsgi_processes \
--threads $uwsgi_threads \
--buffer-size=65535
2 changes: 2 additions & 0 deletions deployment/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.retry
.vault*.txt
99 changes: 99 additions & 0 deletions deployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Deployment instructions - how to use this

The deployment instructions for Kubernetes are documented here. Extended
documentation is/will be available on
[ReadTheDocs](https://open-zaak.readthedocs.io/).

## Prerequisites

**Deployment tooling**

Ensure you have the necessary deployment tooling installed. We recommend using
a Python 3.7+ virtualenv, and then:

```shell
[user@host]$ pip install -r requirements.txt
```

**Ensure you have a `kube.config`**

You need to have a valid, functioning `kube.config` file with cluster admin
permissions. Consult your (cloud) provider documentation/support on how to
obtain this.

**Test that it's working**

If you have `kubectl`, run:

```shell
[user@host]$ kubectl cluster-info
```

This can be used to verified that your credentials are indeed set up to point
to the correct cluster.

**Database**

The deployment assumes that a PostgreSQL 11 database cluster is available.

You need:

* credentials for a superuser role (typically `postgres`)
* the host (name) and port of the cluster (e.g. an ip-address and the default
port `5432`)
* see `vars/db_credentials.example.yml` - save this file as
`vars/db_credentials.yml` and modify with your own credentials.

**Persistent storage**

For multi-replica setups and secure file serving, we need a PVC storage class
that supports `ReadWriteMany` - see
[the Kubernetes docs](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes)
for an overview.

On Google Cloud, we can use:

```shell
[user@host]$ gcloud compute disks create --size=10GB --zone=europe-west4-b gce-nfs-disk
```

## Provisioning

The Ansible playbook `provision.yml`:

* sets up the basic cluster requirements, such as the ingress and required
namespace(s)
* initializes the database: set up the db user, create the application database
and enable the required database extensions

```shell
[user@host]$ ./deploy.sh provision.yml
```

## Application

Deploy Open Zaak and Open Notificaties:

```shell
[user@host]$ ./deploy.sh apps.yml
```


## Troubleshooting

**Missing `GOOGLE_APPLICATION_CREDENTIALS` error**

If you see:

```
Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and
re-run the application.
```

Try to (re-)log in to gcloud:

```shell
[user@host]$ gcloud auth application-default login
```

This should output a new default credentials file, which should be picked up.
2 changes: 2 additions & 0 deletions deployment/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[defaults]
inventory = hosts
20 changes: 20 additions & 0 deletions deployment/apps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---

- name: Deploy the Open Zaak apps in the cluster
hosts: localhost

vars_files:
- vars/db_credentials.yml
- vars/openzaak.yml
- vars/opennotificaties.yml

roles:
- role: openzaak
vars:
openzaak_db_host: "{{ db.privateIp }}"
openzaak_db_port: "{{ db.port }}"

- role: opennotificaties
vars:
opennotificaties_db_host: "{{ db.privateIp }}"
opennotificaties_db_port: "{{ db.port }}"
20 changes: 20 additions & 0 deletions deployment/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
# Deploy an Ansible playbook
#
# Usage: ./deploy.sh playbook.yml [--any-ansible-playbook-arguments]
#
# Requires a virtualenv to be active with the local dependencies, as
# ansible_python_interpreter is derived from that.
#

set -ex

if [[ ! -z "$VIRTUALENV" ]]; then
echo "activate your virtualenv"
exit 1
fi

playbook=$1
shift

ansible-playbook $playbook -e "ansible_python_interpreter=$(which python)" "$@"
13 changes: 13 additions & 0 deletions deployment/files/traefik/cluster-role-binding.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller

roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller # used in deployment!
namespace: default
63 changes: 63 additions & 0 deletions deployment/files/traefik/cluster-role.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller

rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- traefik.containo.us
resources:
- middlewares
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- ingressroutes
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- ingressroutetcps
verbs:
- get
- list
- watch
- apiGroups:
- traefik.containo.us
resources:
- tlsoptions
verbs:
- get
- list
- watch
47 changes: 47 additions & 0 deletions deployment/files/traefik/deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---

kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: traefik
labels:
app: traefik

spec:
replicas: 2
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.0
resources:
requests:
memory: "20Mi"
cpu: "250m"
limits:
memory: "200Mi"
cpu: "500m"
args:
- --api.insecure
- --accesslog
- --entrypoints.web.Address=:80
- --entrypoints.websecure.Address=:443
- --providers.kubernetescrd
- --certificatesResolvers.default.acme.email={{ acme_email }}
- --certificatesResolvers.default.acme.storage=acme.json
- --certificatesResolvers.default.acme.tlsChallenge=true
ports:
- name: web
containerPort: 8000
- name: websecure
containerPort: 4443
- name: admin
containerPort: 8080
15 changes: 15 additions & 0 deletions deployment/files/traefik/ingress-route-tcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.us

spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced
15 changes: 15 additions & 0 deletions deployment/files/traefik/ingress-route.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutes.traefik.containo.us

spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRoute
plural: ingressroutes
singular: ingressroute
scope: Namespaced
15 changes: 15 additions & 0 deletions deployment/files/traefik/middleware.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: middlewares.traefik.containo.us

spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: Middleware
plural: middlewares
singular: middleware
scope: Namespaced
25 changes: 25 additions & 0 deletions deployment/files/traefik/service.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---

apiVersion: v1
kind: Service
metadata:
name: traefik
namespace: default

spec:
type: LoadBalancer
ports:
- protocol: TCP
name: web
port: 80
targetPort: 80
- protocol: TCP
name: admin
port: 8080
targetPort: 8080
- protocol: TCP
name: websecure
port: 443
targetPort: 443
selector:
app: traefik

0 comments on commit 474bc3a

Please sign in to comment.