Categories
Linux

Docker Setup with Traefik, Apache, and Portainer

Requirements

Software

  • Docker and Docker Compose
  • Apache HTTPD 2.4
  • Traefik v3.2
  • Portainer CE

Domain Configuration

Base domain: kabango.eu Required subdomains:

  • www.kabango.eu (main website)
  • kabango.eu (redirects to www)
  • traefik.kabango.eu (Traefik dashboard)
  • portainer.kabango.eu (Portainer interface)

Features

  • Automatic HTTPS with Let's Encrypt
  • HTTP to HTTPS redirect
  • Secure management interfaces
  • Path-based routing for special section
  • Shared Docker network
  • Container management via web interface

Directory Structure

/data/docker/
├── traefik/
│   ├── docker-compose.yml
│   ├── traefik.yml
│   └── config/
│       └── users.txt
├── apache1/
│   ├── docker-compose.yml
│   └── html/
│       └── index.html
├── apache2/
│   ├── docker-compose.yml
│   └── html/
│       └── index.html
└── portainer/
    ├── docker-compose.yml
        └── data/

Configuration Files

Traefik Static Configuration

# /data/docker/traefik/traefik.yml
api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false
    network: traefik-net

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@kabango.eu
      storage: /etc/traefik/acme/acme.json
      httpChallenge:
        entryPoint: web

log:
  level: INFO

Traefik Docker Compose

# /data/docker/traefik/docker-compose.yml
services:
  traefik:
    image: traefik:v3.2
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-net
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./config:/etc/traefik/config
      - acme:/etc/traefik/acme
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.kabango.eu`)
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=auth
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=letsencrypt
      - traefik.http.middlewares.auth.basicauth.usersfile=/etc/traefik/config/users.txt

volumes:
  acme:

networks:
  traefik-net:
    external: true

Apache1 Docker Compose (Main Website)

# /data/docker/apache1/docker-compose.yml
services:
  apache1:
    image: httpd:2.4
    container_name: apache1
    restart: unless-stopped
    networks:
      - traefik-net
    volumes:
      - ./html:/usr/local/apache2/htdocs
    labels:
      - traefik.enable=true
      - traefik.http.routers.apache1.rule=Host(`kabango.eu`) || Host(`www.kabango.eu`)
      - traefik.http.routers.apache1.entrypoints=websecure
      - traefik.http.routers.apache1.tls.certresolver=letsencrypt
      - traefik.http.services.apache1.loadbalancer.server.port=80
      - traefik.http.middlewares.www-redirect.redirectregex.regex=^https://kabango.eu/(.*)
      - traefik.http.middlewares.www-redirect.redirectregex.replacement=https://www.kabango.eu/$${1}
      - traefik.http.routers.apache1.middlewares=www-redirect

networks:
  traefik-net:
    external: true

Apache2 Docker Compose (Special Section)

# /data/docker/apache2/docker-compose.yml
services:
  apache2:
    image: httpd:2.4
    container_name: apache2
    restart: unless-stopped
    networks:
      - traefik-net
    volumes:
      - ./html:/usr/local/apache2/htdocs
    labels:
      - traefik.enable=true
      - traefik.http.routers.apache2.rule=Host(`kabango.eu`) && PathPrefix(`/special`) || Host(`www.kabango.eu`) && PathPrefix(`/special`)
      - traefik.http.routers.apache2.entrypoints=websecure
      - traefik.http.routers.apache2.tls.certresolver=letsencrypt
      - traefik.http.services.apache2.loadbalancer.server.port=80
      - traefik.http.middlewares.strip-special.stripprefix.prefixes=/special
      - traefik.http.routers.apache2.middlewares=strip-special

networks:
  traefik-net:
    external: true

Portainer Docker Compose

# /data/docker/portainer/docker-compose.yml
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-net
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data:/data
    labels:
      - traefik.enable=true
      - traefik.http.routers.portainer.rule=Host(`portainer.kabango.eu`)
      - traefik.http.routers.portainer.entrypoints=websecure
      - traefik.http.routers.portainer.tls.certresolver=letsencrypt
      - traefik.http.services.portainer.loadbalancer.server.port=9000

networks:
  traefik-net:
    external: true

Sample HTML Files

Main Website (apache1):





    Welcome to Kabango.eu


    

Welcome to Kabango.eu

This is the main website.

Visit our special section.

Special Section (apache2):





    Special Section - Kabango.eu


    

Special Section

This is the special section of Kabango.eu

Back to main page

Installation Steps

  1. Create Docker network:

    docker network create traefik-net
  2. Create required directories:

    mkdir -p /data/docker/{traefik/config,apache1/html,apache2/html,portainer,portainer/data}
  3. Create Traefik basic auth credentials:

    htpasswd -nb admin secure_password > /data/docker/traefik/config/users.txt
  4. Create configuration files:

    • Copy all configuration files to their respective locations as shown above
    • Ensure correct file permissions
  5. Configure DNS: Point these domains to your server's IP:

    • kabango.eu
    • www.kabango.eu
    • traefik.kabango.eu
    • portainer.kabango.eu
  6. Start services in order:

    cd /data/docker/traefik && docker compose up -d
    cd /data/docker/apache1 && docker compose up -d
    cd /data/docker/apache2 && docker compose up -d
    cd /data/docker/portainer && docker compose up -d

Access Points

After setup, the following services will be available:

Security Notes

  1. Docker Socket:

    • The Docker socket (/var/run/docker.sock) is only mounted in containers that require it:
      • Traefik: For container discovery
      • Portainer: For Docker management
    • Other containers don't need and shouldn't have access to the Docker socket
  2. Authentication:

    • Traefik dashboard is protected with basic authentication
    • Portainer requires setting up an admin account on first access
    • All management interfaces are only accessible via HTTPS
  3. Network Security:

    • Services communicate through an isolated Docker network
    • Only necessary ports (80, 443) are exposed on the host
    • Automatic redirection from HTTP to HTTPS

Maintenance

Updating Services

To update any service to the latest version:

cd /data/docker/
docker compose pull
docker compose up -d

Viewing Logs

To view logs for any service:

cd /data/docker/
docker compose logs

Add -f flag to follow the logs:

docker compose logs -f

Backup

Important directories to backup:

  • /data/docker/traefik/config - Traefik configuration
  • /data/docker/apache1/html - Main website content
  • /data/docker/apache2/html - Special section content
  • Portainer data volume - Container configurations

Troubleshooting

  1. Certificate Issues:

    • Check Traefik logs for Let's Encrypt errors
    • Verify DNS records are correct
    • Ensure ports 80 and 443 are accessible
  2. Routing Problems:

    • Verify Traefik router rules in docker-compose labels
    • Check if containers are in the correct network
    • Inspect Traefik dashboard for routing status
  3. Container Access:

    • Use docker compose ps to check container status
    • Verify network connectivity with docker network inspect traefik-net
    • Check container logs for errors
Categories
Development Linux

Apache HTTP in den Container

Aufgabe

Ich möchte die nativen Dienste auf meinem Server zur besseren Verwaltung und als Vorbereitung für eine kommende Migration auf Docker umstellen.
Als Vorbereitung für diese Aufgabe habe ich in Lokaler virtueller Server bereits ein grundlegendes Setup lokal evaluiert.
Heute möchte ich den Apache HTTP Server, der auch als Reverse Proxy dient, in einen Container stecken.

Vorbereitung

Docker deinstallieren

Auf dem Server ist bereits eine alte Docker Installation vorhanden. Diese habe ich als erstes rückstandslos entfernt.

Docker installieren

Hier nur kurz die Befehle, aus Lokaler virtueller Server übernommen:

sudo apt update
sudo apt upgrade -y

# Docker-Repository hinzufügen
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Docker installieren:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

sudo systemctl enable docker
sudo systemctl start docker

# Rechte für den aktuellen Benutzer konfigurieren
sudo usermod -aG docker $USER
newgrp docker

Ordnerstrukur

Die Dateien der Docker Dienste sollen in /data/docker/ liegen.
Ein symbolischer Link von /home/docker soll auf das Verzeichnis zeigen.

sudo mkdir -p /data/docker
sudo ln -s /data/docker /home/docker

sudo chown :docker /data/docker
sudo chmod g+w /data/docker

Apache HTTP Container

Ordnerstruktur

mkdir /data/docker/apache
mkdir /data/docker/apache/config \
    /data/docker/apache/html \
    /data/docker/apache/logs

Daten kopieren

sudo cp -r /etc/apache2/* /data/docker/apache/config
sudo cp -r /var/www/html/* /data/docker/apache/html
sudo cp -r /var/log/apache2 /data/docker/apache

sudo chown -R :docker /data/docker/apache
sudo chmod -R g+w /data/docker/apache

mv /data/docker/apache/apache2/* /data/docker/apache/logs
rm -rf /data/docker/apache/apache2

Docker Compose Datei

docker-compose.yml für Apache HTTP im Verzeichnis /data/docker/apache:

services:
  apache:
    image: httpd:2.4
    container_name: apache
    restart: always
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config:/usr/local/apache2/conf
      - ./html:/usr/local/apache2/htdocs
      - ./logs:/usr/local/apache2/logs

erster Start

Auf dem Server Apache HTTP stoppen:

# Service finden
systemctl list-unit-files --type service
# Service stoppen
sudo systemctl stop apache2

Container-Apache starten:

cd /data/docker/apache
docker compose up

Ausgabe:

[+] Building 0.0s (0/0)
Attaching to apache
apache  | httpd: Could not open configuration file /usr/local/apache2/conf/httpd.conf: No such file or directory
apache exited with code 0

Das hat also schon mal so gar nicht geklappt. Woran kann es liegen? Zur Analyse interaktiv in dem Container agieren:

docker compose run -it --entrypoint /bin/bash apache

Ich kann im Container sehen, dass die Konfigurations-Dateien vorhanden sind, d.h. die Docker-Compose-Konfig ist an der Stelle korrekt.
Allerdings fehlt die geforderte httpd.conf.
Bei Ubuntu heißt die Datei apache2.conf, der Docker Container erwartet aber eine httpd.conf. Als Workaround lege ich eine httpd.confan, die auf die apache2.conf verweist:

Include /usr/local/apache2/conf/apache2.conf


Jetzt bekomme ich beim Starten des Containers andere Fehlermeldungen.

Aufräumen

Das entwickelt sich nicht wie gewünscht, ich breche ab und räume auf:

docker compose down -v
sudo rm -rf /data/docker/apache

Kleiner Apache

Um einen minimalen Teilerfolg feiern zu können, setzte ich einen Apache im Container auf, der die HTML-Seiten auf Port 9080 ausliefert.

mkdir /data/docker/apache
mkdir /data/docker/apache/logs

cd /data/docker/apache
vim docker-compose.yml

docker compose up -d
docker logs apache
services:
  apache:
    image: httpd:2.4
    container_name: apache
    restart: always
    ports:
      - 9080:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/www/html:/usr/local/apache2/htdocs
      - ./logs:/usr/local/apache2/logs

Fazit

Der naive "Lift and Shift" Ansatz hat mal wieder nicht funktioniert.
Die Pfade sind bei der nativen Ubuntu Installation und dem Container Apache unterschiedlich. Der simple Workaround mit der httpd.conf-Datei war ein erster Schritt, danach hätten noch Umgebungsvariablen wie APACHE_RUN_DIR gesetzt werden müssen.
Dann hätte ich noch einige Pfade vom Server in den Container mappen müssen.
Und dann ist da noch der Let's encrypt Certbot, der doch mehr mit der Apache Installation verdrahtet ist, als ich am Anfang dachte. Den hätte ich auch noch im Container installieren müssen.
Sicherlich alles machbar, aber für eine Interimslösung zu aufwändig. Am Ende soll ja Traefik SSL und Reverse Proxy übernehmen. Daher belasse ich es hier erstmal.

Categories
Development Java Linux

GitHub Codespace

I was on JCON 2024 and beside other interesting talks I heard one talk about cloud-based IDEs, and I wanted to try out, if GitHub Codespaces could work for me.

Explore the evolving landscape of cloud-based integrated development environments (IDEs), focusing on Gitpod, GitHub codespaces and Devpod. Compare and contrast these cloud IDEs with traditional counterparts, emphasizing the role of container technology, specifically the devcontainer specification. The discussion includes advances, existing limitations, and the potential for developing polyglot, container-based distributed applications. A live demo illustrates the rapid setup and coding process across various languages and frameworks, showcasing testing capabilities and seamless deployment to Kubernetes. Discover how custom additions enhance flexibility. Additionally, uncover the impact of cloud IDEs on teaching and team projects, ensuring consistent development setups for enhanced efficiency and streamlined processes.

[EN] Codespaces, Gitpod, Devpod ... what cloud and container-based IDEs can do for you
by Matthias Haeussler (Novatec Consulting GmbH)

Create GitHub Account

Go to GitHub and create an account. Free plan is suitable.

Create Repository

Create a new repository with name “workshop”. Add a README file.

Create Codespace

TODO: funktioniert das GIF?

Change Keyboard Layout to German: In the lower right corner click on “Layout: US” and enter “German” in the upcoming window.

TODO: Ich hätte gerne die Sprache von Visual Code auf Englisch umgestellt. Wie?

Work in the Terminal

Copy & Paste

Type something into the terminal.
Mark it with your mouse.
One Right Click to copy into Clipboard.
Another Right Click to paste from Clipboard.

Timezone

Set Timzone to Europe -> Berlin

sudo dpkg-reconfigure tzdata

Internet

Do we have access to the Internet? Let’s try with curl:

curl google.com

HTTPie

A modern alternative to curl is HTTPie:

Install httpie:

sudo apt update && \
sudo apt upgrade -y && \
sudo apt install httpie -y

This will take a few minutes. Meanwhile we can work in another Terminal window. Later we come back and test HTTPie:

http google.com

Additional Terminal window

Open a second Terminal with bash:

VIM

ls -lisah
touch test.sh
ls -lisah
vim test.sh
chmod +x test.sh
./test.sh
name=Ingo
echo "My name is $name"
echo "But here I am: $(whoami)"

Python

Do we have Python in our Codespace? Which version(s)?

python3 --version
python --version
vim hello_world.py
python hello_world.py
# Print "Hello World" to the console 
print("Hello World") 

Docker

docker --version
docker-compose --version
docker run hello-world 

Apache HTTPD

docker run -p 8888:80 httpd

Open in Browser:

Find all open Ports in the Ports-Tab:

Normally Port 8888 should be listed here.
We need to add Port, just enter 8888:

Open Website just with a click on the Globus-Icon.

When we try to open the address in another browser, we will see a GitHub-Login.
When we login with another GitHub-Account, we will get a 404-error. Because the page is Private.
Switch to Public:

Now we can access the page in another brower.

At the end we can shutdown HTTPD with + in Terminal window. It should automatically disapear in the Ports-Tab. If not, you can remove it manually.

Microsoft Edge - Caching problem

Open the Public page in MS Edge.
Make the page Private again. Try to open in a new browser, won’t work.
Reload (the Public loaded) page in MS Edge: You can still see the site!
This is a cached version and we need to force MS Edge to reload from server.

Open Developer Tools (F12 or ++), then you can Right Click on the reload button to have additional options:

Java

java --version
vim HelloWorld.java
javac HelloWorld.java
java HelloWorld
rm -f HelloWorld*
class HelloWorld { 
  public static void main(String args[]) { 
      System.out.println("Hello World"); 
  } 
}

Run Java Source as Shell Scripts

type -a java
# java is /home/codespace/java/current/bin/java
# java is /usr/local/sdkman/candidates/java/current/bin/java

vim HelloWorld.sh
chmod +x HelloWorld.sh
./HelloWorld.sh
rm HelloWorld.sh
#!/home/codespace/java/current/bin/java --source 21 

class HelloWorld { 
  public static void main(String args[]) { 
      System.out.println("Hello World"); 
  } 
}

Maven

Start

We create a new pom.xml from scratch.
We need a template. We will take “The Basics”-one from the Apache Maven POM Reference page.

    
      4.0.0

      org.codehaus.mojo
      my-project
      1.0
    
mvn --version
vim pom.xml
mvn clean verify 

Sample Project

Open pom.xml in Explorer (GUI) and change:

  • org.codehaus.mojo to org.acme
  • my-project to workshop

No need to save: Changes are automatically saved

To doublecheck that everything is still ok run mvn clean verify  again.

mkdir -p src/main/java/org/acme
touch src/main/java/org/acme/HelloWorld.java 

Open HelloWorld.java with + in GUI-Editor.

Install Extension Pack for Java as suggested:

And also the next two suggestions:

package org.acme;

class HelloWorld { 
  public static void main(String args[]) { 
      System.out.println("Hello World"); 
  } 
}
mvn package
java -classpath ./target/workshop-1.0.jar org.acme.HelloWorld

Maven - different version

In our Codespace we have Maven 3.9.6 and Java 21.
Let’s test with a different version. We will use Docker.

Official Maven Image on DockerHub.

We want to re-use the local Maven Cache. Let’s find out where it is:

sudo apt install locate -y
sudo updatedb
locate .m2
# /home/codespace/.m2

Adjust the “How to use this image” command:

docker run -it --rm \
--name workshop-maven-project \
-v /home/codespace/.m2:/root/.m2 \
-v "$(pwd)":/usr/src/workshop \
-w /usr/src/workshop \
maven:3.3-jdk-8 \
mvn clean package
java -classpath ./target/workshop-1.0.jar org.acme.HelloWorld

Sourcecode management

We have 7 uncommited changes, but only 2 files should go into the repository:

What we need is a .gitignore file.

touch .gitignore

There are two template files we will copy:

Now there are only 3 files we can commit:

Now we can see these files in our repository:

Secrets

Use GitHub Secrets for API-keys etc.

In the upper-right corner of any page, click your profile photo, then click Settings. Under Codespaces we can set our secrets:

In our Codespace we can access the secret as environment variable:

A running codespace has to be restarted!

Cleanup

Delete all files:

rm -rf target && \
rm -rf src && \
rm pom.xml && \
rm README.md && \
rm .gitignore

Stage & commit changes:

Now we have a clean repository:

Close browser window with codespace and delete the codespace:

Delete the repository:

Go to Settings → General → Danger Zone → Delete this repository