|
1 |
| -# Basic webapp in Go + CNPG - a walk- |
2 |
| - |
3 |
| -## demo |
4 |
| - |
5 |
| -Given: local Kind cluster |
6 |
| - |
7 |
| -1. The "normal" web app development game with CloudNativePG |
8 |
| - Cluster with 1 pod |
9 |
| - Go executable for the webserver |
10 |
| - Port forwarding so the Go executable can talk to the DB (because Kind on macOS) |
11 |
| - Liquibase to run migrations |
12 |
| - -- show codebase: migration, connection string, SQL used |
13 |
| - -- show app working |
14 |
| -2. Taking advantage of CloudNativePG |
15 |
| - Scale cluster up to 3 pods |
16 |
| - Containerize webserver |
17 |
| - Load and deploy into Kind |
18 |
| - Show CNPG services |
19 |
| - Use `-rw` service to let the webserver pods connect to cluster |
20 |
| - Load test the update endpoint |
21 |
| - Kill primary pod |
22 |
| -3. Time permitting |
23 |
| - Rewrite the webapp so the `/latest` endpoint uses the `-ro` service |
24 |
| - Repeat load test and killing of primary |
25 |
| - Talk of Poolers and pgbouncer |
| 1 | +# Webapp development with CloudNativePG |
26 | 2 |
|
27 |
| -https://apoorvtyagi.tech/containerize-your-web-application-and-deploy-it-on-kubernetes |
28 |
| -https://stackoverflow.com/questions/30746888/how-to-know-a-pods-own-ip-address-from-inside-a-container-in-the-pod |
| 3 | +This repo has support code for a Virtual Office Hours demo given at the |
| 4 | +2022 Kubecon Valencia, on 2022-05-18 by me (Jaime). |
29 | 5 |
|
30 |
| -## Introduction |
| 6 | +Contains: |
31 | 7 |
|
32 |
| -Developing a webapp using an RDBMS involves quite a bit of setup on developer |
33 |
| -machines. The Database itself is usually installed locally, or using Docker for those |
34 |
| -teams that already have a Docker investment. |
| 8 | +- Liquibase config and initial migration |
| 9 | +- Webapp in Go |
| 10 | +- Makefile and cheat-sheet (to aid this poor typist) |
| 11 | +- Dockerfile and K8s YAML's for the webapp |
35 | 12 |
|
36 |
| -The deployments are typically of the most basic type, with one Database instance running. |
37 |
| -Replication, load tests, RTO measurements etc., are left for production. Very often, the |
38 |
| -developers have only basic knowledge of SQL, and no experience with DB |
39 |
| -administration and operations. |
| 13 | +Assumed to be installed on demo machine: |
40 | 14 |
|
41 |
| -In this context, there is a large gap between the development environment and the |
42 |
| -production and staging environments. It's deploy and pray. |
| 15 | +- KinD cluster |
| 16 | +- CloudNativePG operator |
| 17 | +- Liquibase |
43 | 18 |
|
44 |
| -CloudNativePG is a great tool to eliminate the gap between development and |
45 |
| -production. |
46 |
| -Developers can use CloudNativePG to spin up a PostgreSQL database on their |
47 |
| -machine, add read replicas, test out disaster scenarios, scale up and down, etc. |
48 |
| -And then use CloudNativePG to deploy their applications, with a good idea of |
49 |
| -what to expect, and a lot of practice. |
| 19 | +Uses the `cluster-example.yaml` sample in `docs/src/samples`. |
50 | 20 |
|
51 |
| -## Why develop with PosgreSQL? |
| 21 | +## summary |
52 | 22 |
|
53 |
| -PostgreSQL is the most advanced Open Source RDBMS out there. It is also |
54 |
| -a pleasure to develop code against. |
| 23 | +The purpose of the demo is to show CloudNativePG is a great tool to develop |
| 24 | +web applications on dev machines and opens up possibilities to do all sorts of |
| 25 | +things before putting in prod. |
55 | 26 |
|
56 |
| -Some great features that will allow you to write streamlined SQL: |
| 27 | +We try to show a viable workflow using Liquibase for schema migrations, |
| 28 | +and a webapp written in Go using only the standard |
| 29 | +`net/http`, `database/sql` and `lib/pq` packages. |
57 | 30 |
|
58 |
| -- Common Table Expressions (i.e. `WITH` statements) |
59 |
| -- Windowing functions |
60 |
| -- Grouping Sets |
61 |
| -- Array types and JSON. |
62 |
| -- **Transactional DDL** (more detail next section) |
63 |
| -- Powerful GIS with PostGIS |
64 |
| -- Transactions (obviously!!) |
| 31 | +Two halves to the demo: |
65 | 32 |
|
66 |
| -## Basic development cycle for a webapp with a Database |
| 33 | +1. "Average" or "old-style" approach: postgres installed locally, possibly even |
| 34 | + dockerized + webapp developed locally or perhaps dockerized. |
| 35 | + We ape this by installing a 1-pod cluster using the `cluster-example.yaml` sample |
| 36 | + with 1 replica instead of 3. |
| 37 | + We run the webapp outside of KinD and hit `localhost:5432` from it. |
67 | 38 |
|
68 |
| -A fairly typical scenario when developing a webapp or other kind of application with |
69 |
| -a RDBMS for storage would be: |
| 39 | +2. Better way: we dockerize the webapp, push to the KinD nodes, create a |
| 40 | + deployment and a service for it. We hit the `cluster-example-rw` service. |
| 41 | + We show the possibilities that open up. |
70 | 42 |
|
71 |
| -- Either install PostgreSQL locally, or get a dockerized version and do port forwarding |
72 |
| -- Write the application to execute natively or dockerize it |
73 |
| -- Avoid using the `postgres` superuser in your webapp. Create a custom user with only |
74 |
| - the necessary permissions |
75 |
| -- Use schema migration tools to capture DB changes in dev, and commit them to your |
76 |
| - code repo so your team can update and apply on their DB's |
77 |
| -- Following 12-factor app recommendations, pass the DB credentials as environment |
78 |
| - variables to the application |
| 43 | +## Demo and slides |
79 | 44 |
|
80 |
| -Schema migration tools are a necessity to have a sane collaboration environment. |
81 |
| -Liquibase or Flybase are well known examples, and there are other tools available |
82 |
| -using different implementation languages. We use liquibase in this post. |
| 45 | +### Slide: Using CloudNativePG from outside Kubernetes |
83 | 46 |
|
84 |
| -Schema migration tools work best when their Rollback/Undo command works |
85 |
| -cleanly. PostgreSQL |
86 |
| -shines here. With its **Transactional DDL**, no cleanup is needed after a Rollback. |
87 |
| -Let's just say there are other RDBMS that can't make that claim. |
| 47 | +Show diagram for "Case 2" from the |
| 48 | +[cnpg docs](https://cloudnative-pg.io/documentation/1.15.0/use_cases/) |
88 | 49 |
|
89 |
| -There's nothing to stop you from using CloudNativePG as you would a local or dockerized |
90 |
| -Postgres instance, eventhough it can do much more. |
| 50 | +### Slide: "The Old Way", but using CloudNativePG |
91 | 51 |
|
92 |
| -Let's start with that: |
| 52 | +#### Ingredient list, game plan |
93 | 53 |
|
94 |
| -I have a Kind cluster running on my computer. |
95 |
| -I have installed the CNPG operator, and created the simplest possible CloudNativePG |
96 |
| -cluster, with 1 Pod |
97 |
| -running PostgreSQL 14. By default, a CloudNativePG creates a database called `app`, |
98 |
| -owned by a user called `app`. |
| 54 | +1. Basic web application written in Go (aka golang) |
| 55 | +1. Kubernetes cluster (in this case KinD running on macOS) |
| 56 | +1. The CloudNativePG operator installed on KinD |
| 57 | +1. Schema migration tool: Liquibase |
99 | 58 |
|
100 |
| -Let's write a basic (very basic) webapp in Go. |
101 |
| -Using the [lib/pq](https://pkg.go.dev/github.com/lib/pq) library, we're going to |
102 |
| -use the following connection string: |
| 59 | +1. Create the simplest CloudNativePG cluster |
| 60 | +1. Start with an empty PostgreSQL DB |
| 61 | +1. Get DB connection credentials |
| 62 | +1. Apply migrations |
| 63 | +1. Add port forwarding to expose DB (necessary if running KinD on macOS) |
| 64 | +1. Start the webapp |
103 | 65 |
|
| 66 | +### Slide: Putting your webapp inside Kubernetes |
104 | 67 |
|
105 |
| -k config set-context --current --namespace=foo |
| 68 | +Show first diagram for "Case 1" from the |
| 69 | +[cnpg docs](https://cloudnative-pg.io/documentation/1.15.0/use_cases/) |
106 | 70 |
|
107 |
| -k rollout deployment |
| 71 | +Take opportunity to show the web page for cnpg and the documentation link. |
108 | 72 |
|
109 |
| -punnett square |
| 73 | +### Slide: Leveraging CloudNativePF - cooking with fire |
110 | 74 |
|
| 75 | +#### Ingredient list, game plan |
111 | 76 |
|
112 |
| -``` none |
113 |
| -"postgres://<user>:<password>@localhost/app?sslmode=require" |
114 |
| -``` |
| 77 | +1. Let’s Dockerize our webapp and load it into our KinD cluster |
| 78 | +1. Make a deployment and a loadBalancer for our webapp |
| 79 | +1. The webapp should now hit the -rw (cluster-example-rw) Service |
115 | 80 |
|
116 |
| -Our app is so simple it's in just one file: `main.go` |
117 |
| -We can run it like so: |
| 81 | +1. Let’s scale our CloudNativePG cluster to 3 instances |
| 82 | +1. Close the running webapp |
| 83 | +1. Deploy the webapp (show Dockerfile and K8s YAML) |
| 84 | +1. Let’s add a watch on the cluster. Let’s put some load on it |
| 85 | +1. Let’s kill the primary!! |
| 86 | +1. (with enough time) - Have a look around in the cloudnative-pg repo |
118 | 87 |
|
119 |
| -``` sh |
120 |
| -PG_PASSWORD=<redacted> PG_USER=app go run main.go |
121 |
| -``` |
| 88 | +## Slide: Reflections and questions |
122 | 89 |
|
123 |
| -The laptop I'm using is a mac, so I need to do port forwarding in order for my Kind |
124 |
| -cluster to open a port in my local network: |
| 90 | +Talk about how once we start leveraging CloudNativePG, we enable developers |
| 91 | +to do experiments that require DBA skills. And we allow them to develop locally |
| 92 | +with a DB much more similar to what production will look like. |
| 93 | +When they deal with prod issues on their DB, those developers will have gained |
| 94 | +hand-on experience already. |
125 | 95 |
|
126 |
| -``` sh |
127 |
| -kubectl port-forward service/cluster-example-rw 5432:5432 |
128 |
| -``` |
129 |
| - |
130 |
| -That's all there is to it. I can now open `http://localhost:8080/` in my browser. |
131 |
| - |
132 |
| -``` sh |
133 |
| -kubectl port-forward service/cluster-example-rw 5432:5432 |
134 |
| -PG_PASSWORD=S3xBbFUX0pQ1t8VVgYxOqVbDRDufAmdQIi5Q2AnwHx457qREWWEhDuJbIVKNP9mh PG_USER=app go run main.go |
135 |
| - |
136 |
| -/usr/local/opt/liquibase/liquibase rollback-count 1 --changelog-file=example-changelog.sql |
137 |
| -/usr/local/opt/liquibase/liquibase update --changelog-file=example-changelog.sql |
138 |
| -``` |
139 |
| - |
140 |
| -## shell |
141 |
| - |
142 |
| -``` sh |
143 |
| -go run main.go |
144 |
| -``` |
145 |
| - |
146 |
| -## docker |
147 |
| - |
148 |
| -``` sh |
149 |
| -docker run -dp 5000:5000 myapp |
150 |
| -docker build -t myapp . |
151 |
| -``` |
152 |
| - |
153 |
| -## kind |
154 |
| - |
155 |
| -``` sh |
156 |
| -k apply -f deployment.yaml |
157 |
| -docker images |
158 |
| -kind load docker-image myapp:latest --name pg-operator-e2e-v1-23-1 |
159 |
| -kubectl port-forward deployment/mywebapp 8080:5000 -n demo |
160 |
| -kubectl port-forward service/mywebapp 8080:8088 -n demo |
161 |
| -``` |
162 |
| - |
163 |
| -Let's run a load generator: |
164 |
| -hey -H "Accept: application/json" -z 5s http://localhost:8080/ |
| 96 | +And bridging the gap betwenn dev and prod is one of the tenets of the DevOps |
| 97 | +movement. |
0 commit comments