# Containers from Scratch: A Deep Dive into Single-Host Container Networking

***This article was orginally published on*** [***zawzaw.blog***](https://www.zawzaw.blog/deep-dive-into-container-networking/)***.***

ဒီဆောင်းပါးမှာ Container တွေ အောက်ခြေမှာ ဘယ်လိုအလုပ်လုပ်လဲ၊ Container တွေ တခုနဲ့တခု Network Layer မှာ ဘယ်လိုဆက်သွယ်လဲဆိုတာ အခြေခံသဘောတရားတွေကို လက်တွေ့ကျကျ လေ့လာရမှာ ဖြစ်ပါတယ်။ လက်တွေ့စမ်းကြည့်လို့လည်း ရနိုင်ပါတယ်။ Linux မှာ အလွယ်တကူရတဲ့ Command-line Tools နဲ့ စမ်းသပ်ထားပါတယ်။ ဒီဆောင်းပါကို ဖက်ပြီး စမ်းကြည့်ပြီးရင်တော့ Docker ရဲ့ Bridge Networking ကို ကောင်းကောင်းနားလည်သွားပါလိမ့်မယ်။

# Summary: Key Points

* ခေတ်သစ် Containerization နည်းပညာရဲ့ အခြေခံအုတ်မြစ်ဖြစ်သော Linux Namespaces အကြောင်း။
    
* Linux Namespaces ဘယ်လိုအလုပ်လုပ်ပြီး Container တွေ Host Machine ကနေ ဘယ်လို သီးခြားခွဲထုတ်ပြီး တည်ဆောက်လို့ရသလဲ။
    
* Virtual Ethernet (VETH) နှင့် Bridge Networking ဘယ်လို အလုပ်လုပ်သလဲ။
    
* Containers နှင့် Container Networking ရဲ့ အခြေခံအဆင့် (low level) တွင် မည်သို့အလုပ်လုပ်သလဲဆိုတာ ဒီဆောင်းပါးမှာ အဓိကလေ့လာရမှာ ဖြစ်ပါတယ်။
    

# Prerequisities

* Linux Host (e.g., Ubuntu, Fedora, etc..)
    
* Linux Namespaces
    
* Command-Line Tools `chroot`, `unshare`, `ip`
    

---

# Setup Project Structure

ပထမဦးဆုံးအနေနဲ့ Container တွေ ဘယ်လိုအလုပ်လုပ်လဲ ဆိုတာကို နားလည်နိုင်ဖို့အတွက် `chroot` နှင့် `unshare` CLI tools များကို အသုံးပြုပြီး Container များကို အစကနေ တည်ဆောက်ပြီး run ကြည့်ပါမယ်။

Project Structure သည် အောက်ပါအတိုင်း ဖြစ်ပါလိမ့်မယ်။

```bash
/home/zawzaw/containers
        ├── alpine-linux
        │   ├── bin
        │   ├── dev
        │   ├── etc
        │   ├── home
        │   ├── lib
        │   ├── media
        │   ├── mnt
        │   ├── opt
        │   ├── proc
        │   ├── root
        │   ├── run
        │   ├── sbin
        │   ├── srv
        │   ├── sys
        │   ├── tmp
        │   ├── usr
        │   └── var
        └── tiny-linux
            ├── bin
            ├── dev
            ├── proc
            ├── sbin
            ├── sys
            └── usr
```

Containers လို့ အမည်ပေးထားတဲ့ Project directory တစ်ခုဆောက်လိုက်ပါမယ်။ အဲဒီနောက် Alpine Linux Root Filesystem image ကို အဲဒီ directory ထဲကို ထည့်လိုက်မှာ ဖြစ်ပါတယ်။

```bash
$ mkdir -p containers/alpine-linux
```

ဒီဆောင်းပါးမှာ Alpine Linux Mini root filesystem ကို အသုံးပြုမှာ ဖြစ်ပါတယ်။ Alpine Linux ရဲ့ Official website ဖြစ်တဲ့ [https://alpinelinux.org/downloads](https://alpinelinux.org/downloads) သို့သွားရောက်ပြီး Alpine Linux Mini root filesystem ကို download လုပ်လိုက်ပါ။ Alpine Linux က Container များနှင့် Minimal chroots များမှာ အသုံးပြုဖို့အတွက် Alpine Mini root filesystem ကို ထောက်ပံ့ပေးထးပါတယ်။ `aarch64`, `armv7`, `riscv64`, `x86`, `x86_64` စသည်ဖြင့် မတူညီသော System architectures များကို ထောက်ပံ့ပေးပါတယ်။

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1753780037530/a8f8285d-d73d-4358-9cf3-a7672168d63d.png align="center")

(သို့မဟုတ်)

`curl` command line tool ဖြင့် download လုပ်နိုင်ပါတယ်။ ဥပမာ - x86\_64 architecture အတွက်။

```bash
$ curl -LO https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-minirootfs-3.21.3-x86_64.tar.gz
```

Download လုပ်ထားသော Alpine Linux Mini rootfs file ကို `containers/alpine-linux` directory အောက်သို့ထည့်ပြီးနောက် mini rootfs tar file ကို ဖြည်ပါ။

```bash
$ cp alpine-minirootfs-3.21.3-x86_64.tar.gz ~/containers/alpine-linux
$ cd ~/containers/alpine-linux
$ tar -xzvf alpine-minirootfs-3.21.3-x86_64.tar.gz
```

ဖြည်ပြီးရင်တော့ `alpine-minirootfs-3.21.3-x86_64.tar.gz` tar file ကို ဖျတ်လိုက်လို့ ရပါပြီ။

```bash
$ rm alpine-minirootfs-3.20.2-x86_64.tar.gz
```

Project တည်ဆောက်ပုံက အောက်ပါပုံစံအတိုင်း ဖြစ်ပါလိမ့်မယ်။

```bash
/home/zawzaw/containers
        ├── alpine-linux
        │   ├── bin
        │   ├── dev
        │   ├── etc
        │   ├── home
        │   ├── lib
        │   ├── media
        │   ├── mnt
        │   ├── opt
        │   ├── proc
        │   ├── root
        │   ├── run
        │   ├── sbin
        │   ├── srv
        │   ├── sys
        │   ├── tmp
        │   ├── usr
        │   └── var
```

---

# Running Containers from Scratch

## Introduction to Chroot

```bash
/ (Host Root Filesystem)
├── bin
├── dev
├── etc
├── home
│   └── zawzaw/
│        └── containers/
│            ├── alpine-linux/
│            │    ├── bin
│            │    ├── dev
│            │    ├── etc
│            │    ├── home
│            │    ├── lib
│            │    ├── proc
│            │    ├── sbin
│            │    └── var
│            └── tiny-linux/
│                 ├── bin
│                 ├── dev
│                 ├── init.sh
│                 ├── linuxrc -> bin/busybox
│                 ├── proc
│                 ├── sbin
│                 ├── sys
│                 ├── sbin
│                 └── usr
├── proc
├── sbin
├── sys
├── usr
└── var
```

Change Root (`chroot`) က Unix နဲ့ Linux မှာ အလွယ်တကူ အသုံးပြုနိုင်တယ်။ Container တခုကို Host OS ကနေ ဘယ်လိုခွဲထုတ်ပြီး တည်ဆောက်တာမလဲဆိုတာ မလေ့လာခင် chroot ကို အရင်လေ့လာကြည့်ပါမယ်။

chroot က Container တွေရဲ့ အခြေခံသဘောတရားတခုလို့လည်း ဆိုနိုင်တယ်။ သမိုင်းကြောင်းအရ မူရင်း chroot system call က ၁၉၇၉ ခုနှစ်မှာ ထွက်ရှိခဲ့တဲ့ Unix Seventh Edition (Version 7) မှာ စတင်မိတ်ဆက်ခဲ့တာ ဖြစ်ပါတယ်။ Linux system တခုမှာ အဓိက Root filesystem တခုရှိပြီး၊ လက်ရှိအလုပ်လုပ်နေတဲ့ process နဲ့ သူ့ရဲ့ child process တွေအတွက် Root directory ကို ပြောင်းလဲပေးတဲ့ လုပ်ဆောင်ချက်တစ်မျိုး ဖြစ်ပါတယ်။ အဲဒီသီးခြား Root filesystem ထဲမှာ ကိုယ်စမ်းသပ်ချင်တာတွေ လုပ်ဆောင်လို့ရနိုင်တယ်။

ဒါပေမယ့် chroot client tool က Linux kernel ကနေ ထောက်ပံ့တဲ့ [chroot(2)](https://man7.org/linux/man-pages/man2/chroot.2.html?ref=zawzaw.blog) system call ကို ခေါ်ယူပြီး အသုံးပြုတာ ဖြစ်ပါတယ်။ ဒါကြောင့် chroot က နောက်ဘက်က Linux kernel ရဲ့ ထောက်ပံ့မှုပေါ်မှာ မူတည်ပါတယ်။

chroot ကို ဘယ်နေရာတွေမှာ သုံးနိုင်မလဲ ဆိုရင်

* **Sandboxing:** Program တစ်ခုကို သတ်မှတ်ထားတဲ့ environment မှာပဲ အလုပ်လုပ်စေပြီး System တစ်ခုလုံးကို ထိခိုက်မှုမရှိအောင် ကာကွယ်ပေးပါတယ်။
    
* **Recovery and Testing:** System တစ်ခု ပျက်သွားတဲ့အခါ ပြန်လည်ပြင်ဆင်ဖို့ ဒါမှမဟုတ် Software အသစ်တွေ စမ်းသပ်ဖို့အတွက် လိုအပ်တဲ့ environment တစ်ခုကို ဖန်တီးပေးပါတယ်။
    

## Using the chroot User-space Tool

`chroot` client tool ကို စပြီး စမ်းသုံးကြည့်ပါမယ်။ အပေါ်မှာပြောခဲ့သလိုပဲ chroot က User-space tool ဖြစ်ပြီး၊ သူက [chroot(2)](https://man7.org/linux/man-pages/man2/chroot.2.html?ref=zawzaw.blog) system call ကို ခေါ်ယူပြီး အသုံးပြုတာဖြစ်ပါတယ်။

အပေါ်ဆုံးအဆင့်မှာ ဆောက်ထားတဲ့ `${HOME}/containers/alpine-linux` အောက်ကို သွားပြီး chroot command ကို စသုံးပါမယ်။

```bash
$ cd $HOME/containers/alpine-linux
$ sudo chroot . /bin/sh
```

အဒီနောက် Linux commands တွေနဲ့ စပြီးစမ်းသပ်ကြည့်နိုင်ပါတယ်။

```bash
/ # ls -l
total 0
drwxr-xr-x    1 1000     1000           858 Jul 22 14:34 bin
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 dev
drwxr-xr-x    1 1000     1000           540 Jul 22 14:34 etc
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 home
drwxr-xr-x    1 1000     1000           272 Jul 22 14:34 lib
drwxr-xr-x    1 1000     1000            28 Jul 22 14:34 media
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 mnt
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 opt
dr-xr-xr-x    1 1000     1000             0 Jul 22 14:34 proc
drwx------    1 1000     1000            24 Jul 30 04:26 root
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 run
drwxr-xr-x    1 1000     1000           790 Jul 22 14:34 sbin
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 srv
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 sys
drwxr-xr-x    1 1000     1000             0 Jul 22 14:34 tmp
drwxr-xr-x    1 1000     1000            40 Jul 22 14:34 usr
drwxr-xr-x    1 1000     1000            86 Jul 22 14:34 var
```

```bash
/ # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.3
PRETTY_NAME="Alpine Linux v3.21"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
```

## The /proc Virutal Filesystem

ပြီးတဲ့နောက် `proc` Virtual filesystem ကို ကိုယ့်ရဲ့ chroot environment ထဲမှာ Mount လုပ်ဖို့ လိုအပ်ပါလိမ့်မယ်။ ဒီ command က chroot environment မှာ `proc` Virtual filesystem ကို /proc directory ထဲကို Mount လုပ်လိုက်တာ ဖြစ်ပါတယ်။

```bash
$ mount -t proc proc /proc
```

*ဒီနေရာမှာ* `proc` *Virtual filesystem ဆိုတာ ဘယ်လိုမျိူးလဲ။ ဘာကြောင့် Mount လုပ်ဖို့ လိုအပ်တာလဲလို့ မေးစရာရှိပါတယ်။*

Linux မှာ `proc` က Virtual Filesystem (VFS) တနည်းအားဖြင့် Special filesystem တခုဖြစ်ပါတယ်။ `proc` က Linux kernel ရဲ့ နောက်ဘက်ကနေ ထောက်ပံ့ပေးထားတဲ့ VFS တခုဖြစ်ပြီး Hardware(System) နဲ့ Process အချက်လက်တွေကို **In-Memory** သိမ်းတာ ဖြစ်ပါတယ်။ ဒီနေရာမှာ ရှင်းရှင်းလင်းလင်းသိရမှာက `/proc` အောက်က files တော့ Linux kernel ကနေ Boot တတ်လာချိန်မှာ Dynamically တည်ဆောက်ပေးတာ ဖြစ်ပြီး၊ **Disk ပေါ်မှာ တကယ်သိမ်းတာ မဟုတ်ဘဲ In-Memory ပဲ သိမ်းထားဖြစ်ပါတယ်။**

ဥပမာ

* `cat /proc/version` က လက်ရှိ Linux kernel version ကို စစ်ကြည့်ဖို့သုံးနိုင်တယ်။
    
* `cat /proc/cpuinfo` က လက်ရှိ Linux system ရဲ့ CPU နဲ့ ပတ်သက်တဲ့ အသေးစိတ်အချက်လက်ကို အသေးစိတ် ကြည့်နိုင်တယ်။
    

ဒါကြောင့် chroot environment ထဲမှာ process နဲ့ ပတ်သက်တဲ့ အသေးစိတ်အချက်လက်ကို သိနိုင်ဖို့၊ သုံးနိုင်ဖို့ `proc` Virtual Filesystem ကို မဖြစ်နေ Mount လုပ်ဖို့လိုအပ်တာ ဖြစ်ပါတယ်။ အဲဒီလို Mount မလုပ်ရင် chroot environment ထဲမှာ `ps` လိုမျိုး command က ကောင်းကောင်း အလုပ်မလုပ်နိုင်ပါဘူး။

ဥပမာ

```bash
$ ps aux
# Error: Could not read /proc/stat
```

ဘာကြောင့်လဲဆိုတော့ `ps` က client tool ဖြစ်ပြီး တကယ့်လုပ်ဆောင်ချက်က နောက်ဘက်က `/proc` ကနေ ထောက်ပံ့ပေးတာကြောင့် ဖြစ်ပါတယ်။

အပေါ်က `mount -t proc proc /proc` ကို ကိုယ့်ရဲ့ chroot environment ထဲမှာ Run ပြီးရင်တော့ အောက်က Linux system နဲ့ process နဲ့ ပတ်သက်တဲ့ commands တွေကို စမ်းကြည့်နိုင်တယ်။

* `cat /proc/cpuinfo`
    
* `ps aux`
    
* `ip addr`
    

```bash
/ # cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 142
model name      : Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
stepping        : 12
microcode       : 0xfc
cpu MHz         : 2900.231
cache size      : 8192 KB
...
```

```bash
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:05 /usr/lib/systemd/systemd --switched-root --system --deserialize=51 rhgb
    2 root      0:00 [kthreadd]
    3 root      0:00 [pool_workqueue_]
    4 root      0:00 [kworker/R-rcu_g]
    5 root      0:00 [kworker/R-sync_]
    6 root      0:00 [kworker/R-slub_]
    7 root      0:00 [kworker/R-netns]
    9 root      0:00 [kworker/0:0H-ev]
   12 root      0:00 [kworker/R-mm_pe]
   14 root      0:00 [rcu_tasks_kthre]
   15 root      0:00 [rcu_tasks_rude_]
   16 root      0:00 [rcu_tasks_trace]
   17 root      0:16 [ksoftirqd/0]
   18 root      0:10 [rcu_preempt]
   19 root      0:00 [rcu_exp_par_gp_]
   20 root      0:00 [rcu_exp_gp_kthr]
...
```

```bash
/ # ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
    link/ether fa:8b:5b:09:59:51 brd ff:ff:ff:ff:ff:ff
    inet 192.168.55.129/24 brd 192.168.55.255 scope global dynamic noprefixroute wlo1
       valid_lft 75600sec preferred_lft 75600sec
    inet6 fe80::fa24:a316:4e60:c881/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
...
```

ဒီနေရာမှာ သတိထားမိပါလိမ့်မယ်။ chroot environment ထဲမှာ Host OS ရဲ့ Process / Network နဲ့ ပတ်သက်တာတွေအားလုံးကို မြင်နေရတာကို တွေ့ရပါလိမ့်မယ်။ နောက်အပိုင်းမှာ ဘယ်လို Isolate လုပ်လို့ရမလဲဆိုတာ အသေးစိတ်လေ့လာကြည့်ပါမယ်။

---

# Isolating from the Host Machine

## Linux Namespaces

Linux Namespaces က Linux kernel ရဲ့ feature တခုဖြစ်ပြီး ခေတ်သစ် Containerization နည်းပညာရဲ့ အခြေခံအုတ်မြစ်တခုလည်း ဖြစ်ပါတယ်။

အဓိက သဘောတရားကတော့ Process အစု၀ေးတခုအတွက် မတူညီတဲ့ System resources တွေကို Host Machine ကနေ သီးခြားခွဲထုတ်ပြီး Virtualize လုပ်တောကို လုပ်ဆောင်ပေးတယ်။ Namespaces ကို ၂၀၀၂ ခုနှစ် Linux kernel version 2.4.19 တည်းက စတင်သုံးခဲ့ပါတယ်။

System resources ဆိုတဲ့နေရာမှာ အောက်ပါအရာတွေပါ၀င်ပါတယ်

* PID (Process ID)
    
* Mount
    
* UTS (UNIX Timesharing System)
    
* IPC (Inter-process Communication)
    
* Network
    
* User
    
* Control Groups (CGroups)
    

Documentation: [https://man7.org/linux/man-pages/man7/namespaces.7.html](https://man7.org/linux/man-pages/man7/namespaces.7.html?ref=zawzaw.blog)

| **Namespace** | **Manual Page** | **Isolates** |
| --- | --- | --- |
| PID (Process ID) | [pid\_namespaces](https://man7.org/linux/man-pages/man7/pid_namespaces.7.html?ref=zawzaw.blog) | Isolates process IDs. |
| Mount | [mount\_namespaces](https://man7.org/linux/man-pages/man7/mount_namespaces.7.html?ref=zawzaw.blog) | Isolates the set of mounted filesystems. |
| UTS (UNIX Timesharing System) | [uts\_namespaces](https://man7.org/linux/man-pages/man7/uts_namespaces.7.html?ref=zawzaw.blog) | Isolates hostname and DNS name. |
| IPC (Inter-process Communication) | [ipc\_namespaces](https://man7.org/linux/man-pages/man7/ipc_namespaces.7.html?ref=zawzaw.blog) | Isolates IPC resources, such as message queue and shared memory. |
| Network | [network\_namespaces](https://man7.org/linux/man-pages/man7/network_namespaces.7.html?ref=zawzaw.blog) | Isolates network interfaces, IP addresses, routing tables, and port numbers. |
| User | [user\_namespaces](https://man7.org/linux/man-pages/man7/user_namespaces.7.html?ref=zawzaw.blog) | Isolates user and group IDs. |
| CGroup | [cgroup\_namespaces](https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html?ref=zawzaw.blog) | Isolates the view of Control Groups (CGroups). |

ဥပမာ - Linux Host Machine တခုပေါ်မှာ Container တခု လည်ပတ်နေတယ် ဆိုပါစို့။

* Container မှာ သီးခြားကိုယ်ပိုင် Root Filesystem ရှိနိုင်တယ်။ အကြောင်းက သူက Host Machine ကနေ **Mount** Namespaces ကို ခွဲထုတ်လိုက်လို့ ဖြစ်ပါတယ်။
    
* Container မှာ သီးခြားကိုယ်ပိုင် Hostname ရှိနိုင်တယ်။ အကြောင်းက သူက Host Machine ကနေ **UTS** Namespaces ကို ခွဲထုတ်လိုက်လို့ ဖြစ်ပါတယ်။
    
* Container မှာ သီးခြားကိုယ်ပိုင် PIDs များ ရှိနိုင်ပါတယ်။ အကြောင်းကတော့ သူမှာ Host Machine ကနေ **PID (Process ID)** Namespaces ကို ခွဲထုတ်လိုက်လို့ ဖြစ်ပါတယ်။
    

## Using the unshare User-space Tool

Linux Namespaces က Linux kernel ကနေ နောက်ဘက်ကနေ ထောက်ပံ့ပေးသူဖြစ်ပြီး သူနဲ့ ဆက်သွယ်ဆောင်ရွတ်ဖို့က `unshare` client tool ကို သုံးနိုင်ပါတယ်။

`unshare` က Client tool ဖြစ်ပြီး Linux Namespaces က Backend service လို့ မြင်လို့ရပါတယ်။ `ushare` CLI tool က [unshare(2](https://man7.org/linux/man-pages/man2/unshare.2.html?ref=zawzaw.blog)) system call ကို ခေါ်ပြီး Namespace အသစ်တခု ဆောက်တာ သို့မဟုတ် ရှိထားပြီးသား Namespace တခုကိုရွေ့တာ စသဖြင့် လုပ်ဆောင်နိုင်ပါတယ်။

အပေါ်က Linux Namespaces အပိုင်းမှာ ပြောခဲ့သလိုပဲ `unshare` CLI tool အောက်ပါ Namespaces တွေကို တည်ဆောက်နိုင်ပါတယ်။

* `--mount`: Create a new mount namespace.
    
* `--uts`: Create a new UTS namespace (isolates hostname and domain name).
    
* `--ipc`: Create a new IPC namespace.
    
* `--net`: Create a new network namespace.
    
* `--pid`: Create a new PID namespace.
    
* `--user`: Create a new user namespace.
    
* `--cgroup`: Create a new cgroup namespace.
    
* `--fork`: Fork a new process to run the command (required for PID namespaces).
    

စပြီး သုံးကြည့်ပါမယ်။ အပေါ်ဦးဆုံးအပိုင်းမှာ ဆောက်ခဲ့တဲ့ `containers/alpine-linux` အောက်ကို သွားပြီး `unshare` ကို Run ပါမယ်။ ဒီ command က PID / Mount / Network Namespaces တွေကို unshare ကို သုံးပြီး Host Machine ကနေ သီးခြားခွဲထုတ်လိုက်တာ ဖြစ်ပါတယ်။

```bash
$ cd containers/alpine-linux
$ sudo unshare --pid --mount --net -f chroot alpine-linux /bin/sh
```

```bash
/ # cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.3
PRETTY_NAME="Alpine Linux v3.21"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
```

Change Root (chroot) အပိုင်းမှာ ပြောခဲ့သလိုပဲ System နဲ့ Process အချက်လက်ကို ရယူနိုင်ဖို့ `/proc` ကို Mount လုပ်ပေးဖို့ လိုအပ်ပါတယ်။

```bash
$ mount -t proc proc /proc
```

ပြီးတဲနောက်တော့ Chroot Container ထဲမှာ လက်ရှိလည်ပတ်နေတဲ့ Process ID (PID) နဲ့ Network interfaces တွေကို စစ်ကြည့်ပါမယ်။

```sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    5 root      0:00 ps aux
```

```sh
/ # ip addr show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```

***ဒီနေရာမှာ သတိထားမိပါလိမ့်မယ်။ PID နဲ့ Network interfaces တွေက Host Machine ကနေ ခွဲထုတ်လိုက်ပြီး သီးခြားစီဖြစ်နေတာကို တွေ့ရပါလိမ့်မယ်။ နောက်အပိုင်းမှာ Network Layer မှာ Container တွေ ဘယ်လိုအလုပ်လုပ်သလဲဆိုတာ ဆက်လက်လေ့လာရမှာ ဖြစ်ပါတယ်။***

---

# Configuring Container Networks from Scratch

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754037520029/afc26b33-47cd-4e15-9634-a2be612cc821.png align="center")

* **Alpine Linux Container IP address:** `172.19.35.3`
    
* **Tiny Linux Container IP address:** `172.19.35.2`
    

ဒီအပိုင်းမှာ Container နှစ်လုံးအတွက် အစကနေ VETH (Virtual Ethernet) နဲ့ Bridge Network တခု တည်ဆောက်ပြီး ဘယ်လိုအလုပ်လုပ်သလဲ လေ့လာရမှာ ဖြစ်ပါတယ်။ **Virtual Ethernet (VETH)** နဲ့ **Bridge Networking က** Linux Virtual Networking ရဲ့ အဓိကအစိတ်ပိုင်းတွေ ဖြစ်ပြီး Container နဲ့ Host Machine သို့မဟုတ် Container ကနေ Container ဆက်သွယ်ဖို့ အသုံးပြုပါတယ်။ Container Networking နည်းပညာဘက်မှာ အလွန်အသုံးများပါတယ်။

## Virtual Ethernet (VETH)

![Photo: Virtual Ethernet (VETH) by Red Hat Developers](https://cdn.hashnode.com/res/hashnode/image/upload/v1754177645148/fed54ad9-0a89-46a9-ac8d-98aaaa1176b7.png align="center")

**Virtual Ethernet (VETH)** ဆိုတာ ပိုက် (pipe) တစ်ခုလိုမျိုး လုပ်ဆောင်တဲ့ Virtual network interfaces နှစ်ခုတွဲ ဖြစ်ပါတယ်။ အဲဒီ Interface နှစ်ခုထဲက တစ်ခုကနေ ပို့လိုက်တဲ့ အချက်အလက် (packets) တွေဟာ ကျန်တစ်ခုကနေ လက်ခံရရှိမှာ ဖြစ်ပါတယ်။ VETH ကို ပုံမှန်အားဖြင့် **Network namespaces** များကို Host machine သို့မဟုတ် အခြား Network namespaces များနှင့် ချိတ်ဆက်ရန် အသုံးပြုပါတယ်။

VETH တွဲတစ်ခုမှာ Interfaces နှစ်ခု ပါဝင်ပါတယ်။

* တစ်ခုက **Host machine** ရဲ့ Network namespace ထဲမှာ ရှိပြီး
    
* ကျန်တစ်ခုက **Container** ရဲ့ Network namespace ထဲမှာ ရှိပါတယ်။
    

Interface တစ်ခုကနေ ပို့လိုက်တဲ့ packets တွေကို အခြား Interface ကနေ လက်ခံရရှိပါတယ်။ ဒါကြောင့် Container နဲ့ Host ဒါမှမဟုတ် အခြား Container တွေကြားမှာ အချင်းချင်း ချိတ်ဆက်ပြောဆိုနိုင်တာ ဖြစ်ပါတယ်။

## Bridge Networking

![Photo: Bridge Networking by Red Hat Developers](https://cdn.hashnode.com/res/hashnode/image/upload/v1754177899770/0ab00a16-f6ab-4e19-81e5-de17b91b1071.png align="center")

**Bridge Network** ဆိုတာ Virtual network switch တစ်ခုဖြစ်ပြီး Network interfaces များစွာကို တစ်ခုနဲ့တစ်ခု ချိတ်ဆက်ပေးပါတယ်။ ဒါ့ကြောင့် Container တွေကို အချင်းချင်း ဆက်သွယ်နိုင်အောင် လုပ်ဆောင်ပေးပါတယ်။

* Bridge တစ်ခုဟာ Layer 2 device တစ်ခုလိုမျိုး လုပ်ဆောင်ပြီး ချိတ်ဆက်ထားတဲ့ Interfaces တွေကြားမှာ Ethernet frames တွေကို လက်ဆင့်ကမ်းပို့ဆောင်ပေးပါတယ်။
    
* Containers တွေ ဒါမှမဟုတ် Virtual Machines (VMs) တွေကို **VETH pairs** ကနေတဆင့် bridge နဲ့ ချိတ်ဆက်ပါတယ်။
    

## Step (1): Setting up VETH Network for Container (A) — Alpine Linux Container

ဒီအပိုင်းမှာ Alpine Linux Container ပေါ်တွင် VETH ကွန်ရက်ကို ဘယ်လိုတည်ဆောက်မလဲဆိုတာ ဖော်ပြသွားပါမယ်။

အပေါ်မှာ စမ်းခဲ့တဲ့နည်းလမ်းအတိုင်းပါပဲ ***unshare*** နှင့် ***chroot*** Command-line tool များကို အသုံးပြုပြီး သီးခြားခွဲထားသော PID (Process ID)၊ Mount နှင့် Network namespaces များပါရှိတဲ့ Alpine Linux Container တခုကို တည်ဆောက်မှာ ဖြစ်ပါတယ်။

```bash
$ cd ${HOME}/containers/alpine-linux
$ sudo unshare --pid --mount --net \
  -f chroot . \
  env -i \
    HOME=/root \
    HOSTNAME=alpine-linux \
  /bin/sh
$ mount -t proc proc /proc
```

ထို့နောက် ကိုယ့်ရဲ့ **Host Linux machine** မှာ Container PID ကို ရယူပါ။ ဒီမှာတော့ ကျွန်တော့်ရဲ့ Alpine Linux Container PID သည် **25473** ဖြစ်ပါတယ်။

***ဒီနေရာမှာ PID က စမ်းတဲ့သူပေါ်မှာ မူတည်ပြီး အပြောင်းလဲ ရှိနိုင်ပါတယ်။***

```bash
[zawzaw@fedora-linux:~]$ ps -C sh
    PID TTY          TIME CMD
  25473 pts/6    00:00:00 sh
```

ပြီးရင်တော့ `export` command ဖြင့် `ALPINE_CONTAINER_PID` environment variable ကို သတ်မှတ်လိုက်ပါ။ VETH Network ကို တည်ဆောက်တဲ့အခါ ဒီ PID ကို လိုအပ်ပါတယ်။

```bash
[zawzaw@fedora-linux:~]$ export ALPINE_CONTAINER_PID=25473
```

**Host Linux machine** မှာ `ip` command-line tool ဖြင့် `veth0`, `veth1` ဟုခေါ်သော VETH network pair တစ်ခုကို တည်ဆောက်လိုက်ပါမယ်။

***ဒီနေရာမှာ သေချာနားလည်ရမှာက*** `veth0` ***က Host machine မှာ ရှိမှာဖြစ်ပြီး၊*** `veth1` ***က Alpine Linux Container ဘက်မှာ ရှိမှာဖြစ်ပါတယ်။***

```bash
[zawzaw@fedora-linux:~]$ sudo ip link add veth0 type veth peer name veth1
[zawzaw@fedora-linux:~]$ sudo ip link set veth1 netns "${ALPINE_CONTAINER_PID}"
[zawzaw@fedora-linux:~]$ sudo ip link set dev veth0 up
```

**Alpine Linux Container** ပေါ်တွင် `veth1` Network interface ရဲ့ IP address ကို `172.19.35.3` လို့ သတ်မှတ်လိုက်ပါမယ်။

```bash
/ # ip addr add dev veth1 172.19.35.3/24
/ # ip link set lo up
/ # ip link set veth1 up
```

**Host Linux machine** ပေါ်တွင် Network interfaces များကို စစ်ကြည့်လိုက်ရင် အောက်ကအတိုင်း တွေ့ရပါလိမ့်မယ်။

```bash
[zawzaw@fedora-linux:~]$ ip addr show veth0
11: veth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c6:23:c1:27:62:a8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::c423:c1ff:fe27:62a8/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
```

**Alpine Linux Container** ပေါ်တွင် Network interfaces နှင့် IP addresses များကို စစ်ကြည့်လိုက်ရင် အောက်ကအတိုင်း တွေ့ရပါလိမ့်မယ်။

```bash
/ # ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
10: veth1@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP qlen 1000
    link/ether 1e:c9:8c:80:fd:9a brd ff:ff:ff:ff:ff:ff
    inet 172.19.35.3/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::1cc9:8cff:fe80:fd9a/64 scope link
       valid_lft forever preferred_lft forever
```

အခုအခြေနေမှာ `veth0`, `veth1` VETH interfaces များ အလုပ်လုပ်နေပြီး ၎င်းတို့ရဲ့ IP address တွေကို `172.19.35.3` လို့ သတ်မှတ်ထားတာကို တွေ့ရပါလိမ့်မယ်။

## Step (2): Setting up VETH Network for Container (B) — Tiny Linux Container

ဒီအပိုင်းမှာ အပေါ်ကဆင့်တွေအတိုင်းပဲ အတူတူပဲ ဖြစ်ပါတယ်။ နောက်ထပ် Container တလုံး တည်ဆောက်မှာ ဖြစ်ပါတယ်။ Container (B) အတွက် Linux kernel source code နဲ့ Busybox တို့ကို အသုံးပြုပြီး ကိုယ်တိုင် Compile လုပ်ထားတဲ့ **Tiny Linux** root filesystem image ကို အသုံးပြုပါမယ်။ [Building a minimal Linux system from Scratch and Booting in QEMU Emulator](https://gist.github.com/zawzaww/cfedae575c7f9fc83ea3a02105dc263e?ref=zawzaw.blog) မှာ အသေးစိတ် လေ့လာနိုင်ပါတယ်။

အောက်ပါ **Tiny Linux** root filesystem image ကို ဒေါင်းလုဒ်ဆွဲပြီး `~/containers/` project directory ထဲသို့ ထည့်လိုက်ပါ။

**Download Link:** [Tiny Linux Root Filesystem Image](https://drive.google.com/file/d/1suciSI1GHOuUs4j6CXU6VYZmt0ESEVC1/view?ref=zawzaw.blog)

```bash
[zawzaw@fedora-linux:~/containers/tiny-linux]$ tree
.
├── bin
├── dev
├── linuxrc -> bin/busybox
├── proc
├── sbin
├── sys
└── usr
```

ဒီအပိုင်းမှာတော့ **Tiny Linux Container** ပေါ်မှာ VETH Network ကို တည်ဆောက်ပါမယ်။ ပထမအတိုင်းပဲ `unshare` နဲ့ `chroot` Command-line tool တွေကို အသုံးပြုပြီး **Container (B)**—**Tiny Linux** ကို PID (Process ID)၊ Mount နဲ့ Network namespaces တွေ သီးခြားခွဲထုတ်ပြီး တည်ဆောက်လိုက်ပါမယ်။

```bash
$ cd ${HOME}/containers/tiny-linux
$ sudo unshare --pid --mount --net \
  -f chroot . \
  env -i \
    HOME=/root \
    HOSTNAME=tiny-linux \
  /bin/sh
$ mount -t proc proc /proc
```

ပြီးရင် ကိုယ့်ရဲ့ **Host Linux machine** ကနေ Container PID ကို ရယူလိုက်ပါ။ ဒီနေရာမှာတော့ Tiny Linux Container PID က **29999** ဖြစ်ပါတယ်။

***ဒီနေရာမှာ PID က စမ်းတဲ့သူပေါ်မှာ မူတည်ပြီး အပြောင်းလဲရှိနိုင်ပါတယ်။***

```bash
[zawzaw@fedora-linux:~]$ ps -C sh
    PID TTY          TIME CMD
  25473 pts/6    00:00:00 sh
  29999 pts/9    00:00:00 sh
```

ထို့နောက် `export` command နဲ့ `TINY_CONTAINER_PID` environment variable ကို သတ်မှတ်လိုက်ပါမယ်။ ဒီ PID ကို VETH pair တည်ဆောက်တဲ့နေရာမှာ ပြန်သုံးမှာ ဖြစ်ပါတယ်။

```bash
[zawzaw@fedora-linux:~]$ export TINY_CONTAINER_PID=29999
```

**Host Linux machine** ပေါ်မှာ `ip` command-line tool နဲ့ `veth2`, `veth3` ဆိုတဲ့ VETH network pair တစ်ခုကို တည်ဆောက်လိုက်ပါမယ်။

***ဒီနေရာမှာ သေချာနားလည်ရမှာကတော့*** `veth2` ***က Host machine ဘက်မှာ ရှိမှာဖြစ်ပြီး၊*** `veth3` ***က Tiny Linux Container ဘက်မှာ ရှိမှာဖြစ်ပါတယ်။***

```bash
[zawzaw@fedora-linux:~]$ sudo ip link add veth2 type veth peer name veth3
[zawzaw@fedora-linux:~]$ sudo ip link set veth3 netns "${TINY_CONTAINER_PID}"
[zawzaw@fedora-linux:~]$ sudo ip link set dev veth2 up
```

**Tiny Linux Container** ပေါ်တွင် `veth3` Network interface ရဲ့ IP address ကို `172.19.35.2` လို့ သတ်မှတ်လိုက်ပါမယ်။

```bash
/ # ip addr add dev veth3 172.19.35.2/24
/ # ip link set lo up
/ # ip link set veth3 up
```

**Host Linux machine** ပေါ်တွင် Network interfaces များကို စစ်ကြည့်လိုက်ရင် အောက်ကအတိုင်း တွေ့ရပါလိမ့်မယ်။

```bash
[zawzaw@fedora-linux:~]$ ip addr show veth2
13: veth2@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 06:97:e6:1f:d4:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::497:e6ff:fe1f:d4b3/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
```

**Alpine Linux Container** ပေါ်တွင် Network interfaces နှင့် IP addresses များကို စစ်ကြည့်လိုက်ရင် အောက်ကအတိုင်း တွေ့ရပါလိမ့်မယ်။

```bash
/ # ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
12: veth3@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue qlen 1000
    link/ether 72:37:8b:a8:26:7e brd ff:ff:ff:ff:ff:ff
    inet 172.19.35.2/24 scope global veth3
       valid_lft forever preferred_lft forever
    inet6 fe80::7037:8bff:fea8:267e/64 scope link
       valid_lft forever preferred_lft forever
```

အခုဆိုရင် `veth2`, `veth3` ဆိုတဲ့ VETH network interfaces တွေ အလုပ်လုပ်နေပြီး IP Address ကို `172.19.35.2` လို့ သတ်မှတ်ထားတာကို တွေ့ရပါလိမ့်မယ်။

ဒါပေမယ့် အခုချိန်မှာ Container A နဲ့ B က အချင်းချင်း ဆက်သွယ်လို့ရသေးမှာ မဟုတ်ပါဘူး။ ဒါကို `ping` command နဲ့ စမ်းသပ်ကြည့်နိုင်ပါတယ်။

* Container (A)—Alpine Linux IP Address: `172.19.35.3`
    
* Container (B)—Tiny Linux IP address: `172.19.35.2`
    

ဥပမာအားဖြင့် **Tiny Linux Container** ကနေ **Alpine Linux (172.19.35.3)** ကို ping လုပ်ကြည့်ပါ။ အလုပ်လုပ်မှာမဟုတ်ပါဘူး။ ဘာကြောင့်လဲဆိုတော့ ကျွန်တော်တို့ Bridge network ကို ထပ်ပြီးတည်ဆောက်ဖို့ လိုအပ်နေလို့ပါပဲ။

```bash
/ # ping -c 5 172.19.35.3
PING 172.19.35.3 (172.19.35.3): 56 data bytes

--- 172.19.35.3 ping statistics ---
5 packets transmitted, 0 packets received, 100% packet loss
```

နောက်တစ်ပိုင်းမှာတော့ Container နှစ်ခုကြားမှာ Network packets တွေကို ပို့ဆောင်ဖို့အတွက် Bridge network တစ်ခုကို ဘယ်လိုတည်ဆောက်ရမလဲဆိုတာကို လေ့လာရမှာ ဖြစ်ပါတယ်။

## Step (3): Setting Up Bridge Network

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754184461028/d77dc27a-c9e4-4979-8e00-2b4b3741042d.png align="center")

အခုဆိုရင် ကျွန်တော်တို့မှာ **Alpine Linux** နဲ့ **Tiny Linux** ဆိုတဲ့ Container နှစ်ခုရှိနေပြီး၊ သူတို့ဟာ သီးခြားစီဖြစ်နေတဲ့ **PID**, **Mount** နဲ့ **Network** Linux namespaces တွေထဲမှာ အလုပ်လုပ်နေကြပါတယ်။ သူတို့မှာ Virtual Ethernet (VETH) pair ကိုလည်း တူညီတဲ့ Network Linux namespace ထဲမှာ ရှိပါတယ်။

ဒီအပိုင်းမှာတော့ Container နှစ်ခု ကြားက Network packets တွေကို လက်ဆင့်ကမ်းပို့ဆောင်ဖို့အတွက် **Bridge network** တစ်ခုကို ဆက်လက်တည်ဆောက်သွားပါမယ်။

**Host Linux machine** ပေါ်တွင် `br0` လို့ခေါ်တဲ့ Bridge network တစ်ခုကို တည်ဆောက်လိုက်ပြီး၊ `br0` Bridge network ကို `veth0` နဲ့ `veth2` Network interfaces တွေနဲ့ ချိတ်ဆက်လိုက်ပါမယ်။

```bash
[zawzaw@fedora-linux:~]$ sudo ip link add br0 type bridge
[zawzaw@fedora-linux:~]$ sudo ip link set veth0 master br0
[zawzaw@fedora-linux:~]$ sudo ip link set veth2 master br0
```

ထို့နောက် `br0` Bridge network interface ရဲ့ IP Address ကို `172.19.35.1` လို့ သတ်မှတ်လိုက်ပါမယ်။ ပြီးရင်တော့ Up လိုက်ပြီး IP address ကို စစ်ကြည့်လို့ရပါပြီ။

```bash
[zawzaw@fedora-linux:~]$ sudo ip addr add dev br0 172.19.35.1/24
[zawzaw@fedora-linux:~]$ sudo ip link set br0 up
```

```bash
[zawzaw@fedora-linux:~]$ ip addr show br0
14: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 06:97:e6:1f:d4:b3 brd ff:ff:ff:ff:ff:ff
    inet 172.19.35.1/24 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::497:e6ff:fe1f:d4b3/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
```

## Testing Connectivity

ပြီးနောက် `ping` command-line tool ကို အသုံးပြုပြီး Container နှစ်ခုကြားက ကွန်ရက်ချိတ်ဆက်မှု ရှိ၊ မရှိကို စစ်ဆေးနိုင်ပါတယ်။

ဥပမာအားဖြင့် **Tiny Linux Container** ကနေ Alpine Linux Container (`172.19.35.3`)ကို ping လုပ်ကြည့်ပါမယ်။

```bash
/ # ping -c 5 172.19.35.3
PING 172.19.35.3 (172.19.35.3): 56 data bytes
64 bytes from 172.19.35.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.19.35.3: seq=1 ttl=64 time=0.041 ms
64 bytes from 172.19.35.3: seq=2 ttl=64 time=0.059 ms
64 bytes from 172.19.35.3: seq=3 ttl=64 time=0.072 ms
64 bytes from 172.19.35.3: seq=4 ttl=64 time=0.050 ms

--- 172.19.35.3 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
```

ဥပမာအားဖြင့် **Alpine Linux Container** ကနေ Tiny Linux Container (`172.19.35.2`**)** ကို ping လုပ်ကြည့်ပါမယ်။

```bash
/ # ping -c 5 172.19.35.2
PING 172.19.35.2 (172.19.35.2): 56 data bytes
64 bytes from 172.19.35.2: seq=0 ttl=64 time=0.051 ms
64 bytes from 172.19.35.2: seq=1 ttl=64 time=0.105 ms
64 bytes from 172.19.35.2: seq=2 ttl=64 time=0.042 ms
64 bytes from 172.19.35.2: seq=3 ttl=64 time=0.053 ms
64 bytes from 172.19.35.2: seq=4 ttl=64 time=0.052 ms

--- 172.19.35.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
```

ယခုဆိုရင် **Container နှစ်လုံးဟာ** အချင်းချင်း ဆက်သွယ်နိုင်ပြီး ကောင်းမွန်စွာ အလုပ်လုပ်နေကြောင်း အတည်ပြုနိုင်ပါပြီ။

Reference Links

* [https://blog.mbrt.dev/posts/container-network](https://blog.mbrt.dev/posts/container-network?ref=zawzaw.blog)
    
* [https://labs.iximiuz.com/tutorials/container-networking-from-scratch](https://labs.iximiuz.com/tutorials/container-networking-from-scratch?ref=zawzaw.blog)
    
* [https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking](https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking?ref=zawzaw.blog)
    

---
