Dockerfile 入门

docker pull 只能拿到别人做好的镜像;要「把自己的应用做成镜像」就得写 Dockerfile——一份描述镜像内容的构建脚本。把 Dockerfile 放进项目,docker build 一执行,应用和环境就固化成可分发、可运行的镜像。

镜像是怎么构建的

docker build 流程 构建的原料叫「构建上下文」,通常就是项目目录。Docker 从基础镜像开始逐条执行 Dockerfile 指令,每条指令产生一个只读层,最终叠加出目标镜像。构建上下文一句话:docker build 末尾的 . 就是要发给 Docker 守护进程的目录,COPY 只能复制它范围内的文件。

最小 Dockerfile:逐行讲解

以把静态网页做成 Nginx 镜像为例:

# FROM:必须放在第一行,指定基础镜像(从哪一层开始叠)
FROM nginx:alpine
# LABEL:给镜像加元数据(作者、用途等说明,不影响运行)
LABEL maintainer="kb@example.com"
# RUN:构建期间执行的命令,用于装依赖、生成文件
RUN echo "<h1>Hello Docker</h1>" > /usr/share/nginx/html/index.html
# COPY:把构建上下文里的文件复制进镜像
COPY about.html /usr/share/nginx/html/about.html
# CMD:容器启动时执行的默认命令,整个文件只生效最后一个
CMD ["nginx", "-g", "daemon off;"]

上面每一条指令都配了 # 注释说明;注意 # 之后到行尾都是注释,构建时会忽略。在项目目录里准备好 about.html 后,执行构建并运行:

docker build -t myweb:1.0 .     # -t 给镜像命名,. 指构建上下文为当前目录
docker images | grep myweb      # 输出:myweb   1.0   ...
docker run -d -p 8080:80 --name web myweb:1.0
curl http://localhost:8080      # 返回自己写的欢迎页

RUN 与 CMD 的区别

两者名字相近,执行时机完全不同:

指令执行时机作用条数
RUNdocker build 构建时改变镜像内容(装包、写文件)可写多条
CMDdocker run 启动容器时指定容器默认启动命令只生效最后一条

CMD 只是「默认值」,docker run 后面跟的命令可以覆盖它。写 CMD 时推荐 exec 形式(JSON 数组,如 ["nginx","-g","daemon off;"])而不是 shell 形式:exec 形式不经过 shell,信号能直达主进程,docker stop 才能优雅退出。

完整示例:打包一个小应用

除了拷静态文件,也可以把代码连同运行时一起打进镜像。下面用极简的 Python HTTP 服务演示(app.py 运行在 python 镜像提供的运行时上):

# app.py:极简 HTTP 服务,任何路径都返回欢迎页
import http.server

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"<h1>Hello Docker</h1>")

http.server.HTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
FROM python:3.12-alpine
COPY app.py /app/app.py
CMD ["python", "/app/app.py"]
docker build -t hello-py:1.0 .
docker run -d -p 8000:8000 --name hello hello-py:1.0
curl http://localhost:8000
# 输出:<h1>Hello Docker</h1>

小结

写 Dockerfile 就是按 FROM → LABEL → RUN → COPY → CMD 的顺序描述镜像内容:RUN 在构建期改变镜像、CMD 定运行期默认命令并推荐 exec 形式;docker build -t 名字 . 构建,docker run 即可跑起属于自己的第一个镜像。

笔记加载中…