Docker入门——基础概念,安装运行Tomcat,MySQL

前言

这篇博客,基于之前一段时间对于Docker的简单使用,南国在这里对于Docker的基础知识做一个简要的总结。

虚拟机

首先我们简要描述一下虚拟机,比如我们常用的VMWare、VirtualBox等。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点:
(1)资源占用多
虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
(2)冗余步骤多
虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
(3)启动慢
启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。

Linux容器

由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
由于容器是进程级别的,相比虚拟机有很多优势。
(1)启动快
容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。
(2)资源占用少
容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。
(3)体积小
容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。

总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。

Docker

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

Docker 的主要用途,目前有三大类。
(1)提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
(3)组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。

Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。

image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。

Docker的基本组成部分

在Docker中,常用到的基本组成:

  • Docker主机(Host): 安装Docker程序的机器(Docker直接安装在操作系统之上),一般为Linux系统。
  • Docker客户端(Client):成功连接Docker主机能进行操作,例如能成功访问Linux系统的XShell MobaXterm等。
  • Docker仓库(Repository):用来保存各种打包好的软件镜像。
  • Docker镜像(Image):软件打包好的镜像;放在docker仓库中;
  • Docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用

这里 重点对后面三个概念进行论述。理解了仓库,镜像和容器,就理解了 Docker 的整个生命周期。

镜像Image

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

容器Container

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

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。

前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层。

容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

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

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。

仓库Repository

镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。

仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

Docker Registry 公开服务

Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 CoreOS 的 Quay.io,CoreOS 相关的镜像存储在这里;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。

由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。在 安装 Docker 一节中有详细的配置方法。

国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 时速云镜像仓库、网易云镜像服务、DaoCloud 镜像市场、阿里云镜像库 等。

私有 Docker Registry

除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。在 私有仓库 一节中,会有进一步的搭建私有 Registry 服务的讲解。

开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。在官方的商业化版本 Docker Trusted Registry 中,提供了这些高级功能。

除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,Harbor 和 Sonatype Nexus。

Docker安装

这里,我们在CentOS7上进行Docker操作,Docker的安装过程比较简单。

  1. 首先检查Linux系统内核版本,最好是选择3.10及以上的版本。
    在这里插入图片描述
  2. yum 安装docker
    yum install docker
    
    这里yum安装 需要root权限,普通用户先选择su或者sudo转变为root用户或者得到root权限,然后yum安装
  3. 启动docker
    systemctl start docker
    
  4. 停止Docker
    systemctl stop docker
    

为了开发者方便,可以开机自启动Docker:

	systemctl enable docker

Docker的常用命令和基本操作

镜像Image操作

在Docker中运行某项服务,例如tomcat,MySQL,我们首选需要下载相关的镜像文件,一般我们都会在Docker hub上搜索相关镜像文件。例如:搜索MYSQL
在这里插入图片描述
在docker客户端中常用的命令如下:
在这里插入图片描述
在docker拉取镜像的时候,国外网站因为众所周知的一道墙下载速度慢,我们这里选择Docker配置阿里云镜像进行加速。具体的方法:

  1. 进入阿里云的容器镜像服务:容器镜像服务
    在这里插入图片描述

容器Container操作

  1. 下载得到相关的镜像文件后,我们可以根据镜像启动容器(例如,我们启动一个Tomcat):
    docker run --name mytomcat -d tomcat
    
  2. 查看运行中的容器
    docker ps  
    
  3. 停止运行中的容器
    docker stop  容器的id(CONTAINER ID)
    
  4. 查看所有的容器(包括正在运行的 和停止运行的)
    docker ps -a
    
  5. 启动之前启动过现在停止的容器
    docker start 容器id
    
  6. 删除一个容器
    docker rm 容器id
    
  7. 进入目标container中
    docker exec -it 20de4f9635a5 /bin/bash
    
  8. 退出当前docker容器,使用exit

运行Tomcat,MySQL遇到的坑

  1. docker安装tomcat启动之后,window主机访问报404错误
    我们知道,在启动docker中的服务时,需要将其启动的端口号和linux服务器的端口好进行绑定,外界才能通过访问Linux的ip地址+端口号才能访问。所以在docker启动tomcat时,我们需要进行端口的映射操作:

    docker run -d -p 8888:8080 tomcat
    

    使用docker ps 可以成功看到tomcat进程被成功启动,但是在浏览器访问tomcat时常出现报404错误: 在这里插入图片描述
    这里的问题在于,docker启动的tomcat容器目录中webapps文件目录下空。
    解决办法如下:
    首先,进入到tomcat目录中

    docker exec -it 目标容器ID /bin/bash
    

    在这里插入图片描述
    发现webapps为空,tomcat的默认项目资源都在webapps.dist文件夹下。
    将webapps删除,将webapps.dist重命名为webapps。

    rm -rf webapps
    mv webapps.dist webapps
    

    之后在浏览器中访问tomcat,可看到tomcat正常启动。
    在这里插入图片描述

  2. 本地不能正常连接docker启动的MYSQL
    首先,docker上启动MySQL
    错误的启动方式

    docker run --name mysql01 -d mysql
    

    这种方式启动,mysql会异常退出。查看docker ps时搜索不到刚才启动的mysql进程。
    通过docker ps -a查看到启动失败的mysql,然后查看相关的错误日志。

    [root@m1 ~]# docker logs 42f09819908b
    error: database is uninitialized and password option is not specified 
    You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD 		  and MYSQL_RANDOM_ROOT_PASSWORD;
    

    意思是MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD;这个三个参数必须指定一个。也就是在实际运用中,我在启动mysql的时候需要指定用户(root)的密码。

    修改后的启动方式:

    [root@m1 ~]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
    b874c56bec49fb43024b3805ab51e9097da779f2f572c22c695305dedd684c5f
    [root@localhost ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    b874c56bec49        mysql               "docker-entrypoint.sh"   4 seconds ago       Up 3 seconds        3306/tcp            mysql01
    

    但是这样依旧不能被外部访问,因为docker容器没有做linux主机的端口映射,所以正确的启动方式是:

    [root@m1 ~]# docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
    ad10e4bc5c6a0f61cbad43898de71d366117d120e39db651844c0e73863b9434
    [root@localhost ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
    ad10e4bc5c6a        mysql               "docker-entrypoint.sh"   4 seconds ago       Up 2 seconds        0.0.0.0:3306->3306/tcp   mysql02
    

    这里有时候会报错 显示3306端口已经被占用:
    在这里插入图片描述
    南国的解决办法是查找到当前Linux的使用端口,将其端口删掉。然后重新启动MySQL:
    在这里插入图片描述
    成功启动MySQL后,本机使用Navicat连接数据库,在查看MySQL成功运行,并且Navicat连接数据库的连接属性正确后。连接时报错1251。如下图:
    在这里插入图片描述
    这里的解决办法时重置MySQL的密码:

  • 进入mysql客户端
  • 输入用户密码进入mysql数据库
  • 重新设置密码
    在这里插入图片描述
    在这里插入图片描述
    密码修改成功后,使用Navicat重新连接:
    在这里插入图片描述
    参考文献:
    https://yeasy.gitbooks.io/docker_practice/basic_concept/repository.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章