Docker

Docker

容器是一種沙盒技術(shù)确徙。沙盒就是能夠像一個(gè)集裝箱一樣把應(yīng)用(進(jìn)程)裝起來。這樣應(yīng)用(進(jìn)程)與應(yīng)用(進(jìn)程)之間因?yàn)橛辛诉吔缍ゲ桓蓴_兆旬,被裝進(jìn)集裝箱的一個(gè)用也可以方便的搬來搬去结胀。

  • 邊界->運(yùn)行態(tài)->container
  • 打包->靜態(tài)->image

image與container是1:n的關(guān)系。一個(gè)image可以生成多個(gè)container饵隙。就是 類與對(duì)象的概念。

運(yùn)行態(tài)

容器的本質(zhì)是進(jìn)程
容器的本質(zhì)是進(jìn)程
容器的本質(zhì)是進(jìn)程

進(jìn)程

進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng)沮脖,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位金矛,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中勺届,進(jìn)程是程序的基本執(zhí)行實(shí)體驶俊;在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器免姿。程序是指令饼酿、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體胚膊。

程序運(yùn)行起來之后計(jì)算機(jī)執(zhí)行環(huán)境(比如內(nèi)存中的數(shù)據(jù)故俐、寄存器里的值、堆棧種的指令紊婉、打開的文件药版、以及各種設(shè)備的狀態(tài)信息的一個(gè)集合)的總和叫做進(jìn)程

線程(英語:thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中喻犁,是進(jìn)程中的實(shí)際運(yùn)作單位槽片。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流何缓,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)筐乳。

同一進(jìn)程中的多條線程將共享該進(jìn)程中的全部系統(tǒng)資源歌殃,如虛擬地址空間,文件描述符和信號(hào)處理等等蝙云。但同一進(jìn)程中的多個(gè)線程有各自的調(diào)用棧(call stack)氓皱,自己的寄存器環(huán)境(register context),自己的線程本地存儲(chǔ)(thread-local storage)勃刨。

線程和進(jìn)程的區(qū)別

對(duì)于進(jìn)程而言波材,它的靜態(tài)表現(xiàn)就是程序,一個(gè)二進(jìn)制包或者一堆庫加可執(zhí)行文件身隐。而一旦運(yùn)行起來廷区,就變成了計(jì)算機(jī)里的數(shù)據(jù)和狀態(tài)的總和,這就是它的動(dòng)態(tài)表現(xiàn)贾铝。

容器技術(shù)的核心功能就是通過約束和修改進(jìn)程的動(dòng)態(tài)表現(xiàn)隙轻,從而為其創(chuàng)造出一個(gè)邊界

通常來說是個(gè)通過Cgroups技術(shù)來約束(限制Memory、CPU等)垢揩,而通過Namespace技術(shù)來修改進(jìn)程視圖(網(wǎng)絡(luò)玖绿、用戶等)。

Namespace

# 查看所有namespace
#  Cgroup namespace 目前還沒有被 docker 采用叁巨。
ll /proc/1/ns/
  • Mount Namespace -> 掛載
  • UTS Namespace -> HostName
  • IPC Namespace -> 進(jìn)程間通信
  • PID Namespace -> 進(jìn)程號(hào)
  • Network Namespace -> 網(wǎng)絡(luò)
  • User Namespace -> 用戶

進(jìn)入某一個(gè)進(jìn)程的namespace可以用nsenter 命令

 $ nsenter --help

Usage:
 nsenter [options] [<program> [<argument>...]]

Run a program with namespaces of other processes.

Options:
 -a, --all              enter all namespaces
 -t, --target <pid>     target process to get namespaces from
 -m, --mount[=<file>]   enter mount namespace
 -u, --uts[=<file>]     enter UTS namespace (hostname etc)
 -i, --ipc[=<file>]     enter System V IPC namespace
 -n, --net[=<file>]     enter network namespace
 -p, --pid[=<file>]     enter pid namespace
 -C, --cgroup[=<file>]  enter cgroup namespace
 -U, --user[=<file>]    enter user namespace
 -S, --setuid <uid>     set uid in entered namespace
 -G, --setgid <gid>     set gid in entered namespace
     --preserve-credentials do not touch uids or gids
 -r, --root[=<dir>]     set the root directory
 -w, --wd[=<dir>]       set the working directory
 -F, --no-fork          do not fork before exec'ing <program>
 -Z, --follow-context   set SELinux context according to --target PID

 -h, --help             display this help
 -V, --version          display version

For more details see nsenter(1).

PID Namespace是用來隔離進(jìn)程ID的 同樣一個(gè)進(jìn)程在不同的PID Namespace里可以擁有不同的PID斑匪。可以看到的一個(gè)現(xiàn)象是锋勺。在docker container中 ps -ef時(shí)蚀瘸,前臺(tái)的進(jìn)程PID是1.但是在容器外,該進(jìn)程有不同的PID

UTC Namespace 主要用來隔離nodename和domainname兩個(gè)系統(tǒng)標(biāo)識(shí)

hostname

IPC Namespace用來隔離System V IPC(信號(hào)量庶橱、消息隊(duì)列贮勃、共享內(nèi)存)和 POSIX message queues

#查詢 message queue
ipcs -q
# 創(chuàng)建 message queue
ipcmk -Q

Mount Namespace 用來隔離各個(gè)進(jìn)程看到的掛載點(diǎn)視圖在不同Namespace的進(jìn)程中,看到的文件系統(tǒng)層次是不一樣的苏章。在Mount Namespace中調(diào)用mount和unmount僅僅只會(huì)影響當(dāng)前Namespace的文件系統(tǒng)衙猪。而對(duì)全局的文件系統(tǒng)沒有影響。

df -h

User Namespace 主要是隔離用戶的用戶組ID 也就是說布近,一個(gè)進(jìn)程的User ID和Group ID在User Namespace內(nèi)外是可以不同的。

cat /etc/passwd
# 用postgresql鏡像切換用戶 到 psostgre
top
# 在宿主機(jī)
ps aux|grep top

掛載的文件在容器內(nèi)外顯示的屬組和屬主不同不是因?yàn)檫@個(gè)丝格,是因?yàn)槲募鎯?chǔ)信息中保存的是userID撑瞧。在容器內(nèi)外的userID對(duì)應(yīng)的用戶名是不一樣的。

Network Namespace 是用來隔離網(wǎng)絡(luò)設(shè)備显蝌、IP地址端口等網(wǎng)絡(luò)棧的

ip addr

Cgroups

Linux Cgroups提供了對(duì)一組進(jìn)程以及將來子進(jìn)程的資源限制预伺、控制和統(tǒng)計(jì)的能力订咸。這些資源包括CPU、內(nèi)存酬诀、存儲(chǔ)脏嚷、網(wǎng)絡(luò)等。通過Cgroups瞒御,可以方便地限制某個(gè)進(jìn)程的資源占用父叙。

  • cgroup 對(duì)進(jìn)程分組管理的一種機(jī)制,一個(gè)cgroup包含一組進(jìn)程肴裙。并且可以在這個(gè)cgroup上增加subsystem的各種參數(shù)配置趾唱。
  • subsystem是一組資源控制的模塊。 lssubsys -a
  • hierarchy的功能是把一組cgroup串成一個(gè)樹狀結(jié)構(gòu)蜻懦。

手動(dòng)配置Cgroups:

# 首先創(chuàng)建并掛載一個(gè)hierarchy(cgroup樹).
$ mkdir cgroup-test
$ mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test
$ ls ./cgroup-test/
cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  notify_on_release  release_agent  tasks
  • cgroup.clone_children cpuset的subsystem會(huì)讀取這個(gè)配置文件甜癞,如果這個(gè)值是1(默認(rèn)是0)子cgroup才會(huì)繼承父cgroup的cpuset的配置
  • cgroup.procs是樹中當(dāng)前節(jié)點(diǎn)cgroup中的進(jìn)程組ID,現(xiàn)在的位置是在根節(jié)點(diǎn)宛乃,這個(gè)文件中會(huì)有現(xiàn)在系統(tǒng)中所有進(jìn)程組的ID
  • notify_on_release和release_agent會(huì)一起使用悠咱,notify_on_release表示當(dāng)這個(gè)cgroup最后一個(gè)進(jìn)程退出的時(shí)候是否執(zhí)行了release_agent; release_agent 則是一個(gè)路徑,通常用作進(jìn)程退出后自動(dòng)清理不再使用的cgroup征炼。
  • tasks 標(biāo)識(shí)該cgroup下面的進(jìn)程ID析既,如果把一個(gè)進(jìn)程ID寫到tasks文件中,便會(huì)將相應(yīng)的進(jìn)程加入到這個(gè)cgroup中柒室。
# 在剛剛創(chuàng)建好的hierarchy上cgroup根節(jié)點(diǎn)擴(kuò)展出兩個(gè)子cgroup渡贾。
$ cd ./cgroup-test/
$ mkdir cgroup-1
$ mkdir cgroup-2
$  tree
.
├── cgroup-1
│   ├── cgroup.clone_children
│   ├── cgroup.procs
│   ├── notify_on_release
│   └── tasks
├── cgroup-2
│   ├── cgroup.clone_children
│   ├── cgroup.procs
│   ├── notify_on_release
│   └── tasks
├── cgroup.clone_children
├── cgroup.procs
├── cgroup.sane_behavior
├── notify_on_release
├── release_agent
└── tasks

可以看到在一個(gè)cgroup的目錄下創(chuàng)建文件夾時(shí),內(nèi)核會(huì)把文件夾標(biāo)記為這個(gè)cgroup的子group雄右,他們會(huì)繼承父cgroup的屬性空骚。

# 在cgroup中添加進(jìn)程
$  cat /proc/$$/cgroup
13:name=cgroup-test:/
12:memory:/user.slice
11:perf_event:/
10:cpuset:/
9:devices:/user.slice
8:blkio:/user.slice
7:pids:/user.slice/user-1000.slice/session-8.scope
6:rdma:/
5:hugetlb:/
4:freezer:/
3:cpu,cpuacct:/user.slice
2:net_cls,net_prio:/
1:name=systemd:/user.slice/user-1000.slice/session-8.scope
0::/user.slice/user-1000.slice/session-8.scope
$ echo $$ >> cgroup-1/tasks
$ cat /proc/$$/cgroup
13:name=cgroup-test:/cgroup-1
12:memory:/user.slice
11:perf_event:/
10:cpuset:/
9:devices:/user.slice
8:blkio:/user.slice
7:pids:/user.slice/user-1000.slice/session-8.scope
6:rdma:/
5:hugetlb:/
4:freezer:/
3:cpu,cpuacct:/user.slice
2:net_cls,net_prio:/
1:name=systemd:/user.slice/user-1000.slice/session-8.scope
0::/user.slice/user-1000.slice/session-8.scope

可以看到當(dāng)前進(jìn)程已經(jīng)被添加到cgroup-test:/cgroup-1中了。

# 通過subsystem限制cgroup中進(jìn)程的資源擂仍。
# 在上面創(chuàng)建hierarchy的時(shí)候囤屹,這個(gè)hierarchy并沒有關(guān)聯(lián)到任何的subsystem,所以沒有辦法通過那個(gè)hierarchy中的cgroup節(jié)點(diǎn)限制進(jìn)程的資源占用逢渔,其實(shí)系統(tǒng)已經(jīng)為每一個(gè)subsystem創(chuàng)建了一個(gè)默認(rèn)的hierarchy肋坚,比如memory的hierarchy。
$ mount|grep memory
$ cd /sys/fs/cgroup/memory
# 在不做限制的情況下肃廓,啟動(dòng)一個(gè)占用內(nèi)存的stress進(jìn)程
$ stress --vm-bytes 200m --vm-keep -m 1
$ mkdir test-limit-memory 
$ cd test-limit-memory
# 設(shè)置最大的內(nèi)存占用為100M
$ echo 100m > memory.limit_in_bytes
# 把當(dāng)前的進(jìn)程移動(dòng)到這個(gè)cgroup中
$ echo $$ > tasks
$ stress --vm-bytes 200m --vm-keep -m 1

docker 使用cgroup智厌,(以內(nèi)存為例子)

$ docker run -itd -m 100m docker-proxy.repo.inspur.com/busybox
$ cd /sys/fs/cgroup/memory/docker/<container ID>/
$ cat memory.limit_in_bytes
104857600

靜態(tài)(鏡像)

Mount Namespace

Mount Namespace 修改的,是容器進(jìn)程對(duì)文件系統(tǒng)“掛載點(diǎn)”的認(rèn)知盲赊。在 Linux 操作系統(tǒng)里铣鹏,有一個(gè)名為 chroot 的命令可以幫助你在 shell 中方便地完成這個(gè)工作。顧名思義哀蘑,它的作用就是幫你“change root file system”诚卸,即改變進(jìn)程的根目錄到你指定的位置葵第。Mount Namespace 正是基于對(duì) chroot 的不斷改良才被發(fā)明出來的,它也是 Linux 操作系統(tǒng)里的第一個(gè) Namespace合溺。
為了能夠讓容器的這個(gè)根目錄看起來更“真實(shí)”卒密,我們一般會(huì)在這個(gè)容器的根目錄下掛載一個(gè)完整操作系統(tǒng)的文件系統(tǒng),比如 Ubuntu16.04 的 ISO棠赛。這樣哮奇,在容器啟動(dòng)之后,我們?cè)谌萜骼锿ㄟ^執(zhí)行 "ls /" 查看根目錄下的內(nèi)容恭朗,就是 Ubuntu 16.04 的所有目錄和文件屏镊。
掛載在容器根目錄上,用來為容器進(jìn)程提供隔離后執(zhí)行環(huán)境的文件系統(tǒng)就是 容器鏡像痰腮。

rootfs

rootfs 只是一個(gè)操作系統(tǒng)所包含的文件而芥、配置和目錄,并不包括操作系統(tǒng)內(nèi)核膀值。在 Linux 操作系統(tǒng)中棍丐,這兩部分是分開存放的,操作系統(tǒng)只有在開機(jī)啟動(dòng)時(shí)才會(huì)加載指定版本的內(nèi)核鏡像沧踏。
同一臺(tái)機(jī)器上的所有容器歌逢,都共享宿主機(jī)操作系統(tǒng)的內(nèi)核。這就意味著翘狱,如果你的應(yīng)用程序需要配置內(nèi)核參數(shù)秘案、加載額外的內(nèi)核模塊,以及跟內(nèi)核進(jìn)行直接的交互潦匈,你就需要注意了:這些操作和依賴的對(duì)象阱高,都是宿主機(jī)操作系統(tǒng)的內(nèi)核,它對(duì)于該機(jī)器上的所有容器來說是一個(gè)“全局變量”茬缩,牽一發(fā)而動(dòng)全身
正是由于 rootfs 的存在赤惊,容器才有了一個(gè)被反復(fù)宣傳至今的重要特性:一致性。
由于 rootfs 里打包的不只是應(yīng)用凰锡,而是整個(gè)操作系統(tǒng)的文件和目錄未舟,也就意味著,應(yīng)用以及它運(yùn)行所需要的所有依賴掂为,都被封裝在了一起裕膀。
但是,如果修改了某個(gè)文件需要重新制作一次rootfs勇哗。新舊rootfs之間就沒有任何關(guān)系了昼扛。不過既然所有的修改都基于一個(gè)舊的rootfs,我們能不能用增量的方式去做這些修改智绸?正是因?yàn)檫@個(gè)野揪,Docker鏡像在設(shè)計(jì)的時(shí)候引用了層(layer)的概念。用到了一種叫做聯(lián)合文件系統(tǒng)(Union FIle system 也叫UnionFS)的技術(shù)瞧栗。

Union File System

Union File System最主要的功能是將多個(gè)不同位置的目錄聯(lián)合掛載(union mount)到同一個(gè)目錄下斯稳。比如,我現(xiàn)在有兩個(gè)目錄 A 和 B迹恐,它們分別有兩個(gè)文件:


$ tree
.
├── A
│  ├── a
│  └── x
└── B
  ├── b
  └── x

然后挣惰,我使用聯(lián)合掛載的方式,將這兩個(gè)目錄掛載到一個(gè)公共的目錄 C 上:

$ mkdir C
$ mount -t aufs -o dirs=./A:./B none ./C

再查看目錄 C 的內(nèi)容殴边,就能看到目錄 A 和 B 下的文件被合并到了一起:


$ tree ./C
./C
├── a
├── b
└── x

對(duì)于一個(gè)鏡像憎茂。它最關(guān)鍵的目錄結(jié)構(gòu)在/var/lib/docker/

一個(gè)容器的rootfs可以分為三部分:

第一部分,只讀層:
這一部分完全繼承自鏡像

第二部分锤岸,可讀寫層竖幔。
它是容器的最上面一層,在沒有寫入文件之前是偷,這個(gè)目錄是空的拳氢。而一旦在容器里做了寫操作,你修改產(chǎn)生的內(nèi)容就會(huì)以增量的方式出現(xiàn)在這個(gè)層中蛋铆。如果是刪除馋评,可讀寫層里會(huì)創(chuàng)建一個(gè) whiteout 文件,把只讀層里的文件“遮擋”起來刺啦。

第三部分留特,Init 層。
它是一個(gè)以“-init”結(jié)尾的層玛瘸,夾在只讀層和讀寫層之間蜕青。Init 層是 Docker 項(xiàng)目單獨(dú)生成的一個(gè)內(nèi)部層,專門用來存放 /etc/hosts捧韵、/etc/resolv.conf 等信息市咆。

最終。這三部分會(huì)聯(lián)合掛載到一個(gè)目錄下再来,表現(xiàn)為一個(gè)完成的鏡像供容器使用

使用dockerfile build鏡像時(shí)蒙兰,為了控制鏡像的大小,刪除文件的命令應(yīng)該和增加這個(gè)文件的命令在同一條語句芒篷。
一個(gè)反例:

如何找到鏡像對(duì)應(yīng)的哪幾層搜变?

$ docker inspect <image id>
...
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:195be5f8be1df6709dafbba7ce48f2eee785ab7775b88e0c115d8205407265c5",
                "sha256:0faad07fc16f96bdb8e433cc55068528472eb3756cc36b7e0134e3581f2babf0",
                "sha256:876b5eb1778f3084b020cf291c2d1e241c964092b0de8044305aba1f620202e5"
            ]
        },
...
$ cat /var/lib/docker/image/overlay2/layerdb/sha256/<layers-id>/cache-id

容器網(wǎng)絡(luò)

在上面的介紹中,通過Namespace和Cgroup技術(shù)實(shí)現(xiàn)了容器進(jìn)程的隔離针炉,并且通過聯(lián)合文件掛載系統(tǒng)讓容器擁有自己的文件系統(tǒng)挠他,容器中的進(jìn)程所感受到的環(huán)境就像在一臺(tái)虛擬機(jī)上一樣,但是篡帕,這臺(tái)虛擬機(jī)還沒有插上網(wǎng)線殖侵。下面就給這個(gè)虛擬機(jī)插上網(wǎng)線贸呢。

前面介紹了Network Namespace,可以給容器分配獨(dú)立的網(wǎng)絡(luò)空間拢军,那么怎么給這個(gè)網(wǎng)絡(luò)空間增加網(wǎng)絡(luò)配置呢楞陷。

Linux 虛擬網(wǎng)絡(luò)設(shè)備

Linux Veth

Veth是成對(duì)出現(xiàn)的網(wǎng)絡(luò)虛擬設(shè)備,發(fā)送到Veth一端虛擬設(shè)備的請(qǐng)求會(huì)送另一端的虛擬設(shè)備中發(fā)出茉唉。在容器化的虛擬場(chǎng)景中固蛾,經(jīng)常會(huì)使用Veth連接不同網(wǎng)絡(luò)的namespace。

# 創(chuàng)建兩個(gè)網(wǎng)絡(luò)namespace
$ ip netns add ns1
$ ip netns add ns2
#查看創(chuàng)建的網(wǎng)絡(luò)namespace
$ ip netns ls
$ ls /var/run/netns/ns
# 創(chuàng)建一對(duì)Veth
$  ip link add veth0 type veth peer name veth1
# 分別將兩個(gè)Veth移動(dòng)到兩個(gè)namespace中
$ ip link set veth0 netns ns1
$ ip link set veth1 netns ns2
# 去ns1中查看網(wǎng)絡(luò)設(shè)備
$ ip netns exec ns1 ip link
# 配置每個(gè)veth的網(wǎng)絡(luò)地址和namepace路由
$ ip netns exec ns1 ifconfig veth0 172.18.0.2/24 up
$ ip netns exec ns2 ifconfig veth1 172.18.0.3/24 up
# dev是設(shè)備 區(qū)別于gw 是網(wǎng)關(guān)
$ ip netns exec ns1 route add default dev veth0
$ ip netns exec ns2 route add default dev veth1
$ ip netns exec ns2 ip addr

Linux Bridge

Bridge虛擬設(shè)備是用來連接橋接的網(wǎng)絡(luò)設(shè)備度陆,它相當(dāng)于交換機(jī)艾凯,可以連接不同的網(wǎng)絡(luò)設(shè)備,當(dāng)請(qǐng)求到達(dá)Bridge設(shè)備時(shí)懂傀,可以通過報(bào)文中的Mac地址進(jìn)行廣播或轉(zhuǎn)發(fā)趾诗。

# 創(chuàng)建veth設(shè)備并將一段移入namespace
$ ip netns add ns3
$ ip link add veth2 type veth peer name veth3
$ ip link set veth3 netns ns3
# 創(chuàng)建網(wǎng)橋
$ brctl addbr br0
# 掛載網(wǎng)絡(luò)設(shè)備
$ brctl addif br0 veth2

linux 路由表

路由表是Linux內(nèi)核的一個(gè)模塊,通過定義路由表來決定某個(gè)網(wǎng)絡(luò)Namespace中包的流向鸿竖,從而定義請(qǐng)求會(huì)到哪個(gè)網(wǎng)絡(luò)設(shè)備上沧竟。

# 啟動(dòng)虛擬網(wǎng)絡(luò)設(shè)備,并設(shè)置它在Net Namespace中的IP地址缚忧。
$ ip link set veth2 up
$ ip link set br0 up
$ ip netns exec ns3 ifconfig veth3 172.18.0.4/24 up
# 分別設(shè)置ns1網(wǎng)絡(luò)空間的路由和宿主機(jī)的路由
# default代表0.0.0.0/0 即在Net Namespace 中所有流量都經(jīng)過veth1的網(wǎng)絡(luò)設(shè)備流出
$ ip netns exec ns3 route add default dev veth3
# 在宿主機(jī)上將172.18.0.0/24的網(wǎng)段請(qǐng)求路由到br0網(wǎng)橋
$ route add -net 172.18.0.0/24 dev br0
# 查看宿主機(jī)的IP地址
$ ifconfig ens33
# 從Namespace中訪問宿主機(jī)
$ ip netns exec ns3 ping -c 1 192.168.141.146
$ ping -c 172.18.0.4

Linux iptables

iptables 是對(duì)Linux內(nèi)核的netfilter模塊進(jìn)行操作和展示的工具悟泵,用來管理包的流動(dòng)和傳送。iptables定義了一套鏈?zhǔn)教幚淼慕Y(jié)構(gòu)闪水,在網(wǎng)絡(luò)包傳輸?shù)母鱾€(gè)階段可以使用不同的策略對(duì)包進(jìn)行加工糕非、傳送或丟棄。在容器虛擬化的技術(shù)中球榆,經(jīng)常會(huì)用到兩種策略MASQUERADE和DNAT

MASQUERADE

iptables中的MASQUERADE策略可以將請(qǐng)求包中的源地址轉(zhuǎn)換成一個(gè)網(wǎng)絡(luò)設(shè)備的地址朽肥。上邊介紹的那個(gè)Namespace中網(wǎng)絡(luò)設(shè)備的地址是172.18.0.4.這個(gè)地址雖然在宿主機(jī)上可以路由到br0網(wǎng)橋,倒是到達(dá)宿主機(jī)外部之后持钉,是不知道如何路由到這個(gè)IP地址的衡招,如果請(qǐng)求外部的地址的話需要先通過MASQUERADE策略將這個(gè)IP轉(zhuǎn)化成宿主機(jī)出口網(wǎng)卡的IP


# 打開ip轉(zhuǎn)發(fā)
$ sysctl -w net.ipv4.conf.all.forwarding=1
# 對(duì)Namespace中發(fā)出的包添加網(wǎng)絡(luò)地址轉(zhuǎn)換
# 在名為POSTROUTING的nat表?xiàng)單蔡砑硬呗詾镸ASQUERADE的匹配,將來自172.18.0.0/24的包通過 ens33 網(wǎng)卡發(fā)出
$ iptables -t nat -A POSTROUTING -s 172.18.0.0/24 -o ens33 -j MASQUERADE

在Namespace中請(qǐng)求宿主機(jī)外部地址時(shí)每强,將Namespace中的源地址轉(zhuǎn)換成宿主機(jī)的地址作為源地址始腾,既可以在Namespace中訪問宿主機(jī)外的網(wǎng)絡(luò)了。

DNAT

DNAT策略也是做網(wǎng)絡(luò)地址的轉(zhuǎn)換空执,不過他是要更換不妙地址浪箭,經(jīng)常用于將內(nèi)部網(wǎng)絡(luò)地址的端口映射到外部去。

 # 將宿主機(jī)上的80端口的請(qǐng)求轉(zhuǎn)發(fā)到Namespace的IP上
 $ iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.4:80
 iptables -nvL -t nat

Docker的架構(gòu)圖:

C/S之間可以通過三種方式連接:

Docker 常用命令

# 啟動(dòng)容器
# -i 顯示 console
# -t 交互
# --rm 容器退出后刪除容器
# container name 可以自定義
# image 可以是image name 也可以是image id
# cmd 是容器運(yùn)行的命令辨绊,跟在entrypoint之后奶栖。(dockerfile 時(shí)詳細(xì)解釋)
$ docker run -it --rm -n <container name> <image> <CMD>
# -d 后臺(tái)運(yùn)行
# --restart=always,容器異常退出時(shí),重啟容器宣鄙,比如dockerd重啟之后袍镀。
# -p 端口映射  宿主機(jī)端口:容器內(nèi)端口
# -v 掛載volume 宿主機(jī)目錄:容器內(nèi)目錄。必須時(shí)絕對(duì)路徑冻晤,當(dāng)前目錄可以用$(pwd)
# --cpu  設(shè)置cpu最大限制
# -m 設(shè)置內(nèi)存最大限制流椒。
$ docker run -d --restart=always -p 8080:8080 -v $(pwd):/root/ --cpu=1 -m 10m -n <container name> <image>
# 停止容器
# container可以是 container name,也可以是container ID
$ docker stop <container>
# 查看容器日志
# -f 可以實(shí)時(shí)顯示
# --tail=10 只顯示最后10行
$ docker logs --tail=10 <container> -f
# 進(jìn)入容器
$ docker exec -it <container> /bin/sh
# 刪除容器
# -f 參數(shù)可以強(qiáng)制停止正在運(yùn)行中的容器并刪除
$ docker rm -f <container>
#鏡像重命名
$ docker tag <source image name/id> <target image name>
# 構(gòu)建鏡像
# -t 可以指定都建出來的鏡像的name及tag
$ docker build <Dockerfile path> -t <image name>
# 查看容器/鏡像的詳細(xì)信息
$ docker inspect <container or image>
# 查看docker server的詳細(xì)信息
$ docker info
# 查看docker 占用的磁盤情況
# -v 列出詳細(xì)信息
$ docker system df -v
# 刪除沒用的數(shù)據(jù),包括鏡像明也、容器。
$ docker system prune
# 從容器內(nèi)外復(fù)制文件
$ docker cp CONTAINER:SRC_PATH DEST_PATH
$ docker cp SRC_PATH CONTAINER:DEST_PATH
# 列出所有 image
$ docker images
# 列出所有容器
# -a 顯示所有容器
$ docker ps -a
# 列出容器資源占用情況
# container 可選
$ docker stats <container>

Dockerfile

FROM:指定基礎(chǔ)鏡像

第一條指令惯裕。scratch是虛擬的鏡像温数,表示一個(gè)空白的鏡像。

LABEL: 添加元數(shù)據(jù)

LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

一般需要在LABEL中至少添加 Name和Version兩條元數(shù)據(jù)

RUN:執(zhí)行命令


shell 格式: RUN <命令> 蜻势,RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exec 格式: RUN ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2"] 撑刺。run可以寫多個(gè),每一個(gè)指令都會(huì)建立一層握玛,所以正確寫法應(yīng)該是↓
RUN buildDeps='gcc libc6-dev make' \
         && apt-get update \
         && apt-get install -y $buildDeps \
         && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
         && mkdir -p /usr/src/redis \
         && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
         && make -C /usr/src/redis \
         && make -C /usr/src/redis install \
         && rm -rf /var/lib/apt/lists/* \
         && rm redis.tar.gz \
         && rm -r /usr/src/redis \
         && apt-get purge -y --auto-remove $buildDeps

COPY:復(fù)制文本

COPY <源路徑>... <目標(biāo)路徑>
COPY ["<源路徑1>",... "<目標(biāo)路徑>"]
<源路徑> 可以是多個(gè)够傍、以及使用通配符,通配符規(guī)則滿足Go的filepath.Match 規(guī)則挠铲,如:COPY hom* /mydir/    COPY hom?.txt /mydir/
<目標(biāo)路徑>使用 COPY 指令冕屯,源文件的各種元數(shù)據(jù)都會(huì)保留。比如讀拂苹、寫安聘、執(zhí)行權(quán)限、文件變更時(shí)間等瓢棒。

ADD:高級(jí)復(fù)制文件

ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
<源路徑> 可以是一個(gè) URL 浴韭,如果是tgz的文件可以自動(dòng)解壓。下載后的文件權(quán)限自動(dòng)設(shè)置為 600 脯宿。

CMD:容器啟動(dòng)命令

shell 格式: CMD <命令>
exec 格式: CMD ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2"...]

CMD ["nginx", "-g", "daemon off;"]

ENTRYPOINT:入口點(diǎn)

同CMD念颈,指定容器啟動(dòng)程序及參數(shù)。
通過--entrypoint 參數(shù)在運(yùn)行時(shí)替換连霉。

用例一:使用CMD要在運(yùn)行時(shí)重新寫命令才能追加運(yùn)行參數(shù)榴芳,ENTRYPOINT則可以運(yùn)行時(shí)接受新參數(shù)。
示例:
FROM ubuntu:16.04RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

追加-i參數(shù)
$ docker run myip -i
......
當(dāng)前 IP:61.148.226.66 來自:北京市 聯(lián)通

ENV:設(shè)置環(huán)境變量

在其他指令中可以直接引用ENV設(shè)置的環(huán)境變量窘面。


ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"

ARG:構(gòu)建參數(shù)

與ENV不同的是翠语,容器運(yùn)行時(shí)不會(huì)存在這些環(huán)境變量〔票撸可以用 docker build --build-arg <參數(shù)名>=<值> 來覆蓋肌括。

VOLUME:定義匿名卷

容器運(yùn)行時(shí)應(yīng)該盡量保持容器存儲(chǔ)層不發(fā)生寫操作,對(duì)于數(shù)據(jù)庫類需要保存動(dòng)態(tài)數(shù)據(jù)的應(yīng)用,其數(shù)據(jù)庫文件應(yīng)該保存于卷(volume)中谍夭。

VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>

volume和run -v參數(shù)的區(qū)別黑滴。
 
VOLUME指令只是起到了聲明了容器中的目錄作為匿名卷,但是并沒有將匿名卷綁定到宿主機(jī)指定目錄的功能紧索。但是當(dāng)我們生成鏡像的Dockerfile中以Volume聲明了匿名卷袁辈,并且我們以這個(gè)鏡像run了一個(gè)容器的時(shí)候,docker會(huì)在安裝目錄下的指定目錄下面生成一個(gè)目錄來綁定容器的匿名卷珠漂。

EXPOSE:暴露端口

EXPOSE <端口1> [<端口2>...] 
EXPOSE :EXPOSE 僅僅是聲明容器打算使用什么端口而已晚缩,并不會(huì)自動(dòng)在宿主進(jìn)行端口映射。

WORKDIR:指定工作目錄

WORKDIR <工作目錄路徑>
RUN cd /app
RUN echo "hello" > world.txt
兩次run不在一個(gè)環(huán)境內(nèi)媳危,可以使用WORKDIR荞彼。

USER:指定當(dāng)前用戶

這個(gè)用戶必須是事先建立好的,否則無法切換待笑。
USER <用戶名>

最佳實(shí)踐:
http://dockone.io/article/9658

Dockerfile 掃描

  • hadolint是目前為止成熟最高的一個(gè)工具鸣皂,一共支持60多條的自帶規(guī)則。而且hadolint還支持對(duì)Run命令中帶的shell命令進(jìn)行審計(jì)暮蹂。這一點(diǎn)功能是目前其他競(jìng)品不具備的寞缝。不過hadolint使用Haskell語言編寫,且目前不支持規(guī)則文件導(dǎo)入仰泻。
    如果有新的規(guī)則需要編程實(shí)現(xiàn)荆陆。

  • dockerlint 使用coffee_script語言編寫,自帶規(guī)則數(shù)量小于hadolint集侯;也不支持規(guī)則文件導(dǎo)入功能慎宾。

  • dockerfile_lint 紅帽發(fā)布,使用node.js編寫的一個(gè)dockerfile掃描工具浅悉。支持的規(guī)則數(shù)量小于hadolint趟据,但是支持通過yaml文件方式導(dǎo)入用戶自定義規(guī)則。

dockerfile_lint 工具的使用

dockerfile_lint代碼在github.com/projectatomic/dockerfile_lint下术健,有兩種方法執(zhí)行dockerfile_lint:

  • 從dockerhub官方下載dockerfile_lint latest的docker鏡像汹碱。
  • 在nodejs執(zhí)行環(huán)境上,運(yùn)行dockerfile_lint掃描工具荞估。
docker run -it --rm -v $(pwd):/target/ docker-proxy.repo.inspur.com/projectatomic/dockerfile-lint:latest /bin/bash

[root@4298ab08644f /]#dockerfile_lint -f /target/Dockerfile -v

dockerfile_lint 支持下面參數(shù)
-h 打印help信息
-j 結(jié)果以jason格式輸出
-r rulefile 指定一個(gè)規(guī)則文件咳促。
-v 打印debug新秀
-f 指定dockerfile的路徑
-e 打印出目前的規(guī)則

dockerfile_lint 規(guī)則

目前為止dockerfile_lint默認(rèn)情況下帶了default_rules.yaml和base_rules.yaml。如果不指定任何rulefile勘伺,那么系統(tǒng)默認(rèn)采用這兩個(gè)規(guī)則文件跪腹,在docker鏡像的/opt/dockerfile_lint/config/目錄下。此外在/opt/dockerfile_lint/sample_rules下面還帶了很多事例rule飞醉〕迦祝可以直接在容器內(nèi)部修改default_rules.yaml和base_rules.yaml文件,然后再運(yùn)行dockerfile_lint。

可以看到dockerfile_lint的規(guī)則基本上都是基于正則匹配的轴术,所以用戶可以很方便的擴(kuò)展新的規(guī)則难衰。

需要注意的是dockerfile_lint語法中required_instruction域中的count字段在19年12月截止的版本中是無效的。

dive

https://github.com/wagoodman/dive

dive <image>

Docker daemon的常用配置

{
    "dns": ["10.100.1.12"], # 設(shè)定容器DNS的地址逗栽,在容器的 /etc/resolv.conf文件中可查看盖袭。
"insecure-registries": [],#配置docker的私庫地址,配置之后可以解決https免證書問題彼宠。
"registry-mirrors":["xxxx"], #鏡像加速的地址鳄虱,增加后在 docker info中可查看。
 "graph": "/mnt/ssd/0/docker", #docker 文件存放地址
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凭峡,一起剝皮案震驚了整個(gè)濱河市醇蝴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌想罕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霉涨,死亡現(xiàn)場(chǎng)離奇詭異按价,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)笙瑟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門楼镐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人往枷,你說我怎么就攤上這事框产。” “怎么了错洁?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵秉宿,是天一觀的道長。 經(jīng)常有香客問我屯碴,道長描睦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任导而,我火速辦了婚禮忱叭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘今艺。我一直安慰自己韵丑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布虚缎。 她就那樣靜靜地躺著撵彻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上千康,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天享幽,我揣著相機(jī)與錄音,去河邊找鬼拾弃。 笑死值桩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的豪椿。 我是一名探鬼主播奔坟,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼搭盾!你這毒婦竟也來了咳秉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤鸯隅,失蹤者是張志新(化名)和其女友劉穎澜建,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝌以,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炕舵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跟畅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咽筋。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖徊件,靈堂內(nèi)的尸體忽然破棺而出奸攻,到底是詐尸還是另有隱情,我是刑警寧澤虱痕,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布睹耐,位于F島的核電站,受9級(jí)特大地震影響部翘,放射性物質(zhì)發(fā)生泄漏疏橄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一略就、第九天 我趴在偏房一處隱蔽的房頂上張望捎迫。 院中可真熱鬧,春花似錦表牢、人聲如沸窄绒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽彰导。三九已至蛔翅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間位谋,已是汗流浹背山析。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掏父,地道東北人凉倚。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓躏率,卻偏偏與公主長得像瘫寝,于是被迫代替她去往敵國和親碴裙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Docker容器技術(shù)已經(jīng)發(fā)展了好些年陶缺,在很多項(xiàng)目都有應(yīng)用钾挟,線上運(yùn)行也很穩(wěn)定。整理了部分Docker的學(xué)習(xí)筆記以及新...
    __七把刀__閱讀 11,450評(píng)論 0 58
  • 一饱岸、Docker 簡(jiǎn)介 Docker 兩個(gè)主要部件:Docker: 開源的容器虛擬化平臺(tái)Docker Hub: 用...
    R_X閱讀 4,385評(píng)論 0 27
  • docker常用命令原理圖概覽: 按照docker官網(wǎng)上的說法掺出,docker的文件系統(tǒng)分為兩層:bootfs和ro...
    燕京博士閱讀 2,959評(píng)論 2 32
  • 什么是Docker 在容器技術(shù)中,我們講到了Docker就是一個(gè)應(yīng)用容器引擎苫费,可以將應(yīng)用及依賴打包汤锨,然后發(fā)布到Li...
    dy2903閱讀 954評(píng)論 0 1
  • 今天,一天都在回想那個(gè)畫面黍衙。我是多粗心,多毛燥荠诬,關(guān)車門夾到了父親的手琅翻。那得有多痛,那一瞬間柑贞,父親哀嚎方椎,我還不明所以...
    zj海水正藍(lán)閱讀 124評(píng)論 0 0