ContainerConf/Continuous Lifecycle 2017, Mannheim: Vortrag von Mario-Leander Reimer (@LeanderReimer, Cheftechnologe bei QAware)
Use the right tool for the job! In Zeiten von DevOps und Continuous Delivery muss man als Entwickler eine Vielzahl an Sprachen und Technologien sicher beherrschen, denn jede hat ihre Stärken in einer bestimmten Domäne. Diese Stärken gilt es zu nutzen.
Diese Session führt anschaulich durch die einzelnen Entwicklungsphasen eines einfachen Microservice und zeigt dabei einen in der Praxis erprobten, stabilen und gut integrierten, polyglotten Technologie-Stack, um moderne Cloud-native Applikationen schnell und einfach zu entwickeln und kontinuierlich in Produktion zu bringen.
9. Software Industrialization
ist eine Schlüsselanforderung
für erfolgreiches DevOps und
Continuous Delivery.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
10. Ein Software-Fließband produziert
und liefert die fertige Software an.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
11. Software Industrialisierung bedeutet ...
• Hoher Automatisiersgrad von arbeitsintensiven
und wiederkehrenden Arbeitsschritten
• Bessere Software-Qualität durch eine
abgestimmte Tool-Chain
• Mehr Produktivität und Zufriedenheit der Teams
• Kosten-Effizienz und Wettbewerbsfähigkeit
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
13. val softwareIndustrialization = everythingAsCode()
open fun everythingAsCode() =
everythingIsMadeFromCode()
&& everythingIsMadeByCode()
private fun everythingIsMadeFromCode() = true
private fun everythingIsMadeByCode() = true
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
14. Auf die Domäne kommt es an!
Eine beste Programmiersprache gibt es nicht.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
15. Die Queste nach dem idealen Polyglot Project Archetype
• Welche Sprachen werden in unseren Projekten in
welchen Domänen verwendet?
• Welche Tools verwenden wir für Setup, Build, Code,
Test, CI, Infrastructure, Documentation?
• Was davon hat sich bewährt und was eher nicht?
• Gibt es bereits Best Practices oder Anti-Patterns?
+ Wishful Greenfield Thinking!
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
18. Lightweight Developer Provisioning mit Gradle
• [ SEU ] -> Software Entwicklungs Umgebung
• Nutzung von Gradle als Build-Tool für das Setup und
die Aktualisierung unserer Entwicklungsumgebungen
• Software-Pakete werden als Dependencies definiert
• Gradle Tasks und Groovy Code statt Shell-Scripting
• Versionskontrolle der SEU Definition und Skripte
• Open Source. http://seu-as-code.io
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
19. plugins { id 'de.qaware.seu.as.code.base' version '2.4.0' }
import static de.qaware.seu.as.code.plugins.base.Platform.isMac
seuAsCode {
seuHome = { if (isMac()) '/Volumes/Everything-as-code' else 'Y:' }
projectName = 'Everything-as-code'
}
dependencies {
// list of software dependencies ...
software 'org.groovy-lang:groovy:2.4.7'
software 'org.scala-lang:scala:2.11.8'
software 'org.jruby:jruby:9.1.4.0'
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
20.
21. # language: en
Functionality: Authentication and Authorization
Scenario Outline: Check proper login behaviour
Given the username "<USERNAME>"
And the password "<PASSWORD>"
When the user logs into the system
Then the login result is "<RESULT>"
Examples: Login data
| USERNAME | PASSWORD | RESULT |
| mario-leander.reimer | invalidpwd | failure |
| unknown | somepwd | failure |
| mario-leander.reimer | correctpwd | success |
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
24. Maven ist gut. Gradle ist 100x schneller.
• Sehr flexibel und vielseitig einsetzbar.
• Einfache Unterstützung für polyglotte Projekte.
• Build Skripte sind maximal kurz und prägnant.
• Deutlich reduzierte Build-Zeiten durch inkrementelle
Builds und Caching
• Zahlreiche neue Features: Composite Builds, Kotlin-
basierte Build-Skripte, Performance Verbesserungen, ...
• Regelmäßige Releases. Stabil und ausgereift.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
25. apply plugin: 'application'
apply plugin: 'war'
apply plugin: 'kotlin'
apply plugin: 'groovy'
repositories { jcenter() }
dependencies {
providedCompile 'fish.payara.extras:payara-micro:4.1.1.164'
// and many more ...
}
task everythingAsCode() << {
println 'Everything-as-code using Gradle @ ContinuousLifecycle 2017.'
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
26.
27. Java ist nach wie vor unsere
primäre Implementierungssprache!
Und das ist gut so!
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
28. Aber:
Kotlin als Alternative zu
Java ist einen Blick wert!
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
29. Warum Kotlin? Und nicht Scala, Clojure, ...
• Für Java Entwickler sehr schnell zu erlernen.
• Sehr ausgewogene Universalsprache.
• Null Safety + Synatctic Sugar + jede Menge
andere nützliche Features.
• JDK6 kompatibel. Kleine Library-Größe.
• Sehr guter IDE und Tool Support.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
30. @JsonIgnoreProperties(ignoreUnknown = true)
data class Book(val title: String, val isbn: String, val author: String)
@ApplicationScoped
open class Bookshelf {
private val books = listOf(Book("The Hitchhiker's Guide to the Galaxy", "0345391802"))
open fun byIsbn(isbn: String): Book? = books.find { it.isbn == isbn }
}
@Path("books")
@Produces(MediaType.APPLICATION_JSON)
open class BookResource @Inject constructor(private val bookshelf: Bookshelf) {
@GET @Path("/{isbn}")
open fun byIsbn(@PathParam("isbn") isbn: String): Response {
val book = bookshelf.byIsbn(isbn)
return if (book != null) Response.ok(book).build() else Response.status(Status.NOT_FOUND).build()
}
}
@ApplicationPath("api")
class BookstoreAPI : Application() {
override fun getClasses() = hashSetOf(JacksonFeature::class.java, BookResource::class.java)
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
31.
32. Willkommen in der JavaScript Wunderwelt.
• Ein Universum für sich!
• Klarer Trend: Single Page Webapplikationen.
• HTML5 + CSS3 + ?
• ? = TypeScript oder
• ? = ECMAScript2015 + Babel
• Rückgrat des Builds: node + npm + webpack
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
33.
34. Grovy + Spock eignen sich für Unit,
Integration, Acceptance und UI-Tests
class BookshelfSpec extends Specification {
@Subject
def bookshelf = new Bookshelf()
@Unroll
def "Find book #title by ISBN #isbn"() {
when: 'we search a book by ISBN'
def book = bookshelf.byIsbn(isbn)
then: 'the title and author are correct'
book?.title == title
book?.author == author
where:
isbn || title | author
"0345391802" || "The Hitchhiker's Guide to the Galaxy" | "Douglas Adams"
"0345391829" || "Life, the Universe and Everything" | "Douglas Adams"
}
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
35. Scala und Gatling für lesbare und
performante Last-Tests
class BooksPerformanceTest extends Simulation {
val conf = http.baseURL("http://localhost:18080").acceptHeader("application/json")
val feeder = csv("books.csv").random
val scn = scenario("Book Search")
.exec(http("Get all books").get("/api/books"))
.during(30 seconds) {
feed(feeder)
.exec(http("Get book by title ${Title}").get("/api/books?title=${Title}"))
.pause(1 second)
.exec(http("Get book with ISBN ${ISBN}").get("/api/books/${ISBN}"))
}
setUp(scn.inject(atOnceUsers(10), rampUsers(50) over (30 seconds)))
.assertions(global.responseTime.max.lessThan(5000))
.protocols(conf)
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
36.
37. Definition einer Build-Pipeline per
Jenkinsfile und Groovy
#!/usr/bin/env groovy
node {
stage 'Checkout SCM'
checkout scm
stage 'Build/Analyse/Test'
sh './gradlew clean build'
archiveUnitTestResults()
archiveDistributions()
stage 'Dockerize'
sh './gradlew buildDockerImage'
stage 'Generate Documentation'
sh './gradlew asciidoctor'
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
40. Architektur-Definition und Prüfung
mit QAvalidator für wartbaren Code.
architecture(name: "Mail Example", prefix: "tview", reflexMLversion: "1.0") {
excludes "java.lang.*"
api "JavaMail" : "javax.mail.*"
component "Mail" {
api "IMail" : "de.qaware.mail.*"
impl ["de.qaware.mail.impl.*", "de.qaware.mail.impl2.*"]
uses "JavaMail"
component "MailSender" {
api ["de.qaware.mail.sender.*", "javax.mail.*"]
impl "de.qaware.mail.impl.javamail.JavaMailSender"
uses "JavaMail"
}
}
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
41.
42. Docker, Docker, Docker, ...
FROM qaware-oss-docker-registry.bintray.io/base/alpine-k8s-openjdk8:8u121
MAINTAINER M.-Leander Reimer <mario-leander.reimer@qaware.de>
RUN mkdir -p /app
ADD build/distributions/everything-as-code-1.2.3.tar /app
WORKDIR /app/everything-as-code-1.2.3
RUN chmod 755 bin/everything-as-code
EXPOSE 18080
CMD ./bin/everything-as-code
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
43. Cluster Orchestration mit K8s et.al.
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: everything-as-code
spec:
replicas: 2
template:
metadata:
labels:
tier: backend
spec:
containers:
- name: everything-as-code
image: "qaware-oss-docker-registry.bintray.io/lreimer/everything-as-code:1.2.3"
ports:
- containerPort: 18080
env:
- name: PORT
value: 18080
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
44. Einfache Provisionierung mit Ansible.
---
# file: jenkinsci.yml
- hosts: jenkinsci
remote_user: root
tasks:
- debug: msg="Creating a Jenkins pipeline job on {{ inventory_hostname }}"
- jenkins_job:
name: Everything-as-code Pipeline
config: "{{ lookup('file', 'templates/pipeline-job.xml') }}"
url: "http://{{ inventory_hostname }}"
user: admin
password: admin
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
45. Vagrant und Ruby zum Setup lokaler VMs.
require 'yaml'
$setup = <<SCRIPT
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install -y ansible sshpass
SCRIPT
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty32"
settings = YAML.load_file 'src/vagrant/vagrant.yml'
config.vm.provider "virtualbox" do |vb|
vb.name = settings['vm']['name']
vb.gui = false
vb.memory = "512"
end
config.vm.provision "shell", inline: $setup
end
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
46.
47. Ja, wir brauchen Dokumentation!
• Und nein. Der Quellcode ist nicht genug!
• Technische Dokumente mit Word sind ! " #
• Dokumentation sollte neben dem Quellcode
liegen: change code, change docs.
• Schnell und einfach zu schreiben.
• Unterstützung für Code, Bilder, Diagramme
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
48. // Beispiel Architektur-Dokumentation mit arc42 (https://arc42.github.io)
:imagesdir: ./images
= image:qaware-logo.png[QAware GmbH,2016] Everything-as-code
:toc-title: Table of Contents
:toc:
[[section-introduction-and-goals]]
== Introduction and Goals
The introduction to the architecture documentation should list the driving forces
that software architects must consider in their decisions.
=== Requirements Overview
=== Quality Goals
=== Stakeholders
<<<<
include::02_architecture_constraints.adoc[]
// further includes for the remaining sections
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
49. Mit AsciidoctorJ und Gradle zum fertigen Dokument.
plugins { id "org.asciidoctor.convert" version "1.5.3" }
asciidoctorj { version = '1.5.4.1' }
asciidoctor {
sourceDir 'src/docs/architecture'
resources {
from('src/docs/architecture') {
include 'images/**/*.png'
include 'images/**/*.jpg'
}
}
backends ['html5', 'pdf']
options doctype: 'article'
attributes 'source-highlighter': 'coderay'
}
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
50. Architectur-Dokumentation mit Structurizr
def workspace = new Workspace("Everything-as-code", "The system context of Everything-as-code.")
def model = workspace.model
// create a model and the software system we want to describe
def bookApp = model.addSoftwareSystem("Book Application", "The best source to get info on books.")
// create the various types of people (roles) that use the software system
def anonymousUser = model.addPerson("Anonymous User", "Anybody on the web.")
anonymousUser.uses(bookApp, "Searches for books and views details.")
def browser = bookApp.addContainer("Web Browser",
"Allows users to view information about books", "Edge, Chrome, Firefox")
anonymousUser.uses(browser, "Views information from and makes requests to")
def webApp = bookApp.addContainer("Web Application",
"Hosts the browser-based web application and services", "Payara Fish")
browser.uses(webApp, "uses [JSON/HTTPS]")
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
53. These slides were written in Markdown.
---
## [fit] These slides were written in Markdown.
- This is for real programmers! :smiley:
- Several open source projects available
- Use HTML and JavaScript alternatively.
---
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
57. Alles mit Augenmaß!
Der richtige Technologie-Stack hängt vom Team,
dem Projekt-Kontext und unseren Kunden ab.
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }
58. Fork me on GitHub.
https://github.com/lreimer/everything-as-code
// ContinuousLifecycle 2017 // Everything-as-code -> { created with ❤ and ☕ by @LeanderReimer @qaware }