Jesse Suen
6 years ago
62 changed files with 287 additions and 150 deletions
@ -1,11 +1,15 @@ |
|||||
apiVersion: 0.1.0 |
apiVersion: 0.3.0 |
||||
environments: |
environments: |
||||
default: |
default: |
||||
destination: |
destination: |
||||
namespace: default |
namespace: blue-green-deploy |
||||
server: https://kubernetes.default.svc |
server: https://kubernetes.default.svc |
||||
k8sVersion: v1.10.0 |
k8sVersion: v1.10.0 |
||||
path: default |
path: default |
||||
kind: ksonnet.io/app |
kind: ksonnet.io/app |
||||
name: blue-green-deploy |
name: blue-green-deploy |
||||
|
registries: |
||||
|
incubator: |
||||
|
protocol: github |
||||
|
uri: github.com/ksonnet/parts/tree/master/incubator |
||||
version: 0.0.1 |
version: 0.0.1 |
||||
|
@ -0,0 +1,71 @@ |
|||||
|
#!bin/bash |
||||
|
|
||||
|
DEPLOYMENT_NAME=$(echo "${DEPLOY_MANIFEST}" | jq -r '.metadata.name') |
||||
|
SERVICE_NAME=$(echo "${SERVICE_MANIFEST}" | jq -r '.metadata.name') |
||||
|
|
||||
|
# 1. Check if the deployment exists. If it doesn't exist, this is the initial deployment and we |
||||
|
# can simply deploy without blue-green. Add the app label using jq |
||||
|
out=$(kubectl get --export -o json deployment.apps/${DEPLOYMENT_NAME} 2>&1) |
||||
|
if [ $? -ne 0 ]; then |
||||
|
if [[ "${out}" =~ "NotFound" ]] ; then |
||||
|
echo "Initial deployment" |
||||
|
echo ${DEPLOY_MANIFEST} | \ |
||||
|
jq ".metadata.labels += {\"app.kubernetes.io/instance\": \"${APPNAME}\"}" | \ |
||||
|
kubectl apply -o yaml -f - || exit 1 |
||||
|
echo ${SERVICE_MANIFEST} | \ |
||||
|
jq ".metadata.labels += {\"app.kubernetes.io/instance\": \"${APPNAME}\"}" | \ |
||||
|
kubectl apply -o yaml -f - || exit 1 |
||||
|
exit 0 |
||||
|
fi |
||||
|
echo "Failed to get deployment: ${out}" |
||||
|
exit 1 |
||||
|
fi |
||||
|
ORIGINAL_DEPLOY_MANIFEST=$out |
||||
|
|
||||
|
# 2. Clone the original, running deployment to a temporary deployment, with tweaks to its name and |
||||
|
# selectors. The jq command carries over all labels and selectors and appends the `-temp` suffix. |
||||
|
TMP_DEPLOYMENT_NAME="${DEPLOYMENT_NAME}-temp" |
||||
|
echo ${ORIGINAL_DEPLOY_MANIFEST} | jq -r '.metadata.name+="-temp" | |
||||
|
.spec.template.metadata.labels += (.spec.template.metadata.labels | to_entries | map(.value+="-temp") | from_entries) | |
||||
|
.spec.selector.matchLabels += (.spec.selector.matchLabels | to_entries | map(.value+="-temp") | from_entries)' | |
||||
|
kubectl apply -f - |
||||
|
|
||||
|
# 3. Wait for cloned deployment to become ready. |
||||
|
sleep 2 |
||||
|
echo "Waiting for successful rollout of new (temporary) deployment" |
||||
|
kubectl rollout status --watch=true deployments.apps/${TMP_DEPLOYMENT_NAME} || exit 1 |
||||
|
echo "Rollout of temporary deployment successful" |
||||
|
|
||||
|
# 4. Patch the service object such that all traffic is redirected to the cloned, temporary |
||||
|
# deployment. After this step, the original deployment will no longer be receiving traffic. |
||||
|
kubectl get service ${SERVICE_NAME} --export -o json | \ |
||||
|
jq '.spec.selector = (.spec.selector | with_entries(.value+="-temp"))' | |
||||
|
kubectl apply -f - || exit 1 |
||||
|
sleep 5 # Sleep slightly to allow iptables to get propagated to all nodes in the cluster |
||||
|
|
||||
|
# 5. Update the original deployment (now receiving no traffic) with the new manifest |
||||
|
echo "Updating original deployment" |
||||
|
echo ${DEPLOY_MANIFEST} | \ |
||||
|
jq ".metadata.labels += {\"app.kubernetes.io/instance\": \"${APPNAME}\"}" | \ |
||||
|
kubectl apply -f - || exit 1 |
||||
|
|
||||
|
# 6. Wait for the new deployment to become complete |
||||
|
sleep 2 |
||||
|
echo "Waiting for successful rollout of new deployment" |
||||
|
kubectl rollout status --watch=true deployments.apps/${DEPLOYMENT_NAME} || exit 1 |
||||
|
echo "Rollout of new deployment successful" |
||||
|
|
||||
|
# dummy wait step for demo purposes |
||||
|
echo "sleeping for 30 seconds" |
||||
|
sleep 30 |
||||
|
|
||||
|
# 7. Apply the new service object. Traffic will be redirected to the new version of the deployment |
||||
|
echo "Updating original service object" |
||||
|
echo ${SERVICE_MANIFEST} | \ |
||||
|
jq ".metadata.labels += {\"app.kubernetes.io/instance\": \"${APPNAME}\"}" | \ |
||||
|
kubectl apply -f - || exit 1 |
||||
|
|
||||
|
sleep 10 |
||||
|
# 8. Remove the cloned deployment, which is no longer receiving any traffic |
||||
|
echo "Deleting ephemeral deployment" |
||||
|
kubectl delete deployments/${TMP_DEPLOYMENT_NAME} --ignore-not-found=true || exit 1 |
@ -0,0 +1,131 @@ |
|||||
|
local bgGuestbook = std.extVar("__ksonnet/components")["bg-guestbook"]; |
||||
|
local bgGuestbookSvc = bgGuestbook[0]; |
||||
|
local bgGuestbookDeploy = bgGuestbook[1]; |
||||
|
|
||||
|
[ |
||||
|
{ |
||||
|
"apiVersion": "v1", |
||||
|
"kind": "Pod", |
||||
|
"metadata": { |
||||
|
"generateName": "blue-green-", |
||||
|
"annotations": { |
||||
|
"argocd.argoproj.io/hook": "Sync", |
||||
|
"deploy-manifest": std.manifestJson(bgGuestbookDeploy), |
||||
|
"svc-manifest": std.manifestJson(bgGuestbookSvc), |
||||
|
}, |
||||
|
}, |
||||
|
"spec": { |
||||
|
"serviceAccountName": "blue-green-sa", |
||||
|
"restartPolicy": "Never", |
||||
|
"containers": [ |
||||
|
{ |
||||
|
"name": "blue-green", |
||||
|
"image": "argoproj/argoexec:latest", |
||||
|
"command": ["bash", "-c"], |
||||
|
"args": [" |
||||
|
curl -L -o /usr/local/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && |
||||
|
chmod +x /usr/local/bin/kubectl && |
||||
|
curl -sSL -o /usr/local/bin/blue-green.sh https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/blue-green-deploy/blue-green.sh && |
||||
|
chmod +x /usr/local/bin/blue-green.sh && |
||||
|
blue-green.sh |
||||
|
"], |
||||
|
"env": [ |
||||
|
{ |
||||
|
"name": "DEPLOY_MANIFEST", |
||||
|
"valueFrom": { |
||||
|
"fieldRef": { |
||||
|
"fieldPath": "metadata.annotations['deploy-manifest']" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "SERVICE_MANIFEST", |
||||
|
"valueFrom": { |
||||
|
"fieldRef": { |
||||
|
"fieldPath": "metadata.annotations['svc-manifest']" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// NOTE: app.kubernetes.io/instance will get injected into the hook object |
||||
|
{ |
||||
|
"name": "APPNAME", |
||||
|
"valueFrom": { |
||||
|
"fieldRef": { |
||||
|
"fieldPath": "metadata.labels['app.kubernetes.io/instance']" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
], |
||||
|
} |
||||
|
}, |
||||
|
// RBAC to allow the blue-green pod privileges to manipulate deployments and services |
||||
|
{ |
||||
|
"apiVersion": "v1", |
||||
|
"kind": "ServiceAccount", |
||||
|
"metadata": { |
||||
|
"name": "blue-green-sa" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"apiVersion": "rbac.authorization.k8s.io/v1", |
||||
|
"kind": "Role", |
||||
|
"metadata": { |
||||
|
"name": "blue-green-role" |
||||
|
}, |
||||
|
"rules": [ |
||||
|
{ |
||||
|
"apiGroups": [ |
||||
|
"apps", |
||||
|
"extensions" |
||||
|
], |
||||
|
"resources": [ |
||||
|
"deployments", |
||||
|
], |
||||
|
"verbs": [ |
||||
|
"list", |
||||
|
"get", |
||||
|
"create", |
||||
|
"update", |
||||
|
"patch", |
||||
|
"delete", |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
"apiGroups": [ |
||||
|
"" |
||||
|
], |
||||
|
"resources": [ |
||||
|
"services" |
||||
|
], |
||||
|
"verbs": [ |
||||
|
"list", |
||||
|
"get", |
||||
|
"create", |
||||
|
"update", |
||||
|
"patch", |
||||
|
"delete", |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
"apiVersion": "rbac.authorization.k8s.io/v1", |
||||
|
"kind": "RoleBinding", |
||||
|
"metadata": { |
||||
|
"name": "blue-green-rolebinding" |
||||
|
}, |
||||
|
"roleRef": { |
||||
|
"apiGroup": "rbac.authorization.k8s.io", |
||||
|
"kind": "Role", |
||||
|
"name": "blue-green-role" |
||||
|
}, |
||||
|
"subjects": [ |
||||
|
{ |
||||
|
"kind": "ServiceAccount", |
||||
|
"name": "blue-green-sa" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
@ -1,8 +1,9 @@ |
|||||
local base = import "base.libsonnet"; |
local base = import "base.libsonnet"; |
||||
// uncomment if you reference ksonnet-lib |
// uncomment if you reference ksonnet-lib |
||||
// local k = import "k.libsonnet"; |
// local k = import "k.libsonnet"; |
||||
|
// local deployment = k.apps.v1beta2.deployment; |
||||
|
|
||||
base + { |
base + { |
||||
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") |
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") |
||||
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"}) |
// "nginx-deployment"+: deployment.mixin.metadata.withLabels({foo: "bar"}) |
||||
} |
} |
||||
|
@ -1,5 +0,0 @@ |
|||||
namePrefix: kustomize- |
|
||||
|
|
||||
resources: |
|
||||
- ../../guestbook/guestbook-ui-deployment.yaml |
|
||||
- ../../guestbook/guestbook-ui-svc.yaml |
|
@ -1,7 +0,0 @@ |
|||||
namePrefix: dev- |
|
||||
|
|
||||
bases: |
|
||||
- ../base |
|
||||
|
|
||||
commonLabels: |
|
||||
environment: dev |
|
@ -0,0 +1,5 @@ |
|||||
|
namePrefix: kustomize- |
||||
|
|
||||
|
resources: |
||||
|
- ../guestbook/guestbook-ui-deployment.yaml |
||||
|
- ../guestbook/guestbook-ui-svc.yaml |
@ -1,11 +0,0 @@ |
|||||
namePrefix: prod- |
|
||||
|
|
||||
bases: |
|
||||
- ../base |
|
||||
|
|
||||
commonLabels: |
|
||||
environment: prod |
|
||||
|
|
||||
imageTags: |
|
||||
- name: gcr.io/heptio-images/ks-guestbook-demo |
|
||||
newTag: "0.1" |
|
@ -0,0 +1,7 @@ |
|||||
|
namePrefix: pre-post-sync- |
||||
|
|
||||
|
resources: |
||||
|
- ../guestbook/guestbook-ui-deployment.yaml |
||||
|
- ../guestbook/guestbook-ui-svc.yaml |
||||
|
- pre-sync-job.yaml |
||||
|
- post-sync-job.yaml |
@ -1,7 +1,7 @@ |
|||||
apiVersion: batch/v1 |
apiVersion: batch/v1 |
||||
kind: Job |
kind: Job |
||||
metadata: |
metadata: |
||||
generateName: after- |
name: after |
||||
annotations: |
annotations: |
||||
argocd.argoproj.io/hook: PostSync |
argocd.argoproj.io/hook: PostSync |
||||
argocd.argoproj.io/hook-delete-policy: HookSucceeded |
argocd.argoproj.io/hook-delete-policy: HookSucceeded |
@ -1,7 +1,7 @@ |
|||||
apiVersion: batch/v1 |
apiVersion: batch/v1 |
||||
kind: Job |
kind: Job |
||||
metadata: |
metadata: |
||||
generateName: before- |
name: before |
||||
annotations: |
annotations: |
||||
argocd.argoproj.io/hook: PreSync |
argocd.argoproj.io/hook: PreSync |
||||
argocd.argoproj.io/hook-delete-policy: HookSucceeded |
argocd.argoproj.io/hook-delete-policy: HookSucceeded |
@ -1,4 +0,0 @@ |
|||||
/lib |
|
||||
/.ksonnet/registries |
|
||||
/app.override.yaml |
|
||||
/.ks_environment |
|
@ -1,11 +0,0 @@ |
|||||
apiVersion: 0.1.0 |
|
||||
environments: |
|
||||
default: |
|
||||
destination: |
|
||||
namespace: default |
|
||||
server: https://kubernetes.default.svc |
|
||||
k8sVersion: v1.10.0 |
|
||||
path: default |
|
||||
kind: ksonnet.io/app |
|
||||
name: sock-shop |
|
||||
version: 0.0.1 |
|
@ -1,37 +0,0 @@ |
|||||
{ |
|
||||
global: { |
|
||||
// User-defined global parameters; accessible to all component and environments, Ex: |
|
||||
// replicas: 4, |
|
||||
}, |
|
||||
components: { |
|
||||
// Component-level parameters, defined initially from 'ks prototype use ...' |
|
||||
// Each object below should correspond to a component in the components/ directory |
|
||||
"carts-db-dep": {}, |
|
||||
"carts-db-svc": {}, |
|
||||
"carts-dep": {}, |
|
||||
"catalogue-db-dep": {}, |
|
||||
"catalogue-db-svc": {}, |
|
||||
"catalogue-dep": {}, |
|
||||
"catalogue-svc": {}, |
|
||||
"front-end-dep": {}, |
|
||||
"front-end-svc": {}, |
|
||||
"orders-db-dep": {}, |
|
||||
"orders-db-svc": {}, |
|
||||
"orders-dep": {}, |
|
||||
"orders-svc": {}, |
|
||||
"payment-dep": {}, |
|
||||
"payment-svc": {}, |
|
||||
"queue-master-dep": {}, |
|
||||
"queue-master-svc": {}, |
|
||||
"rabbitmq-dep": {}, |
|
||||
"rabbitmq-svc": {}, |
|
||||
"session-db-dep": {}, |
|
||||
"session-db-svc": {}, |
|
||||
"shipping-dep": {}, |
|
||||
"shipping-svc": {}, |
|
||||
"user-db-dep": {}, |
|
||||
"user-db-svc": {}, |
|
||||
"user-dep": {}, |
|
||||
"user-svc": {}, |
|
||||
}, |
|
||||
} |
|
@ -1,4 +0,0 @@ |
|||||
local components = std.extVar("__ksonnet/components"); |
|
||||
components + { |
|
||||
// Insert user-specified overrides here. |
|
||||
} |
|
@ -1,2 +0,0 @@ |
|||||
{ |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
local base = import "base.libsonnet"; |
|
||||
// uncomment if you reference ksonnet-lib |
|
||||
// local k = import "k.libsonnet"; |
|
||||
|
|
||||
base + { |
|
||||
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n") |
|
||||
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"}) |
|
||||
} |
|
@ -1,17 +0,0 @@ |
|||||
local params = std.extVar("__ksonnet/params"); |
|
||||
local globals = import "globals.libsonnet"; |
|
||||
local envParams = params + { |
|
||||
components +: { |
|
||||
// Insert component parameter overrides here. Ex: |
|
||||
// guestbook +: { |
|
||||
// name: "guestbook-dev", |
|
||||
// replicas: params.global.replicas, |
|
||||
// }, |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
{ |
|
||||
components: { |
|
||||
[x]: envParams.components[x] + globals, for x in std.objectFields(envParams.components) |
|
||||
}, |
|
||||
} |
|
@ -0,0 +1,28 @@ |
|||||
|
resources: |
||||
|
- base/carts-db-dep.yaml |
||||
|
- base/carts-db-svc.yaml |
||||
|
- base/carts-dep.yaml |
||||
|
- base/catalogue-db-dep.yaml |
||||
|
- base/catalogue-db-svc.yaml |
||||
|
- base/catalogue-dep.yaml |
||||
|
- base/catalogue-svc.yaml |
||||
|
- base/front-end-dep.yaml |
||||
|
- base/front-end-svc.yaml |
||||
|
- base/orders-db-dep.yaml |
||||
|
- base/orders-db-svc.yaml |
||||
|
- base/orders-dep.yaml |
||||
|
- base/orders-svc.yaml |
||||
|
- base/payment-dep.yaml |
||||
|
- base/payment-svc.yaml |
||||
|
- base/queue-master-dep.yaml |
||||
|
- base/queue-master-svc.yaml |
||||
|
- base/rabbitmq-dep.yaml |
||||
|
- base/rabbitmq-svc.yaml |
||||
|
- base/session-db-dep.yaml |
||||
|
- base/session-db-svc.yaml |
||||
|
- base/shipping-dep.yaml |
||||
|
- base/shipping-svc.yaml |
||||
|
- base/user-db-dep.yaml |
||||
|
- base/user-db-svc.yaml |
||||
|
- base/user-dep.yaml |
||||
|
- base/user-svc.yaml |
Loading…
Reference in new issue