" Real coders don't blog, or at least not very often! " A.R.

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 kB
Step 1 : FROM tomcat:7
7: Pulling from library/tomcat
1f2a121fc3e4: 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:0f46fded35f503efd9741285e03335ddfa55f5f2dadd90b6a102d301c43307e5
Status: Downloaded newer image for tomcat:7
---> e496879c35a6
Successfully built e496879c35a6

check image was successfully built
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
tomcat 7 e496879c35a6 16 hours ago 357.3 MB
areg/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 log
INFO: Server version: Apache Tomcat/7.0.73
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built: Nov 7 2016 21:27:23 UTC
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number: 7.0.73.0
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name: Linux
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version: 3.16.0-4-amd64
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture: amd64
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home: /usr/lib/jvm/java-7-openjdk-amd64/jre
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version: 1.7.0_111-b01
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor: Oracle Corporation
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE: /usr/local/tomcat
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME: /usr/local/tomcat
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.endorsed.dirs=/usr/local/tomcat/endorsed
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.base=/usr/local/tomcat
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.home=/usr/local/tomcat
Nov 18, 2016 4:01:10 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
Nov 18, 2016 4:01:10 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: 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 lifecycleEvent
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
Nov 18, 2016 4:01:10 PM org.apache.catalina.core.AprLifecycleListener initializeSSL
INFO: OpenSSL successfully initialized (OpenSSL 1.0.2j 26 Sep 2016)
Nov 18, 2016 4:01:11 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-apr-8080"]
Nov 18, 2016 4:01:11 PM org.apache.coyote.AbstractProtocol init
INFO: 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 SIZE
tomcat 7 e496879c35a6 3 days ago 357.3 MB
areg/tomcat7 1 e496879c35a6 3 days ago 357.3 MB
gcr.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 MB
Step 1 : FROM alpine
latest: Pulling from library/alpine
f58f84d16010: Pull complete
Digest: sha256:fb76bd8c78f158a05b2d7b3ad624d4be0d094c6645a5c713883eb6af47553881
Status: Downloaded newer image for alpine:latest
---> f58f84d16010
Step 2 : RUN apk add --no-cache bash
---> Running in 1b040f978663
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
fetch 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-install
Executing busybox-1.24.2-r11.trigger
OK: 13 MiB in 16 packages
---> 3364f32f74e3
Removing intermediate container 1b040f978663
Step 3 : ADD WebTestApp.war webapps/WebTestApp.war
---> 63b84f1842ae
Removing intermediate container 0b2500a0f7d5
Step 4 : CMD "tail" "-f" "/dev/null"
---> Running in acaeafbd113e
---> e65cc1dcc8d1
Removing intermediate container acaeafbd113e
Successfully 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 SIZE
areg/webapps 1 e65cc1dcc8d1 48 seconds ago 9.885 MB
gcr.io/xtrav42kub/webapps 1 e65cc1dcc8d1 48 seconds ago 9.885 MB
areg/tomcat7 1 e496879c35a6 3 days ago 357.3 MB
gcr.io/xtrav42kub/tomcat7 1 e496879c35a6 3 days ago 357.3 MB
tomcat 7 e496879c35a6 3 days ago 357.3 MB
alpine 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 STATUS
ksmall 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 AGE
mywebapp-deploy 1 1 1 0 10s

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mywebapp-deploy-3029167115-3nnc0 0/2 ContainerCreating 0 16s

check again after a while
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mywebapp-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) AGE
kubernetes 10.3.240.1 <none> 443/TCP 44m
mywebapp-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) AGE
kubernetes 10.3.240.1 <none> 443/TCP 45m
mywebapp-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 AGE
mywebapp-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 log
INFO: Server version: Apache Tomcat/7.0.73
(...)
Nov 21, 2016 11:32:13 AM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deployment of web application archive /usr/local/tomcat/webapps/WebTestApp.war has finished in 5,691 ms
Nov 21, 2016 11:32:13 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-apr-8080"]
Nov 21, 2016 11:32:13 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-apr-8009"]
Nov 21, 2016 11:32:13 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 6399 ms
got 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

November 10, 2016
1608 words


Categories
Tags
cloud container kubernetes docker GKE java tomcat MySQL CloudSQL

Connect. Socialize.