...
 
Commits (13)
......@@ -5,7 +5,7 @@ variables:
DOCKER_DRIVER: overlay
before_script:
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
development:
stage: deploy
......
......@@ -11,14 +11,15 @@ RUN apk add -U openssl curl tar gzip bash ca-certificates && \
RUN apk add ruby git
# Install Helm
#RUN curl https://kubernetes-helm.storage.googleapis.com/helm-v2.0.2-linux-amd64.tar.gz | \
# tar zx && mv linux-amd64/helm /usr/bin/ && \
# helm version --client
RUN curl https://kubernetes-helm.storage.googleapis.com/helm-v2.5.0-linux-amd64.tar.gz | \
tar zx && mv linux-amd64/helm /usr/bin/ && \
helm version --client
## Install Helm Canary
#RUN curl https://kubernetes-helm.storage.googleapis.com/helm-canary-linux-amd64.tar.gz | \
# tar zx && mv linux-amd64/helm /usr/bin/ && \
# helm version --client
# Install Helm Canary
# RUN date && \
# curl https://kubernetes-helm.storage.googleapis.com/helm-canary-linux-amd64.tar.gz | \
# tar zx && mv linux-amd64/helm /usr/bin/ && \
# helm version --client
# Install kubectl
RUN curl -L -o /usr/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/latest.txt)/bin/linux/amd64/kubectl && \
......
REPO := registry.gitlab.com/ayufan/kubernetes-deploy-demo:latest
REPO := registry.gitlab.com/ayufan/kubernetes-deploy:latest
build:
docker build -t $(REPO) .
docker build -q -t $(REPO) .
build_and_push: build
docker push $(REPO)
......@@ -9,10 +9,16 @@ build_and_push: build
build_test: build
source .dev_env && cd examples/rails-app/ && ../../build
deploy_test: build
deploy_test:
source .dev_env && cd examples/rails-app/ && ../../deploy
build_and_enter: build
docker run --privileged -it --rm -v $(shell pwd):/app -w /app $(REPO) /bin/bash --login
.PHONY: build build_and_push build_test build_and_enter deploy_test
build_and_test: build
docker run --privileged -it --rm -v $(shell pwd):/app -w /app $(REPO) /bin/bash --login -c 'source .dev_env && ./deploy'
build_and_test_canary: build
docker run --privileged -it --rm -v $(shell pwd):/app -w /app $(REPO) /bin/bash --login -c 'source .dev_env && ./deploy canary'
.PHONY: build build_and_push build_test build_and_enter deploy_test build_and_test
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
apiVersion: v1
description: Auto Deploy an app using GitLab
name: app
version: 0.1.1
dependencies:
- name: postgresql
version: "0.7.1"
repository: "https://kubernetes-charts.storage.googleapis.com/"
condition: postgresql.enabled
{{- if .Values.service.enabled -}}
Application should be accessible at: {{ .Values.service.url }}
{{- else -}}
Application will be accessible at: {{ .Values.service.url }} when you deploy stable track.
{{- end -}}
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trimSuffix "-app" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "appname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- $releaseName := default .Release.Name .Values.releaseOverride -}}
{{- printf "%s-%s" $releaseName $name | trimSuffix "-app" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Get a hostname from URL
*/}}
{{- define "hostname" -}}
{{- . | trimPrefix "http://" | trimPrefix "https://" | trimSuffix "/" | quote -}}
{{- end -}}
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "appname" . }}
track: "{{ .Values.application.track }}"
tier: "{{ .Values.application.tier }}"
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ template "appname" . }}
track: "{{ .Values.application.track }}"
tier: "{{ .Values.application.tier }}"
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
imagePullSecrets:
{{ toYaml .Values.image.secrets | indent 10 }}
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: DATABASE_URL
value: {{ .Values.application.database_url | quote }}
ports:
- name: "{{ .Values.service.name }}"
containerPort: {{ .Values.service.internalPort }}
livenessProbe:
httpGet:
path: /
port: {{ .Values.service.internalPort }}
initialDelaySeconds: 15
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /
port: {{ .Values.service.internalPort }}
initialDelaySeconds: 5
timeoutSeconds: 3
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.service.enabled -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "appname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
annotations:
kubernetes.io/tls-acme: "true"
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- {{ template "hostname" .Values.service.url }}
secretName: {{ template "fullname" . }}-tls
rules:
- host: {{ template "hostname" .Values.service.url }}
http:
paths:
- path: /
backend:
serviceName: {{ template "fullname" . }}
servicePort: {{ .Values.service.externalPort }}
{{- end -}}
{{- if .Values.service.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "appname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ template "appname" . }}
tier: "{{ .Values.application.tier }}"
{{- end -}}
# Default values for chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: # gitlab.example.com/group/project
tag: stable
pullPolicy: Always
application:
track: stable
tier: web
service:
enabled: true
name: web
type: ClusterIP
url: http://my.host.com/
externalPort: 5000
internalPort: 5000
postgresql:
enabled: true
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
......@@ -14,17 +14,8 @@ source "$DEPLOY_ROOT_DIR/src/common.bash"
ensure_environment_url
ensure_deploy_variables
create_kubeconfig
CI_ENVIRONMENT_HOSTNAME="${CI_ENVIRONMENT_URL}"
CI_ENVIRONMENT_HOSTNAME="${CI_ENVIRONMENT_HOSTNAME/http:\/\//}"
CI_ENVIRONMENT_HOSTNAME="${CI_ENVIRONMENT_HOSTNAME/https:\/\//}"
cat <<EOF | kubectl apply -f -
kind: Namespace
apiVersion: v1
metadata:
name: $KUBE_NAMESPACE
EOF
ensure_namespace
install_tiller
kubectl create secret -n $KUBE_NAMESPACE \
docker-registry gitlab-registry \
......@@ -37,10 +28,6 @@ kubectl create secret -n $KUBE_NAMESPACE \
track="${1-stable}"
name="$CI_ENVIRONMENT_SLUG"
if [[ "$track" != "stable" ]]; then
name="$name-$track"
fi
replicas="1"
env_track="${track^^}"
......@@ -61,176 +48,71 @@ else
fi
fi
if [[ -d chart/Chart.yaml ]]; then
echo "Using project-provided Chart.yaml..."
cd chart/
else
echo "Using Auto Deploy Chart..."
cd "$DEPLOY_ROOT_DIR/chart/"
fi
echo "Downloading dependencies..."
helm dependency build .
echo ""
POSTGRES_ENABLED="false"
if [[ -z "$DISABLE_POSTGRES" ]]; then
echo "Configuring database..."
postgres_name="${CI_ENVIRONMENT_SLUG}-postgres"
postgres_app="${CI_ENVIRONMENT_SLUG}-postgres"
POSTGRES_USER="${POSTGRES_USER:-user}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-password}"
POSTGRES_DB="${POSTGRES_DB:-$env_slug}"
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${postgres_name}:5432/$POSTGRES_DB"
cat <<EOF | kubectl apply -n $KUBE_NAMESPACE --force -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: $postgres_name
namespace: $KUBE_NAMESPACE
labels:
app: $postgres_app
tier: database
spec:
replicas: 1
template:
metadata:
labels:
name: $postgres_name
app: $postgres_app
tier: database
spec:
imagePullSecrets:
- name: gitlab-registry
containers:
- name: app
image: postgres:9.6-alpine
imagePullPolicy: Always
env:
- name: POSTGRES_USER
value: "$POSTGRES_USER"
- name: POSTGRES_PASSWORD
value: "$POSTGRES_PASSWORD"
- name: POSTGRES_DB
value: "$POSTGRES_DB"
ports:
- name: postgres
containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: $postgres_name
namespace: $KUBE_NAMESPACE
labels:
app: $postgres_app
spec:
ports:
- name: postgres
port: 5432
targetPort: postgres
selector:
app: $postgres_app
tier: database
EOF
echo "Waiting for deployment..."
kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$postgres_name"
POSTGRES_ENABLED="true"
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/$POSTGRES_DB/"
fi
set -x
echo "Deploying $CI_ENVIRONMENT_SLUG (track: $track, replicas: $replicas) with $CI_REGISTRY_IMAGE:$CI_REGISTRY_TAG..."
cat <<EOF | kubectl apply -n $KUBE_NAMESPACE --force -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: $name
namespace: $KUBE_NAMESPACE
labels:
app: $CI_ENVIRONMENT_SLUG
track: "$track"
pipeline_id: "$CI_PIPELINE_ID"
build_id: "$CI_BUILD_ID"
tier: web
spec:
replicas: $replicas
template:
metadata:
labels:
name: $name
app: $CI_ENVIRONMENT_SLUG
track: "$track"
tier: web
spec:
imagePullSecrets:
- name: gitlab-registry
containers:
- name: app
image: $CI_REGISTRY_IMAGE:$CI_REGISTRY_TAG
imagePullPolicy: Always
env:
- name: CI_PIPELINE_ID
value: "$CI_PIPELINE_ID"
- name: CI_BUILD_ID
value: "$CI_BUILD_ID"
- name: DATABASE_URL
value: "$DATABASE_URL"
ports:
- name: web
containerPort: 5000
livenessProbe:
httpGet:
path: /
port: 5000
initialDelaySeconds: 15
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /
port: 5000
initialDelaySeconds: 5
timeoutSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: $CI_ENVIRONMENT_SLUG
namespace: $KUBE_NAMESPACE
labels:
app: $CI_ENVIRONMENT_SLUG
pipeline_id: "$CI_PIPELINE_ID"
build_id: "$CI_BUILD_ID"
spec:
ports:
- name: web
port: 5000
targetPort: web
selector:
app: $CI_ENVIRONMENT_SLUG
tier: web
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: $CI_ENVIRONMENT_SLUG
namespace: $KUBE_NAMESPACE
labels:
app: $CI_ENVIRONMENT_SLUG
pipeline_id: "$CI_PIPELINE_ID"
build_id: "$CI_BUILD_ID"
annotations:
kubernetes.io/tls-acme: "true"
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- $CI_ENVIRONMENT_HOSTNAME
secretName: ${CI_ENVIRONMENT_SLUG}-tls
rules:
- host: $CI_ENVIRONMENT_HOSTNAME
http:
paths:
- path: /
backend:
serviceName: $CI_ENVIRONMENT_SLUG
servicePort: 5000
EOF
echo "Waiting for deployment..."
kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name"
if [[ "$track" == "stable" ]]; then
echo "Removing canary deployments (if found)..."
kubectl delete all,ing -l "app=$CI_ENVIRONMENT_SLUG" -l "track=canary" -n "$KUBE_NAMESPACE"
helm upgrade --install \
--wait \
--set image.repository="$CI_REGISTRY_IMAGE" \
--set image.tag="$CI_REGISTRY_TAG" \
--set application.track="$track" \
--set application.postgresDatabase="${DATABASE_URL}" \
--set service.url="$CI_ENVIRONMENT_URL" \
--set replicaCount="$replicas" \
--set postgresql.enabled="$POSTGRES_ENABLED" \
--set postgresql.nameOverride="postgres" \
--set postgresql.postgresUser="$POSTGRES_USER" \
--set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
--set postgresql.postgresDatabase="$POSTGRES_DB" \
--namespace="$KUBE_NAMESPACE" \
--version="$CI_PIPELINE_ID-$CI_BUILD_ID" \
"$CI_ENVIRONMENT_SLUG" \
.
else
helm upgrade --install \
--wait \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
--set image.repository="$CI_REGISTRY_IMAGE" \
--set image.tag="$CI_REGISTRY_TAG" \
--set application.track="$track" \
--set application.postgresDatabase="${DATABASE_URL}" \
--set replicaCount="$replicas" \
--set service.enabled="false" \
--set service.url="$CI_ENVIRONMENT_URL" \
--set postgresql.enabled="false" \
--namespace="$KUBE_NAMESPACE" \
--version="$CI_PIPELINE_ID-$CI_BUILD_ID" \
"$CI_ENVIRONMENT_SLUG-$track" \
.
fi
echo "Application is accessible at: ${CI_ENVIRONMENT_URL}"
echo ""
if [[ "$track" == "stable" ]]; then
echo "Removing canary deployments (if found)..."
helm delete "$CI_ENVIRONMENT_SLUG-canary" || true
fi
......@@ -14,5 +14,5 @@ source "$DEPLOY_ROOT_DIR/src/common.bash"
create_kubeconfig
echo "Removing all pods..."
kubectl delete all,ing -l "app=${CI_ENVIRONMENT_SLUG}" -n "$KUBE_NAMESPACE"
kubectl delete all,ing -l "app=${CI_ENVIRONMENT_SLUG}-postgres" -n "$KUBE_NAMESPACE"
helm delete "$CI_ENVIRONMENT_SLUG" || true
helm delete "$CI_ENVIRONMENT_SLUG-canary" || true
......@@ -4,6 +4,7 @@ set -eo pipefail
export CI_CONTAINER_NAME="ci_job_build_$CI_BUILD_ID"
export CI_REGISTRY_TAG="$CI_BUILD_REF_SLUG"
export TILLER_NAMESPACE="$KUBE_NAMESPACE"
create_kubeconfig() {
[[ -z "$KUBE_URL" ]] && return
......@@ -28,7 +29,7 @@ create_kubeconfig() {
}
ensure_environment_url() {
# [[ -n "$CI_ENVIRONMENT_URL" ]] && return
[[ -n "$CI_ENVIRONMENT_URL" ]] && return
echo "Reading CI_ENVIRONMENT_URL from .gitlab-ci.yml..."
CI_ENVIRONMENT_URL="$(ruby -ryaml -e 'puts YAML.load_file(".gitlab-ci.yml")[ENV["CI_BUILD_NAME"]]["environment"]["url"]')"
......@@ -62,3 +63,26 @@ ping_kube() {
return 1
fi
}
ensure_namespace() {
cat <<EOF | kubectl apply -f -
kind: Namespace
apiVersion: v1
metadata:
name: $KUBE_NAMESPACE
EOF
}
install_tiller() {
echo "Checking Tiller..."
if ! helm version &>/dev/null; then
echo "Configuring Tiller..."
helm init
kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
if ! helm version --debug; then
echo "Failed to init Tiller."
return 1
fi
fi
echo ""
}