- Containere i Linux er afhængige af navnerum for at isolere, hvad processer kan se, og af cgroups for at begrænse, hvor mange ressourcer de kan bruge.
- Der findes forskellige typer navnerum (PID, NET, MNT, UTS, IPC, USER, cgroup, time) og cgroup-controllere (CPU, hukommelse, I/O, pids), der kombineres afhængigt af tilfældet.
- Docker, Kubernetes og systemd er afhængige af disse kerneprimitiver til at oprette isolerede miljøer, håndhæve ressourcepolitikker og administrere tjenester ensartet.
- Overvågning af cgroup-metrikker og forståelse af navneområdetopologi er afgørende for at diagnosticere OOM'er, ydeevneproblemer og sikre solid isolation i produktion.
Hvis du arbejder med Docker, Kubernetes eller en hvilken som helst containerplatform, bliver du før eller siden nødt til at dykke ned i systemets indre funktioner. Bag "magien" ved at lancere containere på få sekunder ligger to nøglekomponenter i Linux-kernen: navnerum og cgroups, den duo, der er ansvarlig for isolering og finjusteret ressourcestyring . At forstå dem gør hele forskellen mellem blot at bruge værktøjer og at designe infrastrukturen.
Gennem hele denne artikel vil vi roligt, men uden at gå for meget i detaljer, hvordan de fungerer. navnerum (hvad en proces kan se) og cgroups (hvad en proces kan bruge)Hvordan Docker og Kubernetes udnytter dem; hvordan du kan eksperimentere med dem på et lavt niveau ved hjælp af kommandoer som unshare, ip netns eller skriver direkte i /sys/fs/cgroupOg hvilke tricks er nyttige til ydeevne, sikkerhed og fejlfinding i produktionsmiljøer.
Hvad gør en container egentlig i Linux?
Når du kører noget som docker runen magisk virtuel maskine oprettes ikke; Det, du får, er en Linux-proces (eller en gruppe af processer) med stærk isolation og begrænsede ressourcer."Tricket" er baseret på fire kernemekanismer:
- NamespacesDe styrer, hvad processen ser (PID'er, netværk, filsystem, værtsnavn osv.).
- GrupperDe styrer, hvor meget processen kan bruge (CPU, RAM, I/O, PID'er…).
- Chroot / rootfs isoleretde giver processen sit eget filsystem.
- Normale Linux-processerI sidste ende handler det hele om vært-PID'er, der administreres af kernen.
Med andre ord er en container simpelthen en proces, der kører med et sæt nye navnerum, indlejret i en eller flere cgroups og med et specialbygget filsystem . Resten håndteres af værktøjet (Docker, containerd, runc osv.) med automatisering og brugervenlige API'er.
Linux-navnerum: isolering af, hvad hver proces kan se
Navnerum er et kerneabstraktionslag, der skaber separate "verdener" for grupper af processer . En proces ser kun ressourcerne inden for sit navnerum; resten af systemet er der stadig, men usynligt for den.
Linux har i øjeblikket flere typer navnerum, der hver især fokuserer på en specifik del af systemet. Kombineret tillader de, at en container opfattes som et uafhængigt operativsystem, selvom den deler en kerne med værten.
Hovedtyper af navnerum
Hver type navnerum isolerer en forskellig dimension. Det er almindelig praksis i en "seriøs" container at bruge flere af dem samtidigt. Disse er de vigtigste:
- PID-navneområde: isolerer procesidentifikatorrummet. Inde i containeren er hovedprocessen normalt PID 1selvom værten har et andet nummer. Processer fra forskellige navnerum kan have samme PID uden at forstyrre hinanden, og de kan ikke sende signaler til hinanden, medmindre værten udtrykkeligt tillader det.
- Netværksnavneområde (NET): opretter en uafhængig netværksstak med sine egne grænseflader, ruter, firewallregler, sockets osv. Hver container har sin egen loopback, sine egne IP-adresser og sin egen routingPå denne måde kan du genbruge den samme IP-adresse i forskellige navnerum uden konflikt og anvende isolerede iptables-regler.
- Monteringsnavneområde (MNT): adskiller udsynet fra monteringspunkterne. Processer ser kun filhierarkiet i deres navneområdeSelvom der er bind-mounts, delte volumener, overlays osv. nedenunder. Det er dette, der gør det muligt for en container at se ud som om, den har sin egen "/" uden at røre værtens faktiske rod.
- UTS-navneområde: styrer værtsnavnet og NIS-domænet. Inden for et UTS-navnerum, Du kan ændre værtsnavnet uden at det påvirker værtssystemetMeget nyttigt til at skelne mellem containere eller simulere noder i testmiljøer.
- IPC-navneområdeisolerer System V-meddelelseskøer, semaforer og delt hukommelse. Forhindr forskellige containerprocesser i at forstyrre IPC'en på grund af uheld eller dårligt design.
- BrugernavneområdeSandsynligvis den mest delikate. Den tillader Knyt interne navneområde-UID'er og GID'er til andre værts-ID'erDette gør det muligt at knytte "roden" i containeren til en ikke-privilegeret bruger på værten, hvilket reducerer risikoen for privilegieeskalering betydeligt og forbedrer containersikkerhed.
- Cgroup-navneområdetgiver en virtualiseret visning af
/proc/self/cgroupog cgroups-hierarkiet. Indefra containeren er kun dens egne grupper synlige.Dette skjuler værtens faktiske struktur og forstærker isolationen. - Tidsnavneområdetillader tilbud forskellige opfattelser af tid (ure som CLOCK_MONOTONIC eller CLOCK_BOOTTIME) for forskellige procesgrupper. Det er nyttigt til testscenarier eller meget fin isolering.
Alle disse navnerum er eksponeret i /proc/<pid>/ns/og kan oprettes eller kombineres med værktøjer som f.eks. unshare y nsenterNår den sidste proces, der tilhører et navneområde, dør, ødelægges navneområdet automatisk.
Eksperimenter med navnerum fra kommandolinjen
Kommandoen unshare Det er et af grundprincipperne for at eksperimentere: opretter en underproces med et eller flere nye navnerumFor eksempel, for at teste et isoleret PID-navnerum:
unshare --pid --fork --mount-proc bash
ps aux
echo $$
Indeni vil du kun se processerne fra det navnerum, og din shell vil vises som PID 1 der, selvom værten stadig har alle sine sædvanlige processer. Det er dybest set skelettet af, hvad en OCI-runtime vil gøre, når en container starter.
Netværksnavnerum i detaljer
Netværksaspektet er et af de mest alsidige. Når man opretter et NET-navnerum med ip netns addDu får en tom stak, bortset fra loopback-stakken. Derfra kan du:
- Forbind navneområdet til værten med et par
veth(to virtuelle grænseflader forbundet med et logisk kabel). - Skab broer tipo
br0og forbinde flere navnerum til den bro, så de kan se hinanden ved hjælp af private IP-adresser. - NAT og videresendelse at give internetadgang til de "hjemmelavede containere" med iptables og
/proc/sys/net/ipv4/ip_forward.
Det er rigtigt, på et meget lavt niveau, det er hvad Docker gør med broen docker0 og deres standardnetværk: Den opsætter netværksnavnerum, veth-peers, broer og NAT-regler. så beholderne kan tale med hinanden og gå udenfor.
Monterings- og brugernavnerum til rootfs og sikkerhed
Med `mount namespace` kan du eksperimentere med at oprette et containerfilsystem uden at bruge Docker. Det typiske mønster er:
- At samle eller forberede en minimum rootfs (for eksempel en Alpine minirootfs downloadet af
wgetog udpakkes til en mappe). - Opret et mount-navnerum og gør det
pivot_rootochroottil den mappe. - mount
/proc,/sysog andre pseudosystemer inden for den nye rod.
Med det vil din shell kun se det filsystem, men Du forbliver en normal værtsproces med dit rigtige PID, som kan kontrolleres af administratoren.Hvis du også bruger et brugernavneområde med --map-root-userDu kan have en "root" indeni, som er uden for en bruger uden rettigheder.
C-grupper: hvor mange ressourcer hver proces kan bruge
Mens navnerum bestemmer, hvad en proces kan se, begrænser cgroups, hvor mange systemressourcer en gruppe af processer kan forbruge . De blev introduceret i kerne 2.6.24 og er nu essentielle på enhver moderne containerplatform.
På et konceptuelt niveau er en c-gruppe en mappe inden for /sys/fs/cgroup med filer, der beskriver grænser, prioriteter og målingerNår du tilføjer et PID til den relevante fil (f.eks. cgroup.procs), er denne proces underlagt disse regler.
Ressourcecontrollere i cgroups
C-grupper er sammensat af controllere (undersystemer), der reagerer på specifikke ressourcer. Blandt de mest almindeligt anvendte er:
- CPU: styrer allokeret CPU-tid. Med aksjer definerer relativ prioritet; med kvoter hårde grænser (f.eks. 50 ms CPU-timeout pr. 100 ms periode); med CPU-sæt Du retter processer til specifikke kerner.
- Hukommelse: begrænser RAM og swap. Tillader Hårde og bløde grænser, OOM killer-indstillinger, statistik og hukommelsestrykMeget nyttigt til at forhindre en tjeneste i at optage hele serveren.
- Blok I/O (blkio / io): begrænser båndbredde og IOPS pr. enhed. Du kan tildel proportional vægt eller sæt præcise grænser i bytes/s ops/s.
- NetværkDer findes mekanismer til at begrænse båndbredde og pakker, selvom understøttelsen er mindre moden og normalt kombineres med tc og QoS.
- PID'er: marker maksimalt antal processer/tråde som kan oprette en gruppe, hvilket afbøder fork bombs eller kodefejl, der udløser tråde.
Den praktiske idé er meget ligetil: du placerer din applikation i en cgroup og angiver maksimal CPU, hukommelse, I/O og PID'er . Derfra tager kernen sig af at håndhæve reglerne.
cgroups v1 vs cgroups v2
I produktion finder du to varianter af cgroups, og det er vigtigt at være klar over, hvilket territorium du bevæger dig på:
- cgroup v1Hver controller har sit eget uafhængige hierarki. Du har ruter som
/sys/fs/cgroup/cpu/...,/sys/fs/cgroup/memory/...Osv Det er fleksibelt, men kompliceret at håndtere i stor skala.Mange klassiske distributioner og platforme bruger det stadig eller understøtter det for kompatibilitet. - cgroup v2: grupperer alt under én samlet hierarki en
/sys/fs/cgroupGrænserne konfigureres via filer som f.eks.cpu.max,memory.max,io.maxog lignende. Det forenkler administrationen og forbedrer finjusteringen af ressourcerModerne distributioner bruger typisk v2 som standard, nogle gange med hybridtilstand.
En /proc/cgroups Du kan se, hvilke controllere der er aktiveret, hvor mange aktive cgroups der er, og om du er i v1 eller v2 (i v2 vil du se et samlet hierarki med ID 0). Kommandoen mount | grep cgroup Den fortæller dig også typen af filsystem (cgroup2fs antyder v2, tmpfs (peger normalt på v1).
Manuel oprettelse af cgroups og typiske grænser
For fuldt ud at forstå, hvad Docker laver under motorhjelmen, er det værd at oprette cgroups manuelt. Det generelle mønster er:
- Opret en cgroup-mappe.
- Konfigurer grænse-/prioritetsfiler (CPU, hukommelse, I/O, PID'er…).
- Tilføj PID'er til cgroup ved at skrive i
cgroup.procsotasks(i v1).
For eksempel, i cgroups v2, kunne du have noget i retning af dette for CPU og hukommelse:
mkdir /sys/fs/cgroup/test_app
echo "+cpu +memory +io +pids" > /sys/fs/cgroup/cgroup.subtree_control
echo "50000 100000" > /sys/fs/cgroup/test_app/cpu.max
echo "512M" > /sys/fs/cgroup/test_app/memory.max
echo $$ > /sys/fs/cgroup/test_app/cgroup.procs
Med dette er den nuværende proces begrænset til 50% af en kerne og 512 MB RAMHvis du starter noget intensivt (en dd til /dev/null, for eksempel), vil du se, at systemet ikke går amok. I cgroups v1 er skemaet lignende, men med filer som cpu.shares, cpu.cfs_quota_us, memory.limit_in_bytesosv., hver i sit eget hierarki.
Hvordan Docker, Kubernetes og kernen stakkes
Ovenfor navnerum og cgroups er der flere lag af software, der sender ansvaret frem og tilbage, indtil de når kernen. Selv hvis værktøjsnavnet ændres, er slutresultatet altid det samme: kernekald for at oprette navnerum, konfigurere cgroups og montere filsystemer.
Den typiske container-"stak" i dag ser meget sådan ud:
- OrkestreringslagKubernetes, Docker Swarm, Nomad… De håndterer pod-/servicelivscyklussen, planlægning, skalering og support. mikroservicearkitekturer.
- containerkørsel på højt niveauDocker Engine, containeriseret, CRI-O. De modtager orkestratorens ordrer og omsætter dem til konkrete handlinger. (download billeder, opret containere, administrer snapshots osv.).
- Lavniveau OCI-kørselstidRunc, crun og lignende værktøjer implementerer OCI-specifikationen og er direkte ansvarlige for at implementere den. de ringer
clone(),unshare(),mount()og selskab at materialisere beholderen. - Linux-kerneDen tilbyder navnerum, cgroups, OverlayFS og alle andre primitiver. Det er her, den virkelige "magi" sker.
Når du kaster en docker runStien er: CLI-klient → dockerd → containerd → runc → kernekald. Langs denne sti oprettes navnerum, cgroups tildeles, rootfs monteres med OverlayFS plus-volumener, og netværksgrænsefladerne og broen forbindes. Det, du ser som "en container", er for kernen en håndfuld processer med bestemte flag og særlige stier..
Krav og forudgående kontroller i systemet
Før du begynder at rode rundt med navnerum og cgroups på en server, er det en god idé at sikre dig, at hardwaren og kernen opfylder rimelige minimumskrav , især hvis du planlægger at opsætte noget, der ligner et produktionsmiljø.
Som reference anbefales det normalt at have mindst 2 logiske CPU'er, 4 GB RAM (helst 8 GB eller mere) , omkring 20 GB ledig diskplads og en kerne 3.10 eller højere (ideelt set 4.x eller 5.x for at have alle fordelene ved cgroups v2 og moderne funktioner).
For at bekræfte at kernen understøtter navnerum og cgroups, skal du blot tjekke konfigurationsfilen for den version, der er i brug:
grep -E "CONFIG_.*_NS" /boot/config-$(uname -r)
grep -E "CONFIG_CGROUP" /boot/config-$(uname -r)
Muligheder som disse burde vises der CONFIG_NAMESPACES, CONFIG_PID_NS, CONFIG_NET_NS, CONFIG_CGROUPS, CONFIG_MEMCG, CONFIG_CGROUP_SCHED markeret som =yUden det får du ikke meget af et spil.
En anden praktisk detalje er, om systemet tillader Opret brugernavnerum uden at være rootDette er meget nyttigt til test og nogle værktøjer. Det styres med sysctl. kernel.unprivileged_userns_cloneHvis den er på 0, bliver du nødt til at aktivere den (eller bruge din sved overalt).
Udforskning af cgroups og navnerum på "rigtige" maskiner
En forholdsvis ligetil måde at internalisere disse koncepter på er at se på, hvordan selve systemet og dine containere bruger dem . Nogle nyttige tips:
ls /proc/self/ns/Den viser dig de navnerum, som din nuværende shell tilhører (cgroup, ipc, mnt, net, pid, user, uts…).systemd-cgtopDen viser CPU-, hukommelses- og I/O-forbrug efter cgroup, meget ligtopmen grupperet efter tjenester og containere.- En
/sys/fs/cgroupDu kan finde de cgroups, som Docker eller Podman opretter for hver container, normalt ved hjælp af container-ID'et i stien. Indeni,cgroup.procsAngiv de tilhørende PID'er. /proc/<pid>/cgroupDen fortæller dig, hvilke cgroups en specifik proces er tildelt, perfekt til at spore en problematisk app.
Du vil se, at containere blot er endnu en gren af cgroups-hierarkiet med deres CPU-, hukommelses-, blkio- og andre begrænsninger, ligesom du kunne oprette dem manuelt . Det smukke ved Docker er, at det gør dette automatisk og med en brugervenlig API.
Performance-, NUMA- og affinitetsstrategier
I scenarier med høj belastning (store databaser, intensiv behandling, streaming osv.) er det ikke nok blot at "lancere containere". Hvordan du allokerer CPU og hukommelse til hver tjeneste påvirker ydeevnen betydeligt.
Med cpuset- driveren kan du tildele processer eller tjenester til bestemte sæt af CPU'er og NUMA-noder. Det er så simpelt som:
mkdir /sys/fs/cgroup/cpuset/dedicated_cpus
echo "4-7" > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.cpus
echo "0" > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.mems
echo 1 > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.cpu_exclusive
Det giver dig mulighed for at reservere en gruppe kerner til en specifik tjeneste og forhindre resten af systemet i at forstyrre. Dette reducerer støj, trådmigrering og latensproblemer , der ofte opstår, når alle konkurrerer om de samme CPU'er.
På hukommelsesniveau hjælper kombinationen af numactl med cgroups (eller direkte brug af hukommelsescontrollerens NUMA-knapper, når de er tilgængelige) med at holde data på den fysiske node, der er tættest på de kerner, der bruger den. Dette er mærkbart i latenstidsfølsomme applikationer.
Overvågning og observerbarhed af cgroups og navnerum
Det er fint at sætte ressourcegrænser, men Hvis du ikke måler, hvad der sker, kan du skyde dig selv i foden uden overhovedet at indse det.cgroup v2 tilbyder i sig selv en hel del nyttige målinger i loggene *.stat, *.events y *.pressure.
Nogle praktiske eksempler:
cpu.statDen fortæller dig, hvor meget CPU-tid gruppen har brugt, hvor mange gange den er blevet begrænset osv.memory.currentymemory.maxDe viser strømforbrug og konfigureret grænse.memory.eventsDen registrerer OOM'er, nåede bløde grænser og andre skræmmende signaler.io.statakkumulerer læsninger/skrivninger pr. enhed.
For et hurtigt overblik, systemd-cgtop Det er meget praktisk, og hvis du ønsker noget mere industrielt, er værktøj som cRådgiver De eksporterer Docker/containerd cgroup-metrikker til Prometheus, så du kan bygge komplette dashboards i Grafana uden for meget besvær, ideelt til en avanceret systemovervågning.
Ved fejlfinding af navneområdeproblemer, simple scripts, der gennemløber /proc/<pid>/ns/ og sammenlign de symbolske links, der hjælper med at vide hvilke processer der deler præcis det samme navnerum, eller for at finde et "tabt" netværksnavnerum, der er gået i stå.
Brug af systemd som en cgroup manager
I de fleste moderne distributioner er systemd i høj grad afhængig af cgroups til at administrere tjenester, slices og scopes . Når du definerer en enhed med ressourcegrænser, opretter systemd faktisk en cgroup for den pågældende tjeneste og konfigurerer dens handlers.
En tjeneste deklareret med direktiver såsom:
CPUQuota=50%
CPUWeight=500
MemoryMax=512M
IOReadBandwidthMax=/dev/sda 10M
Dette omsættes til cgroup-begrænsninger på CPU, hukommelse og I/O for den pågældende enheds proces. Dette giver mulighed for at justere "klassisk" servicehåndtering med containermodellen ved hjælp af det samme underliggende kernelsystem. For flere detaljer om systemd og dets krav, se systemd changes.
For teams, der blander traditionelle tjenester med containere (meget almindeligt), er det en elegant måde at forhindre ressourcer i at blive dræbt af pods i det containeriserede miljø at bruge systemd til at sætte grænser for kritiske dæmoner.
I miljøer med flere arbejdsbelastninger, fra databaser til overvågningsagenter og brugerdefinerede applikationer, giver mestring af navnerum og cgroups dig mulighed for at definere stærke isolationspolitikker, justere servicekvaliteten og sikre, at hver komponent har sin egen velkontrollerede allokering af CPU og hukommelse . Forståelse af, hvordan Docker, Kubernetes og systemd udnytter disse kernefunktioner, udstyrer dig til at designe mere sikre multi-tenant-infrastrukturer, fejlfinde komplekse problemer (fra OOM'er til I/O-flaskehalse) og få mest muligt ud af din hardware uden at ofre stabilitet.