This commit is contained in:
Chad Moon 2017-04-04 02:00:50 -06:00
Родитель 1fcf8da496
Коммит 6e9c0c0ce3
34 изменённых файлов: 1836 добавлений и 119 удалений

1
Jenkinsfile поставляемый
Просмотреть файл

@ -23,4 +23,5 @@ node {
stage "Deploy"
sh "sed 's#127.0.0.1:30400/hello-kenzan:latest#'$BUILDIMG'#' applications/hello-kenzan/k8s/deployment.yaml | kubectl apply -f -"
sh "kubectl rollout status deployment/hello-kenzan"
}

117
README.md
Просмотреть файл

@ -13,124 +13,19 @@ Begin the tutorial `npm start`
## Manual tutorial version
## Part 1
## Part 4
### Part 1
### Part 4
### Step1
Start up the cluster with minikibe
Bootstrap etcd operator on the cluster
`minikube start --memory 6000 --cpus 2 --kubernetes-version v1.6.0`
`scripts/etcd.sh`
### Step2
Enable addons
Setup etcd directory for kubescale
`minikube addons enable heapster; minikube addons enable ingress`
### Step3
Wait 20 seconds and view minikube dashboard
`sleep 20; minikube service kubernetes-dashboard --namespace kube-system`
### Step4
Deploy the public nginx image from DockerHub
`kubectl run nginx --image nginx --port 80`
### Step5
Create a service for deployment
`kubectl expose deployment nginx --type NodePort --port 80`
### Step6
Launch browser to test service
`minikube service nginx`
### Step7
Install registry
`kubectl apply -f manifests/registry.yml`
### Step8
Wait for registry to deploy
`kubectl rollout status deployments/registry`
### Step9
View registry UI
`minikube service registry-ui`
### Step10
Edit the contents of applications/hello-kenzan/index.html
`nano applications/hello-kenzan/index.html`
### Step11
We will now build the image with a special name that is pointing at our cluster registry.
`docker build -t 127.0.0.1:30400/hello-kenzan:latest -f applications/hello-kenzan/Dockerfile applications/hello-kenzan`
### Step12
Before we can push our image we need to set up a temporary proxy. This is a container that listens on 127.0.0.1:30400 and forwads to our cluster. By default the docker client can only push to non https via localhost.
`docker run -d -e "REGIP=`minikube ip`" --name socat-registry -p 30400:5000 chadmoon/socat:latest bash -c "socat TCP4-LISTEN:5000,fork,reuseaddr TCP4:`minikube ip`:30400"`
### Step13
We can now push our image.
`docker push 127.0.0.1:30400/hello-kenzan:latest`
### Step14
Stop the registry proxy
`docker stop socat-registry; docker rm socat-registry`
### Step15
Now that our image is on the cluster we can deploy the manifests
`kubectl apply -f applications/hello-kenzan/k8s/deployment.yaml`
### Step16
View the app
`minikube service hello-kenzan`## Part 2
### Part 2
### Step1
Install Jenkins
`kubectl apply -f manifests/jenkins.yml; kubectl rollout status deployment/jenkins`
### Step2
Get Jenkins admin password
`kubectl exec -it `kubectl get pods --selector=app=jenkins --output=jsonpath={.items..metadata.name}` cat /root/.jenkins/secrets/initialAdminPassword`
### Step3
Configure Jenkins, default settings. Create a new job with type pipeline. Choose "Jenkinsfile from SCM" with target repo as https://github.com/kenzanlabs/kubernetes-ci-cd.git. Run the job.
`minikube service jenkins`
`scripts/kubescale.sh`

Просмотреть файл

@ -0,0 +1,23 @@
FROM node:6
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN apt-get update
RUN apt-get install -y apache2
RUN npm install -g loadtest
RUN npm install -g nodemon
RUN npm install
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
RUN chmod +x ./kubectl; mv ./kubectl /usr/local/bin/kubectl
CMD ["nodemon", "index.js"]

Просмотреть файл

@ -0,0 +1,95 @@
var express = require('express')
var app = express()
var http = require('http').Server(app);
var io = require('socket.io')(http);
var path = require("path");
var Etcd = require('node-etcd')
app.use(express.static('public'))
var exec = require('child_process').exec;
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//etcd = new Etcd("http://localhost:2379")
///tmp/test-etcd/etcdctl --endpoints "http://example-etcd-cluster-client-service:2379" mkdir pod-list
etcd = new Etcd("http://example-etcd-cluster-client-service:2379")
//etcd.mkdir("pods");
watcher = etcd.watcher("pod-list", null, {recursive: true})
watcher.on("change", showVal);
function showVal(val) {
pods = etcd.getSync("pod-list",{ recursive: true })
io.emit('pods', { pods: pods.body.node.nodes });
}
app.post('/scale', function (req, res) {
exec('kubectl scale --replicas=' + req.body.count + ' deployment/set', function(error, stdout, stderr) {
res.send("scaled to " + req.body.count);
});
})
app.post('/loadtest/concurrent', function (req, res) {
//svc = "http://localhost:8001/api/v1/proxy/namespaces/default/services/set:80"
svc = "http://set:80/"
// exec('loadtest -c ' + req.body.count + ' -n ' + req.body.count + ' http://set', function(error, stdout, stderr) {
exec('ab -c ' + req.body.count + ' -n ' + req.body.count + ' ' + svc, function(error, stdout, stderr) {
console.log(stdout);
res.send(stdout);
});
})
app.post('/loadtest/consecutive', function (req, res) {
svc = "http://set:80/"
// exec('loadtest -c ' + req.body.count + ' -n ' + req.body.count + ' http://set', function(error, stdout, stderr) {
exec('ab -c 1 -n ' + req.body.count + ' ' + svc, function(error, stdout, stderr) {
console.log(stdout);
res.send(stdout);
});
})
app.get('/up/:podId', function (req, res) {
etcd.set("pod-list/" + req.params.podId, req.params.podId);
res.send('done');
})
app.get('/down/:podId', function (req, res) {
etcd.del("pod-list/" + req.params.podId, req.params.podId);
res.send('done');
})
app.get('/hit/:podId', function (req, res) {
var d = new Date();
var n = d.getTime();
io.emit('hit', { podId: req.params.podId, time: n });
console.log('hit!');
res.send('done')
})
io.on('connection', function(socket){
pods = etcd.getSync("pod-list",{ recursive: true })
io.emit('pods', { pods: pods.body.node.nodes });
});
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/public/index.html'));
});
http.listen(3000, function () {
console.log('Example app listening on port 3000!')
})

Просмотреть файл

@ -0,0 +1,15 @@
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kube-system
spec:
rules:
- host: dashboard.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80

Просмотреть файл

@ -0,0 +1,46 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-services
namespace: kube-system
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: dashboard.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: grafana.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: monitoring-grafana
servicePort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-services
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: kubescale.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubescale
servicePort: 3000

Просмотреть файл

@ -0,0 +1,96 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: jenkins
labels:
type: local
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/jenkins/"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
labels:
app: jenkins
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: jenkins
tier: jenkins
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins
labels:
app: jenkins
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: jenkins
tier: jenkins
spec:
containers:
- image: chadmoon/jenkins-docker-kubectl:latest
name: jenkins
securityContext:
privileged: true
ports:
- containerPort: 8080
name: jenkins
volumeMounts:
- name: jenkins-persistent-storage
mountPath: /root/.jenkins
- name: docker
mountPath: /var/run/docker.sock
volumes:
- name: docker
hostPath:
path: /var/run/docker.sock
- name: jenkins-persistent-storage
persistentVolumeClaim:
claimName: jenkins-claim
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: jenkins-services
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: jenkins.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 8080

Просмотреть файл

@ -0,0 +1,38 @@
apiVersion: v1
kind: Service
metadata:
name: kubescale
labels:
app: kubescale
spec:
ports:
- port: 3000
targetPort: 3000
selector:
app: kubescale
tier: kubescale
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kubescale
labels:
app: kubescale
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: kubescale
tier: kubescale
spec:
containers:
- image: 127.0.0.1:30400/kubescale:latest
imagePullPolicy: Always
name: kubescale
ports:
- containerPort: 3000
name: kubescale

Просмотреть файл

@ -0,0 +1,142 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: registry
labels:
type: local
spec:
capacity:
storage: 4Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/registry/"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: registry-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
---
apiVersion: v1
kind: Service
metadata:
name: registry
labels:
app: registry
spec:
ports:
- port: 5000
targetPort: 5000
nodePort: 30400
name: registry
selector:
app: registry
tier: registry
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: registry
labels:
app: registry
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: registry
tier: registry
spec:
containers:
- image: registry:2
name: registry
volumeMounts:
- name: docker
mountPath: /var/run/docker.sock
- name: registry-persistent-storage
mountPath: /var/lib/registry
ports:
- containerPort: 5000
name: registry
- name: registryui
image: hyper/docker-registry-web
ports:
- containerPort: 8080
env:
- name: REGISTRY_URL
value: http://localhost:5000/v2
- name: REGISTRY_NAME
value: cluster-registry
volumes:
- name: docker
hostPath:
path: /var/run/docker.sock
- name: registry-persistent-storage
persistentVolumeClaim:
claimName: registry-claim
# ---
# apiVersion: extensions/v1beta1
# kind: Deployment
# metadata:
# name: registryui
# spec:
# replicas: 1
# template:
# metadata:
# labels:
# app: registryui
# spec:
# containers:
# - name: registryui
# image: hyper/docker-registry-web
# ports:
# - containerPort: 8080
# env:
# - name: REGISTRY_URL
# value: http://registry:5000/v2
# - name: REGISTRY_NAME
# value: cluster-registry
# ---
# apiVersion: v1
# kind: Service
# metadata:
# name: registryui
# labels:
# app: registryui
# spec:
# ports:
# - port: 8080
# targetPort: 8080
# name: registry
# selector:
# app: registryui
# tier: registryui
# type: NodePort
# ---
# apiVersion: extensions/v1beta1
# kind: Ingress
# metadata:
# name: registryui-ingress
# spec:
# rules:
# - host: registry.192.168.64.3.xip.io
# http:
# paths:
# - path: /
# backend:
# serviceName: registryui
# servicePort: 8080

Просмотреть файл

@ -0,0 +1,43 @@
apiVersion: v1
kind: Service
metadata:
name: set
labels:
app: set
spec:
ports:
- port: 80
targetPort: 80
selector:
app: set
tier: set
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: set
labels:
app: set
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: set
tier: set
spec:
containers:
- name: lifecycle-demo-container
image: 127.0.0.1:30400/set:latest
lifecycle:
postStart:
exec:
command: ["/up.sh"]
preStop:
exec:
command: ["/down.sh"]

Просмотреть файл

@ -0,0 +1,35 @@
apiVersion: v1
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress-controller
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
terminationGracePeriodSeconds: 60
hostNetwork: true
containers:
- image: traefik
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
hostPort: 80
- name: admin
containerPort: 8081
args:
- -d
- --web
- --web.address=:8081
- --kubernetes

18
applications/kubescale/ksh Executable file
Просмотреть файл

@ -0,0 +1,18 @@
#!/bin/sh
if [ "$1" = "" ]; then
echo "Usage: ksh <pod> [flags_to_kubectl]"
exit 1
fi
POD=$1
shift
COLUMNS=`tput cols`
LINES=`tput lines`
TERM=xterm
KUBE_SHELL=${KUBE_SHELL:-bash}
kubectl exec -i -t $POD "$@" -- env COLUMNS=$COLUMNS LINES=$LINES TERM=$TERM "$KUBE_SHELL"
#$ ./ksh pod_name
#$ ./ksh pod_name --namespace=kube-system
#$ KUBE_SHELL=sh ./ksh pod_name

Просмотреть файл

@ -0,0 +1,29 @@
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"@blueprintjs/core": "^1.12.0",
"body-parser": "^1.17.1",
"classnames": "^2.2.5",
"express": "^4.15.2",
"express-ws": "^3.0.0",
"node-etcd": "^5.0.3",
"node-serialize": "0.0.4",
"nodejs-etcd": "^0.1.1",
"react": "^15.4.2",
"react-addons-css-transition-group": "^15.4.2",
"react-dom": "^15.4.2",
"socket.io": "^1.7.3",
"tether": "^1.4.0",
"ws": "^2.2.1"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC"
}

Просмотреть файл

@ -0,0 +1,232 @@
<!DOCTYPE HTML>
<html>
<head>
<link href="https://unpkg.com/normalize.css@^4.1.1" rel="stylesheet" />
<link href="https://unpkg.com/@blueprintjs/core@^1.11.0/dist/blueprint.css" rel="stylesheet" />
<meta charset="utf-8">
<title>Chat example</title>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="http://code.jquery.com/color/jquery.color-2.1.2.min.js"></script>
<style>
.card{
width: 250px;
margin: 5px;
float: left;
}
</style>
<script>
var socket = io();
var firstRun = true;
socket.on('hit', function(msg){
console.log("hit!" + msg.podId)
pulse(msg.podId);
});
socket.on('pods', function(msg){
console.log(msg);
setInterval(function(){
if ($("#current").val() != $("#count").val()){
$("#progress").show();
}else{
$("#progress").hide();
}
});
$("#pods").html('');
$("#current").val(msg.pods.length)
if(firstRun){
$("#count").val(msg.pods.length)
firstRun = false;
}
msg.pods.forEach(function(pod) {
$("#pods").append('<div style="position:relative;" class="' + pod.value + ' card pt-card pt-elevation-2">' + pod.value + '</div>')
});
})
function down(){
$("#count").val(($("#count").val()*1) - 1);
scale();
}
function up(){
$("#count").val(($("#count").val()*1) + 1);
scale();
}
function scale() {
$.post( "/scale", { count: $("#count").val() } );
$("#progress").show();
}
function loadTest() {
$.post( "/loadtest/" + $("#loadType").val() , { count: $("#loadTest").val() } );
//$("#progress").show();
//pulse("set-1165364669-d0lsf")
}
function pulse(podId) {
//$('.pods').removeClass('pulse')
// $('.' + podId + ' .star').fadeIn( 300, function() {
// $('.' + podId + ' .star').fadeOut( 300)
// });
//$('.' + podId + ' .star').show().fadeOut(500)
//$('.' + podId).slideUp(200).slideDown(200);
//var el = $('<div class="star" style="top:5px;right:5px;font-size:20px;color:#3DCC91"></div>');
$('.' + podId).animate( { backgroundColor: "#3DCC91" }, 500 ).animate( { backgroundColor: "#ffffff" }, 500 ).delay(1000);
//el.fadeOut(600)
//$('.' + podId).append()
//$('.' + podId + ' .star').fadeIn(300).fadeOut(300)
//$('.' + podId).append("<div></div>")
// $('.' + podId).css("backgroundColor", '#3DCC91');
// $('.' + podId).animate({backgroundColor: '#ffffff'}, 500);
}
</script>
</head>
<body>
<nav class="pt-navbar pt-dark">
<div class="pt-navbar-group pt-align-left">
<div class="pt-navbar-heading">KubeScale</div>
<div>
</div>
</div>
<div class="pt-navbar-group pt-align-right">
Current Replicas: &nbsp; <div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input size="3" id="current" type="text" class="pt-input" readonly style="padding-right: 0px;"></div></div>
<span class="pt-navbar-divider"></span>
Desired Replicas: &nbsp; <div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input size="3" type="text" onChange="scale()" id="count" class="pt-input" style="padding-right: 0px;"></div><div class="pt-button-group pt-vertical pt-fixed"><button onClick="up()" type="button" class="pt-button pt-icon-chevron-up"></button><button onClick="down()" type="button" class="pt-button pt-icon-chevron-down"></button></div></div>
<span class="pt-navbar-divider"></span>
<!--
<div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input id="loadTest" style="width:40px" type="text" class="pt-input" style="padding-right: 0px;"></div><div class="pt-button-group pt-vertical pt-fixed"><button type="button" class="pt-button pt-icon-chevron-up"></button><button type="button" class="pt-button pt-icon-chevron-down"></button></div></div>
-->
# of Requests: &nbsp;
<div class="pt-control-group">
<div class="pt-input-group">
<input type="text" size="3" id="loadTest" class="pt-input" />
</div>
<div class="pt-input-group">
<div class="pt-select">
<select id="loadType">
<option selected value="concurrent">Concurrent</option>
<option value="consecutive">Consecutive</option>
</select>
</div>
</div>
<button type="button" onClick="loadTest()" class="pt-button pt-intent-success">Load Test</button>
</div>
<div class="pt-input-group">
</div>
&nbsp;&nbsp;
</div>
</nav>
<div style="height:5px;">
<div id="progress" class="pt-progress-bar pt-intent-primary">
<div class="pt-progress-meter" style="width: 100%"></div>
</div>
</div>
<div id="pods"></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,17 @@
FROM node:6
RUN apt-get update
RUN apt-get install -y curl
COPY . /app
WORKDIR /app
COPY up.sh /up.sh
COPY down.sh /down.sh
RUN npm install
RUN mpm install -g nodemon
CMD ["nodemon", "server.js"]

Просмотреть файл

@ -0,0 +1,3 @@
#!/usr/bin/env bash
HOSTNAME=`hostname`
curl "http://kubescale:3000/down/$HOSTNAME"

Просмотреть файл

@ -0,0 +1,16 @@
{
"name": "set",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.15.2",
"sleep": "^5.1.0"
}
}

Просмотреть файл

@ -0,0 +1,23 @@
var express = require('express')
var app = express()
var sleep = require('sleep')
var exec = require('child_process').exec;
app.get('/', function (req, res) {
exec('export HOSTNAME=`hostname`; curl http://kubescale:3000/hit/$HOSTNAME', function(error, stdout, stderr) {
sleep.sleep(1);
res.send("hit");
});
})
app.listen(80, function () {
console.log('Example app listening on port 80!')
})

Просмотреть файл

@ -0,0 +1,3 @@
#!/usr/bin/env bash
HOSTNAME=`hostname`
curl "http://kubescale:3000/up/$HOSTNAME"

Просмотреть файл

@ -0,0 +1,23 @@
FROM node:6
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN apt-get update
RUN apt-get install -y apache2
RUN npm install -g loadtest
RUN npm install -g nodemon
RUN npm install
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
RUN chmod +x ./kubectl; mv ./kubectl /usr/local/bin/kubectl
CMD ["nodemon", "index.js"]

Просмотреть файл

@ -0,0 +1,95 @@
var express = require('express')
var app = express()
var http = require('http').Server(app);
var io = require('socket.io')(http);
var path = require("path");
var Etcd = require('node-etcd')
app.use(express.static('public'))
var exec = require('child_process').exec;
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//etcd = new Etcd("http://localhost:2379")
///tmp/test-etcd/etcdctl --endpoints "http://example-etcd-cluster-client-service:2379" mkdir pod-list
etcd = new Etcd("http://example-etcd-cluster-client-service:2379")
//etcd.mkdir("pods");
watcher = etcd.watcher("pod-list", null, {recursive: true})
watcher.on("change", showVal);
function showVal(val) {
pods = etcd.getSync("pod-list",{ recursive: true })
io.emit('pods', { pods: pods.body.node.nodes });
}
app.post('/scale', function (req, res) {
exec('kubectl scale --replicas=' + req.body.count + ' deployment/set', function(error, stdout, stderr) {
res.send("scaled to " + req.body.count);
});
})
app.post('/loadtest/concurrent', function (req, res) {
//svc = "http://localhost:8001/api/v1/proxy/namespaces/default/services/set:80"
svc = "http://set:80/"
// exec('loadtest -c ' + req.body.count + ' -n ' + req.body.count + ' http://set', function(error, stdout, stderr) {
exec('ab -c ' + req.body.count + ' -n ' + req.body.count + ' ' + svc, function(error, stdout, stderr) {
console.log(stdout);
res.send(stdout);
});
})
app.post('/loadtest/consecutive', function (req, res) {
svc = "http://set:80/"
// exec('loadtest -c ' + req.body.count + ' -n ' + req.body.count + ' http://set', function(error, stdout, stderr) {
exec('ab -c 1 -n ' + req.body.count + ' ' + svc, function(error, stdout, stderr) {
console.log(stdout);
res.send(stdout);
});
})
app.get('/up/:podId', function (req, res) {
etcd.set("pod-list/" + req.params.podId, req.params.podId);
res.send('done');
})
app.get('/down/:podId', function (req, res) {
etcd.del("pod-list/" + req.params.podId, req.params.podId);
res.send('done');
})
app.get('/hit/:podId', function (req, res) {
var d = new Date();
var n = d.getTime();
io.emit('hit', { podId: req.params.podId, time: n });
console.log('hit!');
res.send('done')
})
io.on('connection', function(socket){
pods = etcd.getSync("pod-list",{ recursive: true })
io.emit('pods', { pods: pods.body.node.nodes });
});
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/public/index.html'));
});
http.listen(3000, function () {
console.log('Example app listening on port 3000!')
})

Просмотреть файл

@ -0,0 +1,15 @@
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kube-system
spec:
rules:
- host: dashboard.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80

Просмотреть файл

@ -0,0 +1,46 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-services
namespace: kube-system
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: dashboard.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: grafana.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: monitoring-grafana
servicePort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-services
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: kubescale.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: kubescale
servicePort: 3000

Просмотреть файл

@ -0,0 +1,96 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: jenkins
labels:
type: local
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/jenkins/"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Service
metadata:
name: jenkins
labels:
app: jenkins
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: jenkins
tier: jenkins
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins
labels:
app: jenkins
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: jenkins
tier: jenkins
spec:
containers:
- image: chadmoon/jenkins-docker-kubectl:latest
name: jenkins
securityContext:
privileged: true
ports:
- containerPort: 8080
name: jenkins
volumeMounts:
- name: jenkins-persistent-storage
mountPath: /root/.jenkins
- name: docker
mountPath: /var/run/docker.sock
volumes:
- name: docker
hostPath:
path: /var/run/docker.sock
- name: jenkins-persistent-storage
persistentVolumeClaim:
claimName: jenkins-claim
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: jenkins-services
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: jenkins.127.0.0.1.xip.io
http:
paths:
- path: /
backend:
serviceName: jenkins
servicePort: 8080

Просмотреть файл

@ -0,0 +1,39 @@
apiVersion: v1
kind: Service
metadata:
name: kubescale
labels:
app: kubescale
spec:
ports:
- port: 3000
targetPort: 3000
nodePort: 31980
selector:
app: kubescale
tier: kubescale
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kubescale
labels:
app: kubescale
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: kubescale
tier: kubescale
spec:
containers:
- image: 127.0.0.1:30400/kubescale:latest
imagePullPolicy: Always
name: kubescale
ports:
- containerPort: 3000
name: kubescale

Просмотреть файл

@ -0,0 +1,142 @@
kind: PersistentVolume
apiVersion: v1
metadata:
name: registry
labels:
type: local
spec:
capacity:
storage: 4Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/registry/"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: registry-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
---
apiVersion: v1
kind: Service
metadata:
name: registry
labels:
app: registry
spec:
ports:
- port: 5000
targetPort: 5000
nodePort: 30400
name: registry
selector:
app: registry
tier: registry
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: registry
labels:
app: registry
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: registry
tier: registry
spec:
containers:
- image: registry:2
name: registry
volumeMounts:
- name: docker
mountPath: /var/run/docker.sock
- name: registry-persistent-storage
mountPath: /var/lib/registry
ports:
- containerPort: 5000
name: registry
- name: registryui
image: hyper/docker-registry-web
ports:
- containerPort: 8080
env:
- name: REGISTRY_URL
value: http://localhost:5000/v2
- name: REGISTRY_NAME
value: cluster-registry
volumes:
- name: docker
hostPath:
path: /var/run/docker.sock
- name: registry-persistent-storage
persistentVolumeClaim:
claimName: registry-claim
# ---
# apiVersion: extensions/v1beta1
# kind: Deployment
# metadata:
# name: registryui
# spec:
# replicas: 1
# template:
# metadata:
# labels:
# app: registryui
# spec:
# containers:
# - name: registryui
# image: hyper/docker-registry-web
# ports:
# - containerPort: 8080
# env:
# - name: REGISTRY_URL
# value: http://registry:5000/v2
# - name: REGISTRY_NAME
# value: cluster-registry
# ---
# apiVersion: v1
# kind: Service
# metadata:
# name: registryui
# labels:
# app: registryui
# spec:
# ports:
# - port: 8080
# targetPort: 8080
# name: registry
# selector:
# app: registryui
# tier: registryui
# type: NodePort
# ---
# apiVersion: extensions/v1beta1
# kind: Ingress
# metadata:
# name: registryui-ingress
# spec:
# rules:
# - host: registry.192.168.64.3.xip.io
# http:
# paths:
# - path: /
# backend:
# serviceName: registryui
# servicePort: 8080

Просмотреть файл

@ -0,0 +1,43 @@
apiVersion: v1
kind: Service
metadata:
name: set
labels:
app: set
spec:
ports:
- port: 80
targetPort: 80
selector:
app: set
tier: set
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: set
labels:
app: set
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: set
tier: set
spec:
containers:
- name: lifecycle-demo-container
image: 127.0.0.1:30400/set:latest
lifecycle:
postStart:
exec:
command: ["/up.sh"]
preStop:
exec:
command: ["/down.sh"]

Просмотреть файл

@ -0,0 +1,35 @@
apiVersion: v1
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress-controller
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
terminationGracePeriodSeconds: 60
hostNetwork: true
containers:
- image: traefik
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
hostPort: 80
- name: admin
containerPort: 8081
args:
- -d
- --web
- --web.address=:8081
- --kubernetes

18
manifests/kubescale/ksh Executable file
Просмотреть файл

@ -0,0 +1,18 @@
#!/bin/sh
if [ "$1" = "" ]; then
echo "Usage: ksh <pod> [flags_to_kubectl]"
exit 1
fi
POD=$1
shift
COLUMNS=`tput cols`
LINES=`tput lines`
TERM=xterm
KUBE_SHELL=${KUBE_SHELL:-bash}
kubectl exec -i -t $POD "$@" -- env COLUMNS=$COLUMNS LINES=$LINES TERM=$TERM "$KUBE_SHELL"
#$ ./ksh pod_name
#$ ./ksh pod_name --namespace=kube-system
#$ KUBE_SHELL=sh ./ksh pod_name

Просмотреть файл

@ -0,0 +1,29 @@
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"@blueprintjs/core": "^1.12.0",
"body-parser": "^1.17.1",
"classnames": "^2.2.5",
"express": "^4.15.2",
"express-ws": "^3.0.0",
"node-etcd": "^5.0.3",
"node-serialize": "0.0.4",
"nodejs-etcd": "^0.1.1",
"react": "^15.4.2",
"react-addons-css-transition-group": "^15.4.2",
"react-dom": "^15.4.2",
"socket.io": "^1.7.3",
"tether": "^1.4.0",
"ws": "^2.2.1"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC"
}

Просмотреть файл

@ -0,0 +1,232 @@
<!DOCTYPE HTML>
<html>
<head>
<link href="https://unpkg.com/normalize.css@^4.1.1" rel="stylesheet" />
<link href="https://unpkg.com/@blueprintjs/core@^1.11.0/dist/blueprint.css" rel="stylesheet" />
<meta charset="utf-8">
<title>Chat example</title>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="http://code.jquery.com/color/jquery.color-2.1.2.min.js"></script>
<style>
.card{
width: 250px;
margin: 5px;
float: left;
}
</style>
<script>
var socket = io();
var firstRun = true;
socket.on('hit', function(msg){
console.log("hit!" + msg.podId)
pulse(msg.podId);
});
socket.on('pods', function(msg){
console.log(msg);
setInterval(function(){
if ($("#current").val() != $("#count").val()){
$("#progress").show();
}else{
$("#progress").hide();
}
});
$("#pods").html('');
$("#current").val(msg.pods.length)
if(firstRun){
$("#count").val(msg.pods.length)
firstRun = false;
}
msg.pods.forEach(function(pod) {
$("#pods").append('<div style="position:relative;" class="' + pod.value + ' card pt-card pt-elevation-2">' + pod.value + '</div>')
});
})
function down(){
$("#count").val(($("#count").val()*1) - 1);
scale();
}
function up(){
$("#count").val(($("#count").val()*1) + 1);
scale();
}
function scale() {
$.post( "/scale", { count: $("#count").val() } );
$("#progress").show();
}
function loadTest() {
$.post( "/loadtest/" + $("#loadType").val() , { count: $("#loadTest").val() } );
//$("#progress").show();
//pulse("set-1165364669-d0lsf")
}
function pulse(podId) {
//$('.pods').removeClass('pulse')
// $('.' + podId + ' .star').fadeIn( 300, function() {
// $('.' + podId + ' .star').fadeOut( 300)
// });
//$('.' + podId + ' .star').show().fadeOut(500)
//$('.' + podId).slideUp(200).slideDown(200);
//var el = $('<div class="star" style="top:5px;right:5px;font-size:20px;color:#3DCC91"></div>');
$('.' + podId).animate( { backgroundColor: "#3DCC91" }, 500 ).animate( { backgroundColor: "#ffffff" }, 500 ).delay(1000);
//el.fadeOut(600)
//$('.' + podId).append()
//$('.' + podId + ' .star').fadeIn(300).fadeOut(300)
//$('.' + podId).append("<div></div>")
// $('.' + podId).css("backgroundColor", '#3DCC91');
// $('.' + podId).animate({backgroundColor: '#ffffff'}, 500);
}
</script>
</head>
<body>
<nav class="pt-navbar pt-dark">
<div class="pt-navbar-group pt-align-left">
<div class="pt-navbar-heading">KubeScale</div>
<div>
</div>
</div>
<div class="pt-navbar-group pt-align-right">
Current Replicas: &nbsp; <div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input size="3" id="current" type="text" class="pt-input" readonly style="padding-right: 0px;"></div></div>
<span class="pt-navbar-divider"></span>
Desired Replicas: &nbsp; <div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input size="3" type="text" onChange="scale()" id="count" class="pt-input" style="padding-right: 0px;"></div><div class="pt-button-group pt-vertical pt-fixed"><button onClick="up()" type="button" class="pt-button pt-icon-chevron-up"></button><button onClick="down()" type="button" class="pt-button pt-icon-chevron-down"></button></div></div>
<span class="pt-navbar-divider"></span>
<!--
<div class="pt-numeric-input pt-control-group"><div class="pt-input-group"><input id="loadTest" style="width:40px" type="text" class="pt-input" style="padding-right: 0px;"></div><div class="pt-button-group pt-vertical pt-fixed"><button type="button" class="pt-button pt-icon-chevron-up"></button><button type="button" class="pt-button pt-icon-chevron-down"></button></div></div>
-->
# of Requests: &nbsp;
<div class="pt-control-group">
<div class="pt-input-group">
<input type="text" size="3" id="loadTest" class="pt-input" />
</div>
<div class="pt-input-group">
<div class="pt-select">
<select id="loadType">
<option selected value="concurrent">Concurrent</option>
<option value="consecutive">Consecutive</option>
</select>
</div>
</div>
<button type="button" onClick="loadTest()" class="pt-button pt-intent-success">Load Test</button>
</div>
<div class="pt-input-group">
</div>
&nbsp;&nbsp;
</div>
</nav>
<div style="height:5px;">
<div id="progress" class="pt-progress-bar pt-intent-primary">
<div class="pt-progress-meter" style="width: 100%"></div>
</div>
</div>
<div id="pods"></div>
</body>
</html>

27
scripts/etcd.sh Executable file
Просмотреть файл

@ -0,0 +1,27 @@
#!/usr/bin/env bash
echo "installing etcd operator"
kubectl create -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/deployment.yaml
kubectl rollout status -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/deployment.yaml
until kubectl get thirdpartyresource cluster.etcd.coreos.com
do
echo "waiting for operator"
sleep 2
done
echo "pausing for 10 seconds for operator to settle"
sleep 10
kubectl create -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/example-etcd-cluster.yaml
echo "installing etcd cluster service"
kubectl create -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/example-etcd-cluster-nodeport-service.json
echo "waiting for etcd cluster to turnup"
until kubectl get pod example-etcd-cluster-0002
do
echo "waiting for etcd cluster to turnup"
sleep 2
done

73
scripts/kubescale.sh Executable file
Просмотреть файл

@ -0,0 +1,73 @@
#!/bin/bash
echo "creating pod-list etcd directory"
kubectl exec -it example-etcd-cluster-0000 apk update
kubectl exec -it example-etcd-cluster-0000 apk add ca-certificates
kubectl exec -it example-etcd-cluster-0000 apk update-ca-certificates
kubectl exec -it example-etcd-cluster-0000 apk add bash wget
kubectl exec -it example-etcd-cluster-0000 wget https://gist.githubusercontent.com/moondev/86ebfc39998049d3f0c10848f4c72c57/raw/62cb237a115d4884cec4c5d94751cf5586f44b4b/mkdir.sh
kubectl exec -it example-etcd-cluster-0000 chmod +x mkdir.sh
kubectl exec -it example-etcd-cluster-0000 /mkdir.sh
# echo "building kubescale image"
# TAG=latest
# docker build -t 127.0.0.1:30400/kubescale:$TAG -f
# # echo "building set image"
# cd set; docker build -t 127.0.0.1:30400/set:$TAG -f set/Dockerfile .
# echo "forwarding registry port"
# export MINIKUBEIP=`minikube ip`
# #temp container for forwarding to registry
# docker run -d -e "MINIKUBEIP=$MINIKUBEIP" --name socat-registry -p 30400:5000 chadmoon/socat:latest bash -c "socat TCP4-LISTEN:5000,fork,reuseaddr TCP4:$MINIKUBEIP:30400"
# sleep 5
# echo "pushing kubescale image"
# docker push 127.0.0.1:30400/kubescale:latest
# docker push 127.0.0.1:30400/set:latest
# echo "pushing set image"
# docker push 127.0.0.1:30400/set:latest
# sleep 2
# echo "killing port-forward"
# docker stop socat-registry
# docker rm socat-registry
# echo "deploying kubescale and set"
# kubectl apply -f k8s/kubescale.yml
# kubectl rollout status deployment/kubescale
# kubectl apply -f k8s/set.yml
# kubectl rollout status deployment/set
# #temp container for forwarding to registry
# docker stop socat-minikube
# docker rm socat-minikube
# proxy container for ingress
# docker run -d -e "MINIKUBEIP=$MINIKUBEIP" --name socat-minikube -p 80:80 chadmoon/socat:latest bash -c "socat TCP4-LISTEN:80,fork,reuseaddr TCP4:$MINIKUBEIP:80"
# # kubectl apply -f k8s/ing.yml
# kubectl apply -f k8s/jenkins.yml
# kubectl rollout status deployments/jenkins
# sleep 10
# open http://$MINIKUBEIP:31980 || true
# xdg-open http://$MINIKUBEIP:31980 || true

Просмотреть файл

@ -44,7 +44,7 @@ parts:
com: docker push 127.0.0.1:30400/hello-kenzan:latest
- cap: Stop the registry proxy.
com: docker stop socat-registry; docker rm socat-registry
com: docker stop socat-registry;
- cap: Now that our image is on the cluster we can deploy the manifests
com: kubectl apply -f applications/hello-kenzan/k8s/deployment.yaml
@ -62,14 +62,48 @@ parts:
- cap: Get Jenkins admin password
com: kubectl exec -it `kubectl get pods --selector=app=jenkins --output=jsonpath={.items..metadata.name}` cat /root/.jenkins/secrets/initialAdminPassword
- cap: Enter the admin password from above and choose "suggested plugins". Create a new job with type pipeline. Scroll down and under "pipeline script" choose "Pipeline script from SCM". Under SCM choose GIT. For "repository url" enter https://github.com/kenzanlabs/kubernetes-ci-cd.git. Save and run the job.
- cap: Enter the admin password from above and choose "suggested plugins". Create a new job with type pipeline. Scroll down and under "pipeline script" choose "Pipeline script from SCM". Under SCM choose GIT. Fork repo and put "repository url" as your fork, such as https://github.com/kenzanlabs/kubernetes-ci-cd.git. Save and run the job.
com: minikube service jenkins
# - name: Part 3
# intro: part 3 intro here
- cap: View updated application
com: minikube service hello-kenzan
# - name: Part 4
# intro: part 4 intro here
- cap: Push a change to your fork. Run job again. View changes
com: minikube service hello-kenzan
# - name: Part 5
# intro: part 5 intro here
- name: Part 4
intro: Kubescale
steps:
- cap: Bootstrap etcd operator on the cluster
com: scripts/etcd.sh
- cap: Setup etcd directory for kubescale
com: scripts/kubescale.sh
- cap: build kubescale image
com: docker build -t 127.0.0.1:30400/kubescale:latest -f applications/kubescale/Dockerfile applications/kubescale
- cap: build scaling image
com: docker build -t 127.0.0.1:30400/set:latest -f applications/kubescale/set/Dockerfile applications/kubescale/set
- cap: Start the registry proxy.
com: docker start socat-registry
- cap: Push the kubescale image
com: docker push 127.0.0.1:30400/kubescale:latest
- cap: Push the scaling image
com: docker push 127.0.0.1:30400/set:latest
- cap: Stop the registry proxy
com: docker stop socat-registry
- cap: Deploy kubescale
com: kubectl apply -f applications/kubescale/k8s/kubescale.yml; kubectl rollout status deployment/kubescale
- cap: Deploy scaling set
com: kubectl apply -f applications/kubescale/k8s/set.yml; kubectl rollout status deployment/set
- cap: View kubescale application
com: minikube service kubescale