Blog
Serverless Battleship - Innovationstag bei Xebia

Bei Xebia wissen wir, dass Spaß der beste Weg zur Innovation ist! Um unsere Berater zu inspirieren und auf dem Laufenden zu halten, organisieren wir Innovationstage. Bei den Innovationstagen experimentieren, testen und basteln wir mit den neuesten Technologien. Am letzten Innovationstag, Freitag, dem 16. November 2018, traten unsere Cloud-Berater Thijs de Vries, Martijn van de Grift, Kevin Kessels und Dennis Vriend mit einer Cross-Cloud-Version von Battleship gegeneinander an - wer wird wohl gewinnen? Lassen Sie es uns herausfinden!
Panzerkreuzer
Die Cloud-Version von Battleship ist einfach. Die Berater wählen ihre Cloud, ihre Architektur und ihren Anwendungsstack. Wir beginnen um 09.00 Uhr und erstellen unsere Schlachtschiffe - sprich 'Cloud Native Applications'. Um 12.00 Uhr treffen wir uns in der Cloud, um 15 Minuten lang gegeneinander anzutreten.
Es gelten die folgenden Regeln:
Technisch:
Architektur:
- DNS für die Dienstsuche,
- Die 'Bullets' haben ein JSON-Format,
- Die Kugeln haben ein Anfrage/Antwort-Muster mit 'ping' für die Anfrage und 'pong' für die Antwort, mit einer UUID als Korrelations-ID und einem Namen zur Identifizierung des Absenders,
- Die Anwendungen protokollieren den "Schuss" und den "Treffer" in Protokolldateien, die zur Ermittlung des Gewinners verarbeitet werden.
Die Teams
Team A besteht aus Kevin Kessels und Dennis Vriend. Team B besteht aus Thijs de Vries und Martijn van de Grift. Diese exzellente Gruppe von Cloud-Beratern ist Experte für Cloud Computing bei Amazon Web Services (AWS) und Google Cloud Platform (GCP) und spezialisiert auf Cloud-Scale-Architekturen, Serverless Compute und Assistenten mit Python, Go, Scala und Java. Das wird eine epische Schlacht!
Die Schlachtschiffe
Um die Schlacht zu schlagen, müssen die Berater ein Schlachtschiff von epischem Ausmaß erstellen. Das bedeutet, dass sie eine Webanwendung entwickeln müssen, die einer Vielzahl von Anfragen standhält. Die Anfrage ist ein HTTP POST mit 'ping' und eine HTTP-Antwort mit 'pong'. Erstellen Sie eine Architektur, die das andere Schlachtschiff überlasten kann.
Treffen wir die Schlachtschiffe
Die Schlachtschiffe sind alle der Zerstörerklasse zuzuordnen, was bedeutet, dass sie auf dem neuesten Stand der Technik sind und eine Menge Power haben! Die geheimen Pläne finden Sie hier, aber verraten Sie es nicht weiter.
Thijs-Zerstörer
Kapitän Thijs de Vries hat ein Schlachtschiff erstellt, das über hundert Kanonen verfügt. Diese Geschütze werden mit
Das Schlachtschiff mit allen hundert Geschützen sieht so aus:

Wenn die Waffen feuern, sieht der Status wie folgt aus:
Die Waffe wird mit Python und CloudFormation implementiert. Sehen wir uns zunächst die Kanone an. Die Kanone verwendet
import http.client
import json
import uuid
import time
import boto3
import os
client = boto3.client("kinesis")
def millis():
return int(round(time.time() * 1000))
def handler(event, context):
results = []
pings = 0
HOSTNAME = os.environ['HOSTNAME']
PATH = os.environ['PATH']
while context.get_remaining_time_in_millis() > 200:
pings += 1
start = millis()
conn = http.client.HTTPSConnection(HOSTNAME)
headers = {'Content-type': 'application/json', "User-Agent": "Lambda-step-function-gun"}
foo = {'timestamp': millis(), 'uuid': "thijs" + str(uuid.uuid4()) }
json_data = json.dumps(foo)
conn.request('POST', PATH, json_data, headers)
response = conn.getresponse()
stop = millis()
response = response.read().decode()
data = json.dumps({"elaps": stop - start, "response": response }).encode('utf-8')
results.append({ "PartitionKey": str(context.log_stream_name), "Data": data })
if len(results) > 10:
response = client.put_records(
Records=results,
StreamName='ping-war'
)
response = []
return { "done": "true", "pings": pings }
if __name__ == "__main__":
print(handler({},{}))
Natürlich könnte der Zerstörer auch getroffen werden, daher gibt es einen 'pong'-Prozess, der auf Anfragen wartet und einen 'pong' zurückgibt. Dieser Teil wird mit AWS Chalice, AWS Lambda und AWS API Gateway implementiert.
from chalice import Chalice
app = Chalice(app_name='pong')
@app.route('/',methods=['GET', 'POST'])
def index():
return { 'pong': app.current_request.json_body }
Die geheimen Baupläne von Kapitän Thijs de Vries können Sie hier herunterladen.
Martijn-Zerstörer
Kapitän Martijn vd. Grift hat ein Schlachtschiff mit einer einzigen großen Kanone entwickelt. Es ist in Google Cloud (GCP) implementiert und läuft in Google App Engine (GAE). Der Kapitän ist überzeugt, dass sein Schlachtschiff gewinnen wird, weil Google App Engine (GAE) eine sehr einfache Lösung für die Bereitstellung (fast) aller Arten von Anwendungen in einer produktionsfähigen Umgebung bietet. GAE-Anwendungen werden standardmäßig automatisch skaliert, unterstützen CI/CD über das gcloud sdk und bieten sogar einen HTTPS-Endpunkt.
Das Schlachtschiff wurde mit Flask und Python implementiert:
from flask import Flask, request, jsonify
import requests
import uuid
import logging
import time
import json
app = Flask(__name__)
def uuid_generator():
return str(uuid.uuid4())
def timestamp_generator():
return int(round(time.time() * 1000))
target_url = 'https://nqyg1t2wwh.execute-api.eu-west-1.amazonaws.com/api'
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/shoot', methods=['GET', 'POST'])
def shoot():
payload = {'timestamp': timestamp_generator(), 'uuid': uuid_generator(), 'name': 'martijn', 'state': 'ping'}
requests.post(target_url, json=payload)
return 'Shooting payload: ' + str(json.dumps(payload))
@app.route('/hit', methods=['POST'])
def hit():
content = request.get_json()
print('Got hot by: ' + str(content))
json_data = {'timestamp': timestamp_generator(), 'uuid': uuid_generator(), 'name': 'martijn', 'state': 'pong'}
response = app.response_class(
response=json.dumps(json_data),
status=200,
mimetype='application/json'
)
return response
if __name__ == '__main__':
app.run()
Das Schlachtschiff sieht in der Tat sehr gefährlich aus und bietet die folgenden Endpunkte: GET /: Lebendiger Sondenendpunkt GET, POST /shoot: Schießen auf eine Zielurl POST /hit: Trefferendpunkt für den Gegner.
Die Die geheimen Baupläne von Kapitän Martijn vd. Grift können Sie hier herunterladen .
Dnvriend-Zerstörer
Kapitän Dennis Vriend hat einen Zerstörer in GCP erstellt, der sowohl GAE als auch Google Cloud Functions (GCF) verwendet. Das Schlachtschiff der Zerstörer-Klasse besteht aus einer großen Kanone, die auf andere Wolken schießt. Die große Kanone befindet sich in GCP und ist auf GCF montiert. Das Schlachtschiff selbst ist in GAE implementiert. Das große Geschütz ist sehr gefährlich! Zum Schutz verfügt es über eine Aktivierungsfunktion. Die Kanone kann mit dem roten Knopf aktiviert werden. Der Kapitän motiviert das Design so, dass die große Kanone skalierbar sein muss, damit sie sich selbst ablädt, wenn die Belastung steigt. Wenn es mehrere Ziele gibt, kann jedes Geschütz ein anderes Ziel anvisieren, was eine gute Waffe gegen Feinde darstellt. Da das große Geschütz sehr gefährlich ist, verfügt es zum Schutz über eine Aktivierungsfunktion. Das Geschütz kann mit dem 'roten Knopf' aktiviert werden, der das Geschütz einschaltet.
Das Schlachtschiff ist in Flask implementiert:
# [START gae_python37_app]
import requests
from flask import Flask
app = Flask(__name__)
enabled = False
shots_fired = 0
@app.route('/')
def hello():
return 'Hello World!'
@app.route('/red-button', methods=['GET'])
def red_button():
global enabled
enabled = True
print('Enabling the big gun')
return 'Enables the big-gun...'
@app.route('/shoot', methods=['GET'])
def shoot():
global shots_fired
print('Received order to shoot!')
if enabled:
for x in range(0, 1000):
shots_fired += 1
print(f'[{shots_fired}]: bang bang!')
requests.post('https://europe-west1-speeltuin-dennis-vriend.cloudfunctions.net/dnvriend_destroyer', json={'shoot': True})
else:
print('Not enabled, not shooting...')
return 'Someone pulled the trigger'
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
# [END gae_python37_app]
Die Bugpistole sieht so aus:
import json
import random
import time
import uuid
import requests
from flask import jsonify
targets = {
'thijs': 'https://nqyg1t2wwh.execute-api.eu-west-1.amazonaws.com/api',
'martijn': 'https://speeltuin-martijn-vd-grift.appspot.com/hit'
}
def get_bullet(uuid: str, timestamp: int) -> dict:
return {
"timestamp": timestamp,
"uuid": uuid,
"name": "dennisvriend",
"state": "ping"
}
def get_uuid() -> str:
return str(uuid.uuid4())
def get_timestamp():
return int(round(time.time() * 1000))
def shoot_at(target: str) -> None:
uuid = get_uuid()
timestamp = get_timestamp()
print(f'[SHOOT_AT:{target}]:dennisvriend,{uuid},{timestamp}')
requests.post(targets.get(target), json=get_bullet(get_uuid(), get_timestamp()))
def shoot():
"shoot at a random target"
if bool(random.getrandbits(1)):
shoot_at('thijs')
else:
shoot_at('martijn')
def dnvriend_destroyer(request):
try:
got_hit_with_bullet = {}
if request.get_json().get('shoot'):
shoot()
else:
got_hit_with_bullet = request.get_json()
got_hit_with_bullet.update({'state': 'pong'})
received_payload = json.dumps(got_hit_with_bullet)
timestamp = get_timestamp()
uuid = got_hit_with_bullet['uuid']
name = got_hit_with_bullet['name']
print(f'[GOT_HIT_BY:{name}]:{name},{uuid},{timestamp},{received_payload}')
return jsonify(got_hit_with_bullet), 200
except Exception as e:
print(f'Error: e')
return jsonify({
'error': str(e),
'usage': 'please post a message with a valid body'
}), 500
Die geheimen Baupläne von Kapitän Dennis Vriend können Sie hier herunterladen.
Kevin-Zerstörer
Das Schlachtschiff von Kevin hat einige Verzögerungen aufgrund von Lieferproblemen durch fragwürdige Waffenhändler aus dem Fernen Osten. Tbd.
Fazit
Die Berater hatten eine Menge Spaß und lernten eine Menge über den Blick auf die Designs der anderen. AWS und GCP bieten beide eine perfekte Plattform zum Experimentieren und Spielen mit den Technologien, aber auch für Produktions-Workloads. Beim nächsten Mal werden wir uns ansehen, wer die Schlacht mit einigen Big Data Analytics-Technologien gewonnen hat.
Verfasst von
Dennis Vriend
Unsere Ideen
Weitere Blogs
Contact



