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

Kubernetes avec Java, Tomcat et CloudSQL - 2eme partie


Cet article fait suite à Kubernetes avec Java, Tomcat et CloudSQL - 1ere partie qu'il est recommandé de lire en premier.


External configuration support in application


La base de cet article est le projet utilisé dans la 1ere partie, qui va être modifié pour pouvoir utiliser un fichier de configuration externe WebTestApp.properties ainsi qu'utiliser Log4j avec un fichier de configuration log4j.properties externe. Ces fichiers de configuration seront gardés en dehors du war ce qui rendra plus facile la gestion de configurations multiples. (dev, test, prod...)


update Servlet source code to use Log4j and external log4j.properties

  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    boolean log4jfile = false;

// (...)

    // configure logging from external file
    try {
      File linux = new File(Config.CONFIG_LOCATION_PROD+"log4j.properties");
      if(linux.exists() && !linux.isDirectory()) { 
        log4jfile=true;
        PropertyConfigurator.configure(Config.CONFIG_LOCATION_PROD+"log4j.properties");
      }
    } catch(NullPointerException npe) {
      log.error("File not found at Prod location!");
    }
    if(log4jfile!=true) { // then try dev location
      try {
        File windows = new File(Config.CONFIG_LOCATION_WINDOW+"log4j.properties");
        if(windows.exists() && !windows.isDirectory()) { 
          PropertyConfigurator.configure(Config.CONFIG_LOCATION_WINDOW+"log4j.properties");
        }
      } catch(NullPointerException npe) {
        log.error("File not found at Window location!");
        throw new RuntimeException("No configuration file found!");
      }
    }

// (...)

  }


update Servlet source code to retrieve configuration from external file

  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    boolean result = false;

// (...)

    // configure application from external file
    try {
      result = Config.parseConfig(Config.CONFIG_LOCATION_PROD+"webtestapp.properties");
    } catch (IOException e) {
      log.error("Config File not found at Prod location!");
    }
    if(result != true) {
      try {
        result = Config.parseConfig(Config.CONFIG_LOCATION_WINDOW+"webtestapp.properties");
      } catch (IOException e) {
        log.error("Config File not found at Window location!");
        throw new RuntimeException("No Config file found!");
      }
    }

// (...)

  }


Application update with new war version


build and retrieve updated war

...


build new version of Docker image
$ cd webapps-image
$ sudo docker build -t areg/webapps:2 .
Sending build context to Docker daemon 6.56 MB
Step 1 : FROM alpine
---> f58f84d16010
Step 2 : RUN apk add --no-cache bash
---> Using cache
---> 3364f32f74e3
Step 3 : ADD WebTestApp.war webapps/WebTestApp.war
---> 26890218f509
Removing intermediate container 5c40c9714b10
Step 4 : CMD "tail" "-f" "/dev/null"
---> Running in 9437edc086ce
---> 52de2e6c5a39
Removing intermediate container 9437edc086ce
Successfully built 52de2e6c5a39

tag new version to prepare it for Google Container Registry
$ sudo docker tag areg/webapps:2 gcr.io/$PROJ/webapps:2

check image is ready
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
areg/webapps 2 52de2e6c5a39 4 minutes ago 9.885 MB
gcr.io/xtrav42kub/webapps 2 52de2e6c5a39 4 minutes ago 9.885 MB
areg/webapps 1 e65cc1dcc8d1 4 hours ago 9.885 MB
gcr.io/xtrav42kub/webapps 1 e65cc1dcc8d1 4 hours ago 9.885 MB
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
alpine latest f58f84d16010 4 weeks ago 4.803 MB

push image to Google Container Registry
$ gcloud docker -- push gcr.io/$PROJ/webapps:2
The push refers to a repository [gcr.io/xtrav42kub/webapps] (len: 1)
52de2e6c5a39: Pushed
26890218f509: Pushed
3364f32f74e3: Image already exists
f58f84d16010: Image already exists
2: digest: sha256:e69e0301dbe14ee138bd39cc7994d7098b16d01410590a7afba9c732808e83aa size: 6223

update deployment descriptor to use new image
$ cd ..
$ cp mywebapp-deploy.yaml mywebapp-deploy-2.yaml
$ vi mywebapp-deploy-2.yaml
(...)
    spec:
      containers:
      - image: gcr.io/xtrav42kub/webapps:2
(...)

stop deployment
$ kubectl delete deployment mywebapp-deploy

start new deployment with new deployment descriptor
$ kubectl create -f mywebapp-deploy-2.yaml

External configuration integration with ConfigMap


prepare folder for ConfigMap
$ mkdir config-webtestapp
$ cd config-webtestapp

prepare log4j.properties
$ vi log4j.properties
log4j.rootLogger=TRACE, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%20.20c] - %20.20M(): %m%n
log4j.logger.com.mydomain.webtestapp=TRACE, A1

prepare configuration file webtestapp.properties
$ vi webtestapp.properties
instance=config-master1
databaseurl=jdbc:mysql://YY.YY.YY.YY:3306/testal1?useUnicode=true&characterEncoding=UTF-8
dblogin=root
dbpassword=XXXX
dbdriver=com.mysql.jdbc.Driver

create config map for those files
$ cd ..
$ kubectl create configmap myconfigmap --from-file=config-webtestapp
configmap "myconfigmap" created

check content of configmap
$ kubectl get configmaps myconfigmap -o yaml
apiVersion: v1
data:
log4j.properties: |
log4j.rootLogger=TRACE, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%20.20c] - %20.20M(): %m%n
log4j.logger.com.mydomain.webtestapp=TRACE, A1
webtestapp.properties: |
instance=config-master1
databaseurl=jdbc:mysql://YY.YY.YY.YY:3306/testal1?useUnicode=true&characterEncoding=UTF-8
dblogin=root
dbpassword=XXXX
dbdriver=com.mysql.jdbc.Driver
kind: ConfigMap
metadata:
creationTimestamp: 2016-11-21T13:58:22Z
name: myconfigmap
namespace: default
resourceVersion: "15973"
selfLink: /api/v1/namespaces/default/configmaps/myconfigmap
uid: 90e1c97f-aff2-11e6-8ca6-42010a80012b

update deployment descriptor to use the configmap
$ vi mywebapp-deploy-2.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
          - mountPath: /config/WebTestApp
            name: config-volume
        ports:
          - containerPort: 8080
      volumes:
      - name: app-volume
        emptyDir: {}
      - name: config-volume
        configMap:
          name: myconfigmap

create deployment with updated description
$ kubectl create -f mywebapp-deploy-2.yaml
deployment "mywebapp-deploy" created

identify running pod
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mywebapp-deploy-1721315703-p9glh 2/2 Running 0 9m

connect to a bash shell on tomcat container on running pod
$ kubectl exec -it mywebapp-deploy-1721315703-p9glh bash -c tomcat
root@mywebapp-deploy-1721315703-p9glh:/usr/local/tomcat#

(in container) verify content of config folder
$ ls /config/WebTestApp/
log4j.properties webtestapp.properties

(in container) verify content of config file
$ cat /config/WebTestApp/webtestapp.properties
instance=config-master1
databaseurl=jdbc:mysql://YY.YY.YY.YY:3306/testal1?useUnicode=true&characterEncoding=UTF-8
dblogin=root
dbpassword=XXXX
dbdriver=com.mysql.jdbc.Driver

create service
$ kubectl create -f mywebapp-service.yaml
service "mywebapp-service" created

retrieve service external ip address and wait a bit for pod to be fully restarted
$ kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.3.240.1 <none> 443/TCP 3h
mywebapp-service 10.3.252.113 104.198.250.211 8080/TCP 1m

test new service from web browser at http://104.198.250.211:8080/WebTestApp/front?action=status


Ressources requests and limitations


update deployment description to request and limit ressources used by pods
$ cp mywebapp-deploy-2.yaml mywebapp-deploy-3.yaml
$ vi mywebapp-deploy-3.yaml
(...)
    spec:
      containers:
      - image: gcr.io/xtrav42kub/webapps:2
        name: webapps
        resources:
          requests:
            cpu: 10m
(...)
      - image: gcr.io/xtrav42kub/tomcat7:1
        name: tomcat
        resources:
          limits:
            cpu: 250m
            memory: 250Mi
(...)

start new deployment
$ kubectl delete deployment mywebapp-deploy
deployment "mywebapp-deploy" deleted
$ kubectl create -f mywebapp-deploy-3.yaml
deployment "mywebapp-deploy" created

November 22, 2016
921 words


Categories
Tags
cloud container kubernetes docker GKE java tomcat MySQL CloudSQL

Connect. Socialize.