SAST2023笔记——Docker相关
我对docker是从零入手,就让我从头讲一讲docker大概是个什么东西吧…
Why
几乎所有码农都有过配环境的痛苦经历…这个包下不了那个包不兼容,可能还会因为操作系统的差别有非常难解决的bug…天下苦配环境久矣,那么有没有什么解决的办法呢?最无脑(先不考虑是否可行)的办法自然是建个虚拟机,把操作系统、运行环境的所有东西全打包给用户。然而显然正常电脑都不会喜欢大几十个GB的虚拟机,于是我们尝试只虚拟软件环境,docker这个轻量级的虚拟化技术也就孕育而生:它试图将应用放在容器container上运行。一般来说容器是MB级别的,对运行很友好。
What
Docker有两个重要的概念:容器container和镜像image。镜像是静态定义的,而容器是动态存在的;说人话就是镜像和容器的关系类似于类和实例的关系,我们可以利用镜像创造容器。我们需要先构建一个镜像,再以此构建容器并运行。
docker hub中有很多我们现成可用的镜像,比如scratch(空镜像),python,gcc等。docker hub可以集中存储并分发镜像,我们称这种服务为注册服务。值得一提的是,每个镜像都形如<镜像名>:<标签>,标签指版本(默认值为latest),例如alpine:latest。
How
docker 安装
首先,在linux系统下安装docker的命令:
1 | export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce" # |
请注意,这些命令可能需要root用户的权限。由于我一直使用的就是root所以没有出什么问题;倘若不然,那么你可能需要将你的用户添加到 docker 用户组中。在此之后运行docker service start
即可。
docker run
那么让我们先试试利用现成的镜像:试试运行命令docker run --name test alpine echo 'Hello, world!'
。该命令意为通过镜像alpine(默认标签为latest,所以其实应该是alpine:latest)构建一个名为test的容器,并运行命令echo 'Hello, world!'
来输出Hello, world!。首先docker会试图在本地寻找alpine镜像,找不到便会去注册服务下载该镜像;然后它会根据参数–name test创造一个名为test的容器并运行命令(如果没有参数–name则会随机赋予一个没什么意义的有趣英文名字,比如laughing_hermann)。请注意,所有参数都应放在镜像名前面(比如–name test应该在alpine前面)。
ps:初学者可以在vscode中下载拓展包docker,这样可以可视化本地镜像和容器
docker build
很好!那么让我们来试一试自行构建一个镜像,让我们创造一个名为Dockerfile的文件(跟git中类似,虽然可以修改这个神奇文件的默认名,但大家基本还是会用这个固定的文件名,不要打错),然后将下列代码粘贴进去:
1 | FROM alpine |
然后我们运行命令docker build -f <path_to_Dockerfile> -t hello:hello .
。该命令意为用给出路径的Dockerfile,也就是基于alpine镜像构建一个名为hello、标签为hello的镜像(如果没有参数-f那么默认在当前目录寻找Dockerfile文件),并且命令最后的 ‘ . ‘ 代表dockerfile中COPY的文件路径为当前路径(回忆linux中 ‘ . ‘ 代表当前目录,而 ‘ .. ‘ 代表上一级目录)。我们会发现此时已经成功创建hello:hello镜像。效仿上面的命令,我们再运行docker run --rm hello:hello
来输出Hello, world!,其中–rm指的是这个容器运行完毕会立刻删除。
多阶段构建
很好!那么让我们来试一下真的配置c++或者python的容器。例如创建一个main.cpp并粘贴下列代码:
1 | #include <iostream> |
然后在Dockerfile中写到:
1 | FROM gcc AS build |
此时的Dockerfile会令人有些困惑:为什么既基于gcc又基于scratch来创造这个镜像?那不就乱套了吗?事实上这是“多阶段构建”,因为假如直接基于gcc构建新镜像,那么因为gcc实在是太大了,最终我们的镜像会有1个多GB…事实上我们根本不需要gcc的内容,只是想利用它预处理、编译、汇编、链接出可执行文件main而已。所以我们将gcc看作工具人,制作出main就可以丢掉了,再次利用FROM scratch将空镜像作为基础镜像即可。默认将gcc构建过程视为过程0,我们可以用COPY --from=0 main .
来利用这个过程的文件;我们也可以像这里一样为构建过程命名为build,然后用COPY --from=build main .
来引用。请注意,多阶段构建的基础镜像永远是最后一个FROM。
对于python文件,配置过程就肉眼可见的复杂了一些…由于解释型语言的特殊性(你说得对,我选择编译型),我们需要安装一坨东西,这里给出一个示例Dockerfile:
1 | FROM python |