开发日常都会接触到容器和docker
,以下作记录:
实现原理
- 资源控制(
CPU/Memory/IO
等):cgroups - 资源隔离: Linux namespaces, chroot 等
- 存储原理: aufs, OverlayFS 等
- 容器边界安全:AppArmor, SELinux, seccomp
- 容器底层实现OCI:lxc, libcontainer, runc, containerd 等
- 容器运行时接口CRI:docker, cri-o,kata-containers, frakti 等
- 用
c
实现简单的容器环境
Dockerfile
- 采用和遵循multi-stage builds 做法, 编译和运行使用不同的基础镜像。
- 承接上一点,由于常规配置下(
docker run
不加任何参数),docker
与宿主机高度隔离,此时docker运行环境的时区timezone
等设置往往是空白的, 如FROM alpine:latest
, 此时, 若应用逻辑依赖时间或时区, 如某golang
程序:
loc, _ = time.LoadLocation("Asia/Shanghai")
t := time.Unix(t1, 0).In(loc)
则docker
运行环境需要添加依赖:
FROM alpine:latest
RUN apk --no-cache tzdata
-
对于
golang
开发的app
,每次构建时均要下载依赖的dockerfile
, 可使用goproxy
加快依赖下载速度, 而其配置因go版本而异, 如go 1.12ENV GOPROXY=https://goproxy.cn
-
Docker
的入口程序与Kubernetes
的入口程序Dockerfile
用于制作镜像,可以在文件最后指定ENTRYPOINT
作为镜像的入口程序,即pid
为1
的进程。- 在
Kubernetes
用于启动pod
的yaml
中,也可指定Command
和Args
作为Docker
启动后的入口程序, 即pid
为1
的进程。 - 若
1
号进程退出(尽管入口程序派生了其他后台进程),则Docker
退出。 - 若
docker
是通过docker run
启动的,docker
会完成生命周期,docker
进程会退出。 - 若
docker
是通过Kubernetes deployment
启动的,则pod
中的docker
在执行完入口程序后会退出,导致pod
不断重启,入口程序反复执行。 - 一般做法会在入口程序的最后添加类似
shell
的sleep infinity
等语句阻止1
号进程退出,使得1号进程派生的后台进程能持续服务。 Docker
和Kubernetes
入口程序先后关系:若在dockerfile和k8s中均指定入口程序,则有如下先后关系, 即使用Kubernetes
创建的docker
若指定了Command
,则该docker
镜像的ENTRYPOINT
会被覆盖, 相当于调用了docker run ... --entrypoint
:
If you do not supply command or args for a Container,
the defaults defined in the Docker image are used.
If you supply a command but no args for a Container, only the supplied command is used.
The default EntryPoint and the default Cmd defined in the Docker image are ignored.
If you supply only args for a Container,
the default Entrypoint defined in the Docker image is run with the args that you supplied.
If you supply a command and args,
the default Entrypoint and the default Cmd defined in the Docker image are ignored. Your command is run with your args.