k8s(二)Docker的HelloWorld
作者:lomtom
个人网站:lomtom.cn
个人公众号:博思奥园
你的支持就是我最大的动力。
k8s系列:
docker安装
docker的安装不在本节的重点范围之内。
- win参考:docker 安装
- linux直接
yum -y install docker-ce-19.03.15 docker-ce-cli-19.03.15
一条命令搞定
dokcer实操
构建镜像
部署一个Go项目,该项目有一个接口/
返回ok
1 | FROM golang:alpine AS builder |
会发现他非常的大,足足有300M,这显然不符合我们的预期。
究其原因是基础镜像太大了,如果想要这个镜像变小应该怎么做?
那么我们可以先构建好,再将构建好的程序放到我们最终的容器中就可以了。这里有两种方法:
- 可以自己编译,再打包,这样一致性不太好,因为如果你在win中编译的放在linux并不能用;
- 在打包之前用一个拿一个镜像来编译,编译后复制到最终的镜像内
所以果断采取第二种方法:
1 | FROM golang:alpine AS builder |
在项目目录下使用docker build
进行编译:
1 | [root@master demo-gin]# docker build -t demo-gin . |
docker build 操作完成后,我可以通过 docker images 命令查看结果:
1 | [root@master demo-gin]# docker images |
最终镜像大小只有14.8MB。接下来,我使用这个镜像,通过 docker run 命令启动容器:
1 | [root@master demo-gin]# docker run -p 3001:8080 demo-gin |
同时,使用-p 3001:8080
参数将容器内的8080端口映射到宿主机器上的3001端口,这样就可以通过宿主机器的3001端口访问容器的8080端口。
后台运行
如果需要后台运行,在run后面加上 -d
参数即可
随后通过暴露的端口访问。
1 | lomtom@atongmudeMacBook ~ % curl http://8.16.0.67:3001 |
挂载卷
如何在运行的时候,将宿主机器的目录挂载在容器内呢?
可以使用-v参数进行指定:-v /root/lomtom/demo-gin/volume:/test
,这里将宿主机器的/root/lomtom/demo-gin/volume
目录挂载在容器的/test
目录下,这样在容器内就可以访问到宿主机器的/root/lomtom/demo-gin/volume
目录。
1 | [root@master demo-gin]# docker run -p 3001:8080 -v /root/lomtom/demo-gin/volume:/test -d demo-gin |
在宿主机器中的/root/lomtom/demo-gin/volume
添加一个test.txt
文件,内容为 are you happy today?
1 | [root@master volume]# cat /root/lomtom/demo-gin/volume/test.txt |
回到容器中进行查看,发现/test
下存在一个test.txt
文件。
1 | / # cat test/test.txt |
这就是Docker 的Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。
除此之外,也可以不指定宿主机器的目录。
1 | [root@master demo-gin]# docker run -p 3001:8080 -v /test -d demo-gin |
容器启动之后,可以查看volume的信息
1 | [root@master volume]# docker volume ls |
容器内/test
目录下的文件就会保存在宿主机器的/var/lib/docker/volumes/4605aba6ab35518ce29fc650e75c157d3c4b95451123a9687d93eed5cf6be6b8/_data/
中。
值得注意的是,如果使用该容器重新构建一个镜像,容器 Volume 里的信息,并不会被 docker commit 提交掉;但这个挂载点目录 /test 本身,则会出现在新的镜像当中。
docker分析
镜像
回归到镜像本身,我们使用docker inspect [镜像 ID]
来查看镜像属性。
1 | [root@master ~]# docker inspect 74b0086fe7c6 |
其中有几个重点属性需要关注一下。
RootFS属性,layers就是代表镜像的层级,其中有三层(三条数据)
再使用
docker history
分析一下镜像的结构1
2
3
4
5
6
7[root@master ~]# docker history 74b0086fe7c6 --no-trunc=true
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:74b0086fe7c6316790a203895e879d387d00d7f733af9060dd9ff1058c39a068 6 days ago /bin/sh -c #(nop) CMD ["./demo-gin"] 0B
<missing> 6 days ago /bin/sh -c #(nop) COPY file:cd53aab436246ffa2e579600132d18c644f289099eced8fe82b285be3861eec9 in /app/demo-gin 9.19MB
<missing> 6 days ago /bin/sh -c #(nop) WORKDIR /app 0B
<missing> 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:5d6753d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / 5.57MB或者你在pull镜像的时候留意一下
1
2
3
4
5
6
7
8
9[root@master ~]# docker pull lomtom/demo-gin
Using default tag: latest
latest: Pulling from lomtom/demo-gin
df9b9388f04a: Pull complete
2f0488c48942: Pull complete
06e8ee5ba102: Pull complete
Digest: sha256:3ea098ee7ec35c266092f94e5c90e1edceb64d91863c2533fc5869eb8c319581
Status: Downloaded newer image for lomtom/demo-gin:latest
docker.io/lomtom/demo-gin:latest不难发现,执行
docker history
之后,根据日期6 days ago,就能知道这三层就是我们自定义的dockerfile生成的三层。那么 2 weeks ago 的两层是哪里来的呢?
去查看使用的基础镜像,不难发现他的最新更新恰好就是两周前。
那么,这三层是怎么和Dockerfile对应的呢。
会看我们真实用到的dockerfile文件
1
2
3
4
5
6FROM alpine
WORKDIR /app
COPY --from=builder /app/demo-gin /app/demo-gin
CMD ["./demo-gin"]- FROM选取基础镜像,也就是
FROM alpine
,构成了层级的第一层和第二层。 WORKDIR /app
对应了第三层,以及他的CREATE BY
为/bin/sh -c #(nop) WORKDIR /app
- …
- FROM选取基础镜像,也就是
其次可以关注
GraphDriver
属性。Name标明了该镜像的
UnionFS
,即overlay2文件系统。Data属性中的LowerDir、MergedDir、UpperDir
LowerDir (只读):只读的 image layer,其实就是 rootfs, 在使用 Dockfile 构建镜像的时候就定好了
Upperdir (读写):upperdir 则是在 lowerdir 之上的一层, 为读写层。容器在启动的时候会创建, 所有对容 器的修改, 都是在这层。比如容器启动写入的日志文件,或者是应用程序写入的临时文件
MergedDir (展示):merged 目录是容器的挂载点,在用户视角能够看到的所有文件
容器
GraphDriver
同样可以使用
docker inspect [容器 ID]
来查看容器属性。1
2
3
4
5
6
7
8
9
10
11.....
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920-init/diff:/var/lib/docker/overlay2/d6f217c3a337c34b5189700a3f94e521b6bde6b0309119f62abe1b51efb024be/diff:/var/lib/docker/overlay2/990ec90661e0868c1fda29618916e4c5e3143a61394055de6e3f1333de9453fe/diff:/var/lib/docker/overlay2/69c24a49f77b361d64ddf7ce24051acee85d44cf27a62affee17203b9eb82d94/diff",
"MergedDir": "/var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920/merged",
"UpperDir": "/var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920/diff",
"WorkDir": "/var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920/work"
},
"Name": "overlay2"
},
......容器也和镜像一样存在LowerDir、MergedDir、UpperDir,并且这些目录能够在宿主机器上直接访问的。
之前构建镜像的dockerfile文件,所做的就是将
demo-gin
存放在/app
目录下,不出意外的话我们能够在MergedDir
目录下看到这个文件。1
2[root@master ~]# ls /var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920/merged/app
demo-gin接着我们进入容器,在容器内执行一些操作,例如在
/app
下创建一个文件。1
2
3
4
5
6[root@master ~]# docker exec -it 4618b0cc2498 /bin/sh
/app # ls
demo-gin
/app # mkdir lomtom
/app # ls
demo-gin lomtom那么,此时容器内的
/app
下存在demo-gin lomtom。我们再在宿主机器上查看merged目录。1
2[root@master ~]# ls /var/lib/docker/overlay2/ea16bd86de8b3c254a2c1582f77ffd9910485422d837aaa445acba6f71192920/merged/app
demo-gin lomtomNamespace 及 Cgroup
前一节已经对docker原理进行讲解,那么对于真实的容器,他的Namespace 及 Cgroup如何查看呢。
首先,找到该容器的进程编号,因为一个容器就是一个进程。
1
2
3[root@master ~]# ps -aux| grep demo
root 23213 0.0 0.0 112676 988 pts/0 S+ 10:33 0:00 grep --color=auto demo
root 25199 0.0 0.0 709460 9156 ? Ssl 09:34 0:00 ./demo-gin那么25199就是该容器的进程编号。使用
ls /proc/25199/
即可查看该进程下的一些资源信息。1
2
3
4[root@master ~]# ls /proc/25199/
attr cgroup comm cwd fd io map_files mountinfo net oom_adj pagemap root sessionid stack status timers
autogroup clear_refs coredump_filter environ fdinfo limits maps mounts ns oom_score personality sched setgroups stat syscall uid_map
auxv cmdline cpuset exe gid_map loginuid mem mountstats numa_maps oom_score_adj projid_map schedstat smaps statm task wchan其中就包括Namespace即ns。
1
2
3
4
5
6
7
8[root@master ~]# ls /proc/25199/ns/ -l
总用量 0
lrwxrwxrwx. 1 root root 0 4月 21 09:36 ipc -> ipc:[4026532696]
lrwxrwxrwx. 1 root root 0 4月 21 09:36 mnt -> mnt:[4026532694]
lrwxrwxrwx. 1 root root 0 4月 21 09:34 net -> net:[4026532699]
lrwxrwxrwx. 1 root root 0 4月 21 09:36 pid -> pid:[4026532697]
lrwxrwxrwx. 1 root root 0 4月 21 10:30 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 4月 21 09:36 uts -> uts:[4026532695]以及文件资源,即root,是不是验证了与容器中的文件一致
1
2[root@master ~]# ls /proc/25199/root/app/
demo-gin lomtom同样我们进入容器,在
/app
,新建一个文件config,并且写入hello world
,同样在宿主机器上能够查看内容。1
2
3
4
5
6
7/app # echo hello world > config
/app # ls
config demo-gin lomtom
[root@master ~]# cat /proc/25199/root/app/config
hello world
k8s(二)Docker的HelloWorld