2. Hello, ${username}!
● Senior Java developer @ DataArt
● Working with Java for 5+ years
● Background in networking and game
development, mostly C/C++
● Interested in everything DevOps related
since started working with Java
● https://www.facebook.com/yegor.volkov.x64
4. Evolution of Environments
Bare metal
Virtualization (HW)
Virtualization (SW)
OS
Container Engine
JVM
Your app
1. Bare metal, VM software-based, VM hardware-based
2. IaaS, PaaS, SaaS
3. Amazon Services, Amazon EC2, Amazon ECS
4. Google Services, Google App Engine, Google
Kubernetes Engine
5. CloudFoundry, Heroku, etc.
6. Evolution of Environments
… and sometimes about these ...
java version "10.0.2" 2018-07-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)
openjdk version "9-internal"
OpenJDK Runtime Environment (build 9-internal+0-2016-04-14-195246.buildd.src)
OpenJDK 64-Bit Server VM (build 9-internal+0-2016-04-14-195246.buildd.src, mixed mode)
7. Evolution of Environments
1. As long as the JRE is the same, that we develop against - everything
should be fine
2. As long as the Servlet container version is the same, that we develop
against - everything should be fine
3. As long as the RDBMS version is the same, that we develop against -
everything should be fine
4. …
8. Evolution of Environments
● We need to solidify versions of everything (JVM, JDK/JRE, servers,
servlet containers, databases, etc)
● There’s no good way to limit Ops people from screwing everything up
● This should be done by software, not people
● We already have solutions for similar problems in Java world -
dependency managers!
22. Diving into Kubernetes
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mysql-646fdcd6b9-8x9f7 1/1 Running 0 3h
pod/starships-68585d5f5b-v5qtr 1/1 Running 0 2m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
service/mysql ClusterIP 10.102.90.77 <none> 3306/TCP 3h
service/starships ClusterIP 10.98.205.45 <none> 8443/TCP 2m
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1 1 1 1 3h
deployment.apps/starships 1 1 1 1 2m
23. Diving into Kubernetes
Take advantage of Kubernetes features: secrets and configuration!
kubectl create secret generic spacekey
--from-file=src/main/resources/spacekey.p12
Update app configuration:
server.ssl.key-store=/app/spacekey.p12
server.ssl.key-store-password=spac3ke1
server.ssl.key-alias=spacecert
24. Diving into Kubernetes
Adjust YAML file to mount the secret:
spec:
containers:
...
volumeMounts:
- mountPath: "/app"
name: spacekey
readOnly: true
...
volumes:
- name: spacekey
secret:
secretName: spacekey
25. Diving into Kubernetes
So far we have:
● MySQL service
● MySQL persistent volume claim
● MySQL deployment
● Application service
● Application deployment
● SSL certificate secret
26. Playing with Kubernetes
Expose the application:
kubectl expose deployment starships
--port=8443 --protocol=TCP --target-port=8443
--type=LoadBalancer --name=starships-lb
Examine new service:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
mysql ClusterIP 10.102.90.77 <none> 3306/TCP 3h
starships-lb LoadBalancer 10.100.175.25 localhost 8443:32699/TCP 3h
31. Playing with Kubernetes
wollf@WOLLF-TYPHOON MINGW64 ~/IdeaProjects/project1 (master-february-kubernetes)
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mysql-646fdcd6b9-8x9f7 1/1 Running 0 2h
pod/starships-68585d5f5b-pcdcg 1/1 Running 0 2m
pod/starships-68585d5f5b-v5qtr 1/1 Running 0 3m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
service/mysql ClusterIP 10.102.90.77 <none> 3306/TCP 2h
service/starships-lb LoadBalancer 10.100.175.25 localhost 8443:32699/TCP 2h
...
32. Playing with Kubernetes
...
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1 1 1 1 2h
deployment.apps/starships 2 2 2 2 2h
NAME DESIRED CURRENT READY AGE
replicaset.apps/mysql-646fdcd6b9 1 1 1 2h
replicaset.apps/starships-68585d5f5b 2 2 2 15m
33. Back to the surface
Things we added:
● App config via ConfigMap
● Environment variable populated
with current pod name
● Load balanced service
35. Stateful services in Kubernetes
Current database configuration:
● Regular ReplicaSet (as for the app)
● Will lose data if re-scheduled
● Credentials are insecure
● Configuration is hard-coded
36. Stateful services in Kubernetes
Moving MySQL configuration and credentials outside of
deployment YAML:
apiVersion: v1
kind: Secret
metadata:
name: mysql-credentials
data:
username: dW5pdmVyc2U=
password: ZDRrZlg4QndBQ3F4Rkt0Mw==
root-password: YWRtaW4=
37. Stateful services in Kubernetes
MySQL master and slave config:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
mysql_database: space
master.cnf: |
# Apply this config only on the master.
[mysqld]
log-bin
slave.cnf: |
# Apply this config only on slaves.
[mysqld]
super-read-only
38. Stateful services in Kubernetes
Use DB configuration from ConfigMap:
containers:
- name: mysql
env:
- name: MYSQL_DATABASE
valueFrom:
configMapKeyRef:
name: mysql-config
key: mysql_database
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
Use credentials configuration from ConfigMap:
containers:
- name: mysql
env:
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-credentials
key: password
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-credentials
key: username
39. Stateful services in Kubernetes
Use DB credentials for the application deployment:
spec:
containers:
- image: starships:latest
name: starships
Env:
...
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-credentials
key: password
- name: MYSQL_USERNAME
valueFrom:
secretKeyRef:
name: mysql-credentials
key: username
40. Stateful services in Kubernetes
Use DB master/slave endpoint and credentials in the app config:
spring.datasource.url=jdbc:mysql://mysql:3306,mysql-read:3306/space
spring.datasource.username=${MYSQL_USERNAME}
spring.datasource.password=${MYSQL_PASSWORD}
41. Stateful services in Kubernetes
# Headless service for stable
# DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
# Client service for connecting to any MySQL
# instance for reads. For writes, you must instead
connect to the master: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
name: mysql-read
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
43. Road ahead
1. Get rid of HTTPS in the application and use a web server for that
2. Configure replica set to support dynamic scaling
3. Deploy multiple types of the same instances (/app,/api,/admin)
4. Utilize namespaces to limit visibility of environments
5. Start using K8S metric API to get more info about your cluster
6. Try deploying somewhere else (AWS, Azure, GCE, OpenStack,
CloudStack)
7. Log aggregation
8. Rolling updates and blue/green deployments