知识点回顾
- Docker命令
- docker run/start/restart/kill/exec
- docker inspect/top/logs
- docker save/export/import
- docker build/tag/push
Dockerfile写法
- FROM
- MAINTAINER
- RUN
- USER
- VOLUME
- WORKDIR
- CMD 每个容器只能执行一条CMD命令,多个CMD命令时,只最后一条被执行
- ENV
- ADD/COPY
- EXPOST
- ENTRYPOINT 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
拆解docker/容器
容器镜像通过UnionFS(联合文件系统)将不同的目录挂载到同一个虚拟文件系统下 ,创建一个新的层叠加上去作为一个可读可写层,然后通过change root(chroot dir/)对根目录进行一个限制,在通过namespace命名空间对内核全局资源进行封装,达到资源隔离的目的。Cgroup技术对一组进程的cpu,内存,磁盘io,网络带宽等资源进行限制。
解压镜像
root@ubuntu:~# docker pull busybox
root@ubuntu:~# docker image save busybox -o busybox.tar
root@ubuntu:~# mkdir -p /var/lib/fmeng/busybox
root@ubuntu:~# tar -xf busybox.tar -C /var/lib/fmeng/busybox/
tar xf layer.tar
chroot /var/lib/fmeng/busybox/75fae911f860e3d9d6898b7af34a3b96374dcb33ee9d8b7a6230b74d4a1f30d4/ /bin/sh
OverlayFS文件系统
- https://zh.wikipedia.org/zh-cn/OverlayFS
- OverlayFS是一个面向Linux的文件系统服务,其实现一个面向其他文件系统的联合挂载。
Overlayfs的基本理念
- 只读底层和可写顶层叠加在一起,看起来像一个单一的文件系统。
- 在这种情况下,目录被叠加,文件从顶部看。
- 如果有冲突,对于文件,顶层将优先考虑,而对于目录,顶层将被合并。
- 如果你编辑一个只在底部的文件(只读),在顶部会有一个副本。
实验
为了进行overlayfs,你需要以下四个目录
- work:工作目录,与上层文件系统相同,必须为空。
- lower: 下一级的目录。 它是只读的,可以使用各种文件系统。
- upper: 上层目录。它必须是可写的,并返回适当的d_type的readdir,所以不能使用NFS。
- merged:overlayfs被挂载到的目录。
准备以下三个文件来测试
- both:上面和下面都有的文件
- only_upper: 只存在于顶部的文件
- only_lower: 只存在于底部的文件
azureuser@build:~$ mkdir -p fmeng/{work,upper,lower,merged}
azureuser@build:~$ echo "upper" >> fmeng/upper/both
azureuser@build:~$ echo "upper" >> fmeng/upper/only_upper
azureuser@build:~$ echo "lower" >> fmeng/lower/both
azureuser@build:~$ echo "lower" >> fmeng/lower/only_lower
azureuser@build:~$ cd fmeng/
azureuser@build:~/fmeng$ sudo mount -t overlay overlay -o lowerdir=lower,upperdir=upper,workdir=work merged
azureuser@build:~/fmeng$
azureuser@build:~/fmeng$ df -h | grep overlay
overlay 29G 2.1G 27G 7% /home/azureuser/fmeng/merged
azureuser@build:~/fmeng$
ls看到这三个文件。
azureuser@build:~/fmeng/merged$ ls
both only_lower only_upper
azureuser@build:~/fmeng/merged$
实际上我们创建了四个文件,其中两个both,我们看看both是什么现在
azureuser@build:~/fmeng/merged$ cat bothupper
azureuser@build:~/fmeng/merged$
如果删除both,出现字符特殊文件
azureuser@build:~/fmeng/merged$ rm both
azureuser@build:~/fmeng/upper$ sudo ls -l
total 4
c--------- 2 root root 0, 0 Jan 15 14:55 both
-rw-rw-r-- 1 azureuser azureuser 0 Jan 15 14:52 mytest
-rw-rw-r-- 1 azureuser azureuser 6 Jan 15 14:47 only_upper
azureuser@build:~/fmeng/upper$
root@build:~# brctl addbr fmengbr0
root@build:~# ip addr add 172.18.0.1/24 dev fmengbr0
root@build:~# ip link set dev fmengbr0 up
root@build:~#
ip link add dev veth0_1 type veth peer name veth1_1
ip link set dev veth0_1 up
ip link set veth0_1 master fmengbr0
ip netns add netns_test
ip link set veth1_1 netns netns_test
ip netns exec netns_test ip link set dev lo up
ip netns exec netns_test ip link set veth1_1 address 02:42:ac:11:00:01
ip netns exec netns_test ip addr add 172.18.0.2/24 dev veth1_1
ip netns exec netns_test ip link set dev veth1_1 up
ip netns exec netns_test ip route add default via 172.18.0.2
root@build:~# ifconfig fmengbr0
fmengbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::b88d:ebff:fe19:d95a prefixlen 64 scopeid 0x20<link>
ether ba:8d:eb:19:d9:5a txqueuelen 1000 (Ethernet)
...
root@build:~# cgcreate -g cpu,cpuacct,memory:/test
root@build:~# cgexec -g "cpu,cpuacct,memory:/test" ip netns exec netns_test unshare -fmuip --mount-proc chroot "/var/lib/fmeng/image/busybox/75fae911f860e3d9d6898b7af34a3b96374dcb33ee9d8b7a6230b74d4a1f30d4" /bin/sh
/ # ls
VERSION etc layer.tar root var
bin home lib tmp
dev json lib64 usr
/ # ip a
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
...
5: veth1_1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue qlen 1000
link/ether 02:42:ac:11:00:01 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/24 scope global veth1_1
...
镜像构建技巧
容器历史
- chroot
- Solaris Zone -> 很像一个VM
- OpenVZ
- LXC - Linux 容器(LXC)是第一个、最完整的 Linux 容器管理器的实现方案
- Docker
- Rocket - CoreOS
- containerd 大一统 (OCI)
Dockerfile 语法
ADD和COPY区别
https://www.ctl.io/developers/blog/post/dockerfile-add-vs-copy/
ENTRYPOINT 和 CMD 区别
https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
http://www.johnzaccone.io/entrypoint-vs-cmd-back-to-basics/
- 至少得定义一个(ENTRYPOINT 或者CMD),以保证运行。否则容器跑不起来
- 容器运行时只定义它们其中一个的话,CMD和ENTRYPOINT的效果是一样的。
- 写法SHELL和exec,推荐exec
- ENTRYPOINT和CMD都可以被 docker run -- entrypoint参数 override
如果你不想开发者修改镜像缺省的执行命令,例如ping, 那么用ENTRYPOINT。
以命令(ENTRYPOINT)+ 参数(CMD)的形式,提供用户修改参数的灵活性。
如果甚至希望提供修改镜像缺省命令的能力,那么只用CMD(没有ENTRYPOINT) 这样用户可以完全地自己定义我起镜像用什么命令,灵活性最高。
测试:
cat Dockerfile
FROM alpine
ENTRYPOINT ["ping"]
CMD ["www.google.com"]
docker run test www.yahoo.com
PING www.yahoo.com (98.139.183.24): 56 data bytes
64 bytes from 98.139.183.24: seq=0 ttl=37 time=0.590 ms
root@build:~# docker run -it test sh
ping: bad address 'sh'
root@build:~#
cat Dockerfile
FROM alpine
CMD ["ping", "www.google.com"]
docker run -it test:0.2 sh
/ # ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
/ #root@build:~# docker run test:0.2 www.baidu.com
docker: Error response from daemon: failed to create shim: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "www.baidu.com": executable file not found in $PATH: unknown.
OCI镜像
OCI镜像规范的目标是:允许开发者只要对容器打包和签名一次,就可以在所有的容器引擎上运行该容器。这意味着开发团队可以根据自己的需要选择构建工具和扩展语法。
OCI 主要有三个规范:运行时规范 runtime-spec ,镜像规范 image-spec 以及不常见的镜像仓库规范 distribution-spec
镜像规范:
├── annotations.md # 注解规范
├── config.md # image config 文件规范
├── considerations.md # 注意事项
├── conversion.md # 转换为 OCI 运行时
├── descriptor.md # OCI Content Descriptors 内容描述
├── image-index.md # manifest list 文件
├── image-layout.md # 镜像的布局
├── implementations.md # 使用 OCI 规范的项目
├── layer.md # 镜像层 layer 规范
├── manifest.md # manifest 规范
├── media-types.md # 文件类型
├── README.md # README 文档
├── spec.md # OCI 镜像规范的概览
容器镜像仓库的选择
公有云
- 官方 https://hub.docker.com
- jfrog artifactory https://www.jfrog.com/confluence/display/JFROG/Docker+Registry
- 各个云厂商的镜像仓库服务
私有云
- harbor
- jfrog
- https://quay.io/
基础镜像的选择
- 静态语言:越小越好
- 动态语言:根据情况测试
https://aws.amazon.com/cn/blogs/china/choose-the-best-docker-image-for-your-python-application/
实战课程
https://time.geekbang.org/column/intro/100063801
runtime:
- 容器运行时
- kernel
- kubernetes源码