15/02/2024
Mange udviklere og systemadministratorer, der arbejder med Docker, har på et eller andet tidspunkt stødt på en mur af uventede fejl. En af de mere kryptiske og frustrerende fejlmeddelelser er utvivlsomt Failed to get D-Bus connection: Operation not permitted. Denne fejl opstår typisk, når man forsøger at starte en service, såsom Apache (httpd), inde i en container baseret på et operativsystem som CentOS. Man følger alle de rigtige trin: man henter et image, starter en container, installerer den nødvendige software, og når man så skal til at starte servicen, bliver man mødt med denne stopper. Det kan virke ulogisk, for kommandoen virker jo perfekt på en almindelig server. Men en Docker-container er ikke en almindelig server, og nøglen til at løse dette problem ligger i at forstå de fundamentale forskelle. I denne artikel vil vi dykke ned i, hvorfor denne fejl opstår, hvordan du kan løse den, og hvilke overvejelser du bør gøre dig for at følge bedste praksis i Docker-universet.

Forstå Problemet: Hvorfor D-Bus Fejler i Docker
For at løse problemet er vi nødt til først at forstå de involverede komponenter. D-Bus (Desktop Bus) er et inter-proces kommunikationssystem (IPC), der lader applikationer kommunikere med hinanden på en standardiseret måde i Linux-systemer. Mange moderne services og dæmoner, især dem der styres af systemd, er afhængige af D-Bus for at kunne starte, stoppe og kommunikere med operativsystemets kerne og andre services.
Her ligger kernen af problemet. Et standard Docker-image, som f.eks. centos, er designet til at være minimalistisk. Det indeholder ikke et fuldt 'init'-system som `systemd`, der normalt kører som Proces ID 1 (PID 1) på et traditionelt Linux-system. PID 1 har en særlig rolle; den er ansvarlig for at starte og stoppe alle andre processer. Når du starter en standard Docker-container, er det den kommando, du angiver (f.eks. /bin/bash), der bliver PID 1. Alle de systemdæmoner og services, som systemctl-kommandoen forsøger at styre, eksisterer simpelthen ikke i denne kontekst. Når du så kører systemctl start httpd, forsøger kommandoen at kommunikere med `systemd`-dæmonen via D-Bus, men da `systemd` ikke kører, mislykkes forbindelsen med fejlen 'Operation not permitted'.
Genskabelse af Fejlen: Et Typisk Scenarie
Lad os gennemgå de præcise trin, der fører til fejlen. Dette hjælper med at bekræfte, at du står over for det samme problem, og bygger fundamentet for løsningen.
- Hent CentOS-imaget: Først hentes det officielle CentOS-image fra Docker Hub.
docker pull centos - Start en Container: Dernæst startes en interaktiv container. Mange tilføjer ekstra rettigheder som
--cap-add=NET_ADMINi forventning om at skulle håndtere netværksoperationer.docker run -itd --name min-test-container --cap-add=NET_ADMIN centos - Tilgå Containeren og Installer Apache: Nu hopper vi ind i containeren og installerer Apache webserveren.
docker exec -it min-test-container /bin/bashyum update -y && yum install -y httpd - Udløs Fejlen: Det sidste skridt er at forsøge at starte Apache-servicen med den velkendte kommando.
systemctl start httpdOg her mødes du af den berygtede fejlmeddelelse:
Failed to get D-Bus connection: Operation not permitted.
Løsning Del 1: Aktivering af `systemd` i Containeren
Den direkte løsning på problemet er at tvinge containeren til at starte med `systemd` som sin init-proces (PID 1). Dette får containeren til at opføre sig mere som en traditionel virtuel maskine, hvor `systemd` håndterer alle services. For at opnå dette skal du modificere din docker run-kommando.
Først, stop og fjern den gamle container:
docker stop min-test-containerdocker rm min-test-container
Start nu en ny container med en afgørende ændring. Vi tilføjer /usr/sbin/init som den kommando, containeren skal køre. Dette er stien til `systemd`-binæren i CentOS. Derudover er det nødvendigt at køre containeren i privileged mode (--privileged) for at give `systemd` de nødvendige rettigheder til at administrere systemet inde i containeren.
docker run -itd --name min-test-container --privileged centos /usr/sbin/init
Nu kan du igen tilgå containeren, installere Apache og prøve at starte servicen:
docker exec -it min-test-container /bin/bashyum install -y httpdsystemctl start httpdsystemctl status httpd
I mange tilfælde vil dette virke, og du vil se, at Apache-servicen nu kører. Men for nogle kan en ny fejl dukke op.
Løsning Del 2: Håndtering af cgroups Fejl
Hvis du efter ovenstående trin i stedet bliver mødt med en fejl som Error response from daemon: cgroups: cannot find cgroup mount destination: unknown, skyldes det, at `systemd` har en stærk afhængighed af Control Groups (cgroups) for ressourcestyring. Docker bruger også cgroups, men måden `systemd` forventer at finde dem på, kan være inkompatibel med Docker-hostens standardopsætning.
Løsningen kræver en konfigurationsændring på selve Docker-hosten. Hvis du bruger et ældre system som `docker-machine`, skal du SSH'e ind på maskinen og manuelt oprette et mount point for `systemd`:
docker-machine sshsudo mkdir /sys/fs/cgroup/systemdsudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
Efter at have udført disse kommandoer på Docker-hosten, skal du igen fjerne din container og genskabe den med /usr/sbin/init-kommandoen. Denne gang bør `systemd` kunne finde sit cgroup-mount, og Apache-servicen skulle starte uden problemer.

En Vigtig Overvejelse: Docker-filosofien og Mikrotjenester
Selvom vi nu har fundet en teknisk løsning på problemet, er det afgørende at stoppe op og overveje, om det er den *rigtige* løsning. At køre et fuldt init-system som `systemd` inde i en container strider imod en af de grundlæggende filosofier bag Docker: princippet om én proces pr. container.
Ideen er, at hver container skal udføre én enkelt opgave og gøre det godt. For en webserver betyder det, at containerens eneste formål er at køre Apache-processen. Dette design gør dine applikationer mere robuste, skalerbare og lettere at administrere. Man taler om en arkitektur baseret på mikrotjenester.
Når du kører `systemd` som PID 1, introducerer du en unødvendig kompleksitet. Et alvorligt problem opstår, når Docker forsøger at stoppe containeren. En docker stop-kommando sender et SIGTERM-signal til processen med PID 1. Hvis PID 1 er din applikation (f.eks. Apache), kan den håndtere signalet og lukke pænt ned. Hvis PID 1 er `systemd`, modtager `systemd` signalet, men det er ikke garanteret, at det videresendes korrekt til alle de services, det administrerer (som Apache). Resultatet kan være, at dine services bliver brat afsluttet, hvilket kan føre til datatab eller korruption – ligesom at hive stikket ud af en fysisk server.
Sammenligning af Tilgange
For at illustrere forskellene er her en tabel, der sammenligner den 'Docker-korrekte' metode med `systemd`-metoden.
| Funktion | Én Service pr. Container (Anbefalet) | Flere Services med `systemd` |
|---|---|---|
| Filosofi | Følger "best practice" for mikrotjenester. | Monolitisk tilgang i en container ("en lille VM"). |
| Nedlukning | Ren og sikker nedlukning via SIGTERM. | Risiko for ukorrekt nedlukning og datakorruption. |
| Kompleksitet | Simpel, let at administrere, skalere og fejlfinde. | Højere kompleksitet; kræver `privileged` mode og konfiguration. |
| Isolering | Høj grad af isolering mellem applikationens dele. | Services deler det samme miljø og kan påvirke hinanden. |
| Anvendelse | Ideel til moderne, cloud-native applikationer. | Kan være en pragmatisk løsning for at migrere gamle systemer. |
Ofte Stillede Spørgsmål (FAQ)
Spørgsmål: Hvad er den 'korrekte' Docker-måde at køre Apache på?
Svar: Den bedste praksis er at bruge et officielt image som httpd eller at oprette dit eget Dockerfile, hvor du bruger CMD eller ENTRYPOINT til at starte Apache-processen direkte i forgrunden. En typisk kommando ville være CMD ["httpd", "-D", "FOREGROUND"]. Dette gør Apache til PID 1, så Docker kan administrere processen direkte, hvilket sikrer korrekt opstart og nedlukning.
Spørgsmål: Er det altid en dårlig idé at bruge `systemd` i en Docker-container?
Svar: Ikke nødvendigvis 'altid', men det bør betragtes som en undtagelse frem for reglen. Det kan være en nødvendig og pragmatisk løsning, når man skal migrere en ældre, monolitisk applikation til Docker, hvor det er for komplekst at adskille de forskellige services i starten. Men for nyudvikling bør man altid sigte efter én proces pr. container.
Spørgsmål: Hvad er alternativet, hvis jeg absolut skal køre flere processer i én container?
Svar: Hvis du har brug for at køre flere processer, er et lettere og mere container-venligt alternativ til `systemd` at bruge en proces-manager som supervisord. `supervisord` er designet til at køre som PID 1 i en container og kan administrere flere underordnede processer. Vigtigst er det, at `supervisord` er designet til at håndtere signaler som SIGTERM korrekt og videresende dem til de processer, det styrer, hvilket muliggør en mere sikker nedlukning.
Konklusion
Fejlen Failed to get D-Bus connection: Operation not permitted er et klassisk eksempel på, hvordan forventninger fra en traditionel server-verden kolliderer med container-teknologiens virkelighed. Den umiddelbare løsning er at tvinge containeren til at opføre sig som en VM ved at køre systemd, og dette kan være en gyldig løsning i visse scenarier. Men det er afgørende at forstå de kompromiser, man indgår, især med hensyn til kompleksitet og sikker nedlukning. Den langsigtede og mere robuste løsning er næsten altid at omfavne Docker-filosofien: Byg små, specialiserede containere, der hver især udfører én opgave. Dette fører ikke kun til et mere stabilt og skalerbart system, men det tvinger dig også til at designe dine applikationer på en mere moderne og vedligeholdelsesvenlig måde.
Hvis du vil læse andre artikler, der ligner Løsning på D-Bus forbindelsesfejl i Docker, kan du besøge kategorien Sundhed.
