Categories
Development Linux

Docker Logs lesen

Problem

Der Server wird gestartet mit:

sudo docker-compose up --detach

Dadurch werden die Logfiles mit dem User root geschrieben und ich kann sie mit meinem User ingo nicht lesen.

Auf der Console kann ich das leicht mit einem vorangestellten sudo lösen, aber um mal eben schnell in die Logfiles rein zu schauen würde ich gerne mein graphisches Tool WinSCP verwenden

Lösung

Man kann Docker / Docker Compose mit einem User starten und mit dem würden dann vermutlich auch die Logfiles geschrieben werden.
Als ich das mit einem Tomcat Image getestet hatte, ist es daran gescheitert, dass mit meinem User ingo auf bestimmte Verzeichnisse im Container nicht zugegriffen werden konnte.

Gelöst habe ich es dann so, dass ich nicht den User, oder User und Gruppe, gesetzt habe, sondern nur die Gruppe.
So wird mit dem root User gearbeitet, die Dateien gehören dem User root und für die gesetzte Gruppe sind sie lesbar.
Mein User muss natürlich ebenfalls in der Gruppe sein.

Gruppe anlegen:

sudo groupadd -g 1001 logfilegroup

Die Group ID ist relativ willkürlich gesetzt. Eigentlich hatte ich groupadd ohne das -g Flag aufgerufen und dann mit cat /etc/group die Group ID rausgesucht. Hier wollte ich das Statement mit explizitem setzen der Group ID hinschreiben, da ich es auch im Projekt verwendet hatte, um auf jedem Server die selbe Group ID zu haben.

User der Gruppe hinzufügen:

sudo usermod --append --groups logfilegroup ingo

Mit den Befehlen groups oder id kann man die Gruppen seines Users sehen, die neue logfilegroup wird aber erst in einer neuen Shell hinzugefügt. Also entweder die Shell schließen und neu öffnen, oder mit su ingo weiter arbeiten.

ingo$ sudo usermod --append --groups logfilegroup ingo
ingo$ groups
ingo adm
ingo$ su ingo
Password:
ingo$ groups
ingo adm logfilegroup

Docker Compose File:

Im Docker Compose File muss die Group ID gesetzt werden, mit dem Namen der Gruppe geht es nicht.

version: "3.2"
services:
  melba-web:
    image: tomcat:10.1.18-jre21 # https://hub.docker.com/_/tomcat
    restart: always
    container_name: myapp-tomcat
    user: :1001
    ports: 
      - "8080:8080"
    environment:
      - _JAVA_OPTIONS=-Duser.language=de -Duser.country=DE
    volumes:
      - ./log/tomcat:/usr/local/tomcat/logs
sudo docker-compose up --detach

ls -lisah log/tomcat/*
4211764 4.0K drwxr-xr-x 2 ingo ingo  4.0K Feb  8 16:52 .
4211762 4.0K drwxr-xr-x 4 ingo ingo  4.0K Feb  5 16:42 ..
4205212  24K -rw-r----- 1 root logfilegroup  24K Feb  8 16:21 catalina.2024-02-08.log
4211758  28K -rw-r----- 1 root logfilegroup  24K Feb  8 16:58 localhost_access_log.2024-02-08.txt

Categories
Development Java Linux

SAPJCO 2

Problem

We have a very, very old application that needs to be migrated into AWS. So we copied all files into AWS EC2 instance and tried to start the application. After fixing a lot of minor problems we faced a tough challenge with a SAPJCO RFC Call.

The Exception message was something like this:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [sapjcorfc (Not found in java.library.path)]. java.library.path [/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64/default:/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64:/usr/lib]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at java.lang.J9VMInternals.initializeImpl(Native Method)
        at java.lang.J9VMInternals.initialize(J9VMInternals.java:199)

I guess, with a JCO Version 3 we would not have much trouble, but in this ancient application JCO Version 2 is used and we cannot update to Version 3 without a huge efford. In other projects I had the luck that I could migrate to Version.

The application is running on a Linux system. But belive me: it would have been much harder on a Windows machine.

Analysis

To find the cause of the problem I wrote the simpliest JCO Test Programm I can image:

import com.sap.mw.jco.JCO;

public class TestMain {

    public static void main(String[] args) {
        System.out.println(JCO.getVersion());
    }

}

My analysis programm structure:

app
├─ JCo/
│  ├─ librfccm.so
│  ├─ libsapjcorfc.so
│  ├─ sapjco.jar
├─ TestMain.java

Compile from command line:

javac -cp ".:/app/JCo/sapjco.jar" TestMain.java

Run from command line:

java -cp ".:/app/JCo/sapjco.jar" TestMain

That gave me another error:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: librfccm.so: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at TestMain.main(TestMain.java:11)

Need to set an environment property first:

export LD_LIBRARY_PATH=/app/JCo

Run command line to start programm again and got another error:

Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
        at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
        at TestMain.main(TestMain.java:11)

The interesting part of the error message:

Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5

Solution

We need the libstdc++.so.5 library, but installed is libstdc++.so.6

To get libstdc++.so.5 we installed package compat-libstdc++-33-3.2.3-66.x86_64:

yum install compat-libstdc++-33-3.2.3-66.x86_64

## to be honest, I am not exactly 100%% sure, what I did in my investigations, so the command may be a little differend, ex:
# yum install compat-libstdc++-33-3
# yum install compat-libstdc++-33
# yum install compat-libstdc++-33 libstdc++.so.5

Test

Run from command line:

java -cp ".:/app/JCo/sapjco.jar" TestMain

That gave me no error, but SAPJCo Version number.:

2.1.10 (2011-05-10)

Finally it worked 😎

Anekdotum

Quote from an StackOverflow post from 2010:

libstdc++.so.5 is a very old version of the standard c++ library.

Some Analysis Details

Writing this article is giving me the feeling, that this was all super easy. But in reality it was a real pain in the allerwertesten.

To isolate the source of the problem, I did not only write the small Java (JCO.getVersion) application, I also set up a Docker environment.

One challenge was to find a useful Docker image to start from. I started with an OpenJDK Image that was already deprecated. Deprecated was not the problem, but I could not install libstdc++.so.5.

Next I tried to use the newer, undeprecated Eclipse-Temurin Image. But still could not install libstdc++.so.5

So I finally ended in a Debian Image and self installed Java where I was able to install libstdc++5.

FROM debian:bookworm-slim
 RUN apt-get update && apt-get install -y locales openjdk-11-jdk libstdc++5 \
     && rm -rf /var/lib/apt/lists/* \
     && localedef -i de_DE -c -f UTF-8 -A /usr/share/locale/locale.alias de_DE.UTF-8
 ENV LANG de_DE.utf8

COPY ./JCo/* /app/JCo/
COPY TestMain.java /app
WORKDIR /app
 ENV LD_LIBRARY_PATH=/app/JCo
 
 RUN javac -cp ".:/app/JCo/sapjco.jar" TestMain.java 

 CMD ["java", "-cp", ".:/app/JCo/sapjco.jar", "-Djava.library.path=/app/JCo", "TestMain"]

Note:

The locale might not be neccessary.
The "-Djava.library.path=/app/JCo" Parameter is not neccessary. But I leave it as an example, how one can use it.

Build and start the app:

docker build -t my-java-app .
docker run -it --rm --name my-running-app my-java-app
2.1.10 (2011-05-10)

Work in container

To work in the running container:

docker exec -it my-running-app /bin/sh

But there is one problem: You can only interact with a running container. But the TestMain-Programm is executed and immediately closed.

So I wrote another Test Programm, that keeps running, so I can enter the running container and test stuff (install packages, compile Java programm, etc.):

import java.io.BufferedReader;
import java.io.InputStreamReader;

import com.sap.mw.jco.JCO;

public class TestMain {

    public static void main(String[] args) {
        System.out.println("Hello World");
        //System.out.println("JCO Version: " + JCO.getVersion());

        while (true) {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Enter Input : ");
            try {
                String s = br.readLine();
                System.out.println(s);
            }catch(Exception e) {
                System.out.println(e);
            }
        }
        
    }

}