Skip to content

Commit 07d57f0

Browse files
committed
fix README
1 parent d89872e commit 07d57f0

File tree

3 files changed

+76
-144
lines changed

3 files changed

+76
-144
lines changed

README.md

+68-135
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,97 @@
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
262

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).
295

30-
## Introduction
6+
Contains:
317

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
3512

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:
4014

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
4318

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`.
5020

51-
## Why develop with PosgreSQL?
21+
## summary
5222

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.
5526

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.
5730

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:
6532

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.
6738

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.
7042

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
7944

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
8346

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/)
8849

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
9151

92-
Let's start with that:
52+
#### Ingredient list, game plan
9353

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
9958

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
10365

66+
### Slide: Putting your webapp inside Kubernetes
10467

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/)
10670

107-
k rollout deployment
71+
Take opportunity to show the web page for cnpg and the documentation link.
10872

109-
punnett square
73+
### Slide: Leveraging CloudNativePF - cooking with fire
11074

75+
#### Ingredient list, game plan
11176

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
11580

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
11887

119-
``` sh
120-
PG_PASSWORD=<redacted> PG_USER=app go run main.go
121-
```
88+
## Slide: Reflections and questions
12289

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.
12595

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.

cheatsheet.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,20 @@
22

33
## requesting and load generating
44

5-
curl localhost:8080/
6-
curl -H "Accept: application/json" localhost:8080/
7-
8-
hey -H "Accept: application/json" -z 5s http://localhost:8080/
5+
curl localhost:8080/update
96

107
hey -z 100s -q 1 -c 2 http://localhost:8080/update
118

129
## kubernetes
1310

14-
k config set-context --current --namespace=foo
11+
kubectl rollout restart deployment mywebapp
1512

16-
kubectl rollout restart deployment/demo
13+
## getting the DB credentials to hit ouside K8s
1714

18-
pq: remaining connection slots are reserved for non-replication superuser connections
15+
1. kubectl get secret cluster-example-app -o yaml
16+
1. copy the `password` in the YAML
17+
1. echo copiedSecret | base64 --decode | pbcopy
1918

2019
## app
2120

22-
PG_PASSWORD=KPzgXuDJ8P8FzmlmGO9Y7KgPXF1BszuHKWMuEPtiGhHVxVpiSc76pTD0b2V7cmOD PG_USER=app go run main.go
21+
PG_PASSWORD=***** PG_USER=app go run main.go

liquibase.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ liquibase.command.url=jdbc:postgresql://localhost:5432/app
88
liquibase.command.username: app
99

1010
# Enter the password for your Target database.
11-
liquibase.command.password: KPzgXuDJ8P8FzmlmGO9Y7KgPXF1BszuHKWMuEPtiGhHVxVpiSc76pTD0b2V7cmOD
11+
liquibase.command.password: *****
1212

1313
liquibase.hub.mode=off

0 commit comments

Comments
 (0)