Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroupnamespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为 容器 。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runCcontainerd

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

Docker与传统虚拟机技术的不同之处:

传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便

Docker - 图1

Docker - 图2

Docker的优势

  1. 更高效的利用系统资源
    由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
  2. 更快速的启动时间
    传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
  3. 一致的运行环境
    开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现
    「这段代码在我机器上没问题啊」** 这类问题。
    一次构建,到处运行
  4. 持续交付和部署
    对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
    使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
    而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
  5. 更轻松的迁移
    由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
  6. 更轻松的维护和扩展
    Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 | 特性 | 容器 | 虚拟机 | | —- | —- | —- | | 启动 | 秒级 | 分钟级 | | 硬盘使用 | 一般为 MB | 一般为 GB | | 性能 | 接近原生 | 弱于 | | 系统支持量 | 单机支持上千个容器 | 一般几十个 |

容器化的好处 ( 进程间隔离 ):

Docker - 图3

Docker 安装

服务器版

平台 x86_64 / amd64 ARM ARM64 / AARCH64 IBM Power (ppc64le) IBM Z (s390x)
CentOS
Debian
Fedora
Ubuntu

  • 卸载旧版本
    apt-get remove docker docker-engine docker.io containerd
  • 使用 apt 进行安装
    apt install docker.io
  • 查看是否安装成功 docker version
    Docker - 图4

如果在这里报错找不到 apt 源的话
执行

  1. vi /etc/apt/sources.list

然后删除自带源,添加国内源

  1. deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  2. deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  3. deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  4. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  5. deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  6. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  7. deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
  8. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multivers
  9. deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
  10. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse

然后

  1. apt update

最后下载即可

  1. apt install docker.io

Docker 镜像加速器

阿里云加速

https://cr.console.aliyun.com/cn-shanghai/instances/mirrors

  • 进入阿里云控制台,搜索 容器镜像服务
    Docker - 图5
  • 找到专属加速服务
    Docker - 图6
  • 通过修改 daemon 配置文件 /etc/docker/daemon.json 来使用加速器
    全部把xxxxxxx替换成上面那张图涂掉的那部分,然后全部 copy 然后回车

    1. tee /etc/docker/daemon.json <<-'EOF'
    2. {
    3. "registry-mirrors": ["https://xxxxxxx.mirror.aliyuncs.com"]
    4. }
    5. EOF
  • 重启 docker :systemctl daemon-reload systemctl restart docker

  • 验证:docker info
    Docker - 图7

体验第一个容器

基于虚拟的环境下如果想要使用 tomcat,则需要先装 jdk ,然后再装 tomcat;而基于容器化环境,则可以直接安装 tomcat 进行使用

通过下载 tomcat9.0.x 镜像的webapps目录为空。。因此可能会遇到以下巨坑,这里有两种解决方案 ( 推荐第二种 )

方案1:https://blog.csdn.net/yl405001832/article/details/104351039

方案2 https://www.cnblogs.com/canglongdao/p/12162269.html

Docker 架构

Docker 引擎

Docker 引擎是一个包含以下主要组件的客户端服务器应用程序

  • 一种服务器,它是一种称为守护进程并且长时间运行的程序
  • REST API 用于指定程序可以用来与守护进程通信的接口,并指示它做什么
  • 一个有命令行界面 (CLI) 工具的客户端

Docker - 图8

Docker架构

  • Docker 使用客户端 - 服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器
  • Docker 容器通过 Docker 镜像来创建
  • 容器与镜像的关系类似于面向对象编程中的对象与类,容器是镜像的示例 | Docker | 面向对象 | | —- | —- | | 容器 | 对象 | | 镜像 | 类 |

Docker - 图9

docker run 若在本地没有找到镜像,则会去仓库中找并下载该镜像,然后基于该镜像再创建一个容器

标题 说明
镜像(Images) Docker 镜像是用于创建 Docker 容器的模板。
容器(Container) 容器是独立运行的一个或一组应用。
客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api
) 与 Docker 的守护进程通信。
主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
仓库(Registry) Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com
) 提供了庞大的镜像集合供使用。
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

Docker 操作镜像

获取镜像

  1. docker pull [选项] [Docker Registry 地址[:端口号]]/仓库名[:标签]

EG:docker pull ubuntu:18.04

  1. 这个命令等同于:docker pull registry.hub.docker.com/ubuntu:12.04
  2. 相当于从默认注册服务器 registry.hub.docker.com 中的 ubuntu 仓库上下载了标签为 12.04 ubuntu 镜像

从官方下载比较慢时,可以通过 指定注册服务器 来从其他仓库进行下载

EG:docker pull dl.dockerpool.com:5000/ubuntu:18.04

列出镜像

  1. docker images
  1. 显示本地已有镜像

Docker - 图10

列信息

  • 来自哪个仓库
  • 镜像标签
  • 唯一 ID 号
  • 创建时间
  • 镜像大小

其中镜像的 ID 唯一标识了镜像, 具有相同的镜像 ID 的两个镜像,实际上是同一镜像。

TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如 10.0412.0412.1013.0414.04 等。例如下面的命令指定使用镜像 ubuntu:14.04 来启动一个容器。

可以看到,上面有个 TAG 为 none 的镜像,这在镜像又被称为 虚悬镜像

  1. 显示镜像体积
    Docker - 图11

    1. docker system df
  2. 中间层镜像
    为了加速镜像构建,重复利用资源,Docker 会利用 中间层镜像 ,所以在使用一段时间后,可能会看到一些依赖的中间层镜像

    1. docker image ls -a

删除镜像

移除本地镜像使用

  1. docker rmi 镜像名:[标签] / 镜像ID

docker rm 是删除容器的

注意:在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器

清除所有虚悬镜像

  1. docker image prune

commit

  1. docker commit -m="提交描述信息" -a="作者" 容器id 生成镜像名[:tag]

Docker 操作容器

显示容器

显示运行中的容器

  1. docker ps

Docker - 图12

显示所有容器,包括运行的和没运行的

  1. docker ps -a

启动容器

docker 本身形成了一个内网,因此 docker 需要网卡

docker的网卡

Docker - 图13

同时,每个容器本身又相当于一台内网中的服务器,因此也需要网卡

容器的网卡

Docker - 图14

1.新建并启动

  1. docker run

docker本身形成了一个内网,因此想要访问 docker 内的容器,就需要指定容器端口

EG:docker run -p 8088:8080 -d tomcat

  1. `参数 -p` 表示进行端口映射,宿主机端口 : 容器端口,用户访问宿主机的端口时,会**路由到指定的容器端口**,从而进行访问。不使用该参数则会进行默认映射 ( tomcat会默认进行8080的映射 )
  2. `参数 -d` 表示后台运行该容器 ( daemon 守护态运行)

当时用 docker run 来创建容器时,Docker 在后台运行的标准操作包括

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

2.启动已停止容器

  1. docker start 已停止运行的容器的ID

停止容器

  1. docker stop 容器ID

删除容器

普通删除

  1. docker rm 容器ID / 容器名

无法删除正在运行的容器

强制删除 (先停掉再删除)

  1. docker rm -f 容器ID / 容器名

交互式操作容器

就跟操作 Linux 中的文件一样操作容器里面的文件 ( 使用 bash 操作容器 )

  1. docker exec -it 容器ID/容器名 /bin/bash

Docker - 图15

退出容器

  1. ctrl + d

查看容器日志

  1. docker logs -f 容器名

清除所有处于终止状态的容器

查看所有已经创建的,包括终止状态的容器

  1. docker container ls -a

清除所有处于终止状态的容器

  1. docker container prune

Docker 查看日志

  1. docker logs [OPTIONS] container_name
  2. Options:
  3. --details 显示更多的信息
  4. -f, --follow 跟踪实时日志
  5. --since string 显示自某个timestamp之后的日志,或相对时间,如42m(即42分钟)
  6. --tail string 从日志末尾显示多少行日志, 默认是all
  7. -t, --timestamps 显示时间戳
  8. --until string 显示自某个timestamp之前的日志,或相对时间,如42m(即42分钟)

DockerFile 定制镜像

DockFile 是一个文本文件,其中包含了一条一条的指令,每一条指令构建一层,因此每一条指令的内容就是描述该层该如何构建

  1. 创建DockFile

名字必须为Dockfile)

  1. vim Dockerfile

先来看一个很简单的 DockFile 配置文件

  1. FROM tomcat
  2. COPY index.jsp /usr/local/tomcat/webapps/ROOT

这段代码的意思就是,继承 tomcat 镜像,然后将当前目录下的 index.jsp 文件复制到镜像的指定目录下

  1. 构建新镜像
  1. docker build -t myTomcat .

这样就构建出了一个叫 myTomcat 的新镜像,具有 tomcat 的所有功能 ( 继承 ) ,但 /usr/local/tomcat/webapps/ROOT 中的 index.jsp 文件使我们指定的 copy 的 index.jsp 文件

Docker - 图16

  1. 查看镜像
    Docker - 图17

镜像构建上下文

在第二步构建新镜像中,使用到了一个 .,这个 . 有两个作用

  • 在当前目录找到 Dockerfile 配置文件
  • 指定 Dockerfile 的上下文目录打包并传递 —-> Docker Server,真正的打包过程是在 Docker Server 中进行的
    因此对文件而言,应该指定的不是其在宿主机中的路径,而是在上下文中的路径 ( 指定 COPY 的文件应该和 Dockerfile 同级或同级目录下,即COPY的是相对路径 )

当构建时,用户指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将这个路径下 并且被 Dockerfile 使用的文件进行打包,然后上传给 Docker Server, 这样 Docker Server 在收到这个上下文包后,就会对相应内容进行打包并制作成镜像

Dockrfile 指令

RUN

运行 shell 指令

COPY

  • 格式:
    • shell格式:COPY <源路径> <目标路径>
    • exec格式:COPY ["<源路径>" "<目标路径>"]

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

  1. COPY package.json /usr/src/app

<源路径> 可以是多个,甚至可以是通配符,其通配符规则满足 go 的 filepath.Math 规则

  1. COPY hom* /mydir/
  2. COPY hom?.txt /mydir/

<目标路径>是容器内的绝对路径,也可以是相对于工作目录的相对路径 ( 工作目录可以通过 WORKDIR 指令来指定 ),目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建确实目录

COPY 指令,会将源文件的各种元数据进行保留,比如读,写,执行权限,创建时间等

ADD

  • 和 COPY 的格式和性质基本一样,但是在 COPY 的基础上增加了一些功能
    • ADD
      • ADD 的 <源路径> 可以是一个 URL 这种情况下,Docker Server 会试图去下载这个链接的文件放到 <目标路径> 去,下载后的权限自动设置为 600,如果不想要默认的权限,则需要额外加一层 RUN 指令进行调整;
      • 如果下载的是个压缩包,需要解压缩,也是需要一层 RUN 指令进行解压缩,所以不如直接使用 RUN 指令,然后使用工具进行下载,因此 <源路径> 为URL的情况并不常用
      • 但是,如果 <源路径> 是一个 tar 压缩文件,压缩格式为 gzip,bzip2 以及 xz 的情况下, ADD 指令会自动解压缩这个文件到 <目标路径>

CMD

  • CMD 和 ENTRYPOINT 相似,用于执行脚本
    • shell格式:CMD <命令>
    • exec格式:CMD ["可执行文件", "参数1", "参数2"...]

普通的 CMD 指令只能执行一次命令

ENTRYPOINT

  • ENTRYPOINT 的格式和 RUN 格式一样,分为 exec 格式和 shell 格式
  • ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数,ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 —entrypoint 来指定
  • 当指定 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令
  1. <ENTRYPOINT> "<CMD>"

因此 ENTRYPOINT 就解决了普通 CMD 指令只能执行一次的情况

但是~,ENTRYPOINT 也只准用一次

因此,当 CMD 命令比较多时,一般会把 CMD 弄成一个 shell 文件,然后用 ENTRYPOINT 来执行这个 shell 文件

ENV

  • 格式
    • ENV <key> <value>
    • ENV <key>=<value> <key2>=<value2>...

设置环境变量

  1. ENV MYSQL_VERSION 5.7.2

VOLUME

数据卷,后面会说

EXPOSE

  • 格式:
    • EXPOSE <端口1> [<端口2>...]

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

  • 在 Dockerfile 中写入这样的声明有两个好处
    • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
    • 另一个用处则是在运行时使用端口映射时,也就是 docker run -p 时,会自动映射 EXPOSE 的端口

WORKDIR

  • 格式
    • WORKDIR <工作目录路径>

使用 WORKDIR 指令可以指定工作目录 (又称为当前目录),就是指定哪个目录下运行后面的 shell

也是指定容器的 初始目录 (就是进入容器以后在哪)

看一个完整一点的例子

  1. FROM tomcat
  2. #删除ROOT目录下所有东西
  3. RUN rm -fr /usr/local/tomcat/webapps/ROOT/*
  4. #复制到指定目录
  5. COPY myTest.tar.gz /usr/local/tomcat/webapps/ROOT
  6. #指定工作目录
  7. WORKDIR /usr/local/tomcat/webapps/ROOT
  8. #已经指定了工作目录,因此这个解压并删除是在工作目录下进行的
  9. RUN tar -zxvf myTesy.tar.gz \
  10. && rm -rf myTest.tar.gz
  11. #暴露端口
  12. EXPOSE 8080

这个例子也可以写成这种格式

  1. FROM tomcat
  2. WORKDIR /usr/local/tomcat/webapps/ROOT
  3. RUN rm -fr *
  4. COPY myTest.tar.gz .
  5. RUN tar -zxvf myTest.tar.gz \
  6. && rm -rf myTest.tar.gz
  7. EXPOSE 8080

镜像的定制就是对 Dockerfile 进行命令的堆叠

Docker Compose

Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排 (简化 Docker 操作)。从功能上看,跟 OpenStack 中的 Heat 十分类似

  1. [Docker Hub](https://hub.docker.com/orgs?overlay=create)

Compose 定位是 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)」,其前身是开源项目 Fig

Docker Compose安装

Compose 支持 Linux、macOS、Windows 10 三大平台。在 Linux 上的也安装十分简单,从Docker官网下载最新稳定版处直接下载编译好的二进制文件即可

下载太慢可以切换到国内源

  1. curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

下载完后执行提权

  1. sudo chmod +x /usr/local/bin/docker-compose

最后验证

  1. docker-compose version

Dcoker Compose 的使用

前置准备

  • 克隆两台虚拟机 ,一台命名为 deploy,一套命名为 Paas
    1.固定IP 地址
    • 修改其中一台的 IP 地址
      1. vim /etc/netplan/50-cloud-init.yaml
      1. network:
      2. ethernets:
      3. ens33:
      4. addresses: [192.168.224.129/24]
      5. #网关地址为VMware中虚拟网络编辑里面的网关地址
      6. gateway4: 192.168.224.2
      7. nameservers:
      8. #同网关地址
      9. addresses: [192.168.224.2]
      10. version: 2
      生效配置
      1. netplan apply

2.修改主机名

  • 修改 cloud.cfg 防止重启后主机名还原
    ```shell vim /etc/cloud/cloud.cfg

将下面的值改为true即可

preserve_hostname: true

  1. - 修改主机名
  2. ```shell
  3. #修改主机名
  4. hostnamectl set-hostname deployment
  5. #配置 hosts
  6. cat >> /etc/hosts << EOF
  7. 192.168.224.132 services
  8. EOF

3.修改 DNS

  1. vim /etc/systemd/resolved.conf

取消 DNS 行的注释,配置一个 114.114.114.114 的 DNS 服务器,然后重启

Docker - 图18

开始使用

在 /usr/local 目录下创建 /docker/tomcat/ 目录用于进行测试,并进入 tomcat 目录下

  1. 创建一个 yml 的配置文件

命名为 docker-compose.yml

  1. version: '3.1'
  2. services:
  3. tomcat:
  4. restart: always
  5. image: tomcat
  6. container_name: tomcat
  7. ports:
  8. - 8080:8080

分析一下这个配置文件都干了些什么

tomcat :服务名

image:使用的镜像名称

container_name:命名容器

restart:重新启动策略,填 always 时系统重启时也会重启

no是默认的重启策略,在任何情况下都不会重启容器。 on-failure表示发生错误时进行重启

  1. restart: "no"
  2. restart: always
  3. restart: on-failure
  4. restart: unless-stopped
  1. 运行

守护态运行

  1. docker-compose up -d

如果提示无权限,则执行命令

  1. sudo chmod +x /usr/local/bin/docker-compose
  1. 停止
  1. docker-compose down

注意: yml 配置文件区分制表符和空格,对齐必须使用空格

Docker数据卷

使用 docker-compose 指定数据卷

  1. volumes:
  2. - ./webapps:/usr/local/tomcat/webapps.dist

volumes 即创建数据卷,右边是容器里面的目录,左边是宿主机目录,指定了数据卷的位置为 ./webapps ,即对 docker-compose.yml 而言的当前目录下的webapps中,两个目录是同步的

Docker - 图19

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS (联合文件系统),可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 卷会一直存在,直到没有容器使用

联合文件系统

联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下 (unite several directories into a single virtual filesystem)。

联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

Docker 中使用的 AUFS(AnotherUnionFS)就是一种联合文件系统。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限,同时 AUFS 里有一个类似分层的概念,对只读权限的分支可以逻辑上进行增量地修改 (不影响只读部分的)。

Docker 目前支持的联合文件系统种类包括 AUFS, btrfs, vfs 和 DeviceMapper。

数据卷创建

在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。

下面创建一个 web 容器,并加载一个数据卷到容器的 /webapp 目录。

  1. docker run -d -P --name web -v /webapp training/webapp python app.py

注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。

或者是在 docker-compose 中指定 volumes 属性,左边为宿主机文件夹,右边为容器文件夹

1.挂载一个主机目录作为数据卷

使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。

  1. run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp 目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前不支持。

Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

  1. docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
  2. training/webapp python app.py

加了 :ro 之后,就挂载为只读了。

2.挂载一个本地主机文件作为数据卷

-v 标记也可以从主机挂载单个文件到容器中

  1. sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

这样就可以记录在容器输入过的命令了。

注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed --in-place,可能会造成文件 inode 的改变,从 Docker 1.1 .0 起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。

部署 tomcat

docker-compose.yml

  1. version: '3.1'
  2. services:
  3. tomcat:
  4. restart: always
  5. image: tomcat
  6. container_name: tomcat
  7. ports:
  8. - 8080:8080
  9. volumes:
  10. - ./webapps:/usr/local/tomcat/webapps.dist
  11. environment:
  12. TZ: Asia/Shanghai

然后守护态启动

部署 MySQL

  1. version: '3.1'
  2. services:
  3. db:
  4. # 目前 latest 版本为 MySQL8.x
  5. image: mysql
  6. restart: always
  7. environment:
  8. #初始化密码
  9. MYSQL_ROOT_PASSWORD: 123456
  10. command:
  11. #为制作的MySQL镜像指定参数
  12. --default-authentication-plugin=mysql_native_password
  13. --character-set-server=utf8mb4
  14. --collation-server=utf8mb4_general_ci
  15. --explicit_defaults_for_timestamp=true
  16. --lower_case_table_names=1
  17. ports:
  18. - 3306:3306
  19. volumes:
  20. - ./data:/var/lib/mysql

然后守护态启动

部署 SpringBoot 项目

需要先将 SpringBoot 项目中连接的数据库地址改为 宿主机IP:3306

Docker - 图20

然后成 jar 包

将 jar 包移动到服务器的一个目录下,在相同目录下通过 Dockerfile 制作一个镜像

  1. FROM mysql
  2. #内置jdk
  3. FROM tomcat
  4. # VOLUME 指定了临时文件目录为/tmp。
  5. # 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
  6. VOLUME /tmp
  7. # 将jar包添加到容器中并更名为app.jar
  8. ADD demo-0.0.1-SNAPSHOT.jar app.jar
  9. # 运行jar包
  10. ENTRYPOINT ["java","-jar","app.jar"]
  1. docker build -t blog .

然后制作 docker-compose.yml 方便启动

  1. version: '3.1'
  2. services:
  3. blog:
  4. restart: always
  5. #选择刚才制作的镜像
  6. image: blog
  7. container_name: myblog
  8. #右边是容器内的服务端口,即访问SpringBoot项目的端口;左边是映射到宿主机上,即外部访问的端口
  9. ports:
  10. - 80:8083

然后

  1. docker-compose up -d

启动后访问 宿主机 IP : 80 端口即可

为持续集成与持续交付做准备

这里所有环境的搭建基于上面创建好的 Paas 虚拟机

Docker Compose 部署 GitLab

部署 GitLab

使用 Docker 来安装和运行 GitLab 中文版, docker-compose.yml 配置如下:

  1. version: '3'
  2. services:
  3. gitlab:
  4. image: 'twang2218/gitlab-ce-zh'
  5. restart: always
  6. #宿主机ip地址,或域名
  7. hostname: '192.168.224.131'
  8. environment:
  9. TZ: 'Asia/Shanghai'
  10. GITLAB_OMNIBUS_CONFIG: |
  11. #gitlab进行拉取时的http地址
  12. external_url 'http://192.168.224.131'
  13. gitlab_rails['time_zone'] = 'Asia/Shanghai'
  14. #GitLab项目中使用SSH进行拉取时的端口
  15. gitlab_rails['gitlab_shell_ssh_port'] = 2222
  16. unicorn['port'] = 8888
  17. #使用nginx进行解析
  18. nginx['listen_port'] = 80
  19. ports:
  20. #右边容器端口要与gitlab进行http拉取时的端口号一致,因为是80,所以上面可以不写
  21. - '80:80'
  22. - '443:443'
  23. - '2222:22'
  24. volumes:
  25. - ./config:/etc/gitlab
  26. - ./data:/var/opt/gitlab
  27. - .logs:/var/log/gitlab

然后访问 192.168.224.131 ,会要求初始化密码,初始化密码后使用用户名 root 和密码

Docker - 图21

登录即可

安装 TortoiseGit

https://tortoisegit.org/download/

下载对应版本和对应汉化包

Docker - 图22

先安装第一个,可以一路默认,也可以更改安装位置

然后安装第二个进行汉化

右击任意位置

Docker - 图23

然后设置为中文,应用并重新打开

Docker - 图24

使用 TortoiseGit 提交和克隆 GitLab

  1. 先在 GitLab 中新建一个项目

Docker - 图25

使用这个链接,最好新建一个专门用来推送和克隆的空白文件夹

  1. 右击空白文件夹,选择克隆

Docker - 图26

  1. 克隆下来以后,把自己的项目复制进这个文件夹

Docker - 图27

  1. 然后右键将提交到 GitLab,注意,第一次会让你输入 GitLab 的账号和密码

Docker - 图28

SSH 的方式进行拉取和推送

在协同开发的持续集成和持续交付中,一般不采用输入账户名和密码的方式进行平台的登录,因为会导致平台的同步并阻塞,一旦有一个人没有输入账号和密码,后面登录的人就会一直排队

因此使用 SSH 进行免密登录更为常用

1.生成秘钥

前往安装 Git 的目录下:D:\Git\usr\bin

Docker - 图29

找到这个文件

在该目录下启动 cmd,输入命令

  1. ssh-keygen -t rsa -C "you_email"

Docker - 图30

因为我已经生成过秘钥,所以这里就选了 n,到生成 key 的目录下

特别注意!!!慎重选择在生成秘钥时是否设置密码

  • 如果设置了,则每次对仓库进行操作都需要输入密码进行验证 (目前没有找到持久化的方法)
  • 如果没设置,就不用

Docker - 图31

打开 .pub 复制里面的内容,回到 GitLab

2.配置 GitLab

Docker - 图32

Docker - 图33

将刚才生成的 .pub 公钥的内容粘贴到Docker - 图34

生成 SSH

Docker - 图35

回到项目中,项目变为默认使用 SSH

Docker - 图36

3.测试

删除原来拉取下来的项目,重新使用 SSH 的方式进行拉取

Docker - 图37

可能会有个报错:Disconnected no supported authentication methods available(server sent: publickey)

解决方法:修改乌龟 git 里面的 ssh 客户端为你自己的客户端

Docker - 图38

在拉取的时候可能会要你输入密码短语 ( 取决于生成秘钥时是否设置了密码 )

最后:

Docker - 图39

Docker Compose 部署 Nexus

Nexus 是强大的 Maven 仓库管理器 ( 私服 ),简化了内部仓库的维护和外部仓库的访问。

将内部使用的依赖上传到私服,提高了安全性也提高了协同开发的效率

这样 maven 进行依赖下载时,顺序如下

  1. 本地仓库查找
  2. 若没有,到指定仓库查找
  3. 仍然没有,到官方仓库查找

Docker - 图40

部署 Nexus

制作 docker-compose

  1. version: '3.1'
  2. services:
  3. nexus:
  4. restart: always
  5. image: sonatype/nexus3
  6. container_name: nexus
  7. ports:
  8. - 8081:8081
  9. volumes:
  10. - ./data:/nexus-data

启动发现报错

Docker - 图41

Docker - 图42

解决方案1:这是由于 /data/instances 目录没有权限造成的,因此进入 data 目录,修改 instances 的权限

  1. chmod 777 instances

解决方案2:修改 docker-compose 为

  1. version: '3.1'
  2. services:
  3. nexus:
  4. restart: always
  5. image: sonatype/nexus3
  6. container_name: nexus
  7. ports:
  8. - 8081:8081
  9. volumes:
  10. - nexus-data:/nexus-data
  11. #外部指定数据卷位置
  12. volumes:
  13. nexus-data:

这种方法中的数据卷位置为 /var/lib/你的nexus父目录/volumes下,该目录下的 docker_compose的目录名_nexus-data(即自己的命名) 这个目录就是数据卷

访问一下

Docker - 图43

右上角进行登录,发现如下提示

Docker - 图44

到 data 目录下找到该文件,复制内容,回到 Nexus 进行登录,用户名为 amdin,密码即为刚才的内容。登陆后会要求设置新密码,设置后全部 next 即可

了解 Nexus 的 Maven 仓库

代理仓库 (Proxy Repository)

Docker - 图45

  • 第三方仓库
    • maven-central 通过设置代理来连接到第三方仓库,从第三方下载到私服,这样下次就不用再去第三方仓库找了

Docker - 图46

  • nuget.org-proxy
    • 版本策略
  • Release:正式版
  • Snapshot:快照版 (开发版),在不更改版本号的情况下进行功能变更
  • Mixed:混合模式
    • 布局策略 ( Layout Policy )
  • Strict:严格
  • Permissive:宽松

根据语义化规范,版本号蕴意如下

EG:V2.0.4

2 0 4
改架构的次数 改功能的次数 改bug的次数

任意一位 +1 时,后面的所有位退回为 0

宿主仓库 (Hosted Repository)

Docker - 图47

  • 存储本地上传的组件和资源
    • maven-release
    • maven-snapshots
    • nuget-hosted

仓库组
  • 一个仓库包含多个仓库的内容
    • maven-public

Docker - 图48

在项目中使用 Nexus

上传规则:在 pom 中,若 0.0.1-SNAPSHOT 后面跟 SNAPSHOT 则为快照版,否则为发行版,Nexus 会根据后缀判断应该放到哪个仓库里

在中央仓库设置认证

Docker - 图49

配置节点

打开项目使用的 maven 的 settings 文件,在 中添加

  1. <server>
  2. <id>maven-releases</id>
  3. <username>admin</username>
  4. <password>123456</password>
  5. </server>
  6. <server>
  7. <id>maven-snapshots</id>
  8. <username>admin</username>
  9. <password>123456</password>
  10. </server>

这里的 id 需要等下和 pom 文件中对应,username 和 passwd 即上一步设置的用户名和密码

配置

在 pom 中进行管理配置,id 要和上一步中配置的 中的 id 相同

  1. <distributionManagement>
  2. <repository>
  3. <id>maven-releases</id>
  4. <name>nexus releases repo</name>
  5. <url>http://192.168.224.131:8081/repository/maven-releases/</url>
  6. </repository>
  7. <snapshotRepository>
  8. <id>maven-snapshots</id>
  9. <name>nexus snapshots repo</name>
  10. <url>http://192.168.224.131:8081/repository/maven-snapshots/</url>
  11. </snapshotRepository>
  12. </distributionManagement>

配置代理仓库

在 pom.xml 中配置代理仓库

  1. <!--依赖仓库-->
  2. <repositories>
  3. <repository>
  4. <id>nexus</id>
  5. <name>nexus respository</name>
  6. <url>http://192.168.224.131:8081/repository/maven-public/</url>
  7. <snapshots>
  8. <enabled>true</enabled>
  9. </snapshots>
  10. <releases>
  11. <enabled>true</enabled>
  12. </releases>
  13. </repository>
  14. </repositories>
  15. <!--插件仓库-->
  16. <pluginRepositories>
  17. <pluginRepository>
  18. <id>nexus</id>
  19. <name>nexus plugin</name>
  20. <url>http://192.168.224.131:8081/repository/maven-public/</url>
  21. <snapshots>
  22. <enabled>true</enabled>
  23. </snapshots>
  24. <releases>
  25. <enabled>true</enabled>
  26. </releases>
  27. </pluginRepository>
  28. </pluginRepositories>

验证配置是否成功

找到本地的 maven 仓库,删掉一些项目中所用的依赖,重新打包一下项目

Docker - 图50

回到 nexus 查看中央仓库

Docker - 图51

上传
  1. mvn deploy

deploy 指令包含了 install,install 指令包含了 package。因此会在本地仓库生成一个和 对应的路径,该路径下的会有一个和 同名的路径,然后进入 会有一个和 同名的路径,其中有一个 jar 包

Docker - 图52

Docker - 图53

回到 Nexus,可以看见成功上传

Docker - 图54

因为我的 pom 文件中版本为 snapshot,因此上传到了 snapshots 的仓库

Docker Compose 部署Harbor

PS:Harbor 是一个典型的分布式架构,因此在重启虚拟机后一定要去 harbor 的目录下使用 docker compose 启动没有启动的项目

Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器,通过添加一些企业必须的功能特性,例如安全,标识和管理等,扩展了开源 Docker Distribution。作为企业级的私有 Registy 服务器,Harbor 提供了更好的性能和安全性,提升用户使用 Registy 构建和运行环境时传输镜像的效率。Harbor 支持安装在多个 Registy 节点的镜像资源复制,镜像全部保存在私有 Registry 中,确保数据和知识产权在公司内部网络中管控,Harbor 也提供了用户管理,访问控制,活动审计的高级安全特性

Harbor 特性

  • 基于角色的访问控制 (RBAC):用户和 Docker 镜像仓库通过 “项目” 进行组织管理,一个用户可以对多个镜像仓库在同一命名空间 (project) 里拥有不同权限
  • 镜像赋值:镜像可以再多个 Registy 实例中复制 (同步),适合于负载均衡,高可用,混合云和多云的场景
  • web界面:用户可以通过浏览器来浏览,检索当前 Docker 镜像仓库,管理项目和命名空间
  • AD/LDAP支持:Harbor 可以集成企业内部已有的 AD/LDAP,进行权鉴认证管理
  • 审计管理:所有针对镜像仓库的操作都可以被记录追溯,用于审计管理
  • 国际化
  • RESTful API:提供给管理员对 Harbor 更多的操控,使得与其它管理软件集成更容易
  • 部署简单:提供在线和离线两种安装工具,也可以安装到 vSphere 平台 ( OVA方式 ) 的虚拟设备上

Harbor 组件

  • Proxy:Harbor 的 registry, UI,token 等服务,通过一个前置的反向代理统一接收web,Docker 客户端的请求,并将请求转发给后端不同的服务
  • Registry:负责储存 Docker 镜像,并处理 docker push/pull 命令,由于需要对用户进行访问控制,即不同用户对 Docker 镜像有不同的读写权限,Registy 会指向一个 token 服务,强制用户每次 docker pull/push 请求都要携带一个合法的 token,Registy 会通过公钥对 token 进行解密验证
  • Core Service:Harbor 的核心功能,主要提供一下服务
    • UI:web界面,帮助管理 registry 服务器上的镜像,并对用户进行授权 (前后端分离)
    • WebHook:registry 上配置的 webhook 或把状态变化及时传递给 UI 模块 (类似于观察者模式的监控)
    • Token:负责根据用户权限给每个 docker push/pull 命令签发 token,docker 客户端向 registry 服务发起请求,如果不含 token,则会被重定向到这里,获得token 后重新发起请求 (授权鉴权)
  • Database:为 core services 提供数据库服务,负责存储用户权限,审计日志,docker 镜像分组信息等数据
  • Job Services:提供镜像远程复制功能,可以把本地镜像同步到其它 Harbor 实例中 (同步复制集群)
  • Log Collector:帮助监控 Harbor 运行,负责收集其他组件的 log

Docker - 图55

安装

  1. https://github.com/goharbor/harbor/releases 下载

Docker - 图56

  1. 上传到虚拟机上

Docker - 图57

  1. 解压
  1. tar -zxvf harbor-offline-installer-v1.10.2.tgz
  1. 修改配置文件
  1. vim harbor.yml
  2. #修改为服务器 IP (即Paas的ip)
  3. hostname: reg.mydomain.com

Docker - 图58

如果没有 https 的证书请注释掉这一块,不然安装会报错

  1. 执行安装脚本
  1. ./install.sh
  1. 安装好以后会在当前目录下自动生成一个 docker-compose,启动即可
  2. 访问配置文件中的 hostname : port
    Docker - 图59
  3. 所有有关harbor的配置信息都在harbor.yml下。用户名默认为 admin,密码为 Harbor12345
    Docker - 图60

配置客户端

  1. /etc/docker/daemon.json 中内容如下
  1. {
  2. "registry-mirrors": ["https://9b35jrsu.mirror.aliyuncs.com"],
  3. //harborhostname:port
  4. "insecure-registries": ["192.168.224.131:9999"]
  5. }

Docker - 图61

  1. 重启 docker
  1. systemctl restart docker
  1. 验证
  1. docker info

Docker - 图62

推送镜像

  1. 新建一个项目,进入项目

Docker - 图63

Docker - 图64

  1. 在推送之前需要进行登录,不然无法推送
  1. docker login 192.168.224.131:9999
  2. #或者使用命令
  3. docker login 192.168.224.131:9999 -u admin -p Harbor12345

Docker - 图65

  1. 通过第一步中的推送命令进行推送
  1. #先标记,tag source_image[:tag] /new_image_name[:tag]
  2. docker tag mytest 192.168.224.131:9999/blog/mytest
  3. #再推送
  4. docker push 192.168.224.131:9999/blog/mytest

Docker - 图66

  1. 验证,回到 harbor 的 blog 项目界面

Docker - 图67

  1. 测试镜像拉取

Docker - 图68

复制命令

  1. docker pull 192.168.224.131:9999/blog/nginx:latest

然后先把原来的镜像删了

Docker - 图69

然后再进行拉取

Docker - 图70

Docker - 图71

拉取成功

体验一次构建,到处运行

  1. 使用 mvn deploy 打包项目并上传到 Nexus

Docker - 图72

  1. 在 Paas 虚拟机上构建一个镜像

创建一个目录,然后上传打包好的项目的 jar 包,使用 Dockerfile 构建镜像

  1. FROM mysql
  2. FROM tomcat
  3. # VOLUME 指定了临时文件目录为/tmp。
  4. # 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
  5. VOLUME /tmp
  6. # 将jar包添加到容器中并更名为app.jar
  7. ADD demo-0.0.1-SNAPSHOT.jar app.jar
  8. # 运行jar包
  9. ENTRYPOINT ["java","-jar","app.jar"]
  1. docker build -t blog .
  1. 使用 Docker compose 运行镜像
  1. version: '3.1'
  2. services:
  3. myblog:
  4. image: blog
  5. container_name: blog
  6. ports:
  7. - 8089:8083
  1. docker-compose up -d
  1. 访问

Docker - 图73

  1. push 镜像到 Harbor
  1. docker tag blog 192.168.224.131:9999/blog/myblog
  2. docker push 192.168.224.131:9999/blog/myblog

Docker - 图74

  1. 在 deployment 虚拟机上配置 Harbor 客户端

/etc/docker/daemon.json 中内容如下

  1. {
  2. "registry-mirrors": ["https://9b35jrsu.mirror.aliyuncs.com"],
  3. //harborhostname:port
  4. "insecure-registries": ["192.168.224.131:9999"]
  5. }
  1. 在 deployment 上拉取镜像

先登录

  1. docker login 192.168.224.131:9999 -u admin -p Harbor12345

再拉取

  1. docker pull 192.168.224.131:9999/blog/myblog:latest

Docker - 图75

  1. 用 docker compose 运行

规范点,在 /usr/local/docker 目录下创建一个 myblog 的文件夹,编写一个 docker compose文件

  1. version: '3.1'
  2. services:
  3. myblog:
  4. image: 192.168.224.131:9999/blog/myblog
  5. container_name: myblog
  6. ports:
  7. - 8999:8083
  1. 访问

Docker - 图76

Docker - 图77

运行成功

Docker Compose 网络设置

由于 docker 的隔离机制,不同的容器其实是在不同的局域网当中Docker - 图78

默认情况下,compose 会为应用创建一个网络,服务中的每个容器都会加入该网络中。这样,溶剂就可以被该网络中的其它容器访问。同时,该容器还能以服务名称作为 Hostname 被其它容器访问

默认情况下,应用程序的默认网络名基于 compose 的工程名称,而项目名称基于 docker-compose.yml 所在目录的名称

同一个 compose 下直接互通

EG:一应用程序在名为 myapp 的目录中,并且 docker-compose.yml如下所示

  1. version: '3.1'
  2. services:
  3. web:
  4. build: .
  5. ports:
  6. - 8000:8000
  7. db:
  8. image: postgres

当我们运行这个 docker-compose.yml 时,会执行以下几步:

  • 创建一个名为 myapp default的默认网络
  • 使用 web 服务的配置创建的容器,以 web 这个名称加入网络 myapp default
  • 使用 db 服务的配置创建的容器,以 db 这个名称加入网络 myapp default

因此,这两个容器其实是在同一个网络中的,可使用服务名称 (web / db) 作为Hostname 相互访问。EG:web 这个服务可以使用postgres://db:5432 访问 db 容器

不同 compose 下的互通

由于不同的 compose 在启动时会在不同的网络下,因此我们可以自己创建一个网络,然后在想要互通的 compose 中指定加入的自定义网络

  1. 创建网络
  1. docker network create myblog_netword

Docker - 图79

  1. 修改想要互通的容器的 compose ,指定要连接的网络
  1. networks:
  2. default:
  3. external:
  4. #网络名
  5. name: myblog_netword

实战

  1. 在 deployment 虚拟机中进入 mysql 目录下,修改其 compose

Docker - 图80

  1. 在 deployment 虚拟机中进入进入 myblog 目录下,修改其 compose

Docker - 图81

  1. 删除 MySQL 的 compose 中的端口映射,不让其暴露在外网当中,只存在于 docker 内网

Docker - 图82

没有了端口映射,此时已经不准外网连接了

Docker - 图83

  1. 由于现在无法通过 IP 对 MySQL 服务进行访问了,因此 myblog 项目中就需要通过网络中的服务名:端口号,对另一个服务进行访问

Docker - 图84

Docker - 图85

  1. 重新打包项目,上传到 Paas 虚拟机的 /usr/local/docker/myblog 目录下,进入 Paas 虚拟机下对应目录,构建镜像
  2. 推送镜像
  1. docker tag myblog 192.168.224.131:9999/blog/myblog
  1. docker push 192.168.224.131:9999/blog/myblog
  1. 回到 deployment,拉取镜像
  1. docker pull 192.168.224.131:9999/blog/myblog:latest
  1. 启动 myblog,访问界面

Docker - 图86

暴露端口给宿主机在进行开发时常用,但实际上上线的时候不应该暴露端口给宿主机,因为这样是不安全的