Docker

92 阅读17分钟

容器和虚拟机的区别

  1. 轻量级:容器比虚拟机更轻量级,因为它们不需要模拟完整的操作系统。容器共享主机操作系统的内核,只需要运行应用程序及其相关的库和依赖项。这使得容器的启动速度更快,资源占用更少。
  2. 快速部署:由于容器的轻量级特性,它们可以快速部署和启动。容器可以在几秒钟内启动,而虚拟机可能需要几分钟甚至更长时间。
  3. 可移植性:容器具有高度的可移植性,因为它们将应用程序及其依赖项打包在一个独立的容器镜像中。这个镜像可以在不同的主机上运行,无需重新安装应用程序或其依赖项。相比之下,虚拟机需要在每个主机上安装操作系统和应用程序。
  4. 资源隔离:虽然容器比虚拟机更轻量级,但它们提供的资源隔离程度较低。容器共享主机操作系统的内核,因此它们可能会受到其他容器或主机系统的影响。相比之下,虚拟机提供了更高级别的资源隔离,因为它们模拟了一个完整的操作系统。
  5. 应用程序兼容性:由于容器共享主机操作系统的内核,它们通常与应用程序的兼容性更好。应用程序可以直接在容器中运行,无需进行任何修改。相比之下,虚拟机需要模拟一个完整的操作系统,这可能会导致应用程序的兼容性问题。

镜像

Docker 镜像 (lmage) 就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。它也相当于是一个 root 文件系统。比如官方镜像 centos 7 就包含了完整的一套 centos 7 最小系统的 root 文件系统。相当于容器的源代码,docker 镜像文件类似于 Java 的类模板,而 docker 容器实例类似于 Java 中 new 出来的实例对象。

容器

  1. 从面向对象角度

    Docker 利用容器 (Container) 独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java是镜像运行时的实体和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体,容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

  2. 从镜像容器角度

    可以把容器看做是一个简易版的 Linux 环境(包舌root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

仓库

集中存放镜像的地方,仓库分为公开仓库 (Public) 和私有仓库 (Private) 两种形式。最大的公开仓库是 Docker Hub (hub.docker.com/) 存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等

基本运行流程

  1. 用户是使用 Docker Client与 Docker Daemon 建立通信,并发送请求给后者;
  2. Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Cient 的请求;
  3. Docker Engine(引擎) 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在;
  4. Job 的运行过程中,当需要容器镜像时,则从 Docker Registy 中下载像,并通过镜像管理驱动将下载镜像以 Graph 的形式存储;
  5. 当需要为 Docker 创建网络环境时,通过网络管理驱动创建并配置 Docker 容器网络环境;
  6. 当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Exec Driver 来完成;
  7. Libcontainer 是一项独立的容器管理包,Network Driver 以及 Exec Diver 都是通过 Libcontainer 来实现具体对容器进行的操作。

安装 Docker

安装 GCC

yum -y install gcc

安装 GCC-C++

yum -y install gcc-c++

安装 yum-utils

yum -y install yum-utils

设定本地仓库

yum -config -manager -- add -repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker- ce.repo

更新 yum 软件包索引

yum makecache fast

安装 Docker CE

yum -y install docker-ce docker-ce-cli containerd.io

启动 Docker

systemctl start docker

查看 Docker 进程

ps -ef | grep docker

阿里云镜像加速配置

  1. 登录阿里云,找到容器镜像服务
  2. 创建实例
  3. 镜像工具 -- 镜像加速器 -- 复制加速器地址
  4. 根据阿里云文档进行命令操作

常用命令

帮助启动类命令

  • 启动:systemctl start docker
  • 停止:systemctl stop docker
  • 重启:systemctl restart docker
  • 查看状态:systemctl status docker
  • 开机自启:systemctl enable docker

镜像命令

  • 列出主机上的镜像:docker images
  • 查找仓库镜像:docker search 镜像名称
  • 下载镜像:docker pull 镜像名称:TAG
  • 查看镜像/容器/数据所占的空间:docker system df
  • 删除镜像::docker rmi 镜像名字或镜像ID

容器命令

  • 新建 + 启动容器
docker run [OPTIONS]

OPTIONS:
--name:为容器指定一个新名称
-d:后台运行容器并返回容器 ID,即启动守护式容器(后台运行)
-i:以交互式运行容器,通常与 -t 同时使用
-t:为容器分配一个伪终端,通常与 -i 同时使用
-P:随机端口映射
-p:指定端口映射

举例:
创建CentOS容器,并进入终端:docker run -it centos /bin/bash
80 端口映射到容器的 8080 端口:docker run -p 80:8080 nginx

PS:Docker 容器后台运行,就必须有一个前台进程,容器运行的命令如果不是那些一直挂起的命令(比如运行 top,tail) ,就是会自动退出的。

  • 列出当前所有正在运行的容器:docker ps
  • 退出容器:1、exit 退出,容器停止;2、ctrl + p + q 退出,容器不停止;
  • 启动已经停止运行的容器:docker start 容器名字或容器 ID
  • 重启容器:docker restart 容器名字或容器 ID
  • 停止容器:docker stop 容器名字或容器 IDD
  • 强制停止容器:docker kill 容器名字或容器 ID
  • 删除已停止的容器:docker rm 容器 ID
  • 查看容器日志:docker logs 容器 ID
  • 查看容器内运行的进程:docker top 容器 ID docker ps 容器 ID
  • 查看容器内部细节:docker inspect 容器 ID
  • 进入正在运行的容器并以命令行交互:
    • docker exec -it 容器 ID /bin/bash
    • docker attach 容器 ID
    • exec 和 attach区别
      • attach 直接进入容器启动命令的终端,不会启动新的进程,用 exit 退出,会导致容器的停止;
      • exec 是在容器中打开新的终端,并且可以启动新的进程,用 exit 退出,不会导致容器的停止
  • 容器内拷贝文件到主机上:docker cp 容器 ID:容器内路径 目的主机路径
  • 容器导出到主机;docker export 容器 ID > 文件名.tar
  • 主机容器导入;cat 文件名.tar | docker import -镜像用户/镜像名:镜像版本号

Docker 镜像

什么是镜像

是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,只有通过镜像文件才能生成 Docker 容器实例。

UnionFS 联合文件系统

是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来。

Docker 镜像分层

Docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。Docker 镜像层都是只读的,容器层是可写的。

Docker 中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似 Java 继承于一个 Base 基础类,自己再按需扩展。

新镜像是从 Base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

Docker Commit

docker commit提交容器副本使之成为一个新的镜像,比如说从 Docker Pull 下来的原始的默认 Ubuntu 镜像是不带着 vim 命令的,可以安装 vim 命令后,生成新的镜像。

docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:版本号

Docker 私有仓库

  1. 拉取私服镜像:docker pull registry
  2. 运行私服镜像:docker run -d -p 5000:5000 -v /user/myregistry/:/tmp/registry -- privileged=true registry,默认情况,仓库被创建在容器的/var/ib/registry目录下,建议自行用容器卷映射,方便于宿主机联调
  3. curl验证私服库上有什么镜像:curl -XGET http://ip:5000/v2/_catalog
  4. 将新镜像修改成符合私服规范的 tag:docker tag 镜像名:tag ip:5000/镜像名:tag
  5. 修改配置文件使之支持 http:
vim /etc/docker/daemon.json

在 json 串里追加:
"insecure- registries" : ["ip:5000""]
注意:地址和端口改成自己的,如果改完不生效,重启 Docker 即可
  1. 推送到私服:docker push ip:5000/镜像名:版本号
  2. pull 到本地并运行:docker pull ip:5000/镜像名:版本号

Docker 挂载目录

如果出现 cannot open directory.:Permission denied,解决办法: 在挂载目录后多加一个 --privileged=true 参数即可

Docker 容器数据卷

卷就是目录或文件,存在于一个或多个容器中,由 Docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System 提供一些用于持续存储或共享数据的特性,卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷,一句话:有点类似 Redis 里面的 rdb 和 aof 文件,将 docker 容器内的数据保存进宿主机的磁盘中

将运行的环境打包镜像,run 后形成容器实例运行 ,但是需要对数据的要求希望是持久化的,docker 容器产生的数据,如果不备份,那么当容器实例删除后,突器内的数据自然也就没有了

命令

  • 运行一个带有容器卷存储功能的容器实例:docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:XX 镜像名,XX 为读或者读写,参数为ro(只读)或rw(读写),默认为rw
  • 查看数据卷是否挂载成功:docker inspect 容器ID,查看 Mounts 节点信息
  • 容器 2 继承容器 1 的卷规则:docker run -it --privileged=true --volumes-from 父类容器名称 --name u2 ubuntu

Docker File

Docker File 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。

构建三步骤

  1. 编写 Docker File 文件
  2. docker build 命令构建镜像
  3. dicker run 镜像运行容器实例

Docker 执行 Docker File 的大致流程

  1. docker 从基础镜像运行一个容器
  2. 执行一条指令并对容器做出修改
  3. 执行类似 docker commit 的操作提交一个新的镜像层
  4. docker 再基于刚提交的镜像运行一个新容器
  5. 执行 docker file 中的下一条指令直到所有指令都执行完成

docker file 面向开发,docker 镜像成为交付标准,docker 容器则涉及部署与运维,三者缺一不可,合力充当 docker 体系的基石。

Docker File 关键保留字

  • FROM:基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是 FROM
  • MAINTAINER:镜像维护者的姓名和邮箱地址
  • RUN:容器构建时需要运行的命令,RUN 是在 docker build时运行,两种格式:shell 和 exec
  • EXPOSE:当前容器对外暴露出的端口
  • WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
  • USER:指定该镜像以什么样的用户去执行,如果都不指定,默认是 root
  • ENV:用来在构建镜像过程中设置环境变量
  • ADD:将宿主机目录下的文件拷贝进镜像且会自动处理 URL 和解压 tar 压缩包
  • COPY:类似 ADD,拷贝文件和目录到镜像中
  • VOLUME:容器数据卷,用于数据保存和持久化工作
  • CMD:指定容器启动后的要干的事情,Docker file 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换,CMD 是在docker run 时运行。
  • ENTRYPOINT:用来指定一个容器启动时要运行的命令,类似于 CMD 指令,但是 ENTRYPOINT 不会被 docker run 后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序,在执行docker run的时候可以指 ENTRYPOINT 运行所需的参数,如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

虚悬镜像

仓库名、标签都是 none 的镜像,虚悬镜像已经失去存在价值,可以删除

删除虚悬镜像命令:docker image prune

Docker 网络

docker 启动后,会产生一个名为docker0的虚拟网桥

Docker 网络作用

  1. 容器间的互联和通信以及端口映射
  2. 容器IP变动时候可以通过服务名直接网络通信而不受到影响

Docker 网络模式

  • bridge 模式:使用 --network bridge 指定,默认使用 docker0,为每一个容器分配、设置 IP 等,并将容器连接到一个 docker0,虚拟网桥,默认为该模式;
  • host 模式:使用 --network host 指定,,容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口;
  • none 模式:使用 --network none 指定容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP 等;
  • container 模式:使用 --network container:NAME 或者容器 ID 指定,新创建的容器不会创建自己的网卡和配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等;

常用命令

  • 查看网络:docker network ls
  • 查看网络源数据:docker network inspect XX
  • 删除网络:docker network rm XX
  • 创建网络:docker network create XX

Docker 容器编排

Compose

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。需要定义一个 YAML 格式的配置文件 docker-compose.yml,写好多个容器之间的调用关系。然后只要一个命令,就能同时启动/关闭这些容器

Compose 允许用户通过一个单独的 docker-compose.yml 模板文件来定义一组相关联的应用容器为一个项目。

可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。

安装步骤


// 第一步:下载
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

// 第二步:授权
chmod +x /usr/local/bin/docker-compose

// 第三步:通过查看版本验证是否安装成功
docker-compose --version

使用步骤

  1. 编写 Dockerfile 定义各个微服务应用并构建出对应的镜像文件
  2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
  3. 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线

Docker Portainer

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理 Docker 环境,包括单机环境和集群环境。

安装步骤


docker run 
-d 
-p 8000:8000 
-p 9000:9000 
--name portainer     
--restart=always     
-v /var/run/docker.sock:/var/run/docker.sock     
-v portainer_data:/data     
portainer/portainer

// 第一次登录需创建 admin,访问地址:xxx.xxx.xxx.xxx:9000

Docker 容器监控

Docker 的容器监控就是 CAdvisor 监控收集 + InfluxDB 存储数据 + Granfana 展示图表

安装步骤

  1. 新建目录:mkdir /mydocker/cig

  2. 新建 docker-compose.yml


version: '3.1'
 
volumes:
  grafana_data: {}
 
services:
 influxdb:
  image: tutum/influxdb:0.9
  restart: always
  environment:
    - PRE_CREATE_DB=cadvisor
  ports:
    - "8083:8083"
    - "8086:8086"
  volumes:
    - ./data/influxdb:/data
 
 cadvisor:
  image: google/cadvisor
  links:
    - influxdb:influxsrv
  command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
  restart: always
  ports:
    - "8080:8080"
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro
 
 grafana:
  user: "104"
  image: grafana/grafana
  user: "104"
  restart: always
  links:
    - influxdb:influxsrv
  ports:
    - "3000:3000"
  volumes:
    - grafana_data:/var/lib/grafana
  environment:
    - HTTP_USER=admin
    - HTTP_PASS=admin
    - INFLUXDB_HOST=influxsrv
    - INFLUXDB_PORT=8086
    - INFLUXDB_NAME=cadvisor
    - INFLUXDB_USER=root
    - INFLUXDB_PASS=root

  1. 启动 docker-compose 文件:docker-compose up
  2. 查看三件套是否启动成功:docker ps
  3. 浏览 cAdvisor 收集服务,http://ip:8080/
  4. 浏览 influxdb 存储服务,http://ip:8083/
  5. 浏览 grafana 展现服务,http://ip:3000 ,默认帐户密码(admin/admin)

通过 Docker 部署项目

通过 Dockerfile 部署到容器

  1. 项目打包成 jar
  2. 编写 Dockerfile

# 基础镜像使用java
FROM java:8

# 作者
MAINTAINER zzyy

# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp

# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar

# 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]

#暴露6001端口作为微服务
EXPOSE 6001

  1. 将微服务 jar 包和 Dockerfile 文件上传到同一个目录下/mydocker
  2. 构建镜像:docker build -t zzyy_docker:1.6 .
  3. 运行容器:docker run -d -p 6001:6001 zzyy_docker:1.6

通过 Docker-Compose 部署到容器

  1. 编写 docker-compose.yml 文件

version: "3"
 
services:
  microService:
    image: zzyy_docker:1.6
    container_name: ms01
    ports:
      - "6001:6001"
    volumes:
      - /app/microService:/data
    networks: 
      - atguigu_net 
    depends_on: 
      - redis
      - mysql
 
  redis:
    image: redis:6.0.8
    ports:
      - "6379:6379"
    volumes:
      - /app/redis/redis.conf:/etc/redis/redis.conf
      - /app/redis/data:/data
    networks: 
      - atguigu_net
    command: redis-server /etc/redis/redis.conf
 
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: '123456'
      MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
      MYSQL_DATABASE: 'db2021'
      MYSQL_USER: 'zzyy'
      MYSQL_PASSWORD: 'zzyy123'
    ports:
       - "3306:3306"
    volumes:
       - /app/mysql/db:/var/lib/mysql
       - /app/mysql/conf/my.cnf:/etc/my.cnf
       - /app/mysql/init:/docker-entrypoint-initdb.d
    networks:
      - atguigu_net
    command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
 
networks: 
   atguigu_net: 
 
  1. 修改jar 里的 yml配置文件,把数据库等连接地址改为通过服务名访问
  2. mvn package 命令将微服务形成新的 jar 包,并上传到服务器 /mydocker 目录下
  3. 编写 Dockerfile

# 基础镜像使用java
FROM java:8

# 作者
MAINTAINER zzyy

# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp

# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar

# 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]

#暴露6001端口作为微服务
EXPOSE 6001
 
  1. 构建镜像:docker build -t zzyy_docker:1.6 .
  2. 执行命令:docker-compose up -d