Zugriff auf DB2-DB auf AS/400 mit Go aus Docker Container

Ein Kunde hatte die Notwendigkeit auf eine DB2-Datenbank zuzugreifen. Eine Besonderheit war, dass sich diese Datenbank auf einem AS/400-System von IBM befindet. Der vom Kunden verwendete DB2-Treiber von IBM funktionierte nicht. Es gab keine Fehlermeldung, die Testapplikation reagierte nicht mehr.

Das vom Kunden bereitgestellte Code-Snippet zum Testen der Datenbankverbindung:

	if dbHost == "" || dbPort == "" || dbDatabase == "" || dbUser == "" || dbPassword == "" {
		log.Fatal("Missing one or more environment variables: DB2_HOST, DB2_PORT, DB2_DATABASE, DB2_USER, or DB2_PASSWORD")
	}

	// Construct the DB2 connection string (DSN)
	// dsn := fmt.Sprintf("Driver={iSeries Access ODBC Driver};Hostname=%s;Port=%s;Database=%s;UID=%s;PWD=%s", dbHost, dbPort, dbDatabase, dbUser, dbPassword)
	dsn := fmt.Sprintf("HOST=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s;CONNECTTIMEOUT=30", dbHost, dbPort, dbDatabase, dbUser, dbPassword)
	log.Println("Connecting to DB2 with DSN:", dsn)

	// con := "HOSTNAME=host;DATABASE=name;PORT=number;UID=username;PWD=password"
	db, err := sql.Open("go_ibm_db", dsn)
	if err != nil {
		log.Fatalln(err)
	}
	defer db.Close()

	err = db.Ping()
	if err == nil {
		log.Fatalln(err)
	}

Beispielcode vom Kunden zum Testen der Datenbankverbindung

Benutzt wird der Treiber "go_ibm_db", der zusätzlich installiert sein muss. Er wird von IBM im Binärformat bereitgestellt.

Mit Ausführen des Containers gehörend zum Snippet startet die Analyse. Wie vom Kunden angekündigt machte das Testprogramm nach Beginn des Verbindungsversuches nichts mehr. Mit Hilfe von Wireshark kann allerdings nachvollzogen werden, dass eine TCP-Verbindung zum Zielserver aufgebaut wird. Dieser akzeptiert den Verbindungsversuch auf dem konfigurierten Port. Tests mit geänderten Authentifizierungsdaten bringen keine Veränderungen. Als nächstes findet eine Analyse mit strace statt:

connect(3, {sa_family=AF_INET, sin_port=htons(8471), sin_addr=inet_addr("yyy.yyy.yyy.yyy")}, 16) = 0
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb950f40000
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9508c0000
getsockname(3, {sa_family=AF_INET, sin_port=htons(59032), sin_addr=inet_addr("xxx.xxx.xxx.xxx")}, [128 => 16]) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7fb9538fc050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sendto(3, "\0\351\320A\0\1\0\343\20A\0\224\21^\204\202\362\203\223\211@@@@@@@@@@@@"..., 307, 0, NULL, 0) = 307
recvfrom(3,

Der Datenbanktreiber wartet an diesem Punkt auf eine Antwort vom Server, die an dieser Stelle ausbleibt. Weitere Informationen zum Datenbankserver sind zu dieser Zeit der Untersuchung nicht verfügbar. Eine Internetrecherche ergibt, dass auf AS/400-Plattformen ein weiterer lizenzpflichtiger Server verfügbar sein muss, damit der DB2-Connect-Treiber eingesetzt werden kann. Deshalb wurde ein anderer von IBM verfügbarer Treiber versucht: IBM i Access ODBC-Treiber.

Mit diesem Treiber funktioniert die Verbindung tadellos. Deshalb wird der hier funktionierende Ansatz gezeigt. Beim Erstellen des Dockercontainers (Dockerfile) wird unter anderem der Treiber installiert. Das hier gezeigte Beispiel basiert auf einem Dockercontainer auf APT-Basis:

### Dockerfile

...
RUN apt install ibm-iaccess unixodbc-dev -y
...

Auszug aus Dockerfile zur Installation des IBM i Access ODBC-Treiberrs

Das main.go-File zum Testen der Datenbankverbindung:

package main

import (
    "database/sql"
    "fmt"
    "log"
    "os"

    _ "github.com/alexbrainman/odbc"
)

func main() {
    dbHost := os.Getenv("DB2_HOST")
	dbDatabase := os.Getenv("DB2_DATABASE")
	dbUser := os.Getenv("DB2_USER")
	dbPassword := os.Getenv("DB2_PASSWORD")
    ccsid := os.Getenv("DB2_CCSID")

    dsn := fmt.Sprintf("DSN=*LOCAL;CCSID=%s;SYSTEM=%s;DATABASE=%s;UID=%s;PWD=%s;", ccsid, dbHost, dbDatabase, dbUser, dbPassword)

    db, err := sql.Open("odbc", dsn)
    if err != nil {
        log.Fatal("Error connecting to the database:", err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        log.Fatal("Error in pinging the database:", err)
    }
    fmt.Println("Connection succesfully initiated!")
}

Beispielcode für das Testen der Datenbankverbindung

Dazu muss der Treiber noch containerweit konfiguriert werden. Dazu muss die Datei odbc.ini angelegt werden:

### /etc/odbc.ini

[*LOCAL]
Description =
Driver = IBM i Access ODBC Driver
System =
UserID =
Password =
Naming = 0
TrueAutoCommit = 1
### Dockerfile

...
COPY configuration/odbc.ini /etc
...

Damit ist die Verbindung zur Datenbank ohne Probleme möglich.

Oliver Lott

Oliver Lott

Vielen Dank fürs Lesen! Benötigen Sie Hilfe? Ich helfe Ihnen gerne. Schreiben Sie mir einfach eine Mail oder benutzen Sie das Kontaktformular.