Docker——从坟里到实践

Docker

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

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张图片

Docker 镜像加速器

阿里云加速

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

  • 进入阿里云控制台,搜索 容器镜像服务

    Docker——从坟里到实践_第5张图片

  • 找到专属加速服务

    Docker——从坟里到实践_第6张图片

  • 通过修改 daemon 配置文件 /etc/docker/daemon.json 来使用加速器

    全部把xxxxxxx替换成上面那张图涂掉的那部分,然后全部 copy 然后回车

    tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://xxxxxxx.mirror.aliyuncs.com"]
    }
    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 操作镜像

获取镜像

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

EG:docker pull ubuntu:18.04

​ 这个命令等同于:docker pull registry.hub.docker.com/ubuntu:12.04

​ 相当于从默认注册服务器 registry.hub.docker.com 中的 ubuntu 仓库上下载了标签为 12.04 的 ubuntu 镜像

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

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

列出镜像

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

Docker——从坟里到实践_第10张图片

列信息

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

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

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

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

  1. 显示镜像体积

    docker system df
    

    image-20200429081708640

  2. 中间层镜像

    docker image ls -a
    

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

删除镜像

移除本地镜像使用

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

docker rm 是删除容器的

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

清除所有虚悬镜像

docker image prune

commit

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

Docker 操作容器

显示容器

显示运行中的容器

docker ps

image-20200429084710631

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

docker ps -a

启动容器

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

docker的网卡

Docker——从坟里到实践_第11张图片

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

容器的网卡

Docker——从坟里到实践_第12张图片

1.新建并启动

docker run

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

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

参数 -p 表示进行端口映射,宿主机端口 : 容器端口,用户访问宿主机的端口时,会**路由到指定的容器端口**,从而进行访问。不使用该参数则会进行默认映射 ( 如tomcat会默认进行8080的映射 )

参数 -d 表示后台运行该容器 ( daemon 守护态运行)

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

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载

  • 利用镜像创建并启动一个容器

  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层

  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去

  • 从地址池配置一个 ip 地址给容器

  • 执行用户指定的应用程序

  • 执行完毕后容器被终止

2.启动已停止容器

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

停止容器

docker stop 容器ID

删除容器

普通删除

docker rm 容器ID / 容器名

无法删除正在运行的容器

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

docker rm -f 容器ID / 容器名

交互式操作容器

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

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

Docker——从坟里到实践_第13张图片

退出容器

ctrl + d

查看容器日志

docker logs -f 容器名

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

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

docker container ls -a

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

docker container prune

Docker 查看日志

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

DockerFile 定制镜像

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

  1. 创建DockFile

名字必须为Dockfile)

vim Dockerfile

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

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

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

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

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

Docker——从坟里到实践_第14张图片

  1. 查看镜像

    Docker——从坟里到实践_第15张图片

镜像构建上下文

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

  • 在当前目录找到 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 指令将从构建上下文目录中的 <源路径> 中的文件/目录复制到新的一层镜像内的 <目标路径>

COPY package.json /usr/src/app

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

COPY hom* /mydir/
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 指令

<ENTRYPOINT> ""

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

但是~,ENTRYPOINT 也只准用一次

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

ENV

  • 格式
    • ENV
    • ENV = =...

设置环境变量

ENV MYSQL_VERSION 5.7.2

VOLUME

数据卷,后面会说

EXPOSE

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

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

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

WORKDIR

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

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

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

看一个完整一点的例子

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

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

FROM tomcat
WORKDIR /usr/local/tomcat/webapps/ROOT
RUN rm -fr *
COPY myTest.tar.gz .
RUN tar -zxvf myTest.tar.gz \
    && rm -rf myTest.tar.gz
EXPOSE 8080

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

Docker Compose

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

​ Docker Hub

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

Docker Compose安装

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

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

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

下载完后执行提权

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

最后验证

docker-compose version

Dcoker Compose 的使用

前置准备

  • 克隆两台虚拟机 ,一台命名为 deploy,一套命名为 Paas

    1.固定IP 地址

    • 修改其中一台的 IP 地址
    vim /etc/netplan/50-cloud-init.yaml
    
    network:
         ethernets:
               ens33:  
                   addresses: [192.168.224.129/24]
                   #网关地址为VMware中虚拟网络编辑里面的网关地址
                   gateway4: 192.168.224.2
                   nameservers:
                   		   #同网关地址
                           addresses: [192.168.224.2]
         version: 2
    

    生效配置

    netplan apply
    

2.修改主机名

  • 修改 cloud.cfg 防止重启后主机名还原

    vim /etc/cloud/cloud.cfg
    
    #将下面的值改为true即可
    preserve_hostname: true
    
    
  • 修改主机名

    #修改主机名
    hostnamectl set-hostname deployment
    
    #配置 hosts
    cat >> /etc/hosts << EOF
    192.168.224.132 services
    EOF
    

3.修改 DNS

vim /etc/systemd/resolved.conf

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

Docker——从坟里到实践_第16张图片

开始使用

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

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

命名为 docker-compose.yml

version: '3.1'
services:
     tomcat:
         restart: always
         image: tomcat
         container_name: tomcat
         ports:
            - 8080:8080

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

tomcat :服务名

image:使用的镜像名称

container_name:命名容器

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

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

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

守护态运行

docker-compose up -d
  1. 停止
docker-compose down

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

Docker数据卷

使用 docker-compose 指定数据卷

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

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

image-20200430191041662

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 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 目录。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

services:
  tomcat:
    restart: always
    image: tomcat
    container_name: tomcat
    ports:
      - 8080:8080
    volumes:
      - ./webapps:/usr/local/tomcat/webapps.dist
    environment:
      TZ: Asia/Shanghai

然后守护态启动

部署 MySQL

version: '3.1'
services:
  db:
    # 目前 latest 版本为 MySQL8.x
    image: mysql
    restart: always
    environment:
      #初始化密码
      MYSQL_ROOT_PASSWORD: 123456
    command:
      #为制作的MySQL镜像指定参数
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1
    ports:
      - 3306:3306
    volumes:
      - ./data:/var/lib/mysql

然后守护态启动

部署 SpringBoot 项目

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

image-20200501100047793

然后成 jar 包

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

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

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

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

然后

docker-compose up -d

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

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

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

Docker Compose 部署 GitLab

部署 GitLab

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

version: '3'
services:
    gitlab:
      image: 'twang2218/gitlab-ce-zh'
      restart: always
      #宿主机ip地址,或域名
      hostname: '192.168.224.131'
      environment:
        TZ: 'Asia/Shanghai'
        GITLAB_OMNIBUS_CONFIG: |
        #gitlab进行拉取时的http地址
          external_url 'http://192.168.224.131'
          gitlab_rails['time_zone'] = 'Asia/Shanghai'
          #GitLab项目中使用SSH进行拉取时的端口
gitlab_rails['gitlab_shell_ssh_port'] = 2222
          unicorn['port'] = 8888
          #使用nginx进行解析
          nginx['listen_port'] = 80 
      ports:
        #右边容器端口要与gitlab进行http拉取时的端口号一致,因为是80,所以上面可以不写
        - '80:80'
        - '443:443'
        - '2222:22'
      volumes:
        - ./config:/etc/gitlab
        - ./data:/var/opt/gitlab
        - .logs:/var/log/gitlab

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

Docker——从坟里到实践_第17张图片

登录即可

安装 TortoiseGit

https://tortoisegit.org/download/

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

image-20200501215951911

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

然后安装第二个进行汉化

右击任意位置

Docker——从坟里到实践_第18张图片

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

Docker——从坟里到实践_第19张图片

使用 TortoiseGit 提交和克隆 GitLab

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

Docker——从坟里到实践_第20张图片

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

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

Docker——从坟里到实践_第21张图片

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

Docker——从坟里到实践_第22张图片

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

Docker——从坟里到实践_第23张图片

SSH 的方式进行拉取和推送

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

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

1.生成秘钥

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

image-20200501222718501

找到这个文件

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

ssh-keygen -t rsa -C "you_email"

Docker——从坟里到实践_第24张图片

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

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

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

image-20200501224449378

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

2.配置 GitLab

Docker——从坟里到实践_第25张图片

Docker——从坟里到实践_第26张图片

将刚才生成的 .pub 公钥的内容粘贴到Docker——从坟里到实践_第27张图片

生成 SSH

Docker——从坟里到实践_第28张图片

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

Docker——从坟里到实践_第29张图片

3.测试

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

Docker——从坟里到实践_第30张图片

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

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

Docker——从坟里到实践_第31张图片

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

最后:

Docker——从坟里到实践_第32张图片

Docker Compose 部署 Nexus

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

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

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

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

Docker——从坟里到实践_第33张图片

部署 Nexus

制作 docker-compose

version: '3.1'
services:
    nexus:
      restart: always
      image: sonatype/nexus3
      container_name: nexus
      ports:
        - 8081:8081
      volumes:
        - ./data:/nexus-data

启动发现报错

Docker——从坟里到实践_第34张图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A7xGcN2c-1600656635523)(…/mdPicture/image-20200502104857181.png)]

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

chmod 777 instances

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

version: '3.1'
services:
    nexus:
      restart: always
      image: sonatype/nexus3
      container_name: nexus
      ports:
        - 8081:8081
      volumes:
        - nexus-data:/nexus-data

#外部指定数据卷位置
volumes:
	nexus-data:

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

访问一下

Docker——从坟里到实践_第35张图片

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

image-20200502111830812

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

了解 Nexus 的 Maven 仓库

代理仓库 (Proxy Repository)

Docker——从坟里到实践_第36张图片

  • 第三方仓库

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

    Docker——从坟里到实践_第37张图片

    • nuget.org-proxy
  • 版本策略

    • Release:正式版
    • Snapshot:快照版 (开发版),在不更改版本号的情况下进行功能变更
    • Mixed:混合模式
  • 布局策略 ( Layout Policy )

    • Strict:严格
    • Permissive:宽松

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

EG:V2.0.4

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

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

宿主仓库 (Hosted Repository)

Docker——从坟里到实践_第38张图片

  • 存储本地上传的组件和资源
    • maven-release
    • maven-snapshots
    • nuget-hosted
仓库组
  • 一个仓库包含多个仓库的内容

    • maven-public

    Docker——从坟里到实践_第39张图片

在项目中使用 Nexus

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

在中央仓库设置认证

Docker——从坟里到实践_第40张图片

配置节点

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

<server>
    <id>maven-releasesid>
    <username>adminusername>
    <password>123456password>
server>
<server>
    <id>maven-snapshotsid>
    <username>adminusername>
    <password>123456password>
server>

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

配置

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

<distributionManagement>
    <repository>
        <id>maven-releasesid>
        <name>nexus releases reponame>
        <url>http://192.168.224.131:8081/repository/maven-releases/url>
    repository>
    <snapshotRepository>
        <id>maven-snapshotsid>
        <name>nexus snapshots reponame>
        <url>http://192.168.224.131:8081/repository/maven-snapshots/url>
    snapshotRepository>
distributionManagement>
配置代理仓库

在 pom.xml 中配置代理仓库


<repositories>
    <repository>
        <id>nexusid>
        <name>nexus respositoryname>
        <url>http://192.168.224.131:8081/repository/maven-public/url>
        <snapshots>
            <enabled>trueenabled>
        snapshots>
        <releases>
            <enabled>trueenabled>
        releases>
    repository>
repositories>

<pluginRepositories>
    <pluginRepository>
        <id>nexusid>
        <name>nexus pluginname>
        <url>http://192.168.224.131:8081/repository/maven-public/url>
        <snapshots>
            <enabled>trueenabled>
        snapshots>
        <releases>
            <enabled>trueenabled>
        releases>
    pluginRepository>
pluginRepositories>
验证配置是否成功

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

image-20200502123925819

回到 nexus 查看中央仓库

Docker——从坟里到实践_第41张图片

上传
mvn deploy 

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

Docker——从坟里到实践_第42张图片

Docker——从坟里到实践_第43张图片

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

Docker——从坟里到实践_第44张图片

因为我的 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——从坟里到实践_第45张图片

安装

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

Docker——从坟里到实践_第46张图片

  1. 上传到虚拟机上

Docker——从坟里到实践_第47张图片

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

Docker——从坟里到实践_第48张图片

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

  1. 执行安装脚本
./install.sh
  1. 安装好以后会在当前目录下自动生成一个 docker-compose,启动即可

  2. 访问配置文件中的 hostname : port

    Docker——从坟里到实践_第49张图片

  3. 所有有关harbor的配置信息都在harbor.yml下。用户名默认为 admin,密码为 Harbor12345

    Docker——从坟里到实践_第50张图片

配置客户端

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

image-20200503091404621

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

Docker——从坟里到实践_第51张图片

推送镜像

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

Docker——从坟里到实践_第52张图片

Docker——从坟里到实践_第53张图片

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

image-20200503100544823

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

Docker——从坟里到实践_第54张图片

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

Docker——从坟里到实践_第55张图片

  1. 测试镜像拉取

Docker——从坟里到实践_第56张图片

复制命令

docker pull 192.168.224.131:9999/blog/nginx:latest

然后先把原来的镜像删了

Docker——从坟里到实践_第57张图片

然后再进行拉取

Docker——从坟里到实践_第58张图片

image-20200503102109600

拉取成功

体验一次构建,到处运行

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

Docker——从坟里到实践_第59张图片

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

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

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

Docker——从坟里到实践_第60张图片

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

Docker——从坟里到实践_第61张图片

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

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

{
     
  "registry-mirrors": ["https://9b35jrsu.mirror.aliyuncs.com"],
   //harbor的hostname:port  
  "insecure-registries": ["192.168.224.131:9999"]
}
  1. 在 deployment 上拉取镜像

先登录

docker login 192.168.224.131:9999 -u admin -p Harbor12345

再拉取

docker pull 192.168.224.131:9999/blog/myblog:latest

image-20200503121025672

  1. 用 docker compose 运行

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

version: '3.1'
services:
        myblog:
            image: 192.168.224.131:9999/blog/myblog
            container_name: myblog
            ports:
              - 8999:8083
  1. 访问

Docker——从坟里到实践_第62张图片

Docker——从坟里到实践_第63张图片

运行成功

Docker Compose 网络设置

由于 docker 的隔离机制,不同的容器其实是在不同的局域网当中Docker——从坟里到实践_第64张图片

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

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

同一个 compose 下直接互通

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

version: '3.1'
services:
	web:
		build: .
		ports:
		   - 8000:8000
	db:
	   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. 创建网络
docker network create myblog_netword

Docker——从坟里到实践_第65张图片

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

实战

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

Docker——从坟里到实践_第66张图片

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

Docker——从坟里到实践_第67张图片

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

Docker——从坟里到实践_第68张图片

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

Docker——从坟里到实践_第69张图片

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

image-20200503130922119

image-20200503130936836

  1. 重新打包项目,上传到 Paas 虚拟机的 /usr/local/docker/myblog 目录下,进入 Paas 虚拟机下对应目录,构建镜像

  2. 推送镜像

docker tag myblog 192.168.224.131:9999/blog/myblog
docker push 192.168.224.131:9999/blog/myblog
  1. 回到 deployment,拉取镜像
docker pull 192.168.224.131:9999/blog/myblog:latest
  1. 启动 myblog,访问界面

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

你可能感兴趣的:(DevOps,运维)