Docker 基础篇

Yaurora

Docker是一种运行在Linux和Windows上的软件,用于创建、管理和编排容器

安装

  • Ubuntu

    • 移除旧版本

      1
      2
      3
      $ sudo apt-get remove docker \
      docker-engine \
      docker.io
    • Apt 安装

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      # 安装https传输的软件包和ca证书
      $ sudo apt-get update

      $ sudo apt-get install \
      apt-transport-https \
      ca-certificates \
      curl \
      software-properties-common



      # 添加软件源的 GPG 密钥
      $ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
      # 官方源
      # $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -


      # 向 source.list 中添加 Docker 软件源
      $ sudo add-apt-repository \
      "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
      $(lsb_release -cs) \
      stable"

      # 官方源
      # $ sudo add-apt-repository \
      # "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
      # $(lsb_release -cs) \
      # stable"
    • Docker CE 安装、启动

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 安装
      $ sudo apt-get update
      $ sudo apt-get install docker-ce

      # 也可以通过脚本自动化安装
      $ curl -fsSL get.docker.com -o get-docker.sh
      $ sudo sh get-docker.sh --mirror Aliyun
      # $ sudo sh get-docker.sh --mirror AzureChinaCloud

      # 启动
      $ sudo systemctl enable docker
      $ sudo systemctl start docker
    • 建立docker用户组

      1
      2
      3
      4
      # 建立用户组
      $ sudo groupadd docker
      # 将当前用户添加到docker组
      $ sudo usermod -aG docker $USER
  • MacOS

    • HomeBrew

      1
      $ brew cask install docker
    • 手动下载安装

      如果需要手动下载,请点击以下链接下载 Stable Edge 版本的 Docker Desktop for Mac。

      如同 macOS 其它软件一样,安装也非常简单,双击下载的 .dmg 文件,然后将那只叫 Moby 的鲸鱼图标拖拽到 Application 文件夹即可(其间需要输入用户密码)。

      img

镜像

  • 搜索

    1
    $ docker search ubuntu
  • 获取镜像

    1
    2
    3
    # 格式 docker (image)  pull <Registry>/<userName of Orgnization>/<Repository>:<Tag>
    # eg
    $ docker pull ubuntu:latest
  • 列举镜像

    1
    2
    3
    4
    5
    6
    7
    8
    # 列举格式 $ docker images <repository>:<tag>
    # filter 参数过滤, 列举所有tag 为latest的镜像 (dangling, before, since, label, reference)
    $ docker image ls --filter reference="*:latest"
    # 格式化显示, 注意格式(大小写、标点)
    $ docker images --format "{{.Repository}}: {{.Size}}"
    $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
    # 删除悬虚镜像
    $ docker image prune
  • 删除本地镜像

    1
    2
    3
    4
    5
    # 删除格式, 其中镜像是镜像ID(长或短)、镜像名、镜像摘要
    $ docker image rm [选项] <镜像1> [<镜像2> ...]

    # 删除所有名为redis的镜像
    $ docker rmi $(docker image ls -q redis)
  • 更新镜像

    当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

    1
    2
    3
    # 产看容器内的变化
    # 格式 docker diff <容器ID或容器名>
    $ docker diff webserver
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      # 格式 $ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
    # 例如, 在nginx容器中修改内容后
    $ docker commit \
    --author "jingyucute@gmail.com" \
    --message "update content" \
    webserver \
    nginx:v2
    # 或者简写
    $ docker commit \
    -a="jingyucute@gmail.com" \
    -m="update content" \
    webserver \
    nginx:v2

    1
    2
    3
    # 产看镜像内的提交记录
    # 格式 docker history <仓库名>[:<标签>
    $ docker history nginx:v2

    特别注意

    docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,定制镜像应该使用 Dockerfile 来完成。使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。这种黑箱镜像的维护工作是非常痛苦的。

  • 使用Dockerfile制作镜像

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 格式 docker build [选项] <上下文路径/URL/->
    $ docker build -t <repository>:<tag> .

    # 直接从Git repo中构建
    $ docker build https://github.com/twang2218/gitlab-ce-zh.git

    # 用给定的 tar 压缩包构建, 下载后会自动解压
    $ docker build http://server/context.tar.gz

    # 标准输入构建
    $ docker build - < context.tar.gz

    $ docker build - < Dockerfile

    $ cat Dockerfile | docker build -

容器

  • 查看

    1
    2
    3
    # 查看正在运行的容器, 加上-a参数表示查看所有的容器(包括退出的)
    $ docker container ls [-a]
    $ docker ps [-a]
  • 启动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 从镜像中创建并启动一个容器 , 参数自己查吧
    # 格式 docker <container> run [--options]

    # 交互式运行ubuntu容器
    # -i: 交互式操作。
    # -t: 终端。
    # ubuntu: ubuntu 镜像。
    # /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash
    $ docker run -it ubuntu --name myOS /bin/bash

    # 守护态运行一个容器
    # 格式 docker <container> run -d
    $ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

    # 启动一个停止了的容器
    # 格式 docker <container> start <Container-name-or-ID>
    $ docker start myOS
  • 重启

    1
    2
    # 格式 docker <container> restart  <Container-name-or-ID>
    $ docker restart myOS
  • 查看容器输出

    1
    2
    # 格式 docker <container> logs  <Container-name-or-ID>
    $ docker logs myOS
  • 停止

    1
    2
    # 格式 docker <container> stop  <Container-name-or-ID>
    $ docker stop myOS
  • 删除

    1
    2
    3
    4
    5
    6
    # 删除的容器不能是运行状态,可以先停止再删除, 也可以通过加上 -f 参数强删(不推荐)
    # 格式 docker container rm <Container-name-or-ID> [-f]
    $ docker rm <Container-name-or-ID>

    # 清楚所有停止状态的容器
    $ docker container prune
  • 进入容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 两种方式, 推荐使用方式二
    # 方式一 docker attach <Container-name-or-ID>
    # 这种方式退出终端,会导致容器停止
    $ docker attach myOS

    # 方式一 docker attach <Container-name-or-ID>
    # 这种方式退出终端,会导致容器停止
    $ docker attach myOS

    # 方式二 docker exec -it <Container-name-or-ID> bash
    # 这种方式退出终端,容器不会停止
    $ docker exec -it myOS bash

Dockerfile参数说明

  • FROM

    所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。FROM 就是指定 基础镜像,因此一个 DockerfileFROM 是必备的指令,并且必须是第一条指令。

    1
    2
    FROM nginx
    ...
  • RUN

    RUN 指令是用来执行命令行命令的。通常有两种形式:

    ​ shell格式 RUN <命令> (常用格式)

    1
    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

    ​ exec格式 RUN ["可执行文件", "参数1", "参数2"] . 这种形式好比函数调用

    1
    2
    3
    RUN ["./test.php", "dev", "offline"] 
    # ====
    RUN ./test.php dev offline

    注意

    这里的RUN要尽量少, 多了会导致镜像臃肿, 因为镜像是按照层来构建的,一个RUN就是一层。所以可以使用 && 来链接命令,使用 \来换行

    举个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    FROM debian:stretch

    RUN apt-get update
    RUN apt-get install -y gcc libc6-dev make wget
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
    RUN mkdir -p /usr/src/redis
    RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
    RUN make -C /usr/src/redis
    RUN make -C /usr/src/redis install

    # --------------------------------------------------------->
    FROM debian:stretch

    RUN buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps
  • COPY

    COPY指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置

    1
    2
    3
    4
    5
    6
    # 格式 COPY [--chown=<user>:<group>] <源路径>... <目标路径>
    # COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
    COPY hom?.txt /mydir/

    # 改变文件的所属用户和用户组 --chown=<user>:<group>
    COPY --chown=55:mygroup files* /mydir/
  • ADD

    ADD 指令和 COPY 的格式和性质基本一致(同样需求下, 推荐使用COPY)。

    • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
    • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

    COPYADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

    1
    2
    # 改变文件的所属用户和用户组 --chown=<user>:<group>
    ADD --chown=55:mygroup files* /mydir/
  • CMD

    CMD 指令的格式和 RUN 相似。

    • CMD 在docker run 时运行。
    • RUN 是在 docker build。

    CMD也有两种形式

    1
    2
    3
    4
    5
    6
    7
    # shell 格式:CMD <命令>
    # exec 格式:CMD ["可执行文件", "参数1", "参数2"...]

    CMD echo $HOME
    # ====
    CMD [ "sh", "-c", "echo $HOME" ]

    Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 systemd 去启动后台服务,容器内没有后台服务的概念。

    1
    2
    3
    # CMD service nginx start
    # 这个命令是将nginx作为后台服务来运行的, 应该写为一下形式
    CMD ["nginx", "-g", "daemon off;"]
  • ENTRYPOINT

    ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。

    ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

    当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。就可以理解为<ENTRYPOINT> "<CMD>"

    1
    # 格式 ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
  • ENV

    设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

    1
    2
    3
    4
    5
    # 格式 ENV <key> <value> 
    # ENV <key1>=<value1> <key2>=<value2>...
    ENV NODE_VERSION 7.2.0
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
  • ARG

    ARG 构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。

    1
    # 格式 ARG <参数名>[=<默认值>]

    该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。

  • VOLUME

    容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中

    1
    2
    3
    # 格式 VOLUME ["<路径1>", "<路径2>"...]
    # VOLUME <路径>
    VOLUME /data
    1
    docker run -d -v mydata:/data 

    在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

  • EXPOSE

    EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。

    • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
    • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    1
    # 格式 EXPOSE <端口1> [<端口2>...]

    要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。

  • WORKDIR

    使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

    1
    # 格式 WORKDIR <工作目录路径>

    谈一下误区

    初学者将Dockerfile 等同于 Shell 脚本来书写,会出现一下错误

    1
    2
    RUN cd /app
    RUN echo "hello" > world.txt

    将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其内容不是 hello

    原因: 在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器

    每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。

  • USRE

    USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份(用户和用户组必须提前已经存在)。

    1
    # 格式 USER <用户名>[:<用户组>]
  • 标题: Docker 基础篇
  • 作者: Yaurora
  • 创建于 : 2020-07-31 16:40:08
  • 更新于 : 2020-08-01 17:22:24
  • 链接: https://jingyu.life/2020/07/31/docker/basic/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。