Browse code

PetSet examples

Clayton Coleman authored on 2016/06/18 03:12:32
Showing 33 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,10 @@
0
+# PetSet examples
1
+
2
+These examples are tracked from the [Kubernetes contrib project @d6e4be](https://github.com/kubernetes/contrib/tree/d6e4be066cc076fbb91ff69691819e117711b30b/pets)
3
+
4
+Note that some of these examples require the ability to run root containers which may not be possible for all users in all environments. To grant
5
+access to run containers as root to a service account in your project, run:
6
+
7
+    oadm policy add-scc-to-user anyuid -z default
8
+
9
+which allows the `default` service account to run root containers.
0 10
\ No newline at end of file
1 11
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+# Mysql Galera
1
+
2
+This example runs mysql galera through a petset.
3
+
4
+## Bootstrap
5
+
6
+Create the petset in this directory
7
+```
8
+$ kubectl create -f galera.yaml
9
+```
10
+
11
+Once you have all 3 nodes in Running, you can run the "test.sh" script in this directory.
12
+This example requires manual intervention.
13
+Once you have all 3 nodes in Running, you can run the "test.sh" script in this directory.
14
+
15
+## Caveats
16
+
17
+Starting up all galera nodes at once leads to an issue where all the mysqls
18
+belive they're in the primary component because they don't see the others in
19
+the DNS. For the bootstrapping to work: mysql-0 needs to see itself, mysql-1
20
+needs to see itself and mysql-0, and so on, because the first node that sees
21
+a peer list of 1 will assume it's the leader.
22
+
23
+## TODO
24
+
25
+Expect better solutions for the following as petset matures.
26
+
27
+* Failover
28
+* Scaling Up
29
+* Scaling Down
30
+* Image Upgrade
31
+* Maintenance
32
+
0 33
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+# TODO: get rid of bash dependency and switch to plain busybox.
15
+# The tar in busybox also doesn't seem to understand compression.
16
+FROM debian:jessie
17
+MAINTAINER Prashanth.B <beeps@google.com>
18
+
19
+RUN apt-get update && apt-get install -y wget
20
+
21
+ADD on-start.sh /
22
+ADD my-galera.cnf /
23
+# See contrib/pets/peer-finder for details
24
+RUN wget -qO /peer-finder https://storage.googleapis.com/kubernetes-release/pets/peer-finder
25
+
26
+ADD install.sh /
27
+RUN chmod -c 755 /install.sh /on-start.sh /peer-finder
28
+Entrypoint ["/install.sh"]
0 29
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+all: push
15
+
16
+TAG = 0.1
17
+PREFIX = gcr.io/google_containers/galera-install
18
+
19
+container:
20
+	docker build -t $(PREFIX):$(TAG) .
21
+
22
+push: container
23
+	gcloud docker push $(PREFIX):$(TAG)
24
+
25
+clean:
26
+	docker rmi $(PREFIX):$(TAG)
0 27
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This volume is assumed to exist and is shared with parent of the init
17
+# container. It contains the mysq config.
18
+CONFIG_VOLUME="/etc/mysql"
19
+
20
+# This volume is assumed to exist and is shared with the peer-finder
21
+# init container. It contains on-start/change configuration scripts.
22
+WORKDIR_VOLUME="/work-dir"
23
+
24
+for i in "$@"
25
+do
26
+case $i in
27
+    -c=*|--config=*)
28
+    CONFIG_VOLUME="${i#*=}"
29
+    shift
30
+    ;;
31
+    -w=*|--work-dir=*)
32
+    WORKDIR_VOLUME="${i#*=}"
33
+    shift
34
+    ;;
35
+    *)
36
+    # unknown option
37
+    ;;
38
+esac
39
+done
40
+
41
+echo installing config scripts into "${WORKDIR_VOLUME}"
42
+mkdir -p "${WORKDIR_VOLUME}"
43
+cp /on-start.sh "${WORKDIR_VOLUME}"/
44
+cp /peer-finder "${WORKDIR_VOLUME}"/
45
+
46
+echo installing my-galera.cnf into "${CONFIG_VOLUME}"
47
+mkdir -p "${CONFIG_VOLUME}"
48
+chown -R mysql:mysql "${CONFIG_VOLUME}"
49
+cp /my-galera.cnf "${CONFIG_VOLUME}"/
0 50
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+[mysqld]
1
+user = mysql
2
+bind-address = 0.0.0.0
3
+wsrep_provider = /usr/lib/galera/libgalera_smm.so
4
+# TODO: is rsync the best option?
5
+wsrep_sst_method = rsync
6
+default_storage_engine = innodb
7
+binlog_format = row
8
+innodb_autoinc_lock_mode = 2
9
+innodb_flush_log_at_trx_commit = 0
10
+query_cache_size = 0
11
+query_cache_type = 0
12
+
13
+# By default every node is standalone
14
+wsrep_cluster_address=gcomm://
15
+wsrep_cluster_name=galera
16
+wsrep_node_address=127.0.0.1
17
+
18
+# TODO: Enable use privileges. This doesn't work
19
+# on mysql restart, for some reason after the SST
20
+# permissions are not setup correctly.
21
+skip-grant-tables
0 22
new file mode 100755
... ...
@@ -0,0 +1,52 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This script writes out a mysql galera config using a list of newline seperated
17
+# peer DNS names it accepts through stdin.
18
+
19
+# /etc/mysql is assumed to be a shared volume so we can modify my.cnf as required
20
+# to keep the config up to date, without wrapping mysqld in a custom pid1.
21
+# The config location is intentionally not /etc/mysql/my.cnf because the
22
+# standard base image clobbers that location.
23
+CFG=/etc/mysql/my-galera.cnf
24
+
25
+function join {
26
+    local IFS="$1"; shift; echo "$*";
27
+}
28
+
29
+HOSTNAME=$(hostname)
30
+# Parse out cluster name, formatted as: petset_name-index
31
+IFS='-' read -ra ADDR <<< "$(hostname)"
32
+CLUSTER_NAME="${ADDR[0]}"
33
+
34
+while read -ra LINE; do
35
+    if [[ "${LINE}" == *"${HOSTNAME}"* ]]; then
36
+        MY_NAME=$LINE
37
+    fi
38
+    PEERS=("${PEERS[@]}" $LINE)
39
+done
40
+
41
+if [ "${#PEERS[@]}" = 1 ]; then
42
+    WSREP_CLUSTER_ADDRESS=""
43
+else
44
+    WSREP_CLUSTER_ADDRESS=$(join , "${PEERS[@]}")
45
+fi
46
+sed -i -e "s|^wsrep_node_address=.*$|wsrep_node_address=${MY_NAME}|" ${CFG}
47
+sed -i -e "s|^wsrep_cluster_name=.*$|wsrep_cluster_name=${CLUSTER_NAME}|" ${CFG}
48
+sed -i -e "s|^wsrep_cluster_address=.*$|wsrep_cluster_address=gcomm://${WSREP_CLUSTER_ADDRESS}|" ${CFG}
49
+
50
+# don't need a restart, we're just writing the conf in case there's an
51
+# unexpected restart on the node.
0 52
new file mode 100644
... ...
@@ -0,0 +1,120 @@
0
+# A headless service to create DNS records
1
+apiVersion: v1
2
+kind: Service
3
+metadata:
4
+  annotations:
5
+    service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
6
+  name: galera
7
+  labels:
8
+    app: mysql
9
+spec:
10
+  ports:
11
+  - port: 3306
12
+    name: mysql
13
+  # *.galear.default.svc.cluster.local
14
+  clusterIP: None
15
+  selector:
16
+    app: mysql
17
+---
18
+apiVersion: apps/v1alpha1
19
+kind: PetSet
20
+metadata:
21
+  name: mysql
22
+spec:
23
+  serviceName: "galera"
24
+  replicas: 3
25
+  template:
26
+    metadata:
27
+      labels:
28
+        app: mysql
29
+      annotations:
30
+        pod.alpha.kubernetes.io/initialized: "true"
31
+        pod.alpha.kubernetes.io/init-containers: '[
32
+            {
33
+                "name": "install",
34
+                "image": "gcr.io/google_containers/galera-install:0.1",
35
+                "imagePullPolicy": "Always",
36
+                "args": ["--work-dir=/work-dir"],
37
+                "volumeMounts": [
38
+                    {
39
+                        "name": "workdir",
40
+                        "mountPath": "/work-dir"
41
+                    },
42
+                    {
43
+                        "name": "config",
44
+                        "mountPath": "/etc/mysql"
45
+                    }
46
+                ]
47
+            },
48
+            {
49
+                "name": "bootstrap",
50
+                "image": "debian:jessie",
51
+                "command": ["/work-dir/peer-finder"],
52
+                "args": ["-on-start=\"/work-dir/on-start.sh\"", "-service=galera"],
53
+                "env": [
54
+                  {
55
+                      "name": "POD_NAMESPACE",
56
+                      "valueFrom": {
57
+                          "fieldRef": {
58
+                              "apiVersion": "v1",
59
+                              "fieldPath": "metadata.namespace"
60
+                          }
61
+                      }
62
+                   }
63
+                ],
64
+                "volumeMounts": [
65
+                    {
66
+                        "name": "workdir",
67
+                        "mountPath": "/work-dir"
68
+                    },
69
+                    {
70
+                        "name": "config",
71
+                        "mountPath": "/etc/mysql"
72
+                    }
73
+                ]
74
+            }
75
+        ]'
76
+    spec:
77
+      containers:
78
+      - name: mysql
79
+        image: erkules/galera:basic
80
+        ports:
81
+        - containerPort: 3306
82
+          name: mysql
83
+        - containerPort: 4444
84
+          name: sst
85
+        - containerPort: 4567
86
+          name: replication
87
+        - containerPort: 4568
88
+          name: ist
89
+        args:
90
+        - --defaults-file=/etc/mysql/my-galera.cnf
91
+        - --user=root
92
+        readinessProbe:
93
+          exec:
94
+            command:
95
+            - sh
96
+            - -c
97
+            - "mysql -u root -e 'show databases;'"
98
+          initialDelaySeconds: 15
99
+          timeoutSeconds: 5
100
+        volumeMounts:
101
+        - name: datadir
102
+          mountPath: /var/lib/
103
+        - name: config
104
+          mountPath: /etc/mysql
105
+      volumes:
106
+      - name: config
107
+        emptyDir: {}
108
+      - name: workdir
109
+        emptyDir: {}
110
+  volumeClaimTemplates:
111
+  - metadata:
112
+      name: datadir
113
+      annotations:
114
+        volume.alpha.kubernetes.io/storage-class: anything
115
+    spec:
116
+      accessModes: [ "ReadWriteOnce" ]
117
+      resources:
118
+        requests:
119
+          storage: 10Gi
0 120
new file mode 100755
... ...
@@ -0,0 +1,21 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+kubectl exec mysql-0 -- mysql -u root -e "create database test;"
17
+kubectl exec mysql-1 -- mysql -u root -e "use test; create table pet (id int(10), name varchar(20));"
18
+kubectl exec mysql-1 -- mysql -u root -e "use test; insert into pet (id, name) values (1, \"galera\");"
19
+kubectl exec mysql-2 -- mysql -u root -e "use test; select * from pet;"
20
+
0 21
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+# TODO: get rid of bash dependency and switch to plain busybox.
15
+# The tar in busybox also doesn't seem to understand compression.
16
+FROM debian:jessie
17
+MAINTAINER Prashanth.B <beeps@google.com>
18
+
19
+RUN apt-get update && apt-get install -y mysql-client wget
20
+ADD mysql_healthz /mysql_healthz
21
+Entrypoint ["/mysql_healthz"]
0 22
new file mode 100644
... ...
@@ -0,0 +1,31 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+all: push
15
+
16
+# 0.0 shouldn't clobber any released builds
17
+TAG = 0.1
18
+PREFIX = gcr.io/google_containers/mysql-healthz
19
+
20
+server: main.go
21
+	CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o mysql_healthz ./main.go
22
+
23
+container: server
24
+	docker build -t $(PREFIX):$(TAG) .
25
+
26
+push: container
27
+	gcloud docker push $(PREFIX):$(TAG)
28
+
29
+clean:
30
+	rm -f mysql_healthz
0 31
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+# Mysql healthz
1
+
2
+This is a simple exec-over-http healthz probe for mysql. Mainly intended as a work-around for (#25456)[https://github.com/kubernetes/kubernetes/issues/25456].
0 3
new file mode 100644
... ...
@@ -0,0 +1,96 @@
0
+/*
1
+Copyright 2015 The Kubernetes Authors All rights reserved.
2
+
3
+Licensed under the Apache License, Version 2.0 (the "License");
4
+you may not use this file except in compliance with the License.
5
+You may obtain a copy of the License at
6
+
7
+    http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+Unless required by applicable law or agreed to in writing, software
10
+distributed under the License is distributed on an "AS IS" BASIS,
11
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+See the License for the specific language governing permissions and
13
+limitations under the License.
14
+*/
15
+
16
+// This is a simple health monitor for mysql. Only needed till #25456 is fixed.
17
+package main
18
+
19
+import (
20
+	"fmt"
21
+	"log"
22
+	"net/http"
23
+	"os"
24
+	osExec "os/exec"
25
+	"strings"
26
+
27
+	flag "github.com/spf13/pflag"
28
+)
29
+
30
+var (
31
+	flags   = flag.NewFlagSet("a http healthz server for mysql.", flag.ExitOnError)
32
+	port    = flags.Int("port", 8080, "port to use for healthz server.")
33
+	verbose = flags.Bool("verbose", false, "log verbose output?")
34
+	pass    = flags.String("password", "", "mysql password.")
35
+	host    = flags.String("host", "", "mysql host.")
36
+
37
+	mysqlChecks = map[string]string{
38
+		"/healthz": "show databases;",
39
+	}
40
+)
41
+
42
+type mysqlManager struct {
43
+	host, pass string
44
+}
45
+
46
+func (m *mysqlManager) exec(cmd string) ([]byte, error) {
47
+	var password string
48
+	if m.pass != "" {
49
+		password = fmt.Sprintf("-p %v", m.pass)
50
+	}
51
+	mysqlCmd := fmt.Sprintf("/usr/bin/mysql -u root %v -h %v -B -e '%v'", password, m.host, cmd)
52
+	return osExec.Command("sh", "-c", mysqlCmd).CombinedOutput()
53
+}
54
+
55
+func registerHandlers(verbose bool, m *mysqlManager) {
56
+	var str string
57
+	for endpoint, cmd := range mysqlChecks {
58
+		str += fmt.Sprintf("\t%v: %q\n", endpoint, cmd)
59
+		http.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
60
+			output, err := m.exec(cmd)
61
+			if err != nil {
62
+				w.WriteHeader(http.StatusServiceUnavailable)
63
+			} else {
64
+				w.WriteHeader(http.StatusOK)
65
+			}
66
+			if verbose {
67
+				log.Printf("Output of %v:\n%v\n", cmd, string(output))
68
+			}
69
+			w.Write(output)
70
+		})
71
+	}
72
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
73
+		w.WriteHeader(http.StatusOK)
74
+		w.Write([]byte(fmt.Sprintf("Available handlers:\n%v", str)))
75
+	})
76
+}
77
+
78
+func getHostnameOrDie() string {
79
+	output, err := osExec.Command("hostname").CombinedOutput()
80
+	if err != nil {
81
+		log.Fatalf("%v", err)
82
+	}
83
+	return strings.Trim(string(output), "\n")
84
+}
85
+
86
+func main() {
87
+	flags.Parse(os.Args)
88
+	hostname := *host
89
+	if hostname == "" {
90
+		hostname = getHostnameOrDie()
91
+	}
92
+	registerHandlers(*verbose, &mysqlManager{pass: *pass, host: hostname})
93
+	log.Printf("Starting mysql healthz server on port %v", *port)
94
+	log.Fatalf(fmt.Sprintf("%v", http.ListenAndServe(fmt.Sprintf(":%v", *port), nil)))
95
+}
0 96
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+apiVersion: v1
1
+kind: Pod
2
+metadata:
3
+  name: mysql
4
+  namespace: default
5
+spec:
6
+  containers:
7
+  - image: mysql:5.6
8
+    name: server
9
+    env:
10
+    - name: MYSQL_ALLOW_EMPTY_PASSWORD
11
+      value: "yes"
12
+    readinessProbe:
13
+      httpGet:
14
+        path: /healthz
15
+        port: 8080
16
+  - name: healthz
17
+    image: gcr.io/google_containers/mysql-healthz:1.0
18
+    imagePullPolicy: Always
0 19
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+FROM gcr.io/google_containers/ubuntu-slim:0.2
15
+MAINTAINER Prashanth.B <beeps@google.com>
16
+
17
+RUN apt-get update && apt-get install -y wget bash dnsutils
18
+ADD peer-finder /peer-finder
19
+ADD peer-finder.go /peer-finder.go
20
+
21
+EXPOSE 9376
22
+ENTRYPOINT ["/peer-finder"]
0 23
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+all: push
15
+
16
+TAG = 0.1
17
+PREFIX = gcr.io/google_containers/peer-finder
18
+
19
+server: peer-finder.go
20
+	CGO_ENABLED=0 go build -a -installsuffix cgo --ldflags '-w' ./peer-finder.go
21
+
22
+release: server
23
+	gsutil cp peer-finder gs://kubernetes-release/pets/peer-finder
24
+
25
+container: server
26
+	docker build -t $(PREFIX):$(TAG) .
27
+
28
+push: container
29
+	gcloud docker push $(PREFIX):$(TAG)
30
+
31
+clean:
32
+	rm -f peer-finder
0 33
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+# Peer finder
1
+
2
+This is a simple peer finder daemon that runs as pid 1 in a petset.
3
+It is expected to be a temporary solution till the main Kubernetes repo supports:
4
+1. Init containers to replace on-start scripts
5
+2. A notification delivery mechanism that allows external controllers to
6
+   declaratively execute on-change scripts in containers.
7
+
8
+Though we don't expect this container to always run as pid1, it will be
9
+necessary in some form. All it does is resolve DNS. Even when we get (2)
10
+the most natural way to update the input for the on-change script is through
11
+a sidecar that runs the peer-finder.
12
+
13
+
14
+
0 15
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+/*
1
+Copyright 2014 The Kubernetes Authors All rights reserved.
2
+
3
+Licensed under the Apache License, Version 2.0 (the "License");
4
+you may not use this file except in compliance with the License.
5
+You may obtain a copy of the License at
6
+
7
+    http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+Unless required by applicable law or agreed to in writing, software
10
+distributed under the License is distributed on an "AS IS" BASIS,
11
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+See the License for the specific language governing permissions and
13
+limitations under the License.
14
+*/
15
+
16
+// A small utility program to lookup hostnames of endpoints in a service.
17
+package main
18
+
19
+import (
20
+	"flag"
21
+	"fmt"
22
+	"log"
23
+	"net"
24
+	"os"
25
+	"os/exec"
26
+	"sort"
27
+	"strings"
28
+	"time"
29
+
30
+	"k8s.io/kubernetes/pkg/util/sets"
31
+)
32
+
33
+const (
34
+	svcLocalSuffix = "svc.cluster.local"
35
+	pollPeriod     = 1 * time.Second
36
+)
37
+
38
+var (
39
+	onChange  = flag.String("on-change", "", "Script to run on change, must accept a new line separated list of peers via stdin.")
40
+	onStart   = flag.String("on-start", "", "Script to run on start, must accept a new line separated list of peers via stdin.")
41
+	svc       = flag.String("service", "", "Governing service responsible for the DNS records of the domain this pod is in.")
42
+	namespace = flag.String("ns", "", "The namespace this pod is running in. If unspecified, the POD_NAMESPACE env var is used.")
43
+)
44
+
45
+func lookup(svcName string) (sets.String, error) {
46
+	endpoints := sets.NewString()
47
+	_, srvRecords, err := net.LookupSRV("", "", svcName)
48
+	if err != nil {
49
+		return endpoints, err
50
+	}
51
+	for _, srvRecord := range srvRecords {
52
+		// The SRV records ends in a "." for the root domain
53
+		ep := fmt.Sprintf("%v", srvRecord.Target[:len(srvRecord.Target)-1])
54
+		endpoints.Insert(ep)
55
+	}
56
+	return endpoints, nil
57
+}
58
+
59
+func shellOut(sendStdin, script string) {
60
+	log.Printf("execing: %v with stdin: %v", script, sendStdin)
61
+	// TODO: Switch to sending stdin from go
62
+	out, err := exec.Command("bash", "-c", fmt.Sprintf("echo -e '%v' | %v", sendStdin, script)).CombinedOutput()
63
+	if err != nil {
64
+		log.Fatalf("Failed to execute %v: %v, err: %v", script, string(out), err)
65
+	}
66
+	log.Print(string(out))
67
+}
68
+
69
+func main() {
70
+	flag.Parse()
71
+
72
+	ns := *namespace
73
+	if ns == "" {
74
+		ns = os.Getenv("POD_NAMESPACE")
75
+	}
76
+	if *svc == "" || ns == "" || (*onChange == "" && *onStart == "") {
77
+		log.Fatalf("Incomplete args, require -on-change and/or -on-start, -service and -ns or an env var for POD_NAMESPACE.")
78
+	}
79
+
80
+	hostname, err := os.Hostname()
81
+	if err != nil {
82
+		log.Fatalf("Failed to get hostname: %s", err)
83
+	}
84
+
85
+	myName := strings.Join([]string{hostname, *svc, ns, svcLocalSuffix}, ".")
86
+	script := *onStart
87
+	if script == "" {
88
+		script = *onChange
89
+		log.Printf("No on-start supplied, on-change %v will be applied on start.", script)
90
+	}
91
+	for newPeers, peers := sets.NewString(), sets.NewString(); script != ""; time.Sleep(pollPeriod) {
92
+		newPeers, err = lookup(*svc)
93
+		if err != nil {
94
+			log.Printf("%v", err)
95
+			continue
96
+		}
97
+		if newPeers.Equal(peers) || !newPeers.Has(myName) {
98
+			continue
99
+		}
100
+		peerList := newPeers.List()
101
+		sort.Strings(peerList)
102
+		log.Printf("Peer list updated\nwas %v\nnow %v", peers.List(), newPeers.List())
103
+		shellOut(strings.Join(peerList, "\n"), script)
104
+		peers = newPeers
105
+		script = *onChange
106
+	}
107
+	// TODO: Exit if there's no on-change?
108
+	log.Printf("Peer finder exiting")
109
+}
0 110
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+# Redis
1
+
2
+This example runs redis through a petset.
3
+
4
+## Master/slave
5
+
6
+### Bootstrap
7
+
8
+Create the yaml in this directory
9
+```
10
+$ kubectl create -f redis.yaml
11
+```
12
+
13
+can run the "test.sh" script in this directory.
14
+
15
+## TODO
16
+
17
+Expect cleaner solutions for the following as petset matures.
18
+
19
+* Scaling Up/down
20
+* Image Upgrade
21
+* Periodic maintenance
22
+* Sentinel failover
0 23
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+# TODO: get rid of bash dependency and switch to plain busybox.
15
+# The tar in busybox also doesn't seem to understand compression.
16
+FROM debian:jessie
17
+MAINTAINER Prashanth.B <beeps@google.com>
18
+
19
+# TODO: just use standard redis when there is one for 3.2.0.
20
+RUN apt-get update && apt-get install -y wget make gcc
21
+
22
+ADD on-start.sh /
23
+# See contrib/pets/peer-finder for details
24
+RUN wget -qO /peer-finder https://storage.googleapis.com/kubernetes-release/pets/peer-finder
25
+
26
+ADD install.sh /
27
+RUN chmod -c 755 /install.sh /on-start.sh /peer-finder
28
+Entrypoint ["/install.sh"]
0 29
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+all: push
15
+
16
+TAG = 0.1
17
+PREFIX = gcr.io/google_containers/redis-install
18
+
19
+container:
20
+	docker build -t $(PREFIX):$(TAG) .
21
+
22
+push: container
23
+	gcloud docker push $(PREFIX):$(TAG)
24
+
25
+clean:
26
+	docker rmi $(PREFIX):$(TAG)
0 27
new file mode 100755
... ...
@@ -0,0 +1,64 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This volume is assumed to exist and is shared with parent of the init
17
+# container. It contains the redis installation.
18
+INSTALL_VOLUME="/opt"
19
+
20
+# This volume is assumed to exist and is shared with the peer-finder
21
+# init container. It contains on-start/change configuration scripts.
22
+WORK_DIR="/work-dir"
23
+
24
+TEMP_DIR="/tmp"
25
+
26
+VERSION="3.2.0"
27
+
28
+for i in "$@"
29
+do
30
+case $i in
31
+    -v=*|--version=*)
32
+    VERSION="${i#*=}"
33
+    shift
34
+    ;;
35
+    -i=*|--install-into=*)
36
+    INSTALL_VOLUME="${i#*=}"
37
+    shift
38
+    ;;
39
+    -w=*|--work-dir=*)
40
+    WORK_DIR="${i#*=}"
41
+    shift
42
+    ;;
43
+    *)
44
+    # unknown option
45
+    ;;
46
+esac
47
+done
48
+
49
+echo installing config scripts into "${WORK_DIR}"
50
+mkdir -p "${WORK_DIR}"
51
+cp /on-start.sh "${WORK_DIR}"/
52
+cp /peer-finder "${WORK_DIR}"/
53
+
54
+echo installing redis-"${VERSION}" into "${INSTALL_VOLUME}"
55
+mkdir -p "${TEMP_DIR}" "${INSTALL_VOLUME}"/redis
56
+wget -q -O - http://download.redis.io/releases/redis-"${VERSION}".tar.gz | tar -xzf - -C "${TEMP_DIR}"
57
+
58
+cd "${TEMP_DIR}"/redis-"${VERSION}"/
59
+# Clean out existing deps, see https://github.com/antirez/redis/issues/722
60
+make distclean
61
+make install INSTALL_BIN="${INSTALL_VOLUME}"/redis
62
+cp "${TEMP_DIR}"/redis-"${VERSION}"/redis.conf ${INSTALL_VOLUME}/redis/redis.conf
63
+
0 64
new file mode 100755
... ...
@@ -0,0 +1,49 @@
0
+#!/bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+set -e
17
+
18
+CFG=/opt/redis/redis.conf
19
+HOSTNAME=$(hostname)
20
+DATADIR="/data"
21
+# Port on which redis listens for connections.
22
+PORT=6379
23
+
24
+# Ping everyone but ourself to see if there's a master. Only one pet starts at
25
+# a time, so if we don't see a master we can assume the position is ours.
26
+while read -ra LINE; do
27
+    if [[ "${LINE}" == *"${HOSTNAME}"* ]]; then
28
+        sed -i -e "s|^bind.*$|bind ${LINE}|" ${CFG}
29
+    elif [ "$(/opt/redis/redis-cli -h $LINE info | grep role | sed 's,\r$,,')" = "role:master" ]; then
30
+        # TODO: More restrictive regex?
31
+        sed -i -e "s|^.*slaveof.*$|slaveof ${LINE} ${PORT}|" ${CFG}
32
+    fi
33
+done
34
+
35
+# Set the data directory for append only log and snapshot files. This should
36
+# be a persistent volume for consistency.
37
+sed -i -e "s|^.*dir .*$|dir ${DATADIR}|" ${CFG}
38
+
39
+# The append only log is written for every SET operation. Without this setting,
40
+# redis just snapshots periodically which is only safe for a cache. This will
41
+# produce an appendonly.aof file in the configured data dir.
42
+sed -i -e "s|^appendonly .*$|appendonly yes|" ${CFG}
43
+
44
+# Every write triggers an fsync. Recommended default is "everysec", which
45
+# is only safe for AP applications.
46
+sed -i -e "s|^appendfsync .*$|appendfsync always|" ${CFG}
47
+
48
+
0 49
new file mode 100644
... ...
@@ -0,0 +1,115 @@
0
+# A headless service to create DNS records
1
+apiVersion: v1
2
+kind: Service
3
+metadata:
4
+  annotations:
5
+    service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
6
+  name: redis
7
+  labels:
8
+    app: redis
9
+spec:
10
+  ports:
11
+  - port: 6379
12
+    name: peer
13
+  # *.redis.default.svc.cluster.local
14
+  clusterIP: None
15
+  selector:
16
+    app: redis
17
+---
18
+apiVersion: apps/v1alpha1
19
+kind: PetSet
20
+metadata:
21
+  name: rd
22
+spec:
23
+  serviceName: "redis"
24
+  replicas: 3
25
+  template:
26
+    metadata:
27
+      labels:
28
+        app: redis
29
+      annotations:
30
+        pod.alpha.kubernetes.io/initialized: "true"
31
+        pod.alpha.kubernetes.io/init-containers: '[
32
+            {
33
+                "name": "install",
34
+                "image": "gcr.io/google_containers/redis-install:0.1",
35
+                "imagePullPolicy": "Always",
36
+                "args": ["--version=3.2.0", "--install-into=/opt", "--work-dir=/work-dir"],
37
+                "volumeMounts": [
38
+                    {
39
+                        "name": "opt",
40
+                        "mountPath": "/opt"
41
+                    },
42
+                    {
43
+                        "name": "workdir",
44
+                        "mountPath": "/work-dir"
45
+                    }
46
+                ]
47
+            },
48
+            {
49
+                "name": "bootstrap",
50
+                "image": "debian:jessie",
51
+                "command": ["/work-dir/peer-finder"],
52
+                "args": ["-on-start=\"/work-dir/on-start.sh\"", "-service=redis"],
53
+                "env": [
54
+                  {
55
+                      "name": "POD_NAMESPACE",
56
+                      "valueFrom": {
57
+                          "fieldRef": {
58
+                              "apiVersion": "v1",
59
+                              "fieldPath": "metadata.namespace"
60
+                          }
61
+                      }
62
+                   }
63
+                ],
64
+                "volumeMounts": [
65
+                    {
66
+                        "name": "opt",
67
+                        "mountPath": "/opt"
68
+                    },
69
+                    {
70
+                        "name": "workdir",
71
+                        "mountPath": "/work-dir"
72
+                    }
73
+                ]
74
+            }
75
+        ]'
76
+    spec:
77
+      containers:
78
+      - name: redis
79
+        image: debian:jessie
80
+        ports:
81
+        - containerPort: 6379
82
+          name: peer
83
+        command:
84
+        - /opt/redis/redis-server
85
+        args:
86
+        - /opt/redis/redis.conf
87
+        readinessProbe:
88
+          exec:
89
+            command:
90
+            - sh
91
+            - -c
92
+            - "/opt/redis/redis-cli -h $(hostname) ping"
93
+          initialDelaySeconds: 15
94
+          timeoutSeconds: 5
95
+        volumeMounts:
96
+        - name: datadir
97
+          mountPath: /data
98
+        - name: opt
99
+          mountPath: /opt
100
+      volumes:
101
+      - name: opt
102
+        emptyDir: {}
103
+      - name: workdir
104
+        emptyDir: {}
105
+  volumeClaimTemplates:
106
+  - metadata:
107
+      name: datadir
108
+      annotations:
109
+        volume.alpha.kubernetes.io/storage-class: anything
110
+    spec:
111
+      accessModes: [ "ReadWriteOnce" ]
112
+      resources:
113
+        requests:
114
+          storage: 20Gi
0 115
new file mode 100755
... ...
@@ -0,0 +1,19 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+kubectl exec rd-0 -- /opt/redis/redis-cli -h rd-0.redis SET replicated:test true
17
+kubectl exec rd-2 -- /opt/redis/redis-cli -h rd-2.redis GET replicated:test
18
+
0 19
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+# Zookeeper
1
+
2
+This example runs zookeeper through a petset.
3
+
4
+## Bootstrap
5
+
6
+Create the petset in this directory
7
+```
8
+$ kubetl create -f zookeeper.yaml
9
+```
10
+
11
+Once you have all 3 nodes in Running, you can run the "test.sh" script in this directory.
12
+
13
+## Failover
14
+
15
+You can test failover by killing the leader. Insert a key:
16
+```console
17
+$ kubectl exec zoo-0 -- /opt/zookeeper/bin/zkCli.sh create /foo bar;
18
+$ kubectl exec zoo-2 -- /opt/zookeeper/bin/zkCli.sh get /foo;
19
+
20
+Watch existing members:
21
+```console
22
+$ kubectl run --attach bbox --image=busybox --restart=Never -- sh -c 'while true; do for i in 0 1 2; do echo zoo-$i $(echo stats | nc zoo-$1.zk:2181 | grep Mode); sleep 1; done; done';
23
+zoo-2 Mode: follower
24
+zoo-0 Mode: follower
25
+zoo-1 Mode: leader
26
+zoo-2 Mode: follower
27
+```
28
+
29
+Delete pets and wait for the petset controller to bring the back up:
30
+```console
31
+$ kubectl delete po -l app=zk
32
+$ kubectl get po --watch-only
33
+NAME      READY     STATUS     RESTARTS   AGE
34
+zoo-0     0/1       Init:0/2   0          16s
35
+zoo-0     0/1       Init:0/2   0         21s
36
+zoo-0     0/1       PodInitializing   0         23s
37
+zoo-0     1/1       Running   0         41s
38
+zoo-1     0/1       Pending   0         0s
39
+zoo-1     0/1       Init:0/2   0         0s
40
+zoo-1     0/1       Init:0/2   0         14s
41
+zoo-1     0/1       PodInitializing   0         17s
42
+zoo-1     0/1       Running   0         18s
43
+zoo-2     0/1       Pending   0         0s
44
+zoo-2     0/1       Init:0/2   0         0s
45
+zoo-2     0/1       Init:0/2   0         12s
46
+zoo-2     0/1       Init:0/2   0         28s
47
+zoo-2     0/1       PodInitializing   0         31s
48
+zoo-2     0/1       Running   0         32s
49
+...
50
+
51
+zoo-0 Mode: follower
52
+zoo-1 Mode: leader
53
+zoo-2 Mode: follower
54
+```
55
+
56
+Check the previously inserted key:
57
+```console
58
+$ kubectl exec zoo-1 -- /opt/zookeeper/bin/zkCli.sh get /foo
59
+ionid = 0x354887858e80035, negotiated timeout = 30000
60
+
61
+WATCHER::
62
+
63
+WatchedEvent state:SyncConnected type:None path:null
64
+bar
65
+```
66
+
67
+## Scaling
68
+
69
+You can scale up by modifying the number of replicas on the PetSet.
70
+
71
+## Image Upgrade
72
+
73
+TODO: Add details
74
+
75
+## Maintenance
76
+
77
+TODO: Add details
78
+
79
+## Limitations
80
+* Both petset and init containers are in alpha
81
+* Look through the on-start and on-change scripts for TODOs
82
+* Doesn't support the addition of observers through the petset
83
+* Only supports storage options that have backends for persistent volume claims
84
+
0 85
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+# TODO: get rid of bash dependency and switch to plain busybox.
15
+# The tar in busybox also doesn't seem to understand compression.
16
+FROM debian:jessie
17
+MAINTAINER Prashanth.B <beeps@google.com>
18
+
19
+RUN apt-get update && apt-get install -y wget netcat
20
+
21
+ADD on-start.sh /
22
+ADD on-change.sh /
23
+# See contrib/pets/peer-finder for details
24
+RUN wget -qO /peer-finder https://storage.googleapis.com/kubernetes-release/pets/peer-finder
25
+
26
+ADD install.sh /
27
+RUN chmod -c 755 /install.sh /on-start.sh /on-change.sh /peer-finder
28
+Entrypoint ["/install.sh"]
0 29
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+# Copyright 2016 The Kubernetes Authors All rights reserved.
1
+#
2
+# Licensed under the Apache License, Version 2.0 (the "License");
3
+# you may not use this file except in compliance with the License.
4
+# You may obtain a copy of the License at
5
+#
6
+#     http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+# Unless required by applicable law or agreed to in writing, software
9
+# distributed under the License is distributed on an "AS IS" BASIS,
10
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+# See the License for the specific language governing permissions and
12
+# limitations under the License.
13
+
14
+all: push
15
+
16
+TAG = 0.1
17
+PREFIX = gcr.io/google_containers/zookeeper-install
18
+
19
+container:
20
+	docker build -t $(PREFIX):$(TAG) .
21
+
22
+push: container
23
+	gcloud docker push $(PREFIX):$(TAG)
24
+
25
+clean:
26
+	docker rmi $(PREFIX):$(TAG)
0 27
new file mode 100755
... ...
@@ -0,0 +1,76 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This volume is assumed to exist and is shared with parent of the init
17
+# container. It contains the zookeeper installation.
18
+INSTALL_VOLUME="/opt"
19
+
20
+# This volume is assumed to exist and is shared with the peer-finder
21
+# init container. It contains on-start/change configuration scripts.
22
+WORKDIR_VOLUME="/work-dir"
23
+
24
+# As of April-2016 is 3.4.8 is the latest stable, but versions 3.5.0 onward
25
+# allow dynamic reconfiguration.
26
+VERSION="3.5.0-alpha"
27
+
28
+for i in "$@"
29
+do
30
+case $i in
31
+    -v=*|--version=*)
32
+    VERSION="${i#*=}"
33
+    shift
34
+    ;;
35
+    -i=*|--install-into=*)
36
+    INSTALL_VOLUME="${i#*=}"
37
+    shift
38
+    ;;
39
+    -w=*|--work-dir=*)
40
+    WORKDIR_VOLUME="${i#*=}"
41
+    shift
42
+    ;;
43
+    *)
44
+    # unknown option
45
+    ;;
46
+esac
47
+done
48
+
49
+echo installing config scripts into "${WORKDIR_VOLUME}"
50
+mkdir -p "${WORKDIR_VOLUME}"
51
+cp /on-start.sh "${WORKDIR_VOLUME}"/
52
+cp /on-change.sh "${WORKDIR_VOLUME}"/
53
+cp /peer-finder "${WORKDIR_VOLUME}"/
54
+
55
+echo installing zookeeper-"${VERSION}" into "${INSTALL_VOLUME}"
56
+mkdir -p "${INSTALL_VOLUME}"
57
+wget -q -O - http://apache.mirrors.pair.com/zookeeper/zookeeper-"${VERSION}"/zookeeper-"${VERSION}".tar.gz | tar -xzf - -C "${INSTALL_VOLUME}"
58
+mv "${INSTALL_VOLUME}"/zookeeper-"${VERSION}" "${INSTALL_VOLUME}"/zookeeper
59
+cp "${INSTALL_VOLUME}"/zookeeper/conf/zoo_sample.cfg "${INSTALL_VOLUME}"/zookeeper/conf/zoo.cfg
60
+
61
+# TODO: Should dynamic config be tied to the version?
62
+IFS="." read -ra RELEASE <<< "${VERSION}"
63
+if [ $(expr "${RELEASE[1]}") -gt 4 ]; then
64
+    echo zookeeper-"${VERSION}" supports dynamic reconfiguration, enabling it
65
+    echo "standaloneEnabled=false" >> "${INSTALL_VOLUME}"/zookeeper/conf/zoo.cfg
66
+    echo "dynamicConfigFile="${INSTALL_VOLUME}"/zookeeper/conf/zoo.cfg.dynamic" >> "${INSTALL_VOLUME}"/zookeeper/conf/zoo.cfg
67
+fi
68
+
69
+# TODO: This is a hack, netcat is convenient to have in the zookeeper container
70
+# I want to avoid using a custom zookeeper image just for this. So copy it.
71
+NC=$(which nc)
72
+if [ "${NC}" != "" ]; then
73
+    echo copying nc into "${INSTALL_VOLUME}"
74
+    cp "${NC}" "${INSTALL_VOLUME}"
75
+fi
0 76
new file mode 100755
... ...
@@ -0,0 +1,49 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This script configures zookeeper cluster member ship for version of zookeeper
17
+# < 3.5.0. It should not be used with the on-start.sh script in this example.
18
+# As of April-2016 is 3.4.8 is the latest stable.
19
+
20
+CFG=/opt/zookeeper/conf/zoo.cfg
21
+CFG_BAK=/opt/zookeeper/conf/zoo.cfg.bak
22
+MY_ID=/tmp/zookeeper/myid
23
+
24
+# write myid
25
+IFS='-' read -ra ADDR <<< "$(hostname)"
26
+echo $(expr "1" + "${ADDR[1]}") > "${MY_ID}"
27
+
28
+# TODO: This is a dumb way to reconfigure zookeeper because it allows dynamic
29
+# reconfig, but it's simple.
30
+i=0
31
+echo "
32
+tickTime=2000
33
+initLimit=10
34
+syncLimit=5
35
+dataDir=/tmp/zookeeper
36
+clientPort=2181
37
+" > "${CFG_BAK}"
38
+
39
+while read -ra LINE; do
40
+    let i=i+1
41
+    echo "server.${i}=${LINE}:2888:3888" >> "${CFG_BAK}"
42
+done
43
+cp ${CFG_BAK} ${CFG}
44
+
45
+# TODO: Typically one needs to first add a new member as an "observer" then
46
+# promote it to "participant", but that requirement is relaxed if we never
47
+# start > 1 at a time.
48
+/opt/zookeeper/bin/zkServer.sh restart
0 49
new file mode 100755
... ...
@@ -0,0 +1,73 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+# This script configures zookeeper cluster member ship for version of zookeeper
17
+# >= 3.5.0. It should not be used with the on-change.sh script in this example.
18
+# As of April-2016 is 3.4.8 is the latest stable.
19
+
20
+# Both /opt and /tmp/zookeeper are assumed to be volumes shared with the parent.
21
+CFG=/opt/zookeeper/conf/zoo.cfg.dynamic
22
+CFG_BAK=/opt/zookeeper/conf/zoo.cfg.bak
23
+MY_ID_FILE=/tmp/zookeeper/myid
24
+HOSTNAME=$(hostname)
25
+
26
+while read -ra LINE; do
27
+    PEERS=("${PEERS[@]}" $LINE)
28
+done
29
+
30
+# Don't add the first member as an observer
31
+if [ ${#PEERS[@]} -eq 1 ]; then
32
+    # We need to write our index in this list of servers into MY_ID_FILE.
33
+    # Note that this may not always coincide with the hostname id.
34
+    echo 1 > "${MY_ID_FILE}"
35
+    echo "server.1=${PEERS[0]}:2888:3888;2181" > "${CFG}"
36
+    # TODO: zkServer-initialize is the safe way to handle changes to datadir
37
+    # because simply starting will create a new datadir, BUT if the user changed
38
+    # pod template they might end up with 2 datadirs and brief split brain.
39
+    exit
40
+fi
41
+
42
+# Every subsequent member is added as an observer and promoted to a participant
43
+echo "" > "${CFG_BAK}"
44
+i=0
45
+for peer in "${PEERS[@]}"; do
46
+    let i=i+1
47
+    if [[ "${peer}" == *"${HOSTNAME}"* ]]; then
48
+      MY_ID=$i
49
+      MY_NAME=${peer}
50
+      echo $i > "${MY_ID_FILE}"
51
+      echo "server.${i}=${peer}:2888:3888:observer;2181" >> "${CFG_BAK}"
52
+    else
53
+      echo "server.${i}=${peer}:2888:3888:participant;2181" >> "${CFG_BAK}"
54
+    fi
55
+done
56
+
57
+# Once the dynamic config file is written it shouldn't be modified, so the final
58
+# reconfigure needs to happen through the "reconfig" command.
59
+cp ${CFG_BAK} ${CFG}
60
+
61
+# TODO: zkServer-initialize is the safe way to handle changes to datadir
62
+# because simply starting will create a new datadir, BUT if the user changed
63
+# pod template they might end up with 2 datadirs and brief split brain.
64
+/opt/zookeeper/bin/zkServer.sh start
65
+
66
+# TODO: We shouldn't need to specify the address of the master as long as
67
+# there's quorum. According to the docs the new server is just not allowed to
68
+# vote, it's still allowed to propose config changes, and it knows the
69
+# existing members of the ensemble from *its* config. This works as expected,
70
+# but we should correlate with more satisfying empirical evidence.
71
+/opt/zookeeper/bin/zkCli.sh reconfig -add "server.$MY_ID=$MY_NAME:2888:3888:participant;2181"
72
+/opt/zookeeper/bin/zkServer.sh stop
0 73
new file mode 100755
... ...
@@ -0,0 +1,19 @@
0
+#! /bin/bash
1
+
2
+# Copyright 2016 The Kubernetes Authors All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+#     http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+kubectl exec zoo-0 -- /opt/zookeeper/bin/zkCli.sh create /foo bar;
17
+kubectl exec zoo-2 -- /opt/zookeeper/bin/zkCli.sh get /foo;
18
+
0 19
new file mode 100644
... ...
@@ -0,0 +1,123 @@
0
+# A headless service to create DNS records
1
+apiVersion: v1
2
+kind: Service
3
+metadata:
4
+  annotations:
5
+    service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
6
+  name: zk
7
+  labels:
8
+    app: zk
9
+spec:
10
+  ports:
11
+  - port: 2888
12
+    name: peer
13
+  - port: 3888
14
+    name: leader-election
15
+  # *.zk.default.svc.cluster.local
16
+  clusterIP: None
17
+  selector:
18
+    app: zk
19
+---
20
+apiVersion: apps/v1alpha1
21
+kind: PetSet
22
+metadata:
23
+  name: zoo
24
+spec:
25
+  serviceName: "zk"
26
+  replicas: 3
27
+  template:
28
+    metadata:
29
+      labels:
30
+        app: zk
31
+      annotations:
32
+        pod.alpha.kubernetes.io/initialized: "true"
33
+        pod.alpha.kubernetes.io/init-containers: '[
34
+            {
35
+                "name": "install",
36
+                "image": "gcr.io/google_containers/zookeeper-install:0.1",
37
+                "imagePullPolicy": "Always",
38
+                "args": ["--version=3.5.0-alpha", "--install-into=/opt", "--work-dir=/work-dir"],
39
+                "volumeMounts": [
40
+                    {
41
+                        "name": "opt",
42
+                        "mountPath": "/opt/"
43
+                    },
44
+                    {
45
+                        "name": "workdir",
46
+                        "mountPath": "/work-dir"
47
+                    }
48
+                ]
49
+            },
50
+            {
51
+                "name": "bootstrap",
52
+                "image": "java:openjdk-8-jre",
53
+                "command": ["/work-dir/peer-finder"],
54
+                "args": ["-on-start=\"/work-dir/on-start.sh\"", "-service=zk"],
55
+                "env": [
56
+                  {
57
+                      "name": "POD_NAMESPACE",
58
+                      "valueFrom": {
59
+                          "fieldRef": {
60
+                              "apiVersion": "v1",
61
+                              "fieldPath": "metadata.namespace"
62
+                          }
63
+                      }
64
+                   }
65
+                ],
66
+                "volumeMounts": [
67
+                    {
68
+                        "name": "opt",
69
+                        "mountPath": "/opt/"
70
+                    },
71
+                    {
72
+                        "name": "workdir",
73
+                        "mountPath": "/work-dir"
74
+                    },
75
+                    {
76
+                        "name": "datadir",
77
+                        "mountPath": "/tmp/zookeeper"
78
+                    }
79
+                ]
80
+            }
81
+        ]'
82
+    spec:
83
+      containers:
84
+      - name: zk
85
+        image: java:openjdk-8-jre
86
+        ports:
87
+        - containerPort: 2888
88
+          name: peer
89
+        - containerPort: 3888
90
+          name: leader-election
91
+        command:
92
+        - /opt/zookeeper/bin/zkServer.sh
93
+        args:
94
+        - start-foreground
95
+        readinessProbe:
96
+          exec:
97
+            command:
98
+            - sh
99
+            - -c
100
+            - "/opt/zookeeper/bin/zkCli.sh ls /"
101
+          initialDelaySeconds: 15
102
+          timeoutSeconds: 5
103
+        volumeMounts:
104
+        - name: datadir
105
+          mountPath: /tmp/zookeeper
106
+        - name: opt
107
+          mountPath: /opt/
108
+      volumes:
109
+      - name: opt
110
+        emptyDir: {}
111
+      - name: workdir
112
+        emptyDir: {}
113
+  volumeClaimTemplates:
114
+  - metadata:
115
+      name: datadir
116
+      annotations:
117
+        volume.alpha.kubernetes.io/storage-class: anything
118
+    spec:
119
+      accessModes: [ "ReadWriteOnce" ]
120
+      resources:
121
+        requests:
122
+          storage: 20Gi