dockerfile

基本知识点

  • 镜像的构建步骤按照dockerfile里的指令顺序来构建。

  • docker镜像是分层的,通过dockerfile构建镜像时,每一句指令就是一层,每执行一句就叠加一层,最底层则是基础镜像,即第一个 from 指令所指定的镜像

  • docker的容器即是在镜像之上加上一个可以读写的临时层, 或者说, 就是基于镜像构建了一个沙箱环境.

  • 构建过程中的删除操作只在所在的指令语句的那一层中起作用。比如有两句RUN指令:前一个是安装了某个软件或添加了每个文件,即执行了对镜像来说增大体积的操作;后一个则是卸载这个软件的下载缓存或删除对应的目标文件,既执行了对镜像来说的减少体积的操作。那么,镜像真的会减去这一部分的体积吗?不,并不会,在docker镜像构建过程中,它是一层一层往上叠的,也就是说,新的层会叠加在旧镜像层之上,所以,在旧的层里加进镜像里的东西,哪怕在后面的层执行了删除操作也并不会真正地在镜像里消失,依旧会存留在镜像某些层内。所以要缩减镜像体积的话,首先要选一个符合要求的、最小的基础镜像,然后构建过程中每一次安装软件都要注意在同一个RUN操作中同时执行清除缓存等操作。

  • docker的规范规定,dockerfile的文件名默认是dockerfile,dockerfile这两个单词的首字母可以不区分大小写。如果你非要将docker的构建文件命名为其他名字 (如aaa) , 那么要跑这个构建文件的话就要加上路径, 如: 当我想要以root用户的身份构建在/root 里的名为aaa的dockerfile的镜像构建文件,那么可以执行

  cd /root
  docker build -f aaa

docker run -f ~/aaa

  • 镜像名称的具体命名方式为: [ip/domain]/[project]/image:version , 只是因为一般来说我们用的都是docker hub上的公共镜像, 所以我们并不需要在镜像名里加上域名. 如果我们要执行的是私有镜像, 那么这个镜像名就必定会加上域名或者IP地址, 举个例子:

假如我有一个私有的docker仓库放在了registry.alexc.cn 上, 并且有一个叫 hero 的项目, 项目里的第一个镜像叫 king , 镜像的版本号是 0.0.1 , 那么我要启动这个镜像的话, 命令就是这样写的:

docker run -rm -p 8080:8080 registry.alexc.cn/hero/king:0.0.1

PS: 有些仓库可能设置了访问权限, 需要先登录才能拉取下来或是更新上去

指令参数的作用

  • FROM:dockerfile文件的第一行必须是该关键字,用于指定dockerfile所构建的镜像的基础镜像是什么,该参数以镜像名称作为值, 如 from: nginx:latest
  • RUN:该参数表示在构建过程中需要执行的bash命令, 如 RUN: uname -a
  • LABEL:用于自定义镜像的元数据,如`LABEL author=“AlexC”记录了author为AlexC
  • ARG:定义了本构建镜像文件在构建时的变量。如ARG a=1,那么如果我在后面的指令中有用到变量a的值的地方的话,我只需要使用美元符+变量名,即$a就可以了,如WORKDIR /root/$a,这里的意思就是工作目录设定在WORKDIR/root/1
  • COPY:用于复制外部文件进镜像。对外部文件支持相对路径和绝对路径。使用格式为COPY {原文件路径}+空格+{目标文件路径},如COPY ROOT.war /home的意思就是把与dockerfile同级的ROOT.war放进镜像内部的/home目录下。

dockerfile示例

# 本镜像的基础镜像
FROM tomcat:8-slim
# 记录维护者联系方式到镜像的元数据
LABEL maintainer="[email protected]"
# 设置工作目录
WORKDIR /root
# 定义构建变量,以下变量只在构建镜像过程中起作用,不会写入到镜像中的操作系统里
ARG tomcat="/usr/local/tomcat"
ARG web="${tomcat}/webapps"
ARG log="${tomcat}/logs"
ARG workspace="/home/ProjectWorkspace"
# 表示以root用户操作镜像内的系统
USER root

# 执行更新系统的操作
RUN apt update -y \
    && apt upgrade -y

# 安装软件  
RUN apt install -y  imagemagick

RUN rm -rf ${web}/*

# 复制与dockerfile文件在同一目录下的一个叫index.html的文件到镜像内的root目录下
COPY ROOT.rar /root/
RUN mkdir ${web}/ROOT \
    && unzip /root/ROOT.war -d ${web}/ROOT/

docker-compose

基本知识点

  • docker-compose是一个简单的docker镜像编排工具,其配置文件名称一般默认为为docker-compose.yml,docker-compose将会根据该配置文件里的设置构建出单个或多个docker容器以及其他和容器相关的东西并按照配置项将不同容器联系起来。

配置项

​ 用过yml做配置文件的应该都知道.yml文件的写法其实和json的格式是类似的,都是键值对。在docker-compose的配置文件里有两大配置项是必要的,他们分别是serviceVERSION

  • VERSION:该属性的用法是用于指定docker-compose在根据配置文件执行时编排操作时使用的API版本,现在最新版是3,所以一般来说该标签的写法都是VERSION: "3"
  • services:按我的了解,对于docker来说,一个容器相当于一个服务,所以在services下的都是服务。每一个容器都需要指定一个镜像用于生成容器。
  • NETWORK:表示网段,其下的二级配置项表示不同的隔离网络,每个网络可以使用driver属性定义网络的连接模式。模式有三种,分别是桥接模式bridge、宿主模式host和容器模式container [container name]
  • VOLUMES:表示数据卷,其下二级配置项表示不同的数据卷。当声明一个空数据卷时(即只有一个数据卷名字且不存在任何属性),该数据卷会根据docker自身的缺省值在/var/lib/docker/volumes/{hash值}/_data(*这个路径是我在Mac OS 10.11.6里查到的,不保证其他版本和其他系统也是这个路径*)里存放被挂载映射的数据。

docker-compose.yml示例

# 声明docker-compose执行编排时使用的api版本
version: '3'

networks:
  # 声明了一个名字叫app的隔离网络,该网络的连接模式为桥接模式
   app:
     driver: bridge

volumes:
  # 声明了一个名字叫db的数据卷,且不做设置,即一切设置都是用docker的缺省值
  db:
services:
  # 定义了一个,名字叫mysql的服务
  mysql: 
    # 声明该服务所使用的镜像是来自于docker hub的mysql:latest镜像
    image: mysql
    # 声明本次启动的基于mysql:latest镜像的容器的名字为mysql
    container_name: mysql
    # 为容器提权,避免容器在启动时遇到权限不足导致启动失败的情况
    privileged: true
    # 表示该容器会随着宿主机的重启而重启
    restart: always
    # 表示对外暴露的端口,这里是要将容器内的3306端口映射到宿主机的3306端口,冒号左边是宿主机的端口,右边是容器内的端口
    ports: 
      - "3306:3306"
    # 这里表示本容器的网络使用的是名字为app的网段
    networks:
      - app
    # 这里表示向其它同样在相同网段内的其他容器暴露自己的3306端口,其他容器可以通过3306端口访问本容器的3306端口
    expose:
      - "3306"
    # 这里表示将容器内的/var/lib/mysql目录与名为db的数据卷映射
    volumes:
      - "db:/var/lib/mysql"
    # 这里表示本service依赖于一个叫abc的服务。在docker-compose中,service的启动顺序越是被其它service所依赖,那么这个service的启动优先级越高,但docker-compose的启动并不是阻塞同步的,而是只是排好启动顺序后按顺序发送启动指令,所以当一个叫“甲”的服务启动时间长而未启动完成时,依赖于“甲”的服务“乙”需要调用“甲”时,“乙就会报错”。所以请注意这一点。
    depend_on:
      - "abc"