Docker 【通过Dockerfile构建镜像】【docker容器与镜像的关系】

80 阅读6分钟

前言

docker构建镜像有三种方式 Dockerfile文件名首字母约定要大写。大多数工具(包括Docker CLI和Docker Compose)都会默认查找名为"Dockerfile"的文件来构建镜像。

  • 使用命令行构建
    适合临时构建,一般就是在项目开发初期,环境没有搭建完善,这时候临时需要一个镜像包,可以直接通过命令打给他。
  • 使用Dockerfile文件
    官方推荐这种方式,配置好dockerfile文件之后,docker根据Dockerfile文件构建
  • 使用脚本调用docker 的 api构建
    一般在CI/CD的时候都会使用脚本,调用Docker api配合dockerfile文件实现一键部署,这种方式比较灵活可以配合脚本文件自由实现自己的定制操作,但前期准备工作比较大
  • 容器构建
    通过在容器中执行commit命令构建

一、前期的准备工作

  1. 一台contos操作机器
cat /etc/os-release # 查看操作系统的版本信息

# out system info
NAME="OpenCloudOS" # 这个是腾讯的兼容contos8操作系统,可以看作contos8
VERSION="8.8"
ID="opencloudos"

  1. 安装好docker,这里我的docker版本为
Docker version 25.0.4, build 1a576c5

如果你还不了解如何安装docker,我的这篇文章可能会对你有帮助


二、上手构建一个简单的镜像

  1. 初始化一个docker项目

mkdir /demo
cd /demo
npm init -y

初始化项目结构

demo
|-src
|  |-index.js
|-Dockerfile
|-package.json

  1. index.js 内容

const { createServer } = require("http");
const server = createServer();
server.listen(8080, () => {
  console.log("启动成功!");
});

  1. Dockerfile文件内容

FROM node
WORKDIR /src
COPY . .
RUN npm install --production
CMD ["node", "src/index.js"]

  1. 执行命令打包

# 进入目录 dockerfile同级目录
cd /demo

# 执行构建镜像
docker build -t "cs:1.0.0" . # -t:为镜像打上标签 cs:为镜像名 .:为当前目录

  1. 查看打包结果

docker images

在这里插入图片描述


三、DcokerFile

Dockerfile是本质来讲是一个文本文件,内部包含了一条条指令,能够使docker根据上面的指令定制构建镜像(Image)

1 指令总览

命令列子
FROM指定镜像的依赖可以有多条。但多个相连的FROM指令只会执行最后一个。详情在#指令详情中说明
RUN指定运行命令 RUN ls ./相当于在镜像构建目录下的shell窗口中执行ls ./ 命令
COPY将本地文件copy到镜像支持正则表达式拷贝(COPY /local? .)所有匹配local?的文件目录都会拷贝
ADD同COPY一样,但是更强大(支持更多的文件类型的拷贝 如网络文件的下载后拷贝,ADD http://xxxx/test.tar.gz /test/)拷贝归档文件(.tar .gz等)文件会在复制到镜像的过程中自动解压
USER指定Dockerfile后续命令使用哪个用户身份执行,默认root。
WORKDIR目录切换指令,类似shell的cd
ENV指定运行容器时的环境变量
CMD用来指定由镜像创建的容器启动后执行的命令,比如你想让容器启动服务,就可以通过它设置 只能有一条出现多条则最后一条生效
ENTRYPOINT作用CMD类似,但与CMD同时使用时可能会将CMD的值最为参数

2 指令详情

  • FROM
    当遇到FROM指令时,docker会在本地库寻找对应的镜像库
    如果没有发现,则通过docker pull拉取,如果存在则使用本地下载的镜像库
    版本号不一致的镜像是不同的node:16.16.20node:18.18.1 是两个不同镜像

FROM  node; # 当前镜像依赖node镜像

FROM可以多条使用,但不能连续使用。多条使用一般用于Docker多阶段构建

# 第一阶段
FROM node:14 AS build # 使用node:14镜像 设置构建阶段别名提供给COPY --from 访问
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build # 操作完成产生打包结果文件

# 第二阶段
FROM node:14-alpine # 使用node:14-alpine镜像此时将会替换掉之前node:14镜像
WORKDIR /app
COPY --from=build /app . # 复制 build构建阶段下的/app目录到镜像目录
CMD ["node", "index.js"]

连续使用,只有最后一条生效

FROM node:16.16.1
FROM node:18.18.1 # 此条生效


  • RUN
    设置镜像执行命令

RUN npm i # 相当于在镜像目录的shell窗口中执行npm i


  • COPY
    将本地文件活目录拷贝至镜像目录

    • 只能将本地文件拷贝到镜像,不能将镜像文件拷贝到本地
    • 拷贝的源路径只能处在当前构建镜像的上下文的目录下,通过docker build命令的url参数指定构建上下文
    • 语法:COPY [--chown=<user>:<group>] <源路径>... <目标路径>
 COPY /local /app # 将本地/local 下的所有文件、目录拷贝到镜像/app目录下
 COPY /local1 /local2 /app # 可以由多个源路径,它会将本地/local1 和 /local2下的所有文件、目录拷贝到镜像/app下
 COPY /local\* /app # 支持通配符,将本地/local1 /local2 /local3 /local...等符合匹配规则的路径复制到镜像/app下
 COPY --chown=user1:group1 /local /app # 本地文件复制到镜像后,将它归属设置为user1用户和group1组


  • ADD
    也是用于将本地文件拷贝到镜像中,但是它与COP有些不同

    • 可以COPY远程文件,类似于 自动下载远程文件-》移动至镜像
    • 在拷贝归档文件(.tar等压缩文件)时会在拷贝至镜像的过程中自动解压
ADD http://www.test.com/test.tar . # 会自动自动解压到镜像文件

请注意!即便ADD命令能够自动解压tar等压缩文件,官方的Dockerfile使用规范仍推荐使用COPY复制压缩文件,配合解压命令解压。这样在语法上更加明确

// Dockerfile
COPY test.tar .
RUN tar -vxf ./test.tar # 执行tar -vxf解压


  • USER

USER [用户名]:[用户组]


  • WORKDIR
    修改镜像当前上下文目录,类似linux cd命令。

WORKDIR ./src # 之后的指令上下文都是./src
RUN ls ./ 


  • CMDENTRYPOINT
    CMD和ENTRYPOINT指令都是用来指定镜像创建的容器启动时执行的指令,相当于在容器启动时开启了一个shell窗口,然后运行CMD和ENTRYPOINT的指令参数。

它们有相同的语法:

# CMD <shell 命令> 
CMD echo 11;// 容器启动执行 echo 11
ENTRYPOINT echo 11; // 同上

# CMD ["<可执行文件或命令>","<param1>","<param2>",...] 

CMD ["node","-inspect","index.js"] # 相当于shell窗口执行 node -inspect index.js
ENTRYPONT ["node","-inspect","index.js"] // 同上

如果CMDENTRYPONT同时使用

1. `ENTRYPONT`是数组
```
CMD ["--inspect","index.js"]
# or
CMD echo 11

ENTRYPONT ["node"]

```
 `CMD`会作为`ENTRYPONT`的参数,相当于容器启动时执行
```
node --inspect index.js
# or
node echo 11

```
2. `ENTRYPONT`不是数组,`ENTRYPONT`无法接受参数最终只执行ENTRYPONT
```
CMD echo 或者 CMD ["hello"]
ENTRYPONT echo 

```
 最终容器执行
```
echo

```

  • ENV
    定义环境变量,定义之后就可以在后面的任何命令中使用。
    并且连续多次定义ENV的话,它将会叠加而不是覆盖,下面的例子可以访问3个环境变量

ENV $var1 value1; # 定义单个
ENV $var2=value2 $var3=$value3 # 定义多个

RUN echo $var1
COPY $var1 /app
ADD ./src/$var1 /app
CMD echo $var1
WORKDIR $var1

它会在容器内部存在,你可以在代码中访问环境变量

console.log(process.env.$var1);