Skip to main content

Dockerfile

📃Dockerfile

  • dockerfile(不区分大小写)是镜像配置的入口,相当于每次我们使用构件镜像时( docker build)都需要准备一份dockerfiledocker根据文件内的指令一条一条的执行,最终生成镜像
  • 多阶段构建:创建镜像的过程中,每执行一行关键字都相当于新建一个数据镜像层。最终输出中,我们可以将一些做准备的层,安装软件的层都抛弃,只保留最终软件安装成后的层,确保最终镜像的最小化。

必要流程

  • 指定环境的时区,在dockerfile中定义,可以确保所有平台兼容
ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

基础流程

# 使用当前目录下的 dockerfile 生成容器
# 一般吗建议添加 -t 参数指定 tagName,否则创建的对象会是一个None对象
docker build . -t name
docker build -t adobe-test .

# 运行容器,同时赋予一个tag 前台
docer run -it --name adobe-test-api -d

# 运行容器,同时赋予一个tag 后台
docer run -it --name adobe-test-api -d

# 默认采用的是bridge模块,可能会无法访问
docker run -it --name adobe-test-api -p 4040:4040 -d adobe-test
# 使用host模块
docker run -it --net host --name adobe-test-api -d adobe-test

# 返回一个容器的id
9d8d4e0774396ed7cf7043378adxxxxxx

# 通过id进入容器
docker exec -it {容器di} /bin/bash

常用关键字

关键字说明特性
FROM引用镜像多阶段构建
WORKDIR工作目录(镜像内)
COPY从外部复制文件到镜像
RUN镜像内执行命令
CMD每次执行镜像时运行的命令
MAINTAINER
EXPOSE指定端口和协议 ,一般使用更灵活的docker -p 代替
ENV给镜像设置环境变量
ADD
ENTRYPOINT
VOLUME
USER指定镜像内使用什么用户运行指令
ONBUILD
ARG添加build时可以外部提供的动态参数

加粗的指令是最常用的,加星牢记

From

  • 必须是dockerfile的首个命令
  • 基于哪个镜像文件
  • 配合as关键字多阶段构建,(或者默认第一个FROM为索引0)

VOLUME

Volume指令的主要作用是指定一个映射卷,既直接将宿主系统下的目录映射到镜像内,保证了数据不需要存储到镜像,且多个镜像能访问同一个卷,保证了数据的唯一性。

# 声明了镜像内的两个目录是挂载目录
# docker会自动在宿主系统创建挂载点
# 需要自由指定挂载点的话,需要使用Docker-Cpompose或者docker run命令
VOLUME ["/xxxx", "/ddddd"]

通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的

  • 镜像间共享挂载点
# 镜像共享卷
docker run --name xxx -it --volumes-from {关联镜像} image:tag command
  • 通过 run 指定映射

    通过run指定的挂载设置会覆盖镜像原本的设置,所以建议所有卷挂载都使用run命令来指定

# 通过 run 命令设置
docker run --name xxx -it -v {宿主路径}:{镜像路径} image:tag command
  • 查看挂载点信息
docker inspect {imageName}
"Mounts": [
{
"Name": "0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01",
"Source": "/var/lib/docker/volumes/0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true
}

// Destination
// 镜像挂载的目录

// Source
// 绑定在宿主机的目录
  • 制作专门共享卷的镜像作为中转

    多个容器要共享数据的时候,可以先制作出一个专门保存挂载信息的数据容器,其他容器使用 --volumes-from {} 来共享关联这个数据容器,只需要维护一份dockerfile

    因为容器的卷本质上对应主机上的目录,所以这个数据容器也不需要启动。

    尽量使用 docker-compose 来应付这种需要共享数据的场合

WORKDIR

  • 切换镜像内的工作目录,如果目录不存在会自动创建
WORKDIR {镜像路径}

WORKDIR /src/app # 等价于 RUN mkdir -p /src/app && cd /src/app

COPY

  • 从外部复制文件到镜像内
COPY {本地路径} {镜像路径}

COPY . . # 复制当前文件夹下的所有内容到 workdir

ADD

详细规则:https://www.cnblogs.com/zdz8207/p/linux-docker-add-copy.html

  • 可以外部复制文件到镜像内
  • 可直接从网络路径下载文件到镜像内,但会新建多层,需配合多阶段构建使用
  • 通过复制的文件,会被自动加压
  • 任何URL形式下载的压缩包都不会自动解压
  • 目标目录使用 "/" 结尾,ADD会将目标识别为目录,如果不存在, 则自动创建

RUN

  • 在镜像内执行shell命令
  • 每执行一次RUN指令都会新建一个数据层,可以将多个指令合并在同一个RUN
# shell格式
RUN <command>
#或者
# exec格式
# exce默认不调用shell,所以需要指定shell,否则对应用户的环境变量不会生效
RUN ["/bin/bash", "-c", "echo", "$HOME"]
  • 多条命令执行写法
# 保证同一层使用同一个缓存文件
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all

注意:

apt-get update 等更新语句不应在单行RUN指令内执行,应使用&&添加后续操作,这样可以更好的使用缓存

注意:

避免单单使用全局的包更新指令,建议指定安装最新版的包来实现更新

不建议: apt-get update

建议: apt-get install -y xxx包名

CMD

  • 外部通过docker run启动镜像的时候运行的命令
  • 多条CMD指令时,只有最后一条会被执行
# shell格式
CMD node app.js -p

# exec执行
CMD ["node", "app.js", '-p']

# 配合ENTRYPOIN指令使用
CMD ["param1", "param2"]

ENTRYPOINT

USER

  • 指定一个或多个用户来运行指令,用户存在与镜像内
FROM python:3.7.5-slim

# 镜像内新建用户
RUN net user /add python375

# 镜像内使用该用户
USER python375

ENV

  • 添加环境变量到镜像
# 声明
ENV PYTHON_PATH="github.com/lattecake/hello"

# 使用
RUN mkdir -p /go/src/${GITPATH}

ARG

ARG TEST
 docker build -t yibu-mongo -f ./dockerfile-mongodb . --build-arg TEST=123

ARG指令需要再FROM指令后面才能正常使用,

🌟参考文献

🌰 常用方案

python + fastapi 环境

FROM python:3.10.2-slim as base
WORKDIR /temp
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt


FROM python:3.10.2-slim
WORKDIR /app
COPY --from=base /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY . .

RUN python -V
WORKDIR /app/src
CMD ["python", "main.py"]
echo "\
FROM python:3.10.2-slim as base
WORKDIR /temp
COPY requirements.txt .
RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/ && \
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

FROM python:3.10.2-slim
WORKDIR /app
COPY --from=base /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages

RUN python -V
WORKDIR /app/src
CMD [\"python\", \"-V\"]
" > Dockerfile
  • build
docker build -f dockerfile-server -t fastapi-image:base .
  • run
docker run -d -p 0.0.0.0:4040:4040 --net=psd-tools --name psd-tools-test psd-tools-image:v1 python main.py
  • create
docker run -d -p 0.0.0.0:4040:4040 --net=psd-tools --name psd-tools-v1 psd-tools-image:v1 /bin/bash
  • 优化版(国内)
# 先构建fastapi的环境
echo "\
FROM python:3.10.2-slim
WORKDIR /temp
COPY requirements.txt .
RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/ && \
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
RUN python -V
CMD [\"python\", \"-v\"]
" > Dockerfile_base
docker build -t fastapi-image:base -f ./Dockerfile_base .

# 基于环境构建项目
echo "\
FROM fastapi-image:base as base
COPY --from=base /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
WORKDIR /app
COPY . .
CMD [\"python\", \"-v\"]
" > Dockerfile_project
docker build -t fastapi-psd-tools:v1 -f ./Dockerfile_project .
echo "\
FROM python:3.10.2-slim as base
WORKDIR /temp
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt


FROM python:3.10.2-slim
WORKDIR /app
COPY --from=base /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY . .

RUN python -V
WORKDIR /app/src
CMD [\"python\", \"test.py\"]
" > Dockerfile
docker build -t fastapi-image:base .
docker run -it --name fastapi-test fastapi-image:base python

python3.7.5 + nodejs12.4.0

参考资料:https://juejin.cn/post/6844903870439620622

docker build -f  <dockerfile名称> -t <新镜像名称>

docker build --no-cache -t yibuwx-app-test .

docker build -t yibuwx-egg .

docker run -it --network wxyibu --ip 172.25.0.100 -p 7001:7001 yibuwx-egg:latest /bin/bash

docker cp /home/test/Project/app/docker/app-admin/code_test/config/config.default.js 9d3af7b442d9:/app/server-eggjs/config/config.default.js
  • 传统构建
FROM python:3.7.5-slim AS base
LABEL maintainer="capsion@qq373704015"
WORKDIR /temp

ADD ./pngquant-linux.tar.bz2 /temp

################################# 部署python3.7.5 #####################################
# 复制python依赖列表
COPY ./requirements.txt .

# 安装python要用到的包
RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

################################# 部署nodejs #####################################
# 在线下载
# ADD http://nodejs.org/dist/v12.4.0/node-v12.4.0-linux-x64.tar.gz .

# 离线安装
ENV NODE_TAR=node-v12.4.0-linux-x64
COPY ./${NODE_TAR}.tar.gz .

# 使用ADD指令下载远程压缩文件,不会触发自动解压功能
ADD ./$NODE_TAR.tar.gz /temp

ENV PATH=$PATH:/temp/${NODE_TAR}/bin

COPY ./package.json .

RUN npm config set registry https://registry.npmmirror.com
RUN npm i --unsafe-perm=true

FROM python:3.7.5-slim

ENV NODE_TAR=node-v12.4.0-linux-x64

WORKDIR /app

# 复制解压好的node
COPY --from=base /temp/${NODE_TAR} /app/${NODE_TAR}
COPY --from=base /temp/node_modules /app/server-eggjs/node_modules

# 复制安装好的py包
COPY --from=base /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.7/site-packages

# 复制pngquant
COPY --from=base /temp/pngquant /app/bin/pngquant

# 添加node环境变量
ENV PATH=$PATH:/app/${NODE_TAR}/bin:/app/bin
RUN python -V
RUN node -v

CMD ["/bin/bash"]
  • 多阶段构建
FROM python:3.7.5-slim AS base
MAINTAINER capsion@qq373704015
WORKDIR /temp

# 复制python依赖列表
COPY ./requirements.txt .

# 下载nodejs
ADD http://nodejs.org/dist/v12.4.0/node-v12.4.0-linux-x64.tar.gz .

# 使用ADD指令下载远程压缩文件,不会触发自动解压功能
RUN tar -zxvf node-v12.4.0-linux-x64.tar.gz -C /opt && \
ENV PATH=$PATH:/opt/node-v12.4.0-linux-x64/bin
RUN npm install

# 安装python要用到的包
RUN pip install -r requirements.txt -i https://pypi.douban.com/simple/

FROM python:3.7.5-slim

WORKDIR /app

# 复制解压好的node
COPY --from=base /opt/node-v12.4.0-linux-x64 /app/node-v12.4.0-linux-x64
COPY --from=base /temp/node_modules /app/core/node_modules

# 复制安装好的py包
COPY --from=base /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.7/site-packages

# 添加node环境变量
ENV PATH=$PATH:/opt/node-v12.4.0-linux-x64/bin
RUN python --version && node -v && npm -v

CMD ["/bin/bash"]
# 使用apt安装nodejs
# 修改apt源 => 安装wget => 下载node压缩包
# 使用gz而不使用xz因为xz需要另外安装xz解压依赖,apt安装并不容易,所以改用gz
# RUN echo "" > /etc/apt/sources.list && \
# echo "deb http://mirrors.ustc.edu.cn/debian stable main contrib non-free" >> /etc/apt/sources.list && \
# echo "deb http://mirrors.ustc.edu.cn/debian stable-updates main contrib non-free" >> /etc/apt/sources.list && \
# apt-get update && \
# apt-get install wget -y && \
# wget http://nodejs.org/dist/v12.4.0/node-v12.4.0-linux-x64.tar.gz && \
# tar -zxvf node-v12.4.0-linux-x64.tar.gz -C /opt/ && \

调试


pngquant 环境搭建

echo "\
# syntax=docker/dockerfile:1
FROM alpine:latest

# 作者信息
MAINTAINER capsion@qq373704015

ENV username=\"pngquant\"

RUN set -eux && \
sed -i s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g /etc/apk/repositories && \
apk add --no-cache pngquant

COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh

ENTRYPOINT [\"/entrypoint.sh\"]
CMD [\"--help\"]
" > dockerfile

nginx

参考文献:官方文档

在docker部署nginx采用官方基础镜像+自定义配置文件,基本可以满足大部分中小项目场景需求,所以了解和学习官方镜像使用也非常重要

# 使用alpine版本
FROM nginx:1.21.4-alpine

MAINTAINER capsion@qq373704015

# 复制 ssl文件和配置文件到镜像内
# 外部指定配置文件比较灵活
# COPY . .

# 添加 daemon off 关闭后台启动,docker需要一个前台程序
CMD ["nginx" "-c" "/etc/nginx/nginx.conf" "-g" "daemon" "off"]
# 运行
docker run --name xxxx-nginx-container -d nginx
docker run --name xxxx-nginx-container -d nginx -v xxxx:/etc/nginx/nginx.conf -p 80:80

如果使用自定义的.conf配置文件,需要把后台运行关闭,使用前台模式运行 -g daemon off,不然的容器无法启动。

动态环境变量 (> =1.19 的官方镜像)

  • 🐙 docker-compose.yml 映射 .templates 文件夹
web:
image: nginx:1.21.4-alpine

# 添加templates文件夹的映射,可以在这个文件夹存放我们的变量文件
# templates 目录下存在 *.template
volumes:
- ./templates:/etc/nginx/templates

ports:
- "8080:80"
environment:
- NGINX_HOST=foobar.com
- NGINX_PORT=80
  • 准备 *.template

    准备模板文件,且内容是对应需要替换的变量

listen       ${NGINX_PORT};
  • 输出

    最终输出在镜像内的配置文件会:

listen       80;

只读模式

使用 --read-only 模式运行,需要确保nginx映射以下目录:

# 这两个目录是ngxin基础要求的写目录
-v $(pwd)/nginx-cache:/var/cache/nginx
-v $(pwd)/nginx-pid:/var/run nginx

redis

FROM redis:6.0.9-alpine
COPY redis.conf /redis/redis.conf
COPY dump.rdb /redis/data
CMD [ "redis-server", "/redis/redis.conf" ]
docker 
run
--name myredis
redis:6.0.9
redis-server /usr/local/etc/redis/redis.conf
docker pull redis:6.0.9-alpine

mongodb

事前准备

# 创建目录

使用镜像:docker.io/mongo4.4

docker pull 
# 使用 MongoDB 4.4 镜像作为基础镜像
FROM mongo:4.4

# 使用变量来定义 MongoDB 配置参数
ARG MONGO_INITDB_ROOT_USERNAME=hongqi
ARG MONGO_INITDB_ROOT_PASSWORD=HI.123
ARG MONGO_PORT=27017
ARG MONGO_LOG_DIR=/var/log/mongodb
ARG MONGO_DATA_DIR=/data/db

# 创建 MongoDB 数据目录和日志目录
RUN mkdir -p ${MONGO_DATA_DIR} ${MONGO_LOG_DIR} \
&& chown -R mongodb:mongodb ${MONGO_DATA_DIR} ${MONGO_LOG_DIR}

# 创建一个初始化脚本,该脚本会在 MongoDB 首次启动时运行
COPY init-mongo.js /docker-entrypoint-initdb.d/

# 设置容器启动时执行的命令
CMD ["mongod"]

# 初始化脚本内容
RUN echo "Waiting for MongoDB to start..."
RUN echo "db.createUser({user: '${MONGO_INITDB_ROOT_USERNAME}', pwd: '${MONGO_INITDB_ROOT_PASSWORD}', roles: [{role: 'root', db: 'admin'}]});" > /docker-entrypoint-initdb.d/init-mongo.js \
&& chmod +x /docker-entrypoint-initdb.d/init-mongo.js

# 确保 MongoDB 容器在启动时执行初始化脚本
RUN chmod 0755 /docker-entrypoint-initdb.d/init-mongo.js

# 暴露 MongoDB 端口
EXPOSE ${MONGO_PORT}
# 构建镜像
docker build -t my-mongo:4.4 .
docker build -t yibu-mongo -f ./dockerfile-mongodb

# 运行容器
docker run -d --name my-mongo -p ${MONGO_PORT}:${MONGO_PORT} my-mongo:4.4