This commit is contained in:
Yingting Huang 2020-06-06 13:49:20 +08:00
Родитель 0da0a63922
Коммит fc3fa0e7d1
8 изменённых файлов: 440 добавлений и 0 удалений

3
Dockerfile Normal file
Просмотреть файл

@ -0,0 +1,3 @@
FROM tiangolo/uwsgi-nginx-flask:python3.6
RUN pip install redis
ADD /azure-vote /app

142
azure-pipelines.yml Normal file
Просмотреть файл

@ -0,0 +1,142 @@
# Deploy to Azure Kubernetes Service
# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
trigger:
- master
resources:
- repo: self
variables:
# Container registry service connection established during pipeline creation
dockerRegistryServiceConnection: '540ecf2e-798b-4d1b-b6ec-4b7b846bc760'
imageRepository: 'azure-vote-front-devops'
containerRegistry: 'acrsea.azurecr.io'
dockerfilePath: '**/Dockerfile'
tag: '$(Build.BuildId)'
imagePullSecret: 'acrsea98100991-auth'
# Agent VM image name
vmImageName: 'ubuntu-latest'
# Name of the new namespace being created to deploy the PR changes.
k8sNamespaceForPR: 'review-app-$(System.PullRequest.PullRequestId)'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
- upload: manifests
artifact: manifests
- stage: Deploy
displayName: Deploy stage
dependsOn: Build
jobs:
- deployment: Deploy
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/')))
displayName: Deploy
pool:
vmImage: $(vmImageName)
environment: 'devopsonazurevotedevops.devops'
strategy:
runOnce:
deploy:
steps:
- task: KubernetesManifest@0
displayName: Create imagePullSecret
inputs:
action: createSecret
secretName: $(imagePullSecret)
dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
- task: KubernetesManifest@0
displayName: Deploy to Kubernetes cluster
inputs:
action: deploy
manifests: |
$(Pipeline.Workspace)/manifests/deployment.yml
$(Pipeline.Workspace)/manifests/service.yml
imagePullSecrets: |
$(imagePullSecret)
containers: |
$(containerRegistry)/$(imageRepository):$(tag)
- deployment: DeployPullRequest
displayName: Deploy Pull request
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/pull/'))
pool:
vmImage: $(vmImageName)
environment: 'devopsonazurevotedevops.$(k8sNamespaceForPR)'
strategy:
runOnce:
deploy:
steps:
- reviewApp: devops
- task: Kubernetes@1
displayName: 'Create a new namespace for the pull request'
inputs:
command: apply
useConfigurationFile: true
inline: '{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "$(k8sNamespaceForPR)" }}'
- task: KubernetesManifest@0
displayName: Create imagePullSecret
inputs:
action: createSecret
secretName: $(imagePullSecret)
namespace: $(k8sNamespaceForPR)
dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
- task: KubernetesManifest@0
displayName: Deploy to the new namespace in the Kubernetes cluster
inputs:
action: deploy
namespace: $(k8sNamespaceForPR)
manifests: |
$(Pipeline.Workspace)/manifests/deployment.yml
$(Pipeline.Workspace)/manifests/service.yml
imagePullSecrets: |
$(imagePullSecret)
containers: |
$(containerRegistry)/$(imageRepository):$(tag)
- task: Kubernetes@1
name: get
displayName: 'Get services in the new namespace'
continueOnError: true
inputs:
command: get
namespace: $(k8sNamespaceForPR)
arguments: svc
outputFormat: jsonpath='http://{.items[0].status.loadBalancer.ingress[0].ip}:{.items[0].spec.ports[0].port}'
# Getting the IP of the deployed service and writing it to a variable for posing comment
- script: |
url="$(get.KubectlOutput)"
message="Your review app has been deployed"
if [ ! -z "$url" -a "$url" != "http://:" ]
then
message="${message} and is available at $url.<br><br>[Learn More](https://aka.ms/testwithreviewapps) about how to test and provide feedback for the app."
fi
echo "##vso[task.setvariable variable=GITHUB_COMMENT]$message"

Просмотреть файл

@ -0,0 +1,5 @@
# UI Configurations
TITLE = 'Azure Voting App - Azure Devops Pipeline'
VOTE1VALUE = 'Cats'
VOTE2VALUE = 'Dogs'
SHOWHOST = 'false'

88
azure-vote/main.py Normal file
Просмотреть файл

@ -0,0 +1,88 @@
from flask import Flask, request, render_template
import os
import random
import redis
import socket
import sys
app = Flask(__name__)
# Load configurations from environment or config file
app.config.from_pyfile('config_file.cfg')
if ("VOTE1VALUE" in os.environ and os.environ['VOTE1VALUE']):
button1 = os.environ['VOTE1VALUE']
else:
button1 = app.config['VOTE1VALUE']
if ("VOTE2VALUE" in os.environ and os.environ['VOTE2VALUE']):
button2 = os.environ['VOTE2VALUE']
else:
button2 = app.config['VOTE2VALUE']
if ("TITLE" in os.environ and os.environ['TITLE']):
title = os.environ['TITLE']
else:
title = app.config['TITLE']
# Redis configurations
redis_server = os.environ['REDIS']
# Redis Connection
try:
if "REDIS_PWD" in os.environ:
r = redis.StrictRedis(host=redis_server,
port=6379,
password=os.environ['REDIS_PWD'])
else:
r = redis.Redis(redis_server)
r.ping()
except redis.ConnectionError:
exit('Failed to connect to Redis, terminating.')
# Change title to host name to demo NLB
if app.config['SHOWHOST'] == "true":
title = socket.gethostname()
# Init Redis
if not r.get(button1): r.set(button1,0)
if not r.get(button2): r.set(button2,0)
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
# Get current values
vote1 = r.get(button1).decode('utf-8')
vote2 = r.get(button2).decode('utf-8')
# Return index with values
return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title)
elif request.method == 'POST':
if request.form['vote'] == 'reset':
# Empty table and return results
r.set(button1,0)
r.set(button2,0)
vote1 = r.get(button1).decode('utf-8')
vote2 = r.get(button2).decode('utf-8')
return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title)
else:
# Insert vote result into DB
vote = request.form['vote']
r.incr(vote,1)
# Get current values
vote1 = r.get(button1).decode('utf-8')
vote2 = r.get(button2).decode('utf-8')
# Return results
return render_template("index.html", value1=int(vote1), value2=int(vote2), button1=button1, button2=button2, title=title)
if __name__ == "__main__":
app.run()

Просмотреть файл

@ -0,0 +1,96 @@
body {
background-color:#F8F8F8;
}
div#container {
margin-top:5%;
}
div#space {
display:block;
margin: 0 auto;
width: 500px;
height: 10px;
}
div#logo {
display:block;
margin: 0 auto;
width: 500px;
text-align: center;
font-size:30px;
font-family:Helvetica;
/*border-bottom: 1px solid black;*/
}
div#form {
padding: 20px;
padding-right: 20px;
padding-top: 20px;
display:block;
margin: 0 auto;
width: 500px;
text-align: center;
font-size:30px;
font-family:Helvetica;
border-bottom: 1px solid black;
border-top: 1px solid black;
}
div#results {
display:block;
margin: 0 auto;
width: 500px;
text-align: center;
font-size:30px;
font-family:Helvetica;
}
.button {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 16px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
cursor: pointer;
width: 250px;
}
.button1 {
background-color: white;
color: black;
border: 2px solid #008CBA;
}
.button1:hover {
background-color: #008CBA;
color: white;
}
.button2 {
background-color: white;
color: black;
border: 2px solid #555555;
}
.button2:hover {
background-color: #555555;
color: white;
}
.button3 {
background-color: white;
color: black;
border: 2px solid #f44336;
}
.button3:hover {
background-color: #f44336;
color: white;
}

Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='default.css') }}">
<title>{{title}}</title>
<script language="JavaScript">
function send(form){
}
</script>
</head>
<body>
<div id="container">
<form id="form" name="form" action="/"" method="post"><center>
<div id="logo">{{title}}</div>
<div id="space"></div>
<div id="form">
<button name="vote" value="{{button1}}" onclick="send()" class="button button1">{{button1}}</button>
<button name="vote" value="{{button2}}" onclick="send()" class="button button2">{{button2}}</button>
<button name="vote" value="reset" onclick="send()" class="button button3">Reset</button>
<div id="space"></div>
<div id="space"></div>
<div id="results"> {{button1}} - {{ value1 }} | {{button2}} - {{ value2 }} </div>
</form>
</div>
</div>
</body>
</html>

57
manifests/deployment.yml Normal file
Просмотреть файл

@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-back
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-back
template:
metadata:
labels:
app: azure-vote-back
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-back
image: redis
ports:
- containerPort: 6379
name: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: acrsea.azurecr.io/azure-vote-front-devops
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"

20
manifests/service.yml Normal file
Просмотреть файл

@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: azure-vote-back
spec:
ports:
- port: 6379
selector:
app: azure-vote-back
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-front
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: azure-vote-front