Merge from master into my fork
This commit is contained in:
Коммит
c8805f9dc2
|
@ -1,2 +1,5 @@
|
|||
.DS_Store
|
||||
.vscode
|
||||
node_modules
|
||||
node_modules/
|
||||
node_modules/*
|
|
@ -0,0 +1,27 @@
|
|||
node {
|
||||
|
||||
checkout scm
|
||||
|
||||
env.DOCKER_API_VERSION="1.23"
|
||||
|
||||
sh "git rev-parse --short HEAD > commit-id"
|
||||
|
||||
tag = readFile('commit-id').replace("\n", "").replace("\r", "")
|
||||
appName = "hello-kenzan"
|
||||
registryHost = "127.0.0.1:30400/"
|
||||
imageName = "${registryHost}${appName}:${tag}"
|
||||
env.BUILDIMG=imageName
|
||||
|
||||
stage "Build"
|
||||
|
||||
sh "docker build -t ${imageName} -f applications/hello-kenzan/Dockerfile applications/hello-kenzan"
|
||||
|
||||
stage "Push"
|
||||
|
||||
sh "docker push ${imageName}"
|
||||
|
||||
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"
|
||||
}
|
228
README.md
228
README.md
|
@ -1,5 +1,229 @@
|
|||
# Kubernetes white paper for Linux.com
|
||||
# Kubernetes ci/cd whitepaper for Linux.com
|
||||
|
||||
Running `build.rb` compiles docs.yml into `README.md` into `RUNME.sh` where all commands can be automatically executed, one at a time.
|
||||
This readme is dynamically generated when `node readme.js`
|
||||
|
||||
## Interactive tutorial version
|
||||
* clone this repo
|
||||
|
||||
* Ensure you are starting with a clean slate: `minikube delete; minikube rm -rf ~/.minikube; rm -rf ~/.kube`
|
||||
|
||||
* run `npm install`
|
||||
|
||||
Begin the tutorial `npm start`
|
||||
|
||||
## Manual tutorial version
|
||||
|
||||
## Part 1
|
||||
|
||||
|
||||
### Part 1
|
||||
|
||||
|
||||
|
||||
### Step1
|
||||
|
||||
Start up the cluster with minikibe
|
||||
|
||||
`minikube start --memory 6000 --cpus 2 --kubernetes-version v1.6.0`
|
||||
|
||||
### Step2
|
||||
|
||||
Enable addons
|
||||
|
||||
`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. This will open the file with the nano editor. When finished press ctrl + x to exit and confirm save.
|
||||
|
||||
`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 stop socat-registry; docker rm socat-registry; 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;`
|
||||
|
||||
### 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
|
||||
|
||||
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.
|
||||
|
||||
`minikube service jenkins`
|
||||
|
||||
### Step4
|
||||
|
||||
View updated application
|
||||
|
||||
`minikube service hello-kenzan`
|
||||
|
||||
### Step5
|
||||
|
||||
Push a change to your fork. Run job again. View changes
|
||||
|
||||
`minikube service hello-kenzan`## Part 4
|
||||
|
||||
|
||||
### Part 4
|
||||
|
||||
|
||||
|
||||
### Step1
|
||||
|
||||
Bootstrap etcd operator on the cluster
|
||||
|
||||
`scripts/etcd.sh`
|
||||
|
||||
### Step2
|
||||
|
||||
Run job to create etcd directory
|
||||
|
||||
`kubectl create -f manifests/etcd-job.yml`
|
||||
|
||||
### Step3
|
||||
|
||||
Check job status
|
||||
|
||||
`kubectl describe jobs/etcd-job`
|
||||
|
||||
### Step4
|
||||
|
||||
build kubescale image
|
||||
|
||||
`docker build -t 127.0.0.1:30400/kubescale:latest -f applications/kubescale/Dockerfile applications/kubescale`
|
||||
|
||||
### Step5
|
||||
|
||||
build scaling image
|
||||
|
||||
`docker build -t 127.0.0.1:30400/set:latest -f applications/kubescale/set/Dockerfile applications/kubescale/set`
|
||||
|
||||
### Step6
|
||||
|
||||
Start the registry proxy.
|
||||
|
||||
`docker stop socat-registry; docker rm socat-registry; 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"`
|
||||
|
||||
### Step7
|
||||
|
||||
Push the kubescale image
|
||||
|
||||
`docker push 127.0.0.1:30400/kubescale:latest`
|
||||
|
||||
### Step8
|
||||
|
||||
Push the scaling image
|
||||
|
||||
`docker push 127.0.0.1:30400/set:latest`
|
||||
|
||||
### Step9
|
||||
|
||||
Stop the registry proxy
|
||||
|
||||
`docker stop socat-registry`
|
||||
|
||||
### Step10
|
||||
|
||||
Deploy kubescale
|
||||
|
||||
`kubectl apply -f applications/kubescale/k8s/kubescale.yml; kubectl rollout status deployment/kubescale`
|
||||
|
||||
### Step11
|
||||
|
||||
Deploy scaling set
|
||||
|
||||
`kubectl apply -f applications/kubescale/k8s/set.yml; kubectl rollout status deployment/set`
|
||||
|
||||
### Step12
|
||||
|
||||
View kubescale application
|
||||
|
||||
`minikube service kubescale`
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
<p style="font-family:sans-serif">The Hello-Kenzan app is a modified version of the <a href="https://hub.docker.com/_/nginx/">nginx web server image</a>. If you open up the <b>kubernetes-ci-cd/part1/hello-kenzan/DockerFile</b>, you will note several things:</p>
|
||||
|
||||
<img src="DockerFileEx.jpg">
|
||||
<p style="font-family:sans-serif">For more from Kenzan, check out our <a href="http://techblog.kenzan.com/">blog</a>.</p>
|
|
@ -30,23 +30,23 @@ spec:
|
|||
tier: hello-kenzan
|
||||
spec:
|
||||
containers:
|
||||
- image: 127.0.0.1:30912/hellokenzan:latest
|
||||
- image: 127.0.0.1:30400/hello-kenzan:latest
|
||||
name: hello-kenzan
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: hello-kenzan
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: hello-kenzan-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: hello-kenzan.192.168.99.100.xip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: hello-kenzan
|
||||
servicePort: 80
|
||||
# ---
|
||||
# apiVersion: extensions/v1beta1
|
||||
# kind: Ingress
|
||||
# metadata:
|
||||
# name: hello-kenzan-ingress
|
||||
# spec:
|
||||
# rules:
|
||||
# - host: hello-kenzan.MINIKUBEIP.xip.io
|
||||
# http:
|
||||
# paths:
|
||||
# - path: /
|
||||
# backend:
|
||||
# serviceName: hello-kenzan
|
||||
# servicePort: 80
|
|
@ -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
|
|
@ -12,6 +12,6 @@ COPY down.sh /down.sh
|
|||
|
||||
RUN npm install
|
||||
|
||||
RUN mpm install -g nodemon
|
||||
RUN npm install -g nodemon
|
||||
|
||||
CMD ["nodemon", "server.js"]
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: etcd-job
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
name: etcd-job
|
||||
spec:
|
||||
containers:
|
||||
- name: etcd-job
|
||||
image: tenstartups/etcdctl
|
||||
env:
|
||||
- name: ETCDCTL_ENDPOINT
|
||||
value: 'http://example-etcd-cluster-client-service:2379'
|
||||
command: ["etcdctl", "mkdir", "pod-list"]
|
||||
restartPolicy: Never
|
|
@ -74,23 +74,3 @@ spec:
|
|||
- 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,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,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
|
|
@ -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: <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: <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:
|
||||
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</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>
|
|
@ -42,6 +42,24 @@ spec:
|
|||
tier: registry
|
||||
type: NodePort
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: registry-ui
|
||||
labels:
|
||||
app: registry
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
name: registry
|
||||
selector:
|
||||
app: registry
|
||||
tier: registry
|
||||
type: NodePort
|
||||
---
|
||||
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
|
|
|
@ -7,7 +7,7 @@ node {
|
|||
sh "git rev-parse --short HEAD > commit-id"
|
||||
|
||||
tag = readFile('commit-id').replace("\n", "").replace("\r", "")
|
||||
appName = "hellokenzan"
|
||||
appName = "hello-kenzan"
|
||||
registryHost = "127.0.0.1:30912/"
|
||||
imageName = "${registryHost}${appName}:${tag}"
|
||||
env.BUILDIMG=imageName
|
||||
|
@ -18,11 +18,9 @@ node {
|
|||
|
||||
stage "Push"
|
||||
|
||||
sh "docker login -u TOKEN -p 1konzk48p4i9e0fklikd45pdh9 127.0.0.1:30912"
|
||||
|
||||
sh "docker push ${imageName}"
|
||||
|
||||
stage "Deploy"
|
||||
|
||||
sh "sed 's#127.0.0.1:30912/hellokenzan:latest#'$BUILDIMG'#' part1/hello-kenzan/k8s/deployment.yaml | kubectl apply -f -"
|
||||
sh "sed 's#__IMAGE__#'$BUILDIMG'#' part1/hello-kenzan/k8s/deployment.yaml | kubectl apply -f -"
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t `minikube ip`:30912/kr8sswordz:1.0.4 .
|
||||
docker push `minikube ip`:30912/kr8sswordz:1.0.4
|
||||
docker build -t `minikube ip`:30912/kr8sswordz:1.0.0 .
|
||||
docker push `minikube ip`:30912/kr8sswordz:1.0.0
|
||||
kubectl apply -f k8s/deployment.yaml
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.8 KiB |
|
@ -0,0 +1,66 @@
|
|||
[
|
||||
{
|
||||
"word": "Kenzan",
|
||||
"wordNbr": 1,
|
||||
"startx" : 6,
|
||||
"starty" : 0,
|
||||
"wordOrientation": "down",
|
||||
"hint": "Our company name :-)"
|
||||
},
|
||||
{
|
||||
"word": "Minikube",
|
||||
"wordNbr": 2,
|
||||
"startx" : 9,
|
||||
"starty" : 0,
|
||||
"wordOrientation": "down",
|
||||
"hint": "Tool that makes it easy to run kubernetes locally"
|
||||
},
|
||||
{
|
||||
"word": "Replication",
|
||||
"wordNbr": 3,
|
||||
"startx" : 11,
|
||||
"starty" : 0,
|
||||
"wordOrientation": "down",
|
||||
"hint": "Controller that ensures a specific number of pods are running at a given time"
|
||||
},
|
||||
{
|
||||
"word": "Service",
|
||||
"wordNbr": 4,
|
||||
"startx" : 5,
|
||||
"starty" : 1,
|
||||
"wordOrientation": "across",
|
||||
"hint": "Abstraction which defines a logical set of pods"
|
||||
},
|
||||
{
|
||||
"word": "Kubectl",
|
||||
"wordNbr": 5,
|
||||
"startx" : 3,
|
||||
"starty" : 4,
|
||||
"wordOrientation": "down",
|
||||
"hint": "cli tool for running commands against Kubernetes clusters"
|
||||
},
|
||||
{
|
||||
"word": "Deployment",
|
||||
"wordNbr": 6,
|
||||
"startx" : 2,
|
||||
"starty" : 7,
|
||||
"wordOrientation": "across",
|
||||
"hint": "Defines declarative updates for pods and replica sets"
|
||||
},
|
||||
{
|
||||
"word": "Yaml",
|
||||
"wordNbr": 7,
|
||||
"startx" : 0,
|
||||
"starty" : 10,
|
||||
"wordOrientation": "across",
|
||||
"hint": "Configuration format other then json"
|
||||
},
|
||||
{
|
||||
"word": "k8",
|
||||
"wordNbr": 5,
|
||||
"startx" : 3,
|
||||
"starty" : 4,
|
||||
"wordOrientation": "across",
|
||||
"hint": "Shorthand for Kubernetes"
|
||||
}
|
||||
]
|
|
@ -1,23 +1,38 @@
|
|||
import React from 'react';
|
||||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import homePageActions from '../../actions/homepageActions';
|
||||
import PuzzleComponent from './PuzzleComponent';
|
||||
import InstanceComponent from './InstancesComponent';
|
||||
import DataFlowArrow from './DataFlowArrow';
|
||||
import puzzleArray from '../../../puzzle.json';
|
||||
|
||||
class HomePage extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
puzzleGrid: [],
|
||||
downHintsArray: [],
|
||||
acrossHintsArray: [],
|
||||
instanceData: {
|
||||
instanceFinalCount: 3,
|
||||
instanceCurrentCount: 3
|
||||
}
|
||||
};
|
||||
|
||||
this.initializeGrid = this.initializeGrid.bind(this);
|
||||
this.initializePuzzleArray = this.initializePuzzleArray.bind(this);
|
||||
this.addLetterToPuzzleArray = this.addLetterToPuzzleArray.bind(this);
|
||||
this.handleSlider = this.handleSlider.bind(this);
|
||||
this.onScale = this.onScale.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
// iterate through json and load array. Also populate Across and Down arrays
|
||||
this.initializePuzzleArray();
|
||||
}
|
||||
|
||||
handleSlider (event, value) {
|
||||
const instanceData = Object.assign({}, this.state.instanceData, {
|
||||
instanceCurrentCount: value,
|
||||
|
@ -40,10 +55,66 @@ class HomePage extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
initializeGrid () {
|
||||
const puzzleGrid = [];
|
||||
const maxRows = 12;
|
||||
const maxColumns = 11;
|
||||
|
||||
for (var i = 0; i < maxColumns; i++) {
|
||||
puzzleGrid.push(new Array(maxRows).fill(''));
|
||||
}
|
||||
|
||||
console.log(puzzleGrid);
|
||||
return puzzleGrid;
|
||||
}
|
||||
|
||||
initializePuzzleArray () {
|
||||
const downHintsArray = puzzleArray.filter((word) => {
|
||||
return (word.wordOrientation === 'down');
|
||||
});
|
||||
const acrossHintsArray = puzzleArray.filter((word) => {
|
||||
return (word.wordOrientation === 'across');
|
||||
});
|
||||
|
||||
let puzzleGrid = [...this.initializeGrid()];
|
||||
puzzleArray.forEach((wordObj, index) => {
|
||||
const lettersArray = wordObj.word.split('');
|
||||
lettersArray.forEach((letter, index) => {
|
||||
puzzleGrid = this.addLetterToPuzzleArray(puzzleGrid, wordObj, letter, index);
|
||||
});
|
||||
});
|
||||
|
||||
this.setState({
|
||||
downHintsArray,
|
||||
acrossHintsArray,
|
||||
puzzleGrid
|
||||
});
|
||||
}
|
||||
|
||||
addLetterToPuzzleArray (puzzleGrid, wordObj, letter, index) {
|
||||
const letterObj = {
|
||||
word: wordObj.word,
|
||||
wordNbr: wordObj.wordNbr,
|
||||
positionInWord: index,
|
||||
cellLetter: letter,
|
||||
wordOrientation: wordObj.wordOrientation,
|
||||
x: wordObj.wordOrientation === 'across' ? wordObj.startx + index : wordObj.startx,
|
||||
y: wordObj.wordOrientation === 'across' ? wordObj.starty : wordObj.starty + index
|
||||
};
|
||||
|
||||
puzzleGrid[letterObj.y][letterObj.x] = letterObj;
|
||||
|
||||
return puzzleGrid;
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="home-page">
|
||||
<PuzzleComponent />
|
||||
<PuzzleComponent
|
||||
puzzleGrid={this.state.puzzleGrid}
|
||||
downHintsArray={this.state.downHintsArray}
|
||||
acrossHintsArray={this.state.acrossHintsArray}
|
||||
/>
|
||||
<div className="data-flow">
|
||||
<DataFlowArrow className="k8instances" />
|
||||
</div>
|
||||
|
@ -71,6 +142,22 @@ class HomePage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
HomePage.propTypes = {};
|
||||
HomePage.propTypes = {
|
||||
params: PropTypes.objectOf(PropTypes.string),
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
state: PropTypes.object
|
||||
};
|
||||
|
||||
export default HomePage;
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
state: state
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(homePageActions, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
|
||||
|
|
|
@ -1,274 +1,85 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../../actions/puzzleActions';
|
||||
import Cell from '../shared/Cell';
|
||||
import Slider from '../shared/Slider';
|
||||
import _ from 'lodash';
|
||||
|
||||
class PuzzleComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
cells: [],
|
||||
downHints: [],
|
||||
acrossHints: [],
|
||||
puzzleGrid: []
|
||||
};
|
||||
|
||||
this.initializePuzzleArray = this.initializePuzzleArray.bind(this);
|
||||
this.convertPuzzleGridToPuzzleArray = this.convertPuzzleGridToPuzzleArray.bind(this);
|
||||
this.onCellInput = this.onCellInput.bind(this);
|
||||
this.reloadPuzzle = this.reloadPuzzle.bind(this);
|
||||
this.clearPuzzle = this.clearPuzzle.bind(this);
|
||||
this.submitPuzzle = this.submitPuzzle.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.props.actions.getPuzzleData();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
if (this.props.puzzleArray !== newProps.puzzleArray) {
|
||||
this.initializePuzzleArray(newProps.puzzleArray);
|
||||
}
|
||||
}
|
||||
|
||||
initializeGrid () {
|
||||
const puzzleGrid = [];
|
||||
const maxRows = 12;
|
||||
const maxColumns = 11;
|
||||
|
||||
for (var i = 0; i < maxColumns; i++) {
|
||||
puzzleGrid.push(new Array(maxRows).fill(''));
|
||||
}
|
||||
|
||||
return puzzleGrid;
|
||||
}
|
||||
|
||||
initializePuzzleArray (puzzleArray) {
|
||||
const downHintsArray = puzzleArray.filter((word) => {
|
||||
return (word.wordOrientation === 'down');
|
||||
});
|
||||
const acrossHintsArray = puzzleArray.filter((word) => {
|
||||
return (word.wordOrientation === 'across');
|
||||
});
|
||||
|
||||
const downHints = this.buildHints(downHintsArray);
|
||||
const acrossHints = this.buildHints(acrossHintsArray);
|
||||
|
||||
let puzzleGrid = [...this.initializeGrid()];
|
||||
puzzleArray.forEach((wordObj, index) => {
|
||||
const lettersArray = wordObj.word.split('');
|
||||
const enteredValueArray = wordObj.enteredValue.split('');
|
||||
lettersArray.forEach((letter, index) => {
|
||||
puzzleGrid = this.addLetterToPuzzleArray(puzzleGrid, wordObj, letter, enteredValueArray[index], index);
|
||||
});
|
||||
});
|
||||
|
||||
const cells = this.buildCells(puzzleGrid);
|
||||
|
||||
this.setState({
|
||||
downHints,
|
||||
acrossHints,
|
||||
puzzleGrid,
|
||||
cells
|
||||
});
|
||||
}
|
||||
|
||||
addLetterToPuzzleArray (puzzleGrid, wordObj, letter, enteredValue, index) {
|
||||
const letterObj = {
|
||||
word: wordObj.word,
|
||||
wordNbr: wordObj.wordNbr,
|
||||
positionInWord: index,
|
||||
cellLetter: letter,
|
||||
currentValue: enteredValue,
|
||||
wordOrientation: wordObj.wordOrientation,
|
||||
x: wordObj.wordOrientation === 'across' ? wordObj.startx + index : wordObj.startx,
|
||||
y: wordObj.wordOrientation === 'across' ? wordObj.starty : wordObj.starty + index
|
||||
};
|
||||
|
||||
puzzleGrid[letterObj.y][letterObj.x] = letterObj;
|
||||
|
||||
return puzzleGrid;
|
||||
}
|
||||
|
||||
buildCells (puzzleGrid) {
|
||||
return puzzleGrid.map((column, index) => {
|
||||
return column.map((cell, i) => {
|
||||
return (
|
||||
<Cell
|
||||
key={index + i}
|
||||
id={'cell'.concat('-', index, '-', i)}
|
||||
orientation={cell.wordOrientation}
|
||||
letter={cell.cellLetter}
|
||||
value={cell.currentValue}
|
||||
isEmpty={!_.isInteger(cell.positionInWord)}
|
||||
positionInWord={cell.positionInWord}
|
||||
wordNbr={cell.wordNbr}
|
||||
onCellInput={this.onCellInput}
|
||||
/>
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onCellInput (e) {
|
||||
const cellData = e.target.name.split('-');
|
||||
const row = cellData[1];
|
||||
const col = cellData[2];
|
||||
|
||||
const puzzleGrid = this.state.puzzleGrid.map((colData, rowIndex) => {
|
||||
return colData.map((cell, colIndex) => {
|
||||
if (rowIndex == row && colIndex == col) {
|
||||
return Object.assign({}, cell, {
|
||||
currentValue: e.target.value
|
||||
});
|
||||
} else {
|
||||
return cell;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const cells = this.buildCells(puzzleGrid);
|
||||
|
||||
this.setState({
|
||||
puzzleGrid,
|
||||
cells
|
||||
});
|
||||
}
|
||||
|
||||
buildHints (hintsArray) {
|
||||
return _.sortBy(hintsArray, 'wordNbr').map((word, index) => {
|
||||
function PuzzleComponent (props) {
|
||||
const cells = props.puzzleGrid.map((column, index) => {
|
||||
return column.map((cell, i) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<p className="bold inline">{word.wordNbr}.</p> <p className="inline">{word.hint}</p>
|
||||
</li>
|
||||
<Cell
|
||||
key={index + i}
|
||||
orientation={cell.wordOrientation}
|
||||
letter={cell.cellLetter}
|
||||
isEmpty={_.isInteger(cell.positionInWord)}
|
||||
positionInWord={cell.positionInWord}
|
||||
wordNbr={cell.wordNbr}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
convertPuzzleGridToPuzzleArray () {
|
||||
const submission = this.props.puzzleArray.map((word) => {
|
||||
const startx = word.startx;
|
||||
const starty = word.starty;
|
||||
const length = word.word.length;
|
||||
const direction = word.wordOrientation;
|
||||
|
||||
const enteredLetters = this.state.puzzleGrid.map((colData, row) => {
|
||||
return colData.map((cell, col) => {
|
||||
if (startx === col && starty === row) {
|
||||
return cell.currentValue || '*';
|
||||
}
|
||||
|
||||
for (let i = 1; i < length; i++) {
|
||||
if (direction === 'down' && startx === col && starty + i === row) {
|
||||
return cell.currentValue || '*';
|
||||
} else if (direction === 'across' && startx + i === col && starty === row) {
|
||||
return cell.currentValue || '*';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const enteredValue = enteredLetters.reduce((arr, val) => {
|
||||
return arr.concat(val);
|
||||
}).join('');
|
||||
|
||||
return Object.assign({}, word, {
|
||||
enteredValue
|
||||
});
|
||||
});
|
||||
|
||||
return submission;
|
||||
}
|
||||
|
||||
reloadPuzzle () {
|
||||
this.props.actions.getPuzzleData();
|
||||
}
|
||||
|
||||
clearPuzzle () {
|
||||
let submission = [...this.props.puzzleArray];
|
||||
submission = submission.map(obj => {
|
||||
const letters = obj.enteredValue.length;
|
||||
obj.enteredValue = new Array(letters).fill('*').join('');
|
||||
return obj;
|
||||
});
|
||||
|
||||
this.props.actions.submitPuzzleData(this.props.puzzleId, submission);
|
||||
}
|
||||
|
||||
submitPuzzle (e) {
|
||||
e.preventDefault();
|
||||
const submission = this.convertPuzzleGridToPuzzleArray();
|
||||
|
||||
this.props.actions.submitPuzzleData(this.props.puzzleId, submission);
|
||||
}
|
||||
|
||||
render () {
|
||||
const sliderProperties = {
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 1,
|
||||
defaultValue: 1,
|
||||
onChange: () => {}
|
||||
};
|
||||
});
|
||||
|
||||
const downHints = _.sortBy(props.downHintsArray, 'wordNbr').map((word, index) => {
|
||||
return (
|
||||
<div className="crossword-container">
|
||||
<div className="puzzle-container">
|
||||
<form onSubmit={this.submitPuzzle}>
|
||||
<div className="puzzle">
|
||||
{this.state.cells}
|
||||
</div>
|
||||
<Slider properties={sliderProperties} title={'Concurrent Requests: '}/>
|
||||
<div className="button-row">
|
||||
<button className="secondary" type="button" onClick={this.reloadPuzzle}>Reload</button>
|
||||
<button className="secondary" type="button" onClick={this.clearPuzzle}>Clear</button>
|
||||
<input className="button primary" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
<li key={index}>
|
||||
<p className="bold inline">{word.wordNbr}.</p> <p className="inline">{word.hint}</p>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
const acrossHints = _.sortBy(props.acrossHintsArray, 'wordNbr').map((word, index) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<p className="bold inline">{word.wordNbr}.</p> <p className="inline">{word.hint}</p>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
const sliderProperties = {
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 1,
|
||||
defaultValue: 1,
|
||||
onChange: () => {}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="crossword-container">
|
||||
<div className="puzzle-container">
|
||||
<div className="puzzle">
|
||||
{cells}
|
||||
</div>
|
||||
<div className="puzzle-hints">
|
||||
<div className="hint-container">
|
||||
<div className="hint-category down">
|
||||
<h6 className="bold">Down</h6>
|
||||
<ul>
|
||||
{this.state.downHints}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="hint-category across">
|
||||
<h6 className="bold">Across</h6>
|
||||
<ul>
|
||||
{this.state.acrossHints}
|
||||
</ul>
|
||||
</div>
|
||||
<Slider properties={sliderProperties} title={'Concurrent Requests: '}/>
|
||||
<div className="button-row">
|
||||
<button className="secondary">Reload</button>
|
||||
<button className="secondary">Clear</button>
|
||||
<button className="primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="puzzle-hints">
|
||||
<div className="hint-container">
|
||||
<div className="hint-category down">
|
||||
<h6 className="bold">Down</h6>
|
||||
<ul>
|
||||
{downHints}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="hint-category across">
|
||||
<h6 className="bold">Across</h6>
|
||||
<ul>
|
||||
{acrossHints}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
PuzzleComponent.propTypes = {
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
state: PropTypes.object,
|
||||
puzzleArray: PropTypes.array,
|
||||
puzzleId: PropTypes.string
|
||||
downHintsArray: PropTypes.array,
|
||||
acrossHintsArray: PropTypes.array,
|
||||
puzzleGrid: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
puzzleId: state.puzzle.id,
|
||||
puzzleArray: state.puzzle.puzzleData
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PuzzleComponent);
|
||||
export default PuzzleComponent;
|
||||
|
|
|
@ -1,42 +1,15 @@
|
|||
import React, {PropTypes} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
function Cell (props) {
|
||||
const cellClass = classNames({
|
||||
cell: true,
|
||||
isEmpty: props.isEmpty
|
||||
});
|
||||
const Cell = ({isEmpty, positionInWord, wordNbr, letter}) => {
|
||||
|
||||
const currentVal = props.value === '*' ? '' : props.value;
|
||||
const inputClass = classNames({
|
||||
incorrect: currentVal && currentVal.toLowerCase() !== props.letter.toLowerCase()
|
||||
});
|
||||
const showWordNumber = positionInWord === 0 ? true : false
|
||||
|
||||
return (
|
||||
<div className={cellClass}>
|
||||
<div className="superscript-number">{props.positionInWord === 0 ? props.wordNbr : ''}</div>
|
||||
<input
|
||||
name={props.id}
|
||||
type="text"
|
||||
maxLength="1"
|
||||
size="1"
|
||||
onChange={props.onCellInput}
|
||||
className={inputClass}
|
||||
value={currentVal}
|
||||
disabled={props.isEmpty}
|
||||
/>
|
||||
<div className={'cell ' + isEmpty}>
|
||||
<div className="superscript-number">{showWordNumber ? wordNbr : ''}</div>
|
||||
<input type="text" maxLength="1" size="1" defaultValue={letter}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Cell.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
isEmpty: PropTypes.bool.isRequired,
|
||||
positionInWord: PropTypes.number,
|
||||
wordNbr: PropTypes.number,
|
||||
letter: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
onCellInput: PropTypes.func
|
||||
};
|
||||
|
||||
export default Cell;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../node_modules
|
|
@ -1,7 +0,0 @@
|
|||
FROM ubuntu:latest
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y socat
|
||||
|
||||
CMD ["socat"]
|
|
@ -1,121 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
kubectl delete namespace etcd
|
||||
|
||||
sleep 5
|
||||
|
||||
kubectl create namespace etcd
|
||||
|
||||
echo "starting cluster"
|
||||
|
||||
minikube start --memory 5000 --cpus 4 --kubernetes-version v1.6.0-rc.1
|
||||
|
||||
# echo "enabling addons"
|
||||
|
||||
# minikube addons enable heapster
|
||||
|
||||
minikube addons enable ingress
|
||||
|
||||
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 "installing registry"
|
||||
kubectl apply -f k8s/registry.yml
|
||||
kubectl rollout status deployment/registry
|
||||
|
||||
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
|
||||
|
||||
sleep 5
|
||||
|
||||
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=new1
|
||||
|
||||
docker build -t 127.0.0.1:30400/kubescale:$TAG .
|
||||
|
||||
# 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
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "kubernetes-ci-cd",
|
||||
"version": "1.0.0",
|
||||
"description": "## Get started Requirements * nodejs * kubectl * virtualbox * minikube * At least 4GB of available memory, 8GB for part 5",
|
||||
"main": "start.js",
|
||||
"dependencies": {
|
||||
"cmdify": "^0.0.4",
|
||||
"colors": "^1.1.2",
|
||||
"inquirer": "^3.0.6",
|
||||
"rx": "^4.1.0",
|
||||
"yamljs": "^0.2.8"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node start.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/kenzanlabs/kubernetes-ci-cd.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kenzanlabs/kubernetes-ci-cd/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kenzanlabs/kubernetes-ci-cd#readme"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/node
|
||||
|
||||
YAML = require('yamljs');
|
||||
var fs = require('fs');
|
||||
|
||||
var markdown = "# Kubernetes ci/cd whitepaper for Linux.com\n\n This readme is dynamically generated when `node readme.js`";
|
||||
markdown = markdown + "\n\n## Interactive tutorial version"
|
||||
markdown = markdown + "\n* clone this repo\n"
|
||||
markdown = markdown + "\n* Ensure you are starting with a clean slate: `minikube delete; minikube rm -rf ~/.minikube; rm -rf ~/.kube`\n"
|
||||
markdown = markdown + "\n* run `npm install`\n"
|
||||
markdown = markdown + "\nBegin the tutorial `npm start`"
|
||||
markdown = markdown + "\n\n## Manual tutorial version\n\n"
|
||||
|
||||
YAML.load('steps.yml', function(docs)
|
||||
{
|
||||
docList = docs.parts;
|
||||
var parts = docs.parts;
|
||||
|
||||
parts.forEach(function (item) {
|
||||
markdown = markdown + "## " + item.name + "\n"
|
||||
var part = item.name;
|
||||
markdown = markdown + "\n\n### " + item.name + "\n\n"
|
||||
var stepNum = 0;
|
||||
var stepList = item.steps;
|
||||
|
||||
// console.log(item.steps);
|
||||
stepList.forEach(function (step) {
|
||||
stepNum++;
|
||||
markdown = markdown + "\n\n### Step" + stepNum
|
||||
markdown = markdown + "\n\n" + step.cap
|
||||
markdown = markdown + "\n\n`" + step.com + "`"
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
fs.writeFile("README.md", markdown, function(err) {
|
||||
if(err) {
|
||||
return console.log(err);
|
||||
}
|
||||
|
||||
console.log("README.md saved!");
|
||||
});
|
||||
|
||||
});
|
|
@ -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
|
|
@ -0,0 +1,78 @@
|
|||
#!/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 echo "#!/usr/bin/env bash\n\nexport ETCDCTL_ENDPOINT='http://example-etcd-cluster-client-service:2379'\nectdctl mkdir pod-list" > /mkd.sh
|
||||
kubectl exec -it example-etcd-cluster-0000 chmod 0777 /mkd.sh
|
||||
|
||||
kubectl exec -it example-etcd-cluster-0000 cat /mkd.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
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/node
|
||||
|
||||
YAML = require('yamljs');
|
||||
var inquirer = require('inquirer');
|
||||
var Rx = require('rx');
|
||||
const execSync = require('child_process').execSync;
|
||||
var environment = process.env;
|
||||
|
||||
var config = {
|
||||
maxBuffer: 10000 * 1024,
|
||||
env: environment
|
||||
};
|
||||
|
||||
var prompts = new Rx.Subject();
|
||||
|
||||
var stepIndex = 1;
|
||||
var commands = [];
|
||||
|
||||
inquirer.prompt(prompts).ui.process.subscribe(
|
||||
|
||||
function (answers) {
|
||||
answerIndex = answers.name*1
|
||||
answerIndex = answerIndex - 1;
|
||||
cmd = commands[answerIndex];
|
||||
execSync(cmd, {stdio:[0,1,2], env: environment})
|
||||
},
|
||||
function(err){
|
||||
console.log('error')
|
||||
},
|
||||
function(message){
|
||||
console.log('complete')
|
||||
}
|
||||
);
|
||||
|
||||
prompts.onNext({type: 'confirm',name: "Begin", message: "Welcome to the Linux.com interactive Kubernetes tutorial by Kenzan. Press enter to begin\n", default: true});
|
||||
|
||||
|
||||
YAML.load('steps.yml', function(docs)
|
||||
{
|
||||
docList = docs.parts;
|
||||
var parts = docs.parts;
|
||||
|
||||
parts.forEach(function (item) {
|
||||
var part = item.name;
|
||||
var stepNum = 0;
|
||||
var stepList = item.steps;
|
||||
stepList.forEach(function (step) {
|
||||
stepNum++;
|
||||
commands.push(step.com)
|
||||
prompts.onNext({type: 'confirm',name: stepIndex, message: "\n \n \n" + part + " Step: " + stepNum + "\n" + step.cap + "\n \n" + step.com + "\n \nPress enter to the run the above command for the step.", default: true});
|
||||
stepIndex++;
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
prompts.onCompleted();
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,112 @@
|
|||
parts:
|
||||
|
||||
- name: Part 1
|
||||
intro: In this part we will setup a local cluster with minikube, deploy a public image from dockerhub, customize that image, and then finally deploy it inside our local cluster.
|
||||
steps:
|
||||
|
||||
- cap: Start up the cluster with minikibe
|
||||
com: minikube start --memory 6000 --cpus 2 --kubernetes-version v1.6.0
|
||||
|
||||
- cap: Enable addons
|
||||
com: minikube addons enable heapster; minikube addons enable ingress
|
||||
|
||||
- cap: Wait 20 seconds and view minikube dashboard
|
||||
com: sleep 20; minikube service kubernetes-dashboard --namespace kube-system
|
||||
|
||||
- cap: Deploy the public nginx image from DockerHub
|
||||
com: kubectl run nginx --image nginx --port 80
|
||||
|
||||
- cap: Create a service for deployment
|
||||
com: kubectl expose deployment nginx --type NodePort --port 80
|
||||
|
||||
- cap: Launch browser to test service
|
||||
com: minikube service nginx
|
||||
|
||||
- cap: Install registry
|
||||
com: kubectl apply -f manifests/registry.yml
|
||||
|
||||
- cap: Wait for registry to deploy
|
||||
com: kubectl rollout status deployments/registry
|
||||
|
||||
- cap: View registry UI
|
||||
com: minikube service registry-ui
|
||||
|
||||
- cap: Edit the contents of applications/hello-kenzan/index.html. This will open the file with the nano editor. When finished press ctrl + x to exit and confirm save.
|
||||
com: nano applications/hello-kenzan/index.html
|
||||
|
||||
- cap: We will now build the image with a special name that is pointing at our cluster registry.
|
||||
com: docker build -t 127.0.0.1:30400/hello-kenzan:latest -f applications/hello-kenzan/Dockerfile applications/hello-kenzan
|
||||
|
||||
- cap: 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.
|
||||
com: docker stop socat-registry; docker rm socat-registry; 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"
|
||||
|
||||
- cap: We can now push our image.
|
||||
com: docker push 127.0.0.1:30400/hello-kenzan:latest
|
||||
|
||||
- cap: Stop the registry proxy.
|
||||
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
|
||||
|
||||
- cap: View the app
|
||||
com: minikube service hello-kenzan
|
||||
|
||||
- name: Part 2
|
||||
intro: In this part we will Setup Jenkins, and setup an automated jon to build, push and deploy our custom appliction.
|
||||
steps:
|
||||
|
||||
- cap: Install Jenkins
|
||||
com: kubectl apply -f manifests/jenkins.yml; kubectl rollout status deployment/jenkins
|
||||
|
||||
- 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. 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
|
||||
|
||||
- cap: View updated application
|
||||
com: minikube service hello-kenzan
|
||||
|
||||
- cap: Push a change to your fork. Run job again. View changes
|
||||
com: minikube service hello-kenzan
|
||||
|
||||
- name: Part 4
|
||||
intro: Kubescale
|
||||
steps:
|
||||
|
||||
- cap: Bootstrap etcd operator on the cluster
|
||||
com: scripts/etcd.sh
|
||||
|
||||
- cap: Run job to create etcd directory
|
||||
com: kubectl create -f manifests/etcd-job.yml
|
||||
|
||||
- cap: Check job status
|
||||
com: kubectl describe jobs/etcd-job
|
||||
|
||||
- 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 stop socat-registry; docker rm socat-registry; 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"
|
||||
|
||||
- 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
|
Загрузка…
Ссылка в новой задаче