这个有点标题党的意思,但确实是事实:容器的镜像只是组织rootfs,如果我们提前准备好rootfs,那么就不需要容器的镜像。
下面我们通过一个实战演示一下,如何直接通过runc启动容器。
$ mkdir my_container && cd my_container
$ runc spec
- 1.
- 2.
通过spec命令便可以生成一个config.json文件。文件的内容大概是下面这样的
{
"ociVersion": "1.0.1-dev",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"cwd": "/",
"env": [ ... ],
"capabilities": { ... },
"rlimits": [ ... ]
},
"root": {
"path": "rootfs",
"readonly": true
},
"hostname": "runc",
"mounts": [ ... ],
"linux": {
"namespaces": [
{ "type": "pid" },
{ "type": "network" },
{ "type": "ipc" },
{ "type": "uts" },
{ "type": "mount" }
]
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
里面就是标准的RUNC格式,主要是定义了启动命令、env、rootfs、主机名、mount挂载、namespace等。
然后我们创建一个rootfs目录
$ mkdir rootfs
- 1.
然后我们写一个打印主机名的Go程序,编译后并拷贝到rootfs里面。
$ cat <<EOF > main.go
package main
import "fmt"
import "os"
func main() {
fmt.Println(os.Hostname())
}
EOF
$ GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o printme
$ mv printme rootfs/
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
然后启动容器
$ sudo runc create mycont1
$ sudo runc start mycont1
- 1.
- 2.
便可以直接输出”runc“(因为上面spec定义的hostname就是runc)。
所以对应容器来说,只是需要一个rootfs,其实这个rootfs是怎么生成的,它其实并不关心。至于是不是用overlay 制作的更是无从感知。