Docker基础讲解

docker是什么?

1.1.docker介绍

Docker 技术使用 Linux 内核和内核功能(例如 Cgroups 和 namespaces)来分隔进程,以便各进程相互独立运行。这种独立性正是采用容器的目的所在;它可以独立运行多种进程、多个应用,更加充分地发挥基础设施的作用,同时保持各个独立系统的安全性。

容器工具(包括 Docker)可提供基于镜像的部署模式。这使得它能够轻松跨多种环境,与其依赖程序共享应用或服务组。Docker 还可在这一容器环境中自动部署应用(或者合并多种流程,以构建单个应用)。

此外,由于这些工具基于 Linux 容器构建,使得 Docker 既易于使用,又别具一格------它可为用户提供前所未有的高度应用程访问权限、快速部署以及版本控制和分发能力。

1.2.与虚拟机的区别

从概念上来看 Docker 和我们传统的虚拟机比较类似,只是更加轻量级,更加方便使,Docker 和虚拟机最主要的区别有以下几点:

  • 虚拟化技术依赖的是物理CPU和内存,是硬件级别的;而我们的 Docker 是构建在操作系统层面的,利用操作系统的容器化技术,所以 Docker 同样的可以运行在虚拟机上面。
  • 我们知道虚拟机中的系统就是我们常说的操作系统镜像,比较复杂;而 Docker 比较轻量级,我们可以用 Docker 部署一个独立的 Redis,就类似于在虚拟机当中安装一个 Redis 应用,但是我们用 Docker 部署的应用是完全隔离的。
  • 我们都知道传统的虚拟化技术是通过快照来保存状态的;而 Docker 引入了类似于源码管理的机制,将容器的快照历史版本一一记录下来,切换成本非常之低。
  • 传统虚拟化技术在构建系统的时候非常复杂;而 Docker 可以通过一个简单的 Dockerfile 文件来构建整个容器,更重要的是 Dockerfile 可以手动编写,这样应用程序开发人员可以通过发布 Dockerfile 来定义应用的环境和依赖,这样对于持续交付非常有利。
  • Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源,vm与docker框架,直观上来讲vm多了一层guest OS,同时Hypervisor会对硬件资源进行虚拟化,docker直接使用硬件资源,所以资源利用率相对docker低也是比较容易理解的;从虚拟化层面来看,传统虚拟化技术是对硬件资源的虚拟,容器技术则是对进程的虚拟,从而可提供更轻量 级的虚拟化,实现进程和资源的隔离

1.2.基础概念

1.2.1.镜像image

  • Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。
  • 镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
  • image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,照样可以使用。类似java的一次编译到处运行。

1.2.2.容器

  • 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等

按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

1.2.3.Dockerfile

  • 从前面的内容我们了解到容器是镜像的实例化,而镜像则是服务所需环境依赖和参数变量的集合因此我们需要一个通用的方式将这些集合进行打包,以便随时随地构建镜像。Dockerfile实质上就是通过一些特定的指令将服务所需的依赖环境进行打包此处,可以类比我们平时所写的代码就是业务需求的集合。
  • Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

1.2.4.标签及版本管理

我们都知道git的tag功能是为了将代码的某个状态打上一个戳,通过tag我们可以很轻易的找到对应的提交。docker的tag似乎更加灵活,docker将文件等信息的变动抽象为一次次的commit,每一次commit以后可能走向不同的分支,当我们完成dockerfile的构建后,会生成一串无规则的字符串代表此次生成的ID,此时,tag的作用就是为他创建一个友好的NAME,方便我们对镜像库的管理。

Dockerfile编写

2.1.常见指令

这里只介绍一些常见的指令

2.1.1.FROM

  • FROM用于拉取指定的基础镜像,所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制,可以认为是整个镜像的基石,我们后续所有的环境在此基础上进行构建,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
  • 用法如下:

在Dockerfile第一行添加,指令后面为基础镜像及其tag

bash 复制代码
FROM centos/python-36-centos7:latest

2.1.2.ENV

  • ENV指令用于设置镜像全局的环境变量相当于操作系统中的export命令,在注意,此处设置的环境变量在运行时依然有效,ENV指令一个key-value结构。
  • 用法如下:
bash 复制代码
# 设置时区
ENV TZ Asia/Shanghai

2.1.3.COPY

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

bash 复制代码
# 复制本地的依赖文件到镜像内部的指定路径
COPY requirements /home/work/requirements

2.1.4.ADD

  • ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。

比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。

  • 在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。
  • 用法如下:
sql 复制代码
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /

2.1.5.ARG 构建参数

  • 构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
  • Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。
  • 用法如下:
bash 复制代码
# 构建时传入业务代码的仓库地址
ARG BUS_PROJECT_GIT
# 业务侧项目名
ARG PROJECT_NAME
# 分支名,用于指定分支进行镜像构建
ARG BRANCH_NAME

2.1.6.RUN 执行命令

  • RUN 指令是用来执行命令行(shell命令)命令的。由于命令行的强大能力,就像直接在命令行中输入的命令一样,RUN 指令在定制镜像时是最常用的指令之一。
  • 用法如下:
bash 复制代码
RUN set -ex \
    # 修改ssh私钥权限
    && chmod 600 /root/.ssh/id_rsa \
    && chmod 644 /root/.ssh/id_rsa.pub \
    && ssh-keyscan 101.200.81.40 >> /root/.ssh/known_hosts \
    # 安装所需软件
    && yum localinstall -y --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm \
    && yum install ffmpeg ffmpeg-devel mesa-libGL -y \
    # 安装python包
    && pip3 install -i https://mirrors.aliyun.com/pypi/simple --upgrade pip \
    && pip3 install -i https://mirrors.aliyun.com/pypi/simple -r /home/work/requirements/requirements-1.txt \
    && pip3 install -i https://mirrors.aliyun.com/pypi/simple -r /home/work/requirements/requirements-2.txt \

2.1.7.EXPOSE 暴露端口

  • EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
  • 要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
  • 用法如下:
yaml 复制代码
EXPOSE 4242

2.1.8.CMD 容器启动命令

  • 之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。
  • 在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。
  • 在指令格式上,一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号。
  • 如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
bash 复制代码
CMD echo $HOME

在实际执行中,会将其变更为:

bash 复制代码
CMD [ "sh", "-c", "echo $HOME" ]

2.1.9.ENTRYPOINT 入口点

  • ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
  • 当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
xml 复制代码
<ENTRYPOINT> "<CMD>"
  • 启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。

比如 mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。

此外,可能希望避免使用 root 用户去启动服务,从而提高安全性,而在启动服务前还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 root 身份执行,方便调试等。

这些准备工作是和容器 CMD 无关的,无论 CMD 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 )作为命令,在脚本最后执行。以redis官方的dockerfile为例:

less 复制代码
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 6379
CMD [ "redis-server" ]

构建镜像

3.1.示例

我们以zscrapy业务侧镜像构建为例,其构建命令为docker build

ruby 复制代码
build --no-cache=true -t sp_ads:0.0.20 -f DockerFile.Code --build-arg BUS_PROJECT_GIT=git@101.200.81.40:bigdata/zs-ads-data.git --build-arg BRANCH_NAME=master  --build-arg PROJECT_NAME=zs-ads-data  .

3.2.参数说明

3.2.1.--no-cache

使用Dockerfile构建镜像可以利用它的缓存功能:只有在命令已更改的情况下,才会重建已构建的步骤。这也是高效构建镜像的方式之一,缓存虽然非常有用并且省时间,不过有时候docker缓存的行为不都能达到你的期望。例如在我们实际的场景中,你更改了代码并push到Git仓库。新代码不会git clone下来,因为git clone命令没有更改。在Docker看来git clone的步骤一样,所以使用了缓存。

在这种情况下,你可能不想开启docker的缓存了。因此在构建镜像时需要加上 --no-cache 参数。

3.2.2.-t

用于为构建完成的镜像打上标签,格式为<镜像名称>:<标签>,例如示例中的sp_ads:0.0.20,注意镜像标签是可以被覆盖的,因此在多次使用同一个tag构建镜像时需要把旧镜像删除,不然会占用磁盘空间。

3.2.3. -f

用于指定需要编译打包的Dockerfile文件

3.2.4. --build-arg

用于传入Dockerfile中设置的ARG 构建参数

镜像上传

4.1.登入镜像仓库

css 复制代码
docker login --username=zf_k8s@zingfront registry.cn-beijing.aliyuncs.com

4.2.设置镜像库TAG

4.2.1.获取Image id

在设置tag前我们需要获取到镜像id

arduino 复制代码
docker image ls

4.2.2.设置tag

bash 复制代码
docker tag c911 registry.cn-beijing.aliyuncs.com/zscrapy/zs-ads-data:test

注意这里image id 取前几位即可

4.2.3.上传到镜像库

bash 复制代码
docker push registry.cn-beijing.aliyuncs.com/zscrapy/zs-ads-data:test

4.2.4.注意事项

  • 非master分支的代码不允许将tag设置为latest,因为会直接覆盖线上的镜像
  • 测试过程的镜像tag取分支名称即可,允许覆盖tag,没必要使用多个tag
  • 镜像上传之后应该将本地镜像删除,避免占用过多的空间

运行容器

5.1.拉取镜像

bash 复制代码
docker pull registry.cn-beijing.aliyuncs.com/zscrapy/zs-ads-data:test

5.2.运行容器

bash 复制代码
docker run -e ZS_SETTINGS_POSTFIX=product  -e ZS_HOST_PREFIX=bj -e ZS_PROJECT_NAME=zs-ads-data -e ZS_SPIDER_NAME=huoshan_device_micro_game -m 300M --hostname bj.zs_ads_douyin -ti --rm 9f276248eda9 /bin/bash -c /home/work/zscrapy/docker/start.sh

5.3.运行参数说明

  • -e 设置环境变量
  • -m 设置内存分配上限
  • --hostname 设置容器的主机名称
  • -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,该可以支持终端登录
  • -i 则让容器的标准输入保持打开,用于控制台交互
  • --rm 指定容器停止后自动删除容器(不支持以docker run -d启动的容器),即临时启动容器
  • 若需要指定容器运行于后台则需要加上-d,该参数与--rm互斥
  • 指定暴露端口需要加上-p参数其格式为:,例如-p 3600:3600
  • 将容器内部文件挂载到外部,需要加上-v参数,其格式为:,例如-v /data:/data/
  • 若需要指定容器名称,则加上--name

常用命令

  • 查看所有容器及其状态
css 复制代码
docker ps -a
  • 查看所有的镜像
arduino 复制代码
docker image ls
  • 查看容器日志,docker logs 容器id

    docker logs 0f66e3ba526e

  • 进入容器

bash 复制代码
docker exec -ti 0f66e3ba526e bash
  • 停止容器
arduino 复制代码
docker stop 0f66e3ba526e
  • 删除容器
bash 复制代码
docker rm 0f66e3ba526e

这里需要注意的是需要将容器关停后才能删除容器

  • 删除镜像

通过docker image ls获取镜像id后删除容器

arduino 复制代码
docker image rm f6c71408a451

这里需要注意的是需要把与该镜像相关联的所有容器删除后才能删除镜像

7.调试技巧

有时候我们需要依赖docker环境才能进行测试、调试及排查问题,但又不希望立刻执行容器内部的任务。那么,我们在启动容器的时候将默认的启动命令修改为sleep 3600即可,意思是让容器执行3600s,然后进入容器内部进行调试。

以广告线为例,

  • 我们把启动的爬虫的脚本替换为sleep 3600,即
bash 复制代码
docker run -e ZS_SETTINGS_POSTFIX=product  -e ZS_HOST_PREFIX=bj -e ZS_PROJECT_NAME=zs-ads-data -e ZS_SPIDER_NAME=huoshan_device_micro_game -m 300M --hostname bj.zs_ads_douyin -ti --rm 9f276248eda9 /bin/bash -c "sleep 3600"
  • 找到容器id
  • 进入容器
  • 开始调试
相关推荐
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#1 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@1 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣1 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
Algorithm15762 小时前
mac上使用docker搭建gitlab
macos·docker·gitlab
前端SkyRain2 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁3 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
老猿讲编程3 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust
黄小耶@3 小时前
python如何使用Rabbitmq
分布式·后端·python·rabbitmq
宅小海4 小时前
Scala-List列表
开发语言·后端·scala