Kubernetes avec Java, Tomcat et CloudSQL - 1ere partie
Cet article fait suite à Kubernetes pas à pas et illustre commet utiliser Kubernetes pour deployer une Web Application en Java avec Tomcat.
configure Project and Zone environment variable
$ export PROJ="xtrav42kub"
$ export ZONE="us-central1-a"
$ gcloud config set project xtrav42kub
create work folder
$ mkdir kubejava
$ cd kubejava
prepare basic servlet and export it as a war
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name=request.getParameter("name");
if (name==null)
name="unknown";
PrintWriter writer = response.getWriter();
writer.print("Hello "+name+" it's "+new Date());
System.out.println("got call from "+name);
writer.flush();
// ...
}
Docker images preparation
L'application sera installée en mode Sidecar: un premier container contiendra Tomcat et sa configuration et un deuxième container contiendra le war à deployer. Ainsi on pourra mettre à jour le war indépendamment de Tomcat et vice versa.
- first Container image to host tomcat
create work folder
$ makedir tomcat-image
$ cd tomcat-image
create Dockerfile to run and potentially customize tomcat
$ vi Dockerfile
FROM tomcat:7
build docker image
$ sudo docker build -t areg/tomcat7:1 .
Sending build context to Docker daemon 2.048 kBStep 1 : FROM tomcat:77: Pulling from library/tomcat1f2a121fc3e4: Pull complete 8d8b679a55bc: Pull complete 2a1ce3eb330c: Pull complete a48d61da1b70: Pull complete a469a58f6803: Pull complete 77d8798fc332: Pull complete bb2785667f5e: Pull complete 2cae4d158c1c: Pull complete b7fb9a217ea3: Pull complete 54ffc57b840d: Pull complete 54f7fa9e536e: Pull complete 76e0dd78fa62: Pull complete 8aaf0bad5728: Pull complete ce6a445b1a20: Pull complete 08e9c0b45302: Pull complete 683465acb42b: Pull complete 69c62c62c1d6: Pull complete e29f81b19936: Pull complete a043b7865f70: Pull complete 5f3dc6e7ba6c: Pull complete 1169bfdc7fb7: Pull complete 01e976630d11: Pull complete 0e247a777747: Pull complete cf491d01af62: Pull complete 2eb1248c0bce: Pull complete 13c125e171b2: Pull complete deb868653a84: Pull complete 4f6c718f39d0: Pull complete e496879c35a6: Pull complete Digest: sha256:0f46fded35f503efd9741285e03335ddfa55f5f2dadd90b6a102d301c43307e5Status: Downloaded newer image for tomcat:7 ---> e496879c35a6Successfully built e496879c35a6
check image was successfully built
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEtomcat 7 e496879c35a6 16 hours ago 357.3 MBareg/mytomcat 1 e496879c35a6 16 hours ago 357.3 MB
start container and map port 8080
$ sudo docker run -p 8080:8080 areg/mytomcat:1
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Server version: Apache Tomcat/7.0.73Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Server built: Nov 7 2016 21:27:23 UTCNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Server number: 7.0.73.0Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: OS Name: LinuxNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: OS Version: 3.16.0-4-amd64Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Architecture: amd64Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Java Home: /usr/lib/jvm/java-7-openjdk-amd64/jreNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: JVM Version: 1.7.0_111-b01Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: JVM Vendor: Oracle CorporationNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: CATALINA_BASE: /usr/local/tomcatNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: CATALINA_HOME: /usr/local/tomcatNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.propertiesNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManagerNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Djdk.tls.ephemeralDHKeySize=2048Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Djava.endorsed.dirs=/usr/local/tomcat/endorsedNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Dcatalina.base=/usr/local/tomcatNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Dcatalina.home=/usr/local/tomcatNov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener logINFO: Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/tempNov 18, 2016 4:01:10 PM org.apache.catalina.core.AprLifecycleListener lifecycleEventINFO: Loaded APR based Apache Tomcat Native library 1.2.10 using APR version 1.5.1.Nov 18, 2016 4:01:10 PM org.apache.catalina.core.AprLifecycleListener lifecycleEventINFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].Nov 18, 2016 4:01:10 PM org.apache.catalina.core.AprLifecycleListener initializeSSLINFO: OpenSSL successfully initialized (OpenSSL 1.0.2j 26 Sep 2016)Nov 18, 2016 4:01:11 PM org.apache.coyote.AbstractProtocol initINFO: Initializing ProtocolHandler ["http-apr-8080"]Nov 18, 2016 4:01:11 PM org.apache.coyote.AbstractProtocol initINFO: Initializing ProtocolHandler ["ajp-apr-8009"]
test with Cloud Console Web Preview on port 8080 (stop it with ctrl-c)
...
if needed start container and connect to a shell to inspect its content
$ sudo docker run -it areg/tomcat7:1 /bin/bash
tag image to prepare it for Google Container Registry
$ sudo docker tag areg/tomcat7:1 gcr.io/$PROJ/tomcat7:1
check docker images
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEtomcat 7 e496879c35a6 3 days ago 357.3 MBareg/tomcat7 1 e496879c35a6 3 days ago 357.3 MBgcr.io/xtrav42kub/tomcat7 1 e496879c35a6 3 days ago 357.3 MB
push image to Google Container Registry
$ gcloud docker -- push gcr.io/$PROJ/tomcat7:1
The push refers to a repository [gcr.io/xtrav42kub/tomcat7] (len: 1)e496879c35a6: Image already exists deb868653a84: Pushed 13c125e171b2: Pushed 1169bfdc7fb7: Image already exists a043b7865f70: Image already exists e29f81b19936: Image already exists 8aaf0bad5728: Image already exists 54ffc57b840d: Image already exists 77d8798fc332: Image already exists a48d61da1b70: Image already exists 2a1ce3eb330c: Image already exists 1f2a121fc3e4: Image already exists 1: digest: sha256:37f852058e41456fb42566ae7ed41da1c10fafd13db423f2bc8bead440bd21f7 size: 20951
>TIP< if error "token auth attempt for registry ... 400 Bad Request", check billing has been enabled for the current project
- second Container image that will contain the webapps
create work folder
$ cd ..
$ mkdir webapps-image
$ cd webapps-image
copy war file to work folder
...
create Dockerfile to contain war files
$ vi Dockerfile
FROM alpine
RUN apk add --no-cache bash
ADD WebTestApp.war webapps/WebTestApp.war
CMD "tail" "-f" "/dev/null"
build docker image
$ sudo docker build -t areg/webapps:1 .
Sending build context to Docker daemon 3.28 MBStep 1 : FROM alpinelatest: Pulling from library/alpinef58f84d16010: Pull complete Digest: sha256:fb76bd8c78f158a05b2d7b3ad624d4be0d094c6645a5c713883eb6af47553881Status: Downloaded newer image for alpine:latest ---> f58f84d16010Step 2 : RUN apk add --no-cache bash ---> Running in 1b040f978663fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gzfetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz(1/5) Installing ncurses-terminfo-base (6.0-r7)(2/5) Installing ncurses-terminfo (6.0-r7)(3/5) Installing ncurses-libs (6.0-r7)(4/5) Installing readline (6.3.008-r4)(5/5) Installing bash (4.3.42-r4)Executing bash-4.3.42-r4.post-installExecuting busybox-1.24.2-r11.triggerOK: 13 MiB in 16 packages ---> 3364f32f74e3Removing intermediate container 1b040f978663Step 3 : ADD WebTestApp.war webapps/WebTestApp.war ---> 63b84f1842aeRemoving intermediate container 0b2500a0f7d5Step 4 : CMD "tail" "-f" "/dev/null" ---> Running in acaeafbd113e ---> e65cc1dcc8d1Removing intermediate container acaeafbd113eSuccessfully built e65cc1dcc8d1
if needed start container and connect to a shell to inspect its content
$ sudo docker run -it areg/webapps:1 /bin/bash
verify war file was copied in /webapps folder
$ ls webapps/
WebTestApp.war
exit from container
$ exit
tag image to prepare it for Google Container Registry
$ sudo docker tag areg/webapps:1 gcr.io/$PROJ/webapps:1
check docker images
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEareg/webapps 1 e65cc1dcc8d1 48 seconds ago 9.885 MBgcr.io/xtrav42kub/webapps 1 e65cc1dcc8d1 48 seconds ago 9.885 MBareg/tomcat7 1 e496879c35a6 3 days ago 357.3 MBgcr.io/xtrav42kub/tomcat7 1 e496879c35a6 3 days ago 357.3 MBtomcat 7 e496879c35a6 3 days ago 357.3 MBalpine latest f58f84d16010 4 weeks ago 4.803 MB
push image to Google Container Registry
$ gcloud docker -- push gcr.io/$PROJ/webapps:1
The push refers to a repository [gcr.io/xtrav42kub/webapps] (len: 1)e65cc1dcc8d1: Pushed 63b84f1842ae: Pushed 3364f32f74e3: Pushed f58f84d16010: Image already exists 1: digest: sha256:dfeb081fb9110c2f695817a0495a89805eb0548d3d7a962239c935769cb1d26b size: 6224
Project deployment preparation
create deployment descriptor
$ cd ..
$ vi mywebapp-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mywebapp-deploy
spec:
replicas: 1
template:
metadata:
labels:
app: mywebapp
spec:
containers:
- image: gcr.io/xtrav42kub/webapps:1
name: webapps
lifecycle:
postStart:
exec:
command:
- "sh"
- "-c"
- >
touch /var/log/preparation_start ;
/bin/cp -rf /webapps/* /apps/
volumeMounts:
- mountPath: /apps
name: app-volume
- image: gcr.io/xtrav42kub/tomcat7:1
name: tomcat
volumeMounts:
- mountPath: /usr/local/tomcat/webapps
name: app-volume
ports:
- containerPort: 8080
volumes:
- name: app-volume
emptyDir: {}
create Service descriptor
$ vi mywebapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mywebapp-service
labels:
app: mywebapp
spec:
ports:
- port: 8080
type: LoadBalancer
sessionAffinity: ClientIP
selector:
app: mywebapp
Kubernetes Cluster creation
verify $ZONE variable and configure Zone
$ echo $ZONE
us-central1-a$ gcloud config set compute/zone $ZONE
Updated property [compute/zone].
create small cluster 2 nodes cluster with small VMs (may take a while)
$ gcloud container clusters create ksmall --num-nodes 2 --zone $ZONE --machine-type g1-small
Creating cluster ksmall...done. Created [https://container.googleapis.com/v1/projects/xtrav42kub/zones/us-central1-a/clusters/ksmall].kubeconfig entry generated for ksmall.NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUSksmall us-central1-a 1.4.6 104.198.33.146 g1-small 1.4.6 2 RUNNING
create deployment on new cluster
$ kubectl create -f mywebapp-deploy.yaml
deployment "mywebapp-deploy" created
>TIP< when getting an error such as "yaml: line 38: did not find expected key" verify tabs/spaces alignments in yaml file
verify deployment and pods created
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEmywebapp-deploy 1 1 1 0 10s
$ kubectl get pods
NAME READY STATUS RESTARTS AGEmywebapp-deploy-3029167115-3nnc0 0/2 ContainerCreating 0 16s
check again after a while
$ kubectl get pods
NAME READY STATUS RESTARTS AGEmywebapp-deploy-3029167115-3nnc0 2/2 Running 0 30s
>TIP< to delete deployment: kubectl delete deployment mywebapp-deploy
>TIP< to delete cluster: gcloud container clusters delete ksmall
create service on new cluster
$ kubectl create -f mywebapp-service.yaml
service "mywebapp-service" created
check running service
$ kubectl get service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes 10.3.240.1 <none> 443/TCP 44mmywebapp-service 10.3.244.205 <pending> 8080/TCP 9s
check running services a little later to verify external IP
$ kubectl get service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes 10.3.240.1 <none> 443/TCP 45mmywebapp-service 10.3.244.205 104.197.209.12 8080/TCP 1m
open in web browser http://104.197.209.12:8080/WebTestApp/basicfront
check pod where the webapp is running
$ kubectl get pods
NAME READY STATUS RESTARTS AGEmywebapp-deploy-3029167115-3nnc0 2/2 Running 0 5m
check logs generated by tomcat container
$ kubectl logs mywebapp-deploy-3029167115-3nnc0 tomcat
Nov 21, 2016 11:32:05 AM org.apache.catalina.startup.VersionLoggerListener logINFO: Server version: Apache Tomcat/7.0.73(...)Nov 21, 2016 11:32:13 AM org.apache.catalina.startup.HostConfig deployWARINFO: Deployment of web application archive /usr/local/tomcat/webapps/WebTestApp.war has finished in 5,691 msNov 21, 2016 11:32:13 AM org.apache.coyote.AbstractProtocol startINFO: Starting ProtocolHandler ["http-apr-8080"]Nov 21, 2016 11:32:13 AM org.apache.coyote.AbstractProtocol startINFO: Starting ProtocolHandler ["ajp-apr-8009"]Nov 21, 2016 11:32:13 AM org.apache.catalina.startup.Catalina startINFO: Server startup in 6399 msgot call from unknown
Cleanup
(not needed if you plan to follow part 2)
cleanup service
$ kubectl delete service mywebapp-service
cleanup deployment
$ kubectl delete deployment mywebapp-deploy
cleanup cluster
$ gcloud container clusters delete ksmall