Skip to content

Commit ca822a6

Browse files
committed
add gitlab-ci; travis-ci to deploy on GKE, Heroku
1 parent 1f7d0cf commit ca822a6

13 files changed

+466
-0
lines changed

.gitlab-ci.yml

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
image: docker:latest
2+
#
3+
services:
4+
- docker:dind
5+
6+
variables:
7+
# # see https://gitlab.com/gitlab-org/gitlab-ce/issues/17861#note_12991518
8+
DOCKER_DRIVER: overlay
9+
10+
stages:
11+
- build
12+
- test
13+
- deploy
14+
15+
variables:
16+
HEROKU_USER_NAME: _
17+
HEROKU_REGISTRY: registry.heroku.com
18+
19+
build-verify:
20+
image: docker:latest
21+
stage: build
22+
before_script:
23+
- apk add --no-cache py-pip
24+
- pip install docker-compose
25+
- docker-compose --version
26+
# convert / to -, remove # character from docker tag name to make it a valid name
27+
- export IMG_TAG=`if [ "$CI_BUILD_REF_NAME" == "master" ]; then echo "latest"; else echo $CI_BUILD_REF_NAME | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
28+
- export DOCKER_IMAGE_PROD=$CI_REGISTRY_IMAGE:$IMG_TAG
29+
- docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
30+
- export CI_BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
31+
script:
32+
# build
33+
- docker-compose -f docker-compose.prod.yml build --pull prod
34+
- docker push $DOCKER_IMAGE_PROD
35+
36+
deploy-gcp-staging:
37+
image: teracy/google-cloud-sdk
38+
stage: deploy
39+
before_script:
40+
# convert / to -, remove # character from docker tag name to make it a valid name
41+
- export IMG_TAG=`if [ "$CI_BUILD_REF_NAME" == "master" ]; then echo "latest"; else echo $CI_BUILD_REF_NAME | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
42+
- export DOCKER_IMAGE_PROD=$CI_REGISTRY_IMAGE:$IMG_TAG
43+
- export GCP_ACCOUNT=$GCP_ACCOUNT_STAGING
44+
- export GCP_PROJECT_ID=$GCP_PROJECT_ID_STAGING
45+
- export GCP_ZONE=$GCP_ZONE_STAGING
46+
- export GCP_CLUSTER_NAME=$GCP_CLUSTER_NAME_STAGING
47+
- export HELM_RELEASE_NAME=$IMG_TAG-${HELM_PROJECT_NAMESPACE:-$CI_PROJECT_NAMESPACE}-nhw
48+
- export HELM_CHART=helm-charts/nodejs-hello-world
49+
- export HELM_IMAGE_REPOSITORY=$CI_REGISTRY_IMAGE
50+
- export HELM_IMAGE_TAG=$IMG_TAG
51+
- export HELM_IMAGE_PULL_POLICY=Always
52+
- export HELM_IMAGE_LAST_DEPLOYED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
53+
#- docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
54+
- echo $GCP_KEY_FILE_STAGING | base64 --decode > /tmp/gcp_key_file.json
55+
script:
56+
- sh scripts/setup_gcp.sh
57+
- sh scripts/deploy_gcp.sh
58+
after_script:
59+
- rm /tmp/gcp_key_file.json
60+
environment: staging
61+
only:
62+
- develop
63+
allow_failure: true # for downstream failure
64+
when: on_success
65+
66+
67+
deploy-gcp-prod:
68+
image: teracy/google-cloud-sdk
69+
stage: deploy
70+
before_script:
71+
# convert / to -, remove # character from docker tag name to make it a valid name
72+
- export IMG_TAG=`if [ "$CI_BUILD_REF_NAME" == "master" ]; then echo "latest"; else echo $CI_BUILD_REF_NAME | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
73+
- export DOCKER_IMAGE_PROD=$CI_REGISTRY_IMAGE:$IMG_TAG
74+
- export GCP_ACCOUNT=$GCP_ACCOUNT_PROD
75+
- export GCP_PROJECT_ID=$GCP_PROJECT_ID_PROD
76+
- export GCP_ZONE=$GCP_ZONE_PROD
77+
- export GCP_CLUSTER_NAME=$GCP_CLUSTER_NAME_PROD
78+
- export HELM_RELEASE_NAME=$IMG_TAG-${HELM_PROJECT_NAMESPACE:-$CI_PROJECT_NAMESPACE}-nhw
79+
- export HELM_CHART=helm-charts/nodejs-hello-world
80+
- export HELM_IMAGE_REPOSITORY=$CI_REGISTRY_IMAGE
81+
- export HELM_IMAGE_TAG=$IMG_TAG
82+
- export HELM_IMAGE_PULL_POLICY=Always
83+
- export HELM_IMAGE_LAST_DEPLOYED=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
84+
#- docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
85+
- echo $GCP_KEY_FILE_PROD | base64 --decode > /tmp/gcp_key_file.json
86+
script:
87+
- sh scripts/setup_gcp.sh
88+
- sh scripts/deploy_gcp.sh
89+
after_script:
90+
- rm /tmp/gcp_key_file.json
91+
environment: prod
92+
only:
93+
- master
94+
allow_failure: true # for downstream failure
95+
when: on_success
96+
97+
98+
deploy-heroku-staging:
99+
stage: deploy
100+
before_script:
101+
# convert / to -, remove # character from docker tag name to make it a valid name
102+
- export IMG_TAG=`if [ "$CI_BUILD_REF_NAME" == "master" ]; then echo "latest"; else echo $CI_BUILD_REF_NAME | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
103+
- export DOCKER_IMAGE=$CI_REGISTRY_IMAGE:$IMG_TAG
104+
- export HEROKU_IMAGE=$HEROKU_REGISTRY/$HEROKU_APP_NAME_STAGING/web
105+
- docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
106+
script:
107+
- echo "login into Heroku Docker registry"
108+
- docker login -u "$HEROKU_USER_NAME" -p "$HEROKU_API_KEY_STAGING" $HEROKU_REGISTRY
109+
- sh scripts/deploy_heroku.sh
110+
environment: staging
111+
only:
112+
- develop
113+
allow_failure: true # for downstream failure
114+
when: on_success
115+
116+
deploy-heroku-prod:
117+
stage: deploy
118+
before_script:
119+
# convert / to -, remove # character from docker tag name to make it a valid name
120+
- export IMG_TAG=`if [ "$CI_BUILD_REF_NAME" == "master" ]; then echo "latest"; else echo $CI_BUILD_REF_NAME | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
121+
- export DOCKER_IMAGE=$CI_REGISTRY_IMAGE:$IMG_TAG
122+
- export HEROKU_IMAGE=$HEROKU_REGISTRY/$HEROKU_APP_NAME_PROD/web
123+
- docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
124+
script:
125+
- docker login -u "$HEROKU_USER_NAME" -p "$HEROKU_API_KEY_PROD" $HEROKU_REGISTRY
126+
- sh scripts/deploy_heroku.sh
127+
environment: prod
128+
only:
129+
- master
130+
allow_failure: true # for downstream failure
131+
when: on_success

.travis.yml

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
sudo: required
2+
services:
3+
- docker
4+
env:
5+
global:
6+
CI_BUILD_TIME: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
7+
CI_BUILD_ID: $TRAVIS_BUILD_ID
8+
CI_BUILD_REF: $TRAVIS_COMMIT
9+
CI_BUILD_REF_NAME: $TRAVIS_BRANCH
10+
CI_BUILD_NUMBER: $TRAVIS_BUILD_NUMBER
11+
CI_BUILDER: travis-ci
12+
CI_PROJECT_NAME: $TRAVIS_REPO_SLUG
13+
HEROKU_USER_NAME: _
14+
HEROKU_REGISTRY: registry.heroku.com
15+
16+
before_install:
17+
# install the latest docker and docker-compose versions
18+
- sudo apt-get remove docker docker-engine
19+
- sudo curl -sSL https://get.docker.com/ | sh
20+
- sudo rm /usr/local/bin/docker-compose
21+
# the latest docker-compose version
22+
- export DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
23+
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
24+
- chmod +x docker-compose
25+
- sudo mv docker-compose /usr/local/bin
26+
- docker --version
27+
- docker-compose --version
28+
# install google-cloud-sdk
29+
# clean up existing gcloud pre-installed by travis-ci
30+
- sudo rm -rf /usr/bin/git-credential-gcloud.sh /usr/bin/bq /usr/bin/gcloud /usr/bin/gsutil
31+
# Make sure gcloud command is on our PATH
32+
- export PATH=$PATH:${HOME}/google-cloud-sdk/bin
33+
- export CLOUDSDK_CORE_DISABLE_PROMPTS=1
34+
# If the SDK is not already cached, download it and unpack it
35+
- if [ ! -d ${HOME}/google-cloud-sdk ]; then
36+
curl https://sdk.cloud.google.com | bash;
37+
fi
38+
- gcloud version
39+
- gcloud components install kubectl
40+
# install helm
41+
- curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
42+
- helm version --client
43+
before_script:
44+
- export CI_REGISTRY_IMAGE=$DOCKER_USER/$DOCKER_REPO
45+
- export IMG_TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH | sed -e 's/[\/]/-/g' | sed -e 's/[\#]//g'; fi`
46+
- export DOCKER_IMAGE_PROD=$CI_REGISTRY_IMAGE:$IMG_TAG
47+
- export CI_PROJECT_NAMESPACE=`echo $CI_PROJECT_NAME | cut -d"/" -f1`;
48+
script:
49+
- docker-compose -f docker-compose.prod.yml build --pull prod;
50+
after_success:
51+
- docker login -u=$DOCKER_USERNAME -p=$DOCKER_PASSWORD
52+
- docker push $DOCKER_IMAGE_PROD
53+
- if [[ "$TRAVIS_BRANCH" == "develop" ]]; then
54+
export GCP_PROJECT_ID=$GCP_PROJECT_ID_STAGING;
55+
export GCP_ZONE=$GCP_ZONE_STAGING;
56+
export GCP_CLUSTER_NAME=$GCP_CLUSTER_NAME_STAGING;
57+
export HELM_RELEASE_NAME=$IMG_TAG-${HELM_PROJECT_NAMESPACE:-$CI_PROJECT_NAMESPACE}-nhw;
58+
export HELM_CHART=helm-charts/nodejs-hello-world;
59+
export HELM_IMAGE_REPOSITORY=$CI_REGISTRY_IMAGE;
60+
export HELM_IMAGE_TAG=$IMG_TAG;
61+
export HELM_IMAGE_PULL_POLICY=Always;
62+
export HELM_IMAGE_LAST_DEPLOYED=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
63+
echo $GCP_KEY_FILE_STAGING | base64 --decode > /tmp/gcp_key_file.json;
64+
export DOCKER_IMAGE=$DOCKER_IMAGE_PROD;
65+
export HEROKU_IMAGE=$HEROKU_REGISTRY/$HEROKU_APP_NAME_STAGING/web;
66+
export HEROKU_API_KEY=$HEROKU_API_KEY_STAGING;
67+
fi
68+
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then
69+
export GCP_PROJECT_ID=$GCP_PROJECT_ID_PROD;
70+
export GCP_ZONE=$GCP_ZONE_PROD;
71+
export GCP_CLUSTER_NAME=$GCP_CLUSTER_NAME_PROD;
72+
export HELM_RELEASE_NAME=$IMG_TAG-${HELM_PROJECT_NAMESPACE:-$CI_PROJECT_NAMESPACE}-nhw;
73+
export HELM_CHART=helm-charts/nodejs-hello-world;
74+
export HELM_IMAGE_REPOSITORY=$CI_REGISTRY_IMAGE;
75+
export HELM_IMAGE_TAG=$IMG_TAG;
76+
export HELM_IMAGE_PULL_POLICY=Always;
77+
export HELM_IMAGE_LAST_DEPLOYED=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
78+
echo $GCP_KEY_FILE_PROD | base64 --decode > /tmp/gcp_key_file.json;
79+
export DOCKER_IMAGE=$DOCKER_IMAGE_PROD;
80+
export HEROKU_IMAGE=$HEROKU_REGISTRY/$HEROKU_APP_NAME_PROD/web;
81+
export HEROKU_API_KEY=$HEROKU_API_KEY_PROD;
82+
fi
83+
- if [[ "$TRAVIS_BRANCH" == "master" ]] || [[ "$TRAVIS_BRANCH" == "develop" ]]; then
84+
sh scripts/setup_gcp.sh;
85+
sh scripts/deploy_gcp.sh;
86+
docker login -u "$HEROKU_USER_NAME" -p "$HEROKU_API_KEY" $HEROKU_REGISTRY;
87+
sh scripts/deploy_heroku.sh;
88+
fi
89+
- rm -rf /tmp/gcp_key_file.json
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*~
18+
# Various IDEs
19+
.project
20+
.idea/
21+
*.tmproj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: v1
2+
description: A Helm chart for Kubernetes
3+
name: nodejs-hello-world
4+
version: 0.1.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
1. Get the application URL by running these commands:
2+
{{- if .Values.ingress.hostname }}
3+
http://{{- .Values.ingress.hostname }}
4+
{{- else if contains "NodePort" .Values.service.type }}
5+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
6+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
7+
echo http://$NODE_IP:$NODE_PORT
8+
{{- else if contains "LoadBalancer" .Values.service.type }}
9+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
10+
You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
11+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
12+
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
13+
{{- else if contains "ClusterIP" .Values.service.type }}
14+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
15+
echo "Visit http://127.0.0.1:8080 to use your application"
16+
kubectl port-forward $POD_NAME 8080:{{ .Values.service.externalPort }}
17+
{{- end }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{{/* vim: set filetype=mustache: */}}
2+
{{/*
3+
Expand the name of the chart.
4+
*/}}
5+
{{- define "name" -}}
6+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7+
{{- end -}}
8+
9+
{{/*
10+
Create a default fully qualified app name.
11+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12+
*/}}
13+
{{- define "fullname" -}}
14+
{{- $name := default .Chart.Name .Values.nameOverride -}}
15+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16+
{{- end -}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
apiVersion: extensions/v1beta1
2+
kind: Deployment
3+
metadata:
4+
name: {{ template "fullname" . }}
5+
labels:
6+
app: {{ template "name" . }}
7+
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8+
release: {{ .Release.Name }}
9+
heritage: {{ .Release.Service }}
10+
spec:
11+
replicas: {{ .Values.replicaCount }}
12+
template:
13+
metadata:
14+
labels:
15+
app: {{ template "name" . }}
16+
release: {{ .Release.Name }}
17+
spec:
18+
containers:
19+
- name: {{ .Chart.Name }}
20+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
21+
imagePullPolicy: {{ .Values.image.pullPolicy }}
22+
env:
23+
- name: PORT
24+
value: {{ .Values.service.internalPort | quote }}
25+
# for rolling updates without downtime
26+
# see: https://github.com/kubernetes/kubernetes/issues/33664#issuecomment-292895327
27+
- name: LAST_DEPLOYED
28+
value: {{ .Values.image.lastDeployed | quote }}
29+
ports:
30+
- containerPort: {{ .Values.service.internalPort }}
31+
livenessProbe:
32+
httpGet:
33+
path: /
34+
port: {{ .Values.service.internalPort }}
35+
readinessProbe:
36+
httpGet:
37+
path: /
38+
port: {{ .Values.service.internalPort }}
39+
resources:
40+
{{ toYaml .Values.resources | indent 12 }}
41+
{{- if .Values.nodeSelector }}
42+
nodeSelector:
43+
{{ toYaml .Values.nodeSelector | indent 8 }}
44+
{{- end }}
45+
imagePullSecrets:
46+
- name: registry.gitlab.com
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{{- if .Values.ingress.enabled -}}
2+
{{- $serviceName := include "fullname" . -}}
3+
{{- $servicePort := .Values.service.externalPort -}}
4+
apiVersion: extensions/v1beta1
5+
kind: Ingress
6+
metadata:
7+
name: {{ template "fullname" . }}
8+
labels:
9+
app: {{ template "name" . }}
10+
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
11+
release: {{ .Release.Name }}
12+
heritage: {{ .Release.Service }}
13+
annotations:
14+
{{- range $key, $value := .Values.ingress.annotations }}
15+
{{ $key }}: {{ $value | quote }}
16+
{{- end }}
17+
spec:
18+
rules:
19+
{{- range $host := .Values.ingress.hosts }}
20+
- host: {{ $host }}
21+
http:
22+
paths:
23+
- path: /
24+
backend:
25+
serviceName: {{ $serviceName }}
26+
servicePort: {{ $servicePort }}
27+
{{- end -}}
28+
{{- if .Values.ingress.tls }}
29+
tls:
30+
{{ toYaml .Values.ingress.tls | indent 4 }}
31+
{{- end -}}
32+
{{- end -}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: {{ template "fullname" . }}
5+
labels:
6+
app: {{ template "name" . }}
7+
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
8+
release: {{ .Release.Name }}
9+
heritage: {{ .Release.Service }}
10+
spec:
11+
type: {{ .Values.service.type }}
12+
ports:
13+
- port: {{ .Values.service.externalPort }}
14+
targetPort: {{ .Values.service.internalPort }}
15+
protocol: TCP
16+
name: {{ .Values.service.name }}
17+
selector:
18+
app: {{ template "name" . }}
19+
release: {{ .Release.Name }}

0 commit comments

Comments
 (0)