├── .gitignore ├── autoscale.yaml ├── service.yaml ├── service-build.yaml ├── ingress.yaml ├── ingress-klinikk.yaml ├── LICENSE ├── deployment.yaml ├── worker.yaml ├── aws-dns ├── README.md ├── create └── deploy /.gitignore: -------------------------------------------------------------------------------- 1 | config 2 | -------------------------------------------------------------------------------- /autoscale.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v1 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: {{deploy_name}} 5 | namespace: {{namespace}} 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: extensions/v1beta1 9 | kind: Deployment 10 | name: {{deploy_name}} 11 | minReplicas: 1 12 | maxReplicas: 10 13 | targetCPUUtilizationPercentage: 70 14 | --- 15 | -------------------------------------------------------------------------------- /service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: "{{application_name}}" 5 | namespace: "{{namespace}}" 6 | spec: 7 | clusterIP: "{{cluster_ip}}" 8 | ports: 9 | - port: 80 10 | protocol: TCP 11 | targetPort: 80 12 | selector: 13 | application: "{{application_name}}" 14 | build: "{{build_id}}" 15 | sessionAffinity: None 16 | --- 17 | -------------------------------------------------------------------------------- /service-build.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: "{{application_name}}-{{build_id}}" 5 | namespace: "{{namespace}}" 6 | spec: 7 | clusterIP: "{{cluster_ip}}" 8 | ports: 9 | - port: 80 10 | protocol: TCP 11 | targetPort: 80 12 | selector: 13 | application: "{{application_name}}" 14 | build: "{{build_id}}" 15 | sessionAffinity: None 16 | --- 17 | -------------------------------------------------------------------------------- /ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: "{{application_name}}" 5 | namespace: "{{namespace}}" 6 | annotations: 7 | # enable kube-lego for this ingress 8 | kubernetes.io/tls-acme: "true" 9 | kubernetes.io/ingress.class: "nginx" 10 | # nginx params 11 | nginx.org/client-max-body-size: "100m" 12 | nginx.org/http2: "true" 13 | nginx.org/hsts: "true" 14 | nginx.org/hsts-max-age: "15638400" 15 | nginx.org/hsts-include-subdomains: "true" 16 | nginx.org/server-tokens: "false" 17 | spec: 18 | -------------------------------------------------------------------------------- /ingress-klinikk.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: "{{application_name}}-clinics" 5 | namespace: "{{namespace}}" 6 | annotations: 7 | # enable kube-lego for this ingress 8 | kubernetes.io/tls-acme: "false" 9 | kubernetes.io/ingress.class: "nginx" 10 | # nginx params 11 | nginx.org/client-max-body-size: "100m" 12 | nginx.org/http2: "true" 13 | nginx.org/hsts: "true" 14 | nginx.org/hsts-max-age: "15638400" 15 | nginx.org/hsts-include-subdomains: "true" 16 | nginx.org/server-tokens: "false" 17 | spec: 18 | tls: 19 | - hosts: 20 | - "{{hostname}}" 21 | secretName: "{{cert}}" 22 | rules: 23 | - host: "{{hostname}}" 24 | http: 25 | paths: 26 | - backend: 27 | serviceName: "{{application_name}}" 28 | servicePort: 80 29 | path: / 30 | --- 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 pasientskyhosting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | application: "{{application_name}}" 6 | build: "{{build_id}}" 7 | type: "application" 8 | bamboo_deploy_release: "{{bamboo_deploy_release}}" 9 | name: "{{deploy_name}}" 10 | namespace: "{{namespace}}" 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | application: "{{application_name}}" 16 | build: "{{build_id}}" 17 | strategy: 18 | rollingUpdate: 19 | maxSurge: 1 20 | maxUnavailable: 1 21 | type: RollingUpdate 22 | template: 23 | metadata: 24 | labels: 25 | application: "{{application_name}}" 26 | build: "{{build_id}}" 27 | type: "application" 28 | bamboo_deploy_release: "{{bamboo_deploy_release}}" 29 | spec: 30 | containers: 31 | - image: {{image}} 32 | imagePullPolicy: Always 33 | name: "{{deploy_name}}" 34 | readinessProbe: 35 | httpGet: 36 | path: /readiness 37 | port: 80 38 | scheme: HTTP 39 | initialDelaySeconds: 60 40 | periodSeconds: 60 41 | failureThreshold: 5 42 | timeoutSeconds: 5 43 | successThreshold: 2 44 | livenessProbe: 45 | httpGet: 46 | path: /healthz 47 | port: 80 48 | scheme: HTTP 49 | initialDelaySeconds: 60 50 | periodSeconds: 60 51 | failureThreshold: 3 52 | timeoutSeconds: 1 53 | successThreshold: 1 54 | env: 55 | - name: PS_CONSUL_URL 56 | value: "{{CONSUL_URL}}" 57 | - name: PS_CONSUL_USERNAME 58 | value: "{{CONSUL_USERNAME}}" 59 | - name: PS_CONSUL_PASSWORD 60 | value: "{{CONSUL_PASSWORD}}" 61 | - name: PS_APPLICATION 62 | value: "{{CONSUL_APPLICATION}}" 63 | - name: PS_ENVIRONMENT 64 | value: "{{CONSUL_ENVIRONMENT}}" 65 | - name: PS_BUILD_ID 66 | value: "{{build_id}}" 67 | - name: PS_BUILD_NR 68 | value: "{{build_nr}}" 69 | - name: GIT_REPO 70 | value: "{{git_repo}}" 71 | - name: SSH_KEY 72 | value: "{{ssh_key}}" 73 | - name: NEW_RELIC_LICENSE_KEY 74 | value: "{{NEW_RELIC_LICENSE_KEY}}" 75 | resources: 76 | requests: 77 | cpu: 100m #1000m = 1 cpu core 78 | ports: 79 | - containerPort: 80 80 | protocol: TCP 81 | restartPolicy: Always 82 | terminationGracePeriodSeconds: 30 83 | --- 84 | -------------------------------------------------------------------------------- /worker.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | application: "wrk-{{application_name}}" 6 | build: "{{build_id}}" 7 | type: "worker" 8 | bamboo_deploy_release: "{{bamboo_deploy_release}}" 9 | name: "wrk-{{deploy_name}}" 10 | namespace: "{{namespace}}" 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | application: "wrk-{{application_name}}" 16 | build: "{{build_id}}" 17 | strategy: 18 | rollingUpdate: 19 | maxSurge: 1 20 | maxUnavailable: 1 21 | type: RollingUpdate 22 | template: 23 | metadata: 24 | labels: 25 | application: "wrk-{{application_name}}" 26 | build: "{{build_id}}" 27 | type: "worker" 28 | bamboo_deploy_release: "{{bamboo_deploy_release}}" 29 | spec: 30 | containers: 31 | - image: "{{image}}" 32 | imagePullPolicy: Always 33 | name: "wrk-{{deploy_name}}" 34 | # readinessProbe: 35 | # httpGet: 36 | # path: /readiness 37 | # port: 80 38 | # scheme: HTTP 39 | # initialDelaySeconds: 60 40 | # periodSeconds: 60 41 | # failureThreshold: 5 42 | # timeoutSeconds: 5 43 | # successThreshold: 2 44 | # livenessProbe: 45 | # httpGet: 46 | # path: /healthz 47 | # port: 80 48 | # scheme: HTTP 49 | # initialDelaySeconds: 60 50 | # periodSeconds: 60 51 | # failureThreshold: 3 52 | # timeoutSeconds: 1 53 | # successThreshold: 1 54 | env: 55 | - name: PS_CONSUL_URL 56 | value: "{{CONSUL_URL}}" 57 | - name: PS_CONSUL_USERNAME 58 | value: "{{CONSUL_USERNAME}}" 59 | - name: PS_CONSUL_PASSWORD 60 | value: "{{CONSUL_PASSWORD}}" 61 | - name: PS_APPLICATION 62 | value: "{{CONSUL_APPLICATION}}" 63 | - name: PS_ENVIRONMENT 64 | value: "{{CONSUL_ENVIRONMENT}}" 65 | - name: PS_BUILD_ID 66 | value: "{{build_id}}" 67 | - name: PS_BUILD_NR 68 | value: "{{build_nr}}" 69 | - name: GIT_REPO 70 | value: "{{git_repo}}" 71 | - name: SSH_KEY 72 | value: "{{ssh_key}}" 73 | - name: NEW_RELIC_LICENSE_KEY 74 | value: "{{NEW_RELIC_LICENSE_KEY}}" 75 | resources: 76 | requests: 77 | cpu: 200m #1000m = 1 cpu core 78 | ports: 79 | - containerPort: 80 80 | protocol: TCP 81 | restartPolicy: Always 82 | terminationGracePeriodSeconds: 30 83 | --- 84 | -------------------------------------------------------------------------------- /aws-dns: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 3 | if [ ! -f config ]; 4 | then 5 | echo "Missing config file" 1>&2 6 | exit 1 7 | fi 8 | source config 9 | 10 | echo $AWS_PASSWORD_ACCESS_KEY_ID 11 | echo $AWS_SECRET_ACCESS_KEY 12 | 13 | # More advanced options below 14 | # The Time-To-Live of this recordset 15 | TTL=60 16 | 17 | # Change this if you want 18 | COMMENT="Auto by PSDEV Bamboo @ `date`" 19 | 20 | # Change to AAAA if using an IPv6 address 21 | TYPE="A" 22 | 23 | function valid_ip() 24 | { 25 | local ip=$1 26 | local stat=1 27 | 28 | if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 29 | OIFS=$IFS 30 | IFS='.' 31 | ip=($ip) 32 | IFS=$OIFS 33 | [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ 34 | && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] 35 | stat=$? 36 | fi 37 | return $stat 38 | } 39 | 40 | if ! valid_ip $hostname_ip; then 41 | echo "Invalid IP address: $hostname_ip" 42 | exit 1 43 | fi 44 | 45 | # Add clinic url 46 | if [ $has_clinic_site -eq 1 ]; 47 | then 48 | hostname="$hostname,$aws_hostname_clinic" 49 | fi 50 | 51 | array=(`echo $hostname | sed 's/,/\n/g'`) 52 | for line in "${array[@]}" 53 | do 54 | 55 | if dig @8.8.8.8 "$line" | grep "$line" | grep "$hostname_ip" > /dev/null; 56 | then 57 | echo "DNS record $line already points to $hostname_ip . No need to update it at AWS" 58 | continue 59 | fi 60 | 61 | TMPFILE=$(mktemp /tmp/temporary-file.XXXXXXXX) 62 | cat > ${TMPFILE} << EOF 63 | { 64 | "Comment":"$COMMENT", 65 | "Changes":[ 66 | { 67 | "Action":"UPSERT", 68 | "ResourceRecordSet":{ 69 | "ResourceRecords":[ 70 | { 71 | "Value":"$hostname_ip" 72 | } 73 | ], 74 | "Name":"$line", 75 | "Type":"$TYPE", 76 | "TTL":$TTL 77 | } 78 | } 79 | ] 80 | } 81 | EOF 82 | 83 | echo "Creating dns record for $line that points to ip $hostname_ip" 84 | # Update the Hosted Zone record 85 | aws route53 change-resource-record-sets --hosted-zone-id $zoneid --change-batch file://"$TMPFILE" --debug 86 | if [ $? -ne 0 ]; 87 | then 88 | echo "Failed to create dns record at Amazon" 89 | # Clean up 90 | rm $TMPFILE 91 | exit 1 92 | fi 93 | 94 | # Clean up 95 | rm $TMPFILE 96 | 97 | # Wait for dns to become active 98 | echo "Waiting for dns to become active:" 99 | failtime=$(($(date +%s) + 5 * 60)) 100 | while ! dig @8.8.8.8 "$line" | grep "$line" | grep "$hostname_ip" > /dev/null; 101 | do 102 | 103 | if [ $(date +%s) -gt "$failtime" ]; 104 | then 105 | echo "... Failed" 106 | exit 1 107 | fi 108 | 109 | echo "." 110 | sleep 5 111 | done 112 | echo "... Done" 113 | 114 | sleep 5 115 | done 116 | 117 | 118 | exit 0 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kubernetes-blue-green-deploy 2 | Blue Green deployment script 3 | 4 | This project in integrated into our Bamboo environment where we push code to bitbucket first. Then we do a blue green deployment on K8s 5 | 6 | # Requirements 7 | You need the kubectl commandline tool and the aws-cli tool installed on your bamboo server 8 | 9 | #Sample script 10 | This script is from Bamboo that is run in the deploment process. It needs to get it's parameters from Consul. Should be fairly easy to translate them. 11 | 12 | ``` 13 | #!/bin/bash 14 | 15 | # Clone the deployment code 16 | if [ -d kubernetes-blue-green-deploy ]; then 17 | rm -rf kubernetes-blue-green-deploy 18 | fi 19 | git clone https://github.com/pasientskyhosting/kubernetes-blue-green-deploy.git || exit 1 20 | 21 | # Get into directory 22 | cd kubernetes-blue-green-deploy 23 | 24 | # Cleanup config 25 | rm config 26 | 27 | # Fetch configuration 28 | ssh_key=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/deployment_ssh_key?raw) 29 | git_repo=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/git_url?raw) 30 | image=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/docker_image?raw) 31 | hostname=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/aws_hostname?raw) 32 | hostname_ip=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/aws_hostname_ip?raw) 33 | zoneid=$(curl -skLu ${bamboo_CONSUL_USERNAME}:${bamboo_CONSUL_PASSWORD} ${bamboo_CONSUL_URL}/v1/kv/${bamboo_CONSUL_ENVIRONMENT}/${bamboo_CONSUL_APPLICATION}/bamboo/aws_zoneid?raw) 34 | 35 | # validate vars before we deploy 36 | if [ "$ssh_key" == "" ]; 37 | then 38 | echo "Missing deployment ssh key" 1>&2 39 | exit 1 40 | fi 41 | 42 | if [ "$git_repo" == "" ]; 43 | then 44 | echo "Missing git repo to deploy from" 1>&2 45 | exit 1 46 | fi 47 | 48 | if [ "$bamboo_CONSUL_APPLICATION" == "" ]; 49 | then 50 | echo "Missing application name. What is this?" 1>&2 51 | exit 1 52 | fi 53 | 54 | if [ "$image" == "" ]; 55 | then 56 | echo "Missing docker image to use?" 1>&2 57 | exit 1 58 | fi 59 | 60 | if [ "$hostname" == "" ]; 61 | then 62 | echo "Missing hostname that should respond to this deployment" 1>&2 63 | exit 1 64 | fi 65 | 66 | if [ "$bamboo_CONSUL_ENVIRONMENT" == "" ]; 67 | then 68 | echo "Missing the namespace this belongs to" 1>&2 69 | exit 1 70 | fi 71 | 72 | do_aws_dns=0 73 | if [ "$zoneid" != "" ]; 74 | then 75 | if [ "$hostname_ip" != "" ]; 76 | then 77 | do_aws_dns=1 78 | fi 79 | fi 80 | 81 | if [ ! -f ../version.commit ]; then 82 | echo "Missing version.commit file" 1>&2 83 | exit 1 84 | fi 85 | 86 | # Write config 87 | echo "ssh_key=\"$ssh_key\"" > config 88 | echo "build_id=\"`cat ../version.commit`\"" >> config 89 | echo "git_repo=\"$git_repo\"" >> config 90 | echo "application_name=\"$bamboo_CONSUL_APPLICATION\"" >> config 91 | echo "image=\"$image\"" >> config 92 | echo "hostname=\"$hostname\"" >> config 93 | echo "namespace=\"$bamboo_CONSUL_ENVIRONMENT\"" >> config 94 | echo "zoneid=\"$zoneid\"" >> config 95 | echo "hostname_ip=\"$hostname_ip\"" >> config 96 | 97 | if [ $do_aws_dns -eq 1 ]; then 98 | ./aws-dns || exit 1 99 | fi 100 | 101 | # Create service 102 | ./create || exit 1 103 | 104 | # Deploy to kubernetes 105 | ./deploy || exit 1 106 | ``` 107 | -------------------------------------------------------------------------------- /create: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 3 | 4 | function cleanup() 5 | { 6 | echo "I got canceled" 7 | } 8 | trap "cleanup" SIGKILL SIGTERM SIGHUP SIGINT 9 | 10 | echo "Want to try to see if trap works?" 11 | sleep 5 12 | 13 | if [ ! -f config ]; 14 | then 15 | echo "Missing config file" 1>&2 16 | exit 1 17 | fi 18 | source config 19 | 20 | kubectl get namespace $namespace > /dev/null 2>&1 21 | if [ $? -ne 0 ]; 22 | then 23 | kubectl create namespace $namespace 24 | echo "Created namespace $namespace in cluster" 25 | fi 26 | 27 | method="apply" 28 | kubectl get ingress $application_name --namespace=$namespace > /dev/null 2>&1 29 | if [ $? -eq 0 ]; 30 | then 31 | echo "Ingress controller for application already exists" 1>&2 32 | method="replace" 33 | fi 34 | 35 | # add hostnames 36 | array=(`echo $hostname | sed 's/,/\n/g'`) 37 | 38 | echo -e " tls:" >> ingress.yaml 39 | for line in "${array[@]}" 40 | do 41 | echo -e " - hosts:\n - \"$line\"\n secretName: $line\n" >> ingress.yaml 42 | done 43 | 44 | echo -e " rules:" >> ingress.yaml 45 | 46 | for line in "${array[@]}" 47 | do 48 | echo -e " - host: $line\n http:\n paths:\n - backend:\n serviceName: {{application_name}}\n servicePort: 80\n path: /" >> ingress.yaml 49 | done 50 | 51 | cat ingress.yaml \ 52 | | sed "s/{{application_name}}/$application_name/" \ 53 | | sed "s/{{namespace}}/$namespace/" \ 54 | | sed "s/{{build_id}}/$build_id/" \ 55 | | kubectl $method -f - 56 | 57 | # Check for klinikk portal 58 | if [ $has_clinic_site -eq 1 ]; 59 | then 60 | method="apply" 61 | kubectl get ingress "$application_name-clinics" --namespace=$namespace > /dev/null 2>&1 62 | if [ $? -eq 0 ]; 63 | then 64 | method="replace" 65 | fi 66 | 67 | cat ingress-klinikk.yaml \ 68 | | sed "s/{{application_name}}/$application_name/" \ 69 | | sed "s/{{namespace}}/$namespace/" \ 70 | | sed "s/{{hostname}}/$aws_hostname_clinic/" \ 71 | | sed "s/{{cert}}/$clinic_cert_name/" \ 72 | | sed "s/{{build_id}}/$build_id/" \ 73 | | kubectl $method -f - 74 | fi 75 | 76 | method="apply" 77 | cluster_ip="" 78 | kubectl get service $application_name-$build_id --namespace=$namespace > /dev/null 2>&1 79 | if [ $? -eq 0 ]; 80 | then 81 | echo "Service for application already exists" 1>&2 82 | method="replace" 83 | cluster_ip="`kubectl get service $application_name-$build_id -o yaml --namespace=$namespace | grep 'clusterIP:' | cut -d ':' -f 2 | tr -d ' '`" 84 | build_id="`kubectl get service $application_name-$build_id -o yaml --namespace=$namespace | grep 'build:' | cut -d ':' -f 2 | tr -d ' ' | tr -d '\"'`" 85 | fi 86 | 87 | cat service-build.yaml \ 88 | | sed "s/{{application_name}}/$application_name/" \ 89 | | sed "s/{{namespace}}/$namespace/" \ 90 | | sed "s/{{cluster_ip}}/$cluster_ip/" \ 91 | | sed "s/{{build_id}}/$build_id/" \ 92 | | kubectl $method -f - 93 | 94 | method="apply" 95 | cluster_ip="" 96 | build_id=0 97 | kubectl get service $application_name --namespace=$namespace > /dev/null 2>&1 98 | if [ $? -eq 0 ]; 99 | then 100 | echo "Service for application already exists" 1>&2 101 | method="replace" 102 | cluster_ip="`kubectl get service $application_name -o yaml --namespace=$namespace | grep 'clusterIP:' | cut -d ':' -f 2 | tr -d ' '`" 103 | build_id="`kubectl get service $application_name -o yaml --namespace=$namespace | grep 'build:' | cut -d ':' -f 2 | tr -d ' ' | tr -d '\"'`" 104 | fi 105 | 106 | cat service.yaml \ 107 | | sed "s/{{application_name}}/$application_name/" \ 108 | | sed "s/{{namespace}}/$namespace/" \ 109 | | sed "s/{{cluster_ip}}/$cluster_ip/" \ 110 | | sed "s/{{build_id}}/$build_id/" \ 111 | | kubectl $method -f - 112 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 3 | 4 | if [ ! -f config ]; 5 | then 6 | echo "Missing config file" 1>&2 7 | exit 1 8 | fi 9 | source config 10 | 11 | # Validate application and deployment 12 | kubectl get ingress $application_name --namespace=$namespace > /dev/null 2>&1 13 | if [ $? -ne 0 ]; 14 | then 15 | echo "Ingress controller for application does not exists" 1>&2 16 | exit 1 17 | fi 18 | 19 | #kubectl get service $application_name-${build_id} --namespace=$namespace > /dev/null 2>&1 20 | kubectl get service $application_name --namespace=$namespace > /dev/null 2>&1 21 | if [ $? -ne 0 ]; 22 | then 23 | echo "Service for application does not exists" 1>&2 24 | exit 1 25 | fi 26 | 27 | kubectl get deployment ${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 28 | if [ $? -eq 0 ]; 29 | then 30 | echo "Deployment already exists" 1>&2 31 | exit 1 32 | fi 33 | 34 | kubectl get hpa ${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 35 | if [ $? -eq 0 ]; 36 | then 37 | echo "Autoscale group already exists" 1>&2 38 | exit 1 39 | fi 40 | 41 | has_worker=0 42 | if [ -f ../boot ] || [ -f ../WorkerBoot ] || [ -f ../bin/console ]; then 43 | has_worker=1 44 | echo "Deployment needs a worker too" 45 | else 46 | echo "Deployment does not have a worker" 47 | fi 48 | 49 | # Get current config 50 | current_service_build="`kubectl get service $application_name -o yaml --namespace=$namespace | grep build:`" 51 | #current_service_build="`kubectl get ingress $application_name -o yaml --namespace=$namespace | grep -m 1 build:`" 52 | current_build_id=`echo $current_service_build | cut -d ":" -f 2 | tr -d ' ' | tr -d '"'` 53 | current_release="${application_name}-${current_build_id}" 54 | 55 | # Apply new autoscale and startup new deployment 56 | echo "Deploying autoscaler for application and deployment" 57 | cat autoscale.yaml deployment.yaml \ 58 | | sed "s/{{application_name}}/$application_name/" \ 59 | | sed "s/{{build_id}}/$build_id/" \ 60 | | sed "s/{{deploy_name}}/${application_name}-${build_id}/" \ 61 | | sed "s|{{image}}|$image|" \ 62 | | sed "s|{{git_repo}}|$git_repo|" \ 63 | | sed "s/{{ssh_key}}/$ssh_key/" \ 64 | | sed "s/{{namespace}}/$namespace/" \ 65 | | sed "s|{{CONSUL_URL}}|$bamboo_CONSUL_URL|" \ 66 | | sed "s|{{CONSUL_USERNAME}}|$bamboo_CONSUL_USERNAME|" \ 67 | | sed "s|{{CONSUL_PASSWORD}}|$bamboo_CONSUL_PASSWORD|" \ 68 | | sed "s|{{CONSUL_APPLICATION}}|$bamboo_CONSUL_APPLICATION|" \ 69 | | sed "s|{{CONSUL_ENVIRONMENT}}|$bamboo_CONSUL_ENVIRONMENT|" \ 70 | | sed "s|{{build_nr}}|$bamboo_buildNumber|" \ 71 | | sed "s/{{NEW_RELIC_LICENSE_KEY}}/$bamboo_NEW_RELIC_LICENSE_KEY_PASSWORD/" \ 72 | | sed "s/{{bamboo_deploy_release}}/$bamboo_deploy_release/" \ 73 | | kubectl apply -f - 74 | 75 | echo "Waiting for ${application_name}-${build_id} to be available:" 76 | check=0 77 | failtime=$(($(date +%s) + 5 * 60)) 78 | while [ $check -lt 1 ]; 79 | do 80 | # check deployment 81 | check_app=`kubectl get deployment ${application_name}-${build_id} -o yaml --namespace=$namespace | grep "^ availableReplicas:" | cut -d ":" -f 2 | tr -d ' ' | grep -Eo '[0-9]+'` 82 | 83 | if [ "$check_app" == "" ]; 84 | then 85 | check=0 86 | 87 | echo "." 88 | sleep 5 89 | else 90 | check=1 91 | fi 92 | 93 | if [ $(date +%s) -gt "$failtime" ]; 94 | then 95 | echo "... Failed" 96 | echo "Going to cleanup" 97 | kubectl delete service ${application_name}-${build_id} --namespace=$namespace 98 | kubectl delete hpa ${application_name}-${build_id} --namespace=$namespace 99 | kubectl delete deployment ${application_name}-${build_id} --namespace=$namespace 100 | kubectl delete rs ${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 101 | exit 1 102 | fi 103 | done 104 | 105 | echo ".. Done" 106 | 107 | # Worker 108 | if [ $has_worker -eq 1 ]; then 109 | 110 | if [ "$worker_image" == "" ]; 111 | then 112 | echo "Missing docker image to use for worker" 1>&2 113 | #kubectl delete service ${application_name}-${build_id} --namespace=$namespace 114 | kubectl delete hpa ${application_name}-${build_id} --namespace=$namespace 115 | kubectl delete deployment ${application_name}-${build_id} --namespace=$namespace 116 | kubectl delete rs ${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 117 | exit 1 118 | fi 119 | 120 | echo "Deploying worker" 121 | cat worker.yaml \ 122 | | sed "s/{{application_name}}/$application_name/" \ 123 | | sed "s/{{build_id}}/$build_id/" \ 124 | | sed "s/{{deploy_name}}/${application_name}-${build_id}/" \ 125 | | sed "s|{{image}}|$worker_image|" \ 126 | | sed "s|{{git_repo}}|$git_repo|" \ 127 | | sed "s/{{ssh_key}}/$ssh_key/" \ 128 | | sed "s/{{namespace}}/$namespace/" \ 129 | | sed "s|{{CONSUL_URL}}|$bamboo_CONSUL_URL|" \ 130 | | sed "s|{{CONSUL_USERNAME}}|$bamboo_CONSUL_USERNAME|" \ 131 | | sed "s|{{CONSUL_PASSWORD}}|$bamboo_CONSUL_PASSWORD|" \ 132 | | sed "s|{{CONSUL_APPLICATION}}|$bamboo_CONSUL_APPLICATION|" \ 133 | | sed "s|{{CONSUL_ENVIRONMENT}}|$bamboo_CONSUL_ENVIRONMENT|" \ 134 | | sed "s/{{NEW_RELIC_LICENSE_KEY}}/$bamboo_NEW_RELIC_LICENSE_KEY_PASSWORD/" \ 135 | | sed "s|{{build_nr}}|$bamboo_buildNumber|" \ 136 | | sed "s/{{bamboo_deploy_release}}/$bamboo_deploy_release/" \ 137 | | kubectl apply -f - 138 | 139 | echo "Waiting for wrk-${application_name}-${build_id} to be available:" 140 | check=0 141 | failtime=$(($(date +%s) + 5 * 60)) 142 | while [ $check -lt 1 ]; 143 | do 144 | check_wrk=`kubectl get deployment wrk-${application_name}-${build_id} -o yaml --namespace=$namespace | grep "^ availableReplicas:" | cut -d ":" -f 2 | tr -d ' ' | grep -Eo '[0-9]+'` 145 | 146 | if [ "$check_wrk" == "" ]; 147 | then 148 | check=0 149 | echo "." 150 | sleep 5 151 | else 152 | check=1 153 | fi 154 | 155 | if [ $(date +%s) -gt "$failtime" ]; 156 | then 157 | echo "... Failed" 158 | echo "Going to cleanup" 159 | kubectl delete service ${application_name}-${build_id} --namespace=$namespace 160 | kubectl delete hpa ${application_name}-${build_id} --namespace=$namespace 161 | kubectl delete deployment ${application_name}-${build_id} --namespace=$namespace 162 | kubectl delete deployment wrk-${application_name}-${build_id} --namespace=$namespace 163 | kubectl delete rs ${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 164 | kubectl delete rs wrk-${application_name}-${build_id} --namespace=$namespace > /dev/null 2>&1 165 | exit 1 166 | fi 167 | done 168 | 169 | echo ".. Done" 170 | fi 171 | 172 | # Switch servide to point to new release 173 | echo "Switching to build id: $build_id" 174 | #kubectl get ingress $application_name -o yaml --namespace=$namespace | sed "s/build:.*$/build: \"$build_id\"/" | kubectl replace -f - 175 | kubectl get service $application_name -o yaml --namespace=$namespace | sed "s/build:.*$/build: \"$build_id\"/" | kubectl replace -f - 176 | 177 | # - delete service 178 | kubectl get service $current_release --namespace=$namespace > /dev/null 2>&1 179 | if [ $? -eq 0 ]; 180 | then 181 | kubectl delete service $current_release --namespace=$namespace 182 | fi 183 | 184 | # - delete autoscale 185 | kubectl get hpa $current_release --namespace=$namespace > /dev/null 2>&1 186 | if [ $? -eq 0 ]; 187 | then 188 | kubectl delete hpa $current_release --namespace=$namespace 189 | fi 190 | 191 | # - delete deployment 192 | kubectl get deployment $current_release --namespace=$namespace > /dev/null 2>&1 193 | if [ $? -eq 0 ]; 194 | then 195 | kubectl delete deployment $current_release --namespace=$namespace 196 | fi 197 | 198 | # - delete replica 199 | kubectl get rs $current_release --namespace=$namespace > /dev/null 2>&1 200 | if [ $? -eq 0 ]; 201 | then 202 | kubectl delete rs $current_release --namespace=$namespace 203 | fi 204 | 205 | # - delete deployment worker if it exits 206 | kubectl get deployment wrk-$current_release --namespace=$namespace > /dev/null 2>&1 207 | if [ $? -eq 0 ]; 208 | then 209 | kubectl delete deployment wrk-$current_release --namespace=$namespace 210 | fi 211 | 212 | # - delete deployment worker if it exits 213 | kubectl get rs wrk-$current_release --namespace=$namespace > /dev/null 2>&1 214 | if [ $? -eq 0 ]; 215 | then 216 | kubectl delete rs wrk-$current_release --namespace=$namespace 217 | fi 218 | --------------------------------------------------------------------------------