Docker的使用

何为docker

  • Docker 是一个用于开发,发布和运行应用程序的开放平台。Docker允许用户将基础设施中的应用单独分割出来,形成更小的颗粒(容器),从而提高交付软件的速度。

  • Docker容器与虚拟机类似,但二者在原理上不同。容器是将操作系统层虚拟化,虚拟机则是虚拟化硬件,因此容器更具有便携性、高效地利用服务器。

核心概念

镜像

一个只读的文件和文件夹组合,容器启动的基础,镜像是 Docker 容器启动的先决条件。

镜像操作

image.png

  • 拉取镜像:使用docker pull命令拉取远程仓库的镜像到本地 ;

    // 拉取node镜像,先从本地搜索,如果本地搜索不到则从 Docker Hub 下载镜像
    docker pull node 
    复制代码
  • 重命名镜像:使用docker tag命令“重命名”镜像 ;

    // 重命名match-list镜像,重命名后,镜像列表中多了一个TestList的镜像,并且两个镜像的 IMAGE ID 是完全一样的
    docker tag match-list TestList
    复制代码
  • 查看镜像:使用docker image ls或docker images命令查看本地已经存在的镜像

     // 查看镜像
    docker images
    // 查询指定的镜像
    docker image ls node
    // 使用grep命令进行过滤
    docker images | grep node
    复制代码
  • 删除镜像:使用docker rmi命令删除无用镜像

    ```javascript
     docker rmi node
    ```
    复制代码
  • 构建镜像

    • 第一种方式是使用docker commit命令基于已经运行的容器提交为镜像

      docker commit busybox busybox:hello
      复制代码
    • 第二种方式是使用docker build命令基于 Dockerfile 构建镜像

      Dockerfile 是一个包含了用户所有构建命令的文本

      image.png

      具体例子-Dockerfile文件

      /** Dockerfile文件 **/
      // 基于node创建一个镜像层
      FROM node:carbon-alpine
      // 设置环境变量
      ENV NODE_ENV=prod
      ## 配置 apk包加速镜像为阿里
      RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
      // 设置时区
      RUN apk update \
       && apk add tzdata \
       && rm -rf /var/cache/apk/*
      // 确定工作目录
      WORKDIR /match-list
      // 复制当前目录下的文件到工作目录下
      COPY . /match-list
      // 指定容器监听的端口
      EXPOSE 4001
      // 容器启动后执行npm run start:docker命令来启动容器
      CMD [ "npm", "run", "start:docker" ]
      复制代码

      创建镜像

        // 在Dockerfile文件目录下创建镜像,-t:镜像名,.:当前目录
        docker build -t match-list .
      复制代码

容器

  • 镜像的运行实体,运行着真正的应用进程(docker ps),有自己独立的命名空间隔离和资源限制,无法看到主机上的进程、环境变量、网络等信息。
  • 一个镜像可以创建出多个容器。运行容器化环境时,实际上是在容器内部创建该文件系统的读写副本。 这将添加一个容器层,该层允许修改镜像的整个副本

生命周期

通过docker create命令生成的容器状态为初建状态,初建状态通过docker start命令可以转化为运行状态,运行状态的容器可以通过docker stop命令转化为停止状态,处于停止状态的容器可以通过docker start转化为运行状态,运行状态的容器也可以通过docker pause命令转化为暂停状态,处于暂停状态的容器可以通过docker unpause转化为运行状态 。处于初建状态、运行状态、停止状态、暂停状态的容器都可以直接删除

image.png

常用命令

  • 查看运行中容器信息

      docker ps
    复制代码
  • 查看包括停止状态的容器信息

      docker ps -a
    复制代码
  • 进入容器(查看工作区)

      docker exec -it CONTAINER sh
    复制代码
  • 查看控制台信息

      docker logs CONTAINER
    复制代码
  • 导入导出容器

    主要用于容器的迁移,容器里面所有文件都会被迁移

    /** 导出容器 **/
    // 当前文件夹下生成newImage.zip文件,我们可以将该文件拷贝到其他机器上,通过导入命令实现容器的迁移
    docker exprot CONTAINER > newImage.zip
    /** 导入容器 **/
    // newImage.zip 被导入成为新的镜像,镜像名称为 newImage
    docker import newImage.zip newImage
    复制代码

仓库

Docker 的镜像仓库类似于代码仓库,用于存储和分发 Docker 镜像

image.png

公有仓库

Docker Hub

私有仓库

Docker 官方提供了开源的镜像仓库 Distribution,并且镜像存放在 Docker Hub 的 Registry 仓库下供我们下载

常用命令

  • 登录
 //默认会请求 Docker Hub,如果你想登录第三方镜像仓库或者自建的镜像仓库,在docker login后面加上注册服务器即可
        
docker login registry.cn-beijing.aliyuncs.com
复制代码
  • 推送镜像
// Registry 为注册服务器,Docker默认会从docker.io拉取镜像,如果为私有仓库,可以替换为自己的注册服务器
// Repository 为镜像仓库,通常把一组相关联的镜像归为一个镜像仓库,library为 Docker 默认的镜像仓库。
// Image 为镜像名称。
// Tag 为镜像的标签,如果你不指定拉取镜像的标签,默认为latest。
docker push [Registry]/[Repository]/[Image]:[Tag]
复制代码
  • 拉取镜像
docker pull [Registry]/[Repository]/[Image]:[Tag]
复制代码

底层实现原理及关键技术

资源隔离

Docker 是使用 Linux 的 Namespace 技术实现各种资源隔离的。

  • Mount Namespace:实现在不同的进程中看到不同的挂载目录,使用 Mount Namespace 可以实现容器内只能看到自己的挂载信息,在容器内的挂载操作不会影响主机的挂载目录。

  • PID Namespace:用来隔离进程,在不同的 PID Namespace 中,进程可以拥有相同的 PID 号,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。例如一个进程在主机上 PID 为 122,使用 PID Namespace 可以实现该进程在容器内看到的 PID 为 1。

  • UTS Namespace:主要是用来隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。例如我们的主机名称为 docker,使用 UTS Namespace 可以实现在容器内的主机名称为 lagoudocker 或者其他任意自定义主机名。

  • IPC Namespace:主要是用来隔离进程间通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信

  • User Namespace:主要是用来隔离用户和用户组的。一个比较典型的应用场景就是在主机上以非 root 用户运行的进程可以在一个单独的 User Namespace 中映射成 root 用户。使用 User Namespace 可以实现进程在容器内拥有 root 权限,而在主机上却只是普通用户。

  • Net Namespace:是用来隔离网络设备、IP 地址和端口等信息的。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。例如主机 IP 地址为 172.16.4.1 ,容器内可以设置独立的 IP 地址为 192.168.1.1

当 Docker 新建一个容器时, 它会创建这六种 Namespace,然后将容器中的进程加入这些 Namespace 之中,使得 Docker 容器中的进程只能看到当前 Namespace 中的系统资源。

资源限制

通过 Cgroups 机制实现资源限制

  • 资源限制: 限制资源的使用量,例如我们可以通过限制某个业务的内存上限,从而保护主机其他业务的安全运行。

  • 优先级控制:不同的组可以有不同的资源( CPU 、磁盘 IO 等)使用优先级。

  • 审计:计算控制组的资源使用情况

  • 控制:控制进程的挂起或恢复。

      docker run -it -m=1g nginx #启动了一个 nginx 容器,并且限制内存为 1G
    复制代码

注意: cgroups 虽然可以实现资源的限制,但是不能保证资源的使用。例如,cgroups 限制某个容器最多使用 1 核 CPU,但不保证总是能使用到 1 核 CPU,当 CPU 资源发生竞争时,可能会导致实际使用的 CPU 资源产生竞争。

网络模型

为了更好地构建容器网络标准,Docker 团队把网络功能从 Docker 中剥离出来,成为独立的项目 libnetwork,它通过插件的形式为 Docker 提供网络功能。

  • null 空网络模式:可以帮助我们构建一个没有网络接入的容器环境,以保障数据安全。

     docker run --net=none -it busybox
    复制代码
  • bridge 桥接模式:启动容器时默认的网络模式,可以实现容器与容器的互通,可以从一个容器直接通过容器 IP 访问到另外一个容器。可以实现主机与容器的互通,我们在容器内启动的业务,可以从主机直接请求

  • host 主机网络模式:可以让容器内的进程共享主机网络,从而监听或修改主机网络。

    // 不会创建Net Namespace,而是和宿主共享。Docker不建议使用这种
    docker run -it --net=host busybox;
    复制代码
  • container 网络模式:可以将两个容器放在同一个网络命名空间内,让两个业务通过 localhost 即可实现访问。

      docker run -d --name=busybox1 busybox sleep 3600
      docker run -it --net=container:busybox1 --name=busybox2 busybox sh
    复制代码

数据存储

Docker 的卷,为我们的容器插上磁盘,实现容器数据的持久化

镜像是由多层文件系统组成的,当我们想要启动一个容器时,Docker 会在镜像上层创建一个可读写层,容器中的文件都工作在这个读写层中,当容器删除时,与容器相关的工作文件将全部丢失。为了解决有状态业务的需求,Docker 提出了卷(Volume)的概念。卷的本质是文件或者目录,它可以绕过默认的联合文件系统,直接以文件或目录的形式存在于宿主机上。卷的概念不仅解决了数据持久化的问题,还解决了容器间共享数据的问题。使用卷可以将容器内的目录或文件持久化,当容器重启后保证数据不丢失,例如我们可以使用卷将 MySQL 的目录持久化,实现容器重启数据库数据不丢失。

基本操作

  • 创建数据卷

      // 创建一个名称为myvolume的卷
      docker volume create myvolume
      
    复制代码
    • linux上查看创建的卷,在/var/lib/docker/volumes目录下会看到myvolume目录下还创建了一个 _data目录

    • macos上查看创建的数据卷,由于mac使用一个虚拟机来运行实际的 Docker 进程,先进入虚拟机docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh,进入虚拟机后可以看到在/var/lib/docker/volumes目录下会看到myvolume目录下还创建了一个 _data目录

  • 使用数据卷

      // 使用docker volume创建的卷在容器启动时,添加 --mount 参数指定卷的名称即可使用
      docker run -it --mount source=myvolume,target=/data busybox
    复制代码
  • 删除数据卷

      // 容器的删除并不会自动删除已经创建的数据卷,因此不再使用的数据卷需要我们手动删除
      docker volume rm myvolume
      
    复制代码
  • 容器与容器之间数据共享

    使用volumes-from参数可以在启动新的容器时来挂载已经存在的容器的卷,volumes-from参数后面跟已经启动的容器名称。

    • 启动一个容器

      docker run --mount source=myvolume,target=/tmp/log --name=producer -it busybox
      复制代码
    • 启动另一个容器

      docker run -it --name consumer --volumes-from producer  busybox
      复制代码
    • 我们从producer容器写入的文件内容会自动出现在 consumer 容器中,实现了两个容器间的数据共享,就像主机上的两个进程,一个向主机目录写数据,一个从主机目录读数据,利用主机的目录,实现了容器之间的数据共享

  • 主机与容器之间数据共享

    要实现主机与容器之间数据共享,需要在启动容器的时候添加-v参数

    // 挂载主机的 /tmp 目录到容器中的 /usr/local/data 中
    docker run -it --name=mySqlTest -v /tmp:/usr/local/data -it busybox
    复制代码

    容器启动后,便可以在容器内的 /usr/local/data 访问到主机 /tmp 目录的内容了,并且容器重启后,/data 目录下的数据也不会丢失

实现原理

在主机/虚拟机(mac)的 /var/lib/docker/volumes 目录下,根据卷的名称创建相应的目录,然后在每个卷的目录下创建 _data 目录,在容器启动时如果使用 –mount 参数,Docker 会把主机上的目录直接映射到容器的指定目录下,实现数据持久化

Docker实践

使用Docker Compose解决开发环境的依赖

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,可以使用 YML 文件来配置应用程序需要的所有服务。实现使用一个命令,就可以从 YML 文件配置中创建并启动所有服务

Docker Compose 文件主要分为三部分

  • services(服务):服务定义了容器启动的各项配置,就像我们执行docker run命令时传递的容器启动的参数一样,指定了容器应该如何启动,例如容器的启动参数,容器的镜像和环境变量等

  • networks(网络):网络定义了容器的网络配置,就像我们执行docker network create命令创建网络配置一样

  • volumes(数据卷):数据卷定义了容器的卷配置,就像我们执行docker volume create命令创建数据卷一样。

实例操作

用docker部署一个使用monogoDB数据库的node项目,并使用nginx进行代理

version: '2'

services:
  cms-backend:
    image: 'registry-new.ijunhai.com/nexus/junhai/jh-cms'
    volumes:
      - '/work/data/cms-data/upload-data:/jh-cms/app/public/upload'
      - '/work/data/cms-data/dist-data:/jh-cms/dist'
      - '/work/data/cms-data/template-data:/jh-cms/template'
      - '/work/logs/cms-log:/root/logs'
    environment:
      - EGG_MONGODB_URL=mongodb://cms-db/website
      - ENV_ONLINE=dev_online
    links:
      - cms-lb:cms-backend.ijunhai.com
    depends_on:
      - cms-db
    networks:
      - cms-net
      - cms-sys
  cms-db:
    image: 'mongo:latest'
    volumes:
      - '/work/data/cms-data/mongo-data:/data/db'
    networks:
      - cms-sys
  cms-lb:
    image: 'nginx:latest'
    ports:
      - 80:80
    volumes:
      - '/work/config/cms-config/nginx:/etc/nginx/conf.d'
    networks:
      - cms-net

networks:
  cms-net:
    driver: bridge
  cms-sys:
    driver: bridge
复制代码

Docker Compose 模板文件简析

  • 定义了三个服务名称cms-backend,cms-db,cms-lb,
  • image:各个服务下有对应的镜像
  • volumes:挂载主机的目录到容器中
  • networks:表示服务要使用的网络名称,对应顶级的 networks 中的配置,cms-backend和cms-db共享网络cms-net,cms-backend和cms-lb共享网络cms-sys,实现了容器与容器的互通,可以从cms-backend容器直接通过容器 IP 访问到cms-db,cms-lb,反之亦可
  • depends_on:用于指定服务间的依赖关系,这样可以先启动被依赖的服务。cms-backend服务依赖数据库服务cms-db
  • environment:指定容器启动时的环境变量
  • ports:暴露端口信息,使用格式为 HOST:CONTAINER,前面填写要映射到主机上的端口,后面填写对应的容器内的端口

Docker Compose 操作命令

  build              构建服务

  config             校验和查看 Compose 文件

  create             创建服务

  down               停止服务,并且删除相关资源

  events             实时监控容器的时间信息

  exec               在一个运行的容器中运行指定命令

  help               获取帮助

  images             列出镜像

  kill               杀死容器

  logs               查看容器输出

  pause              暂停容器

  port               打印容器端口所映射出的公共端口

  ps                 列出项目中的容器列表

  pull               拉取服务中的所有镜像

  push               推送服务中的所有镜像

  restart            重启服务

  rm                 删除项目中已经停止的容器

  run                在指定服务上运行一个命令

  scale              设置服务运行的容器个数

  start              启动服务

  stop               停止服务

  top                限制服务中正在运行中的进程信息

  unpause            恢复暂停的容器

  up                 创建并且启动服务

  version            打印版本信息并退出
复制代码

总结

  • Docker的出现可以帮助我们快速的部署一个项目,也免去了我们在每一台机器中配置不同的环境,如node环境,php环境,java环境,mongodb环境等等。特别是服务依赖关系复杂的开发和测试环境,可以使用Docker Compose轻松搞定复杂的开发环境,只需要把复杂的开发环境使用 Docker Compose 模板文件描述出来,之后无论你在哪里可以轻松的一键启动开发和测试环境,极大地提高了我们的开发效率,同时也避免了污染我们开发机器的配置。

  • Docker 的出现也解决了 CI/CD 流程中的各种问题,工作中也可使用Docker+Jenkins+GitLab搭建 CI/CD 系统,实现CI 持续集成和CD 持续部署。将代码托管在了 GitLab 中。然后通过配置 GitLab 和 Jenkins 的相互调用,实现了推送代码到 GitLab 代码仓库自动触发构建镜像并将镜像推送到远程镜像仓库中,最后将最新版本镜像发布到远程服务器上。

参考:郭少《由浅入深吃透 Docker》

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享