- Zur Person
- Ziel
- Vorgehen
- Git
- Gradle
- HandsOn 1:
- Erläuterung zu HandsOn 1
- Update dev infrastruktur
- Continous Integration
- Gogs
- HandsOn 2
- Erläuterung zu HandsOn 2
- Jenkins
- HandsOn 3
- Erläuterung zu HandsOn 3
- Hooks
- HandsOn 4
- Erläuterung zu HandsOn 4
- Was ist…
- CD anwenden
- Warum?
- HandsOn 5
- Erläuterung zu HandsOn 5
- Jenkinsfile
- Docker
- Docker - Techniken
- Docker - Begriffe
- Docker - Erzeugen eines Containers
- Docker - Erzeugen eines Image
- Docker und Daten
- Docker Compose
- HandsOn 6
- Erläuterung zu HandsOn 6
- Testpyramide
- Teams
- Wie ich an automatisierte Tests herangehe
- Phasen / Stages
- HandsOn 7
- Erläuterung zu HandsOn 7
- BDD
- HandsOn 8
- Erläuterung zu HandsOn 8
- Multibranch Build
- HandsOn 9
- Erläuterung zu HandsOn 9
- ???
- mehr Tools
- Vielen Dank!
- Nexus
Oliver Nautsch
Software Crafter (Engineering, Coaching, Consulting)
Nautsch GmbH | Im oberen Boden 1 | 8049 Zürich
-
JVM, Java (Groovy, Kotlin), Go, JavaScript, HTML, SQL, …
-
Microservices, CI/CD, TDD, Build Tools, DB, Docker, Linux, Open Source…
-
HandsOn, Coaching, Cultural Change, DevOps, …
-
Von Startups bis Big Enterprise, von C bis C
-
Finanzindustrie, Bildungsumfeld
email: [email protected]
twitter: @ollispieps
github: @ollin
google+: OliverNautsch
Vorsitzender des Vorstandes der Java User Group Switzerland
Co-Organisator SoCraTes Day Switzerland
-
Verteilte Versionsverwaltung von Dateien
-
git init
-
git add <files>
-
git commit
-
git reset -- <files>
-
git checkout -- <files>
-
git status
-
git log
- Manual
-
man git-init
- Links
-
Git Buch: http://git-scm.com/book/de
-
Visuelle Git Referenz: http://marklodato.github.io/visual-git-guide/index-de.html
-
Git CheatSheet: http://ndpsoftware.com/git-cheatsheet.html
-
build.gradle
-
Groovy-Skript, DSL, Kotlin (ab Version 3)
-
Plugins
-
Konvention vor Konfiguration (Convention over Configuration)
-
Java-Plugin benutzt Standard-Verzeichnis-Layout von Maven
-
-
Drei Phasen der Abarbeitung
-
Initialisierung — findet heraus welche Projekte gebaut werden sollen
-
Konfiguration — erzeugt Directed acyclic graph (kurz DAG)
-
Ausführung — arbeitet DAG ab oder Abbruch
-
-
deklarativ / imperativ
-
der Gradle-Wrapper (
./gradlew <befehl>
z.B.:./gradlew tasks
)
-
Einloggen in Linux Guest innerhalb Virtualbox (
dev
,dev123
)-
rechts unten im Panel ist ein Applet zur Auswahl des Tastaturlayouts
-
links unten → Preferences → Screensaver → Mode: Disable Screen Saver
-
-
Starten des Teminal progammes
dev@vagrant:~$ cd /home/dev/cd-ws-address/ // (1) dev@vagrant:~/cd-ws-address$ git pull // (2) dev@vagrant:~/cd-ws-address$ ./gradlew sync01 // (3) dev@vagrant:~/cd-ws-address$ cd workspace/ // (4) dev@vagrant:~/cd-ws-address/workspace$ ./gradlew build // (5) dev@vagrant:~/cd-ws-address/workspace$ java -jar build/libs/address-0.0.1.jar // (6)
-
Wechsel ins Projektverzeichnis
-
Aktualisieren des Projekten von Github
-
Kopieren der ersten Uebung in den Arbeitsbereich
-
Wechseln in Arbeitsbereich
-
Bauen des Projektes im Arbeitsbereich
-
Starten des servers
-
Zweites Terminal starten
$ http http://localhost:8888/
Tip
|
Mit Ctrl C wird der Server gestoppt.
|
-
Gemeinsame Codebasis
-
Automatisierte Übersetzung
-
Kontinuierliche Test-Entwicklung
-
Test zusammen mit Produktionscode entwickeln
-
Code Coverage
-
-
Häufige Integration des Codes jedes Entwicklers in den Hauptentwicklungszweig
-
Schneller Build und schnelle Test für schnelles Feedback
-
Gespiegelte Produktionsumgebung
-
Einfacher Zugriff auf Ergebnisse
-
Automatisiertes Reporting
-
Automatisierte Verteilung
-
Anmelden in Gogs
URL: |
|
User: |
|
PW: |
|
-
Erzeugen eines Repository mit dem Namen cd-ws-address
-
Terminal (mit Ctrl-C Server stoppen)
$ cd /home/dev/cd-ws-address/ $ git config --global user.email "[email protected]" # (1) $ git config --global user.name "dev" # (2) $ git remote add upstream http://git.nautsch.net/dev/cd-ws-address.git # (3) $ git add -A && git commit -m "handson 01 in workspace" # (4) $ git push -u upstream master # (5)
-
setze globale email
-
setze usernamen ueber alle Projekte
-
gogs als remote repository unter dem Namen upstream hinzufügen
-
füge den Sync in den Workspace (Siehe HandsOn 1) dem Repository hinzu
-
code zu gogs push’en und den lokalen branch mit gogs verbinden (
-u
)
-
erweiterbares, webbasisertes System zur kontinuierlichen Integration von Komponenten
-
ursprünglich entwickelt von Kohsuke Kawaguchi unter dem Namen Hudson
- Begriffe
-
Jobs (Projects)
-
Steps
-
Post-build Actions
-
Views
-
Plugins (z.B. Git-Plugin)
-
Browser öffnen → http://ci.nautsch.net
-
Job anlegen
-
"New Item"
-
"Enter an item name" →
handson_3
-
"Free Style Project" → "Ok"
-
Source Code Management
-
git
-
Repository URL →
http://git.nautsch.net/dev/cd-ws-address.git
-
-
Build Triggers
-
Poll SCM
-
Schedule leer lassen
-
-
Add build step
-
Invoke Gradle script
-
Use Gradle Wrapper
-
Wrapper Location →
${workspace}/workspace/
-
Tasks →
build
-
-
Advanced… (Button rechts)
-
Root Build script →
${workspace}/workspace/
-
-
Save
-
-
Build Now clicken
-
-
automatisiere das Starten des Jenkins Jobs bei jedem Commit
-
keine Scheduled Builds! (erzeugt unnötig Last)
-
-
benutze dazu "Hooks" von git
-
das Git-Plugin von Jenkins unterstützt spezielle URL’s um Jobs zu starten welche das Repository benutzen
-
http://<ciserver>/git/notifyCommit?url=<eingetragenes repository>
-
-
benutze die Url via CLI zum testen
$ curl http://ci.nautsch.net/git/notifyCommit?url=http://git.nautsch.net/dev/cd-ws-address.git
-
wenn Scheduled polling of handson_3 zu sehen ist, dann ist die URL und Konfiguration in Jenkins richtig)
Important
|
Job läuft aber nicht an (kein Änderung in git) |
-
nun hook von git in Gogs eintragen ( http://git.nautsch.net/dev/cd-ws-address )
-
Settings → Git Hooks → post-receive → Edit
-
Hook Content:
-
#!/bin/bash
curl http://ci.nautsch.net/git/notifyCommit?url=http://git.nautsch.net/dev/cd-ws-address.git
#
Warning
|
keine Leerzeilen in Git Hooks erlaubt |
-
Fuege lokal im workspace eine Datei hinzu,
$ cd /home/dev/cd-ws-address/workspace/ $ touch neuedatei.txt $ git add -A && git commit -m "handson 04 via git hook" $ git push
-
add und commit ins lokale Repo und
-
push zum Remote
-
im Jenkins sollte nun ein zweiter Build automatisch anlaufen
The ability to get changes - features, configuration changes, bug fixes, experiments - into production or into the hands of users safely and quickly in a sustainable way.
( Siehe: "Continuous Delivery Sounds Great but it Won’t Work Here" - Jez Humble )
-
kein Trade-off → es ist ein Game-Changer
-
High IT Performers sind statistisch signifiant besser in
-
Deployment frequency
-
Lead time for changes
-
Mean time to recover
-
Change failure rate
-
-
Für Zahlen siehe "2017 State of DevOps Report" (und Reports aus den Jahren zuvor)
Our highest priority is to satisfy the customer through early and continuous delivery of valuable software. …
-
Weniger Nacharbeiten
-
Mehr Automatisierung
-
Weniger Riskio bei Auslieferung
-
Mehr Durchsatz bei höherer Stabilität
-
Weniger Fehler bei Änderungen
-
Fehler einfacher zu finden
-
Schnellere Reaktionszeit
-
Projekt aus Verzeichnis handson-05 in workspace kopieren
$ cd /home/dev/cd-ws-address/ $ ./gradlew sync05 $ git add . && git commit -m "handson 5" && git push
-
Job anlegen
-
"New Item"
-
"Enter an item name" →
address
-
"Pipeline" → "Ok"
-
Build Triggers ("Poll SCM")
-
Pipeline
-
Pipeline script from SCM
-
SCM: Git
-
Repository URL:
http://git.nautsch.net/dev/cd-ws-address.git
-
Script Path:
workspace/Jenkinsfile
-
→ Save
-
-
-
1x manuell den Build starten mit Build Now (liest Jenkinsfile ein)
-
(bitte Jenkins an dieser Stelle neu starten via Link - Restart Safely - Bug?)
-
-
Erzeuge wieder Datei im Arbeitsbereich und push ins gogs.
-
Job soll nun automatisch in Jenkins anlaufen
-
Siehe auch via http://registry.nautsch.net/v2/address/tags/list was in Registry ist
-
Jenkinsfile steuert das Erzeugen einer Pipeline in Jenkins.
-
Unter Versionskontrolle
-
Gehört zum Projekt
-
alle Teile die Jenkins-spezifisch sind in
Jenkinsfile
-
alle Teile die unspezifisch sind in 'build.gradle
Warning
|
Ein Build sollte aber immer mit und ohne Jenkins funktionieren! |
Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications.
Docker ist eine open-source Engine zur automatisierten Bereitstellung von Software als sehr portable und eigenständige Container. Diese Container sind unabhängig von Hardware, Frameworks, Paketverwaltung und Hosting Provider.
$ docker run -t -i alpine sh root@35d2e9236656:/# exit $ docker ps -a
$ time docker run --rm -t -i alpine ls -al
-
time
misst die Zeit -
docker run
erzeugt aus Image einen Container, startet den Container -
-t -i
terminal, interactive -
alpine
der Name des Images -
ls -al
Kommando zum Starten im Container -
--rm
löscht den Container nach beenden
$ docker run -t -i alpine sh root@dcde95ca3e5c:/# touch huschihops.txt root@dcde95ca3e5c:/# exit $ docker ps -a $ docker commit -m="added huschihops.txt" -a="Oliver Nautsch" $(docker ps -aql) ollin/huschihops:1.0
erzeuge Datei /home/dev/docker-test/Dockerfile
FROM alpine
MAINTAINER Oliver Nautsch <[email protected]>
RUN touch huschihops.txt
$ docker build -t ollin/huschihops:2.0 . Sending build context to Docker daemon 2.048 kB Step 1 : FROM alpine:latest ---> 13e1761bf172 ... $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ollin/huschihops 2.0 01ee073272ec 31 seconds ago 4.797 MB . . .
-
Docker volumes
-
ein "anonymes" Verzeichnis - ein Volume - in den Container einhängen
-
ein Verzeichnis oder Datei des Host’s in den Container einhängen
-
-
Docker volume containers
-
Vorgehen:
-
erzeuge einen Daten Container mit einem Volume
-
hänge das Volume des Daten Containers in einen Container ohne Persistance ein.
-
-
docker run --rm -t -i -v /home/dev/docker-test/:/workspace alpine less /workspace/Dockerfile
$ cd /home/dev/cd-ws-address/ $ ./gradlew sync06 $ git add . $ cd workspace $ ./gradlew test $ git commit -m "handson 6" $ git push
-
manuellen Schritt "Produktiv gehen?" in Jenkins ausführen (z.B. mit Maus über deploy to prod gehen)
-
gehe zu http://localhost:7000/
-
Entwicklung von Software wird stark beeinflusst aus Mix von
-
Menschen
-
Tools
-
Infrastruktur
-
Prozessen
-
-
Die Abstimmung ist der Schlüssel
Siehe auch Wikipedia: DevOps
Test ( The Zero Dev Walk)
-
Wie lange braucht ihre Organisation um eine leere
index.html
Seite ins Web zu stellen?
-
DDD - Domain Driven Design
-
bessere Domänenmodelle
-
Design in allgegenwärtiger (ubiquitous) Sprache
-
core domain, bounded context, context map, …
-
-
BDD - Behavior Driven Development (Automated Acceptance Tests)
-
starkte Einbeziehung von Stakeholdern
-
textuelle Beschreibung von Fallbeispielen
-
Automatisierung der Fallbeispiele mit Mocks,
-
Sukzessive Implementierung
-
-
TDD - Test Driven Development (Automated Unit Tests)
-
TDD Zyklus, sehr kurz
-
erhöht die Sicherheit / keine Angst
-
Weniger Bugs
-
Spass
-
-
Ziel - schnell Feedback zu bekommen
-
je längert etwas dauert - je weiter hinten
-
Commit nicht länger als 5 Minuten
→ |
Commit |
AAT |
Expl.T |
UAT |
Pre-Prod |
Prod |
Syntax Check |
X |
_ |
_ |
_ |
_ |
_ |
Unit Tests |
X |
_ |
_ |
_ |
_ |
_ |
Compile |
X |
_ |
_ |
_ |
_ |
_ |
Code Metrics |
X |
_ |
_ |
_ |
_ |
_ |
Story Level Tests |
_ |
X |
_ |
_ |
_ |
_ |
Integration Tests |
_ |
X |
_ |
_ |
_ |
_ |
BDD Tests |
_ |
X |
_ |
_ |
_ |
_ |
Component Tests |
_ |
X |
_ |
_ |
_ |
_ |
Feature-Level Testing |
_ |
X |
_ |
_ |
_ |
_ |
Visual Tests |
_ |
_ |
X |
_ |
_ |
_ |
Usability Tests |
_ |
_ |
X |
_ |
_ |
_ |
Showcases |
_ |
_ |
_ |
X |
_ |
_ |
Feature-Level Testing beim Kunden |
_ |
_ |
_ |
X |
_ |
_ |
Performance Tests |
_ |
_ |
_ |
_ |
X |
_ |
Network Tests |
_ |
_ |
_ |
_ |
X |
_ |
Capacity Tests |
_ |
_ |
_ |
_ |
X |
_ |
Smoke Tests |
_ |
_ |
_ |
_ |
X |
X |
Post-Deployment-Tests |
_ |
_ |
_ |
_ |
X |
X |
Rollback & Redeploy |
_ |
_ |
_ |
_ |
X |
X |
Ongoing Live Tests |
_ |
_ |
_ |
_ |
_ |
X |
-
AAT — Automated Acceptance Testing
-
Expl.T — Exploratory Testing
-
UAT — User Acceptance Testing
-
Pre-Prod — Pre-Production
-
Prod — Production
-
Services von ausserhalb Docker erreichbar machen
-
Phase für Automated Acceptance Test (automatische Phase)
-
erstelle Eintrag in
/etc/hosts
deraat.address.nautsch.net
auf127.0.0.1
abbildet.
-
-
Phase für Exploratives Testen (manuelle Phase)
-
erstelle Eintrag in
/etc/hosts
derextest.address.nautsch.net
auf127.0.0.1
abbildet.
-
-
$ sudo cp /etc/hosts /etc/hosts.bak $ echo "127.0.0.1 aat.address.nautsch.net" | sudo tee --append /etc/hosts $ echo "127.0.0.1 extest.address.nautsch.net" | sudo tee --append /etc/hosts $ cat /etc/hosts
Es sollte dann wir folgt aussehen:
127.0.0.1 localhost
127.0.1.1 vagrant
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1 ci.nautsch.net
127.0.0.1 git.nautsch.net
127.0.0.1 nexus.nautsch.net
127.0.0.1 registry.nautsch.net
127.0.0.1 sonarqube.nautsch.net
127.0.0.1 aat.address.nautsch.net
127.0.0.1 extest.address.nautsch.net
$ cd /home/dev/cd-ws-address/ $ ./gradlew sync07 $ git add . $ cd workspace $ ./gradlew test $ git commit -a -m "handson 7" $ git push
-
manuellen Schritte in Jenkins ausführen
-
extest erreichbar unter http://localhost:7001/
-
prod erreichbar unter http://localhost:7000/
-
-
/home/dev/cd-ws-address/workspace/Jenkinsfile
anschauen (z.B. mit leafpad Editor) -
/home/dev/cd-ws-address/workspace/stage-090-deploy-to-prod/src/main/dockercompose/docker-compose.yml
anschauen
-
Warum erreiche ich nicht http://address.nautsch.net vom Linux Host aus?
-
Wann sollte der Tag im Git gemacht werden, der eine Version bestimmt?
Spock is a testing and specification framework for Java and Groovy applications
Beispiel: Siehe in stage-060-aat
- Klasse: net.nautsch.address.aat.AddressesRestSpec.groovy
Geb - very groovy browser automation… web testing, screen scraping and more
import geb.Page
import geb.spock.GebSpec
class LoginSpec extends GebSpec {
def "login to admin section"() {
given:
to LoginPage
when:
loginForm.with {
username = "admin"
password = "password"
}
and:
loginButton.click()
then:
at AdminPage
}
}
Modularisiere das Jenkinsfile was im Stage AAT ausgeführt wird.
-
erstelle Datei
stage-060-aat.groovy
und kopiere den Befehl ausstage("AAT") node { <Befehl> }
in diese Datei -
programmiere eine Methode
def execute() { <Befehl> }
-
schliesse mit
return this
das Script ab -
im
Jenkinsfile
lade die Datei und führe die Methode aus-
Siehe Beschreibung load()
-
Siehe Beispiel für load()
-
-
commit, push und in Jenkins schauen
Optional: Lade die Datei aus einem separaten Repository.
def execute(){
sh "./gradlew -b ./workspace/build.gradle clean :stage-060-aat:test --info"
}
return this
...
stage ("AAT")
node {
def stage060aat = load("workspace/stage-060-aat.groovy")
stage060aat.execute()
}
...
-
automatisches Erzeugen von neuen Workflows per Branch .
-
History per Branch
-
automatisches Löschen von Jobs wenn Branch gelöscht
-
Branch spezifische Properties
Einschränkung:
-
Jenkinsfile muss im root-Verzeichnis liegen
-
Kopiere workspace Verzeichnis ins home Verzeichnis
-
Erzeuge neues lokales Repository
-
Erzeuge git remote Repository mit dem Namen address-mb
-
Verbinde lokales Repository mit Remote und push von master
-
Erzeuge in Jenkins einen neuen Job address-mb als Multibranch Pipeline
-
Erzeuge lokal neuen Branch
-
Push von Branch
-
Manuelles ausführen von Branch Indexing in Jenkins
Optional:
-
Richte einen hook in gogs ein und
-
teste das au
$ git branch featureX # (create featureX branch) $ git checkout featureX # (switches to featureX branch) $ git push --set-upstream upstream featureX # (pushes featureX branch)
email: [email protected]
twitter: @ollispieps
github: @ollin
google+: OliverNautsch