Docker的基本使用
1、docker的基本介绍
Docker 可以让开发者打包他们的应用以及依赖包(环境)到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。Docker的主要目标是"Build, Ship and Run Any App,Anywhere" ,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP (可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
1.1、为什么需要docker?
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码等(java为例),而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式。开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。Docker镜像的设计,使得Docker得以打破过去「程序即应用」的观念。透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
传统开发存在的问题:
- 1、环境配置麻烦
软件开发最大的麻烦事之一,就是环境配置。环境配置是十分麻烦的,每个机器都要部署环境。
一般我们写程序的,能接触到好几个环境:开发环境、测试环境、生产环境,每个环境都需要进行配置才能将项目(jar、war包)给运用起来。可能会出现好不容易在测试环境下跑起来了,在生产环境就各种出错的问题。并且如果重装了某个机器的系统,或者换了一台机器,你还得去重新配置环境,十分麻烦。
- 2、应用之间需要隔离
比如我写了两个应用(网站),这两个应用部署在同一台服务器上,那可能会出现什么问题?
- 如果一个应用出现了问题,导致CPU占100%。那另一个应用也会受到关联,跟着一起凉凉了。
- 这两个应用是完全不同技术栈的应用,比如一个
PHP
,一个.NET
。这两个应用各种的依赖软件都安装在同一个服务器上,可能就会造成各种冲突/无法兼容,这可能调试就非常麻烦了。
docker 如何解决这些问题:
- 1、解决环境配置问题
比如说装Linux虚拟机,重装Windows系统,都是需要镜像的。有了这个镜像,我们就可以运行这个镜像,来进行安装系统的操作,于是我们的系统就装好了。一般来说,我们去官方渠道下载的镜像,都是纯净的。比如去官方下载Windows镜像,装完后之后桌面只有一个回收站。但有过了解装系统的同学可能就会知道,有的镜像装完可能还有360这些软件,但系统的的确确是变了。简单来说,就是这些镜像添加了其他的东西(比如360软件、腾讯、千千静听等等软件)。
Docker也是这种思路,可以将我们想要的环境构建(打包)成一个镜像,然后我们可以推送(发布)到网上去。想要用这个环境的时候,在网上拉取一份就好了。
有了Docker,我们在搭环境的时候,跟以前的方式就不一样了。
- 之前:在开发环境构建出了一个war包,想跑到Linux下运行。我们得先在Linux下载好Java、Tomcat、MySQL,配置好对应的环境变量,将war包丢到Tomcat的webapps文件夹下,才能跑起来。
- 现在:在Linux下直接拉取一份镜像(各种环境都配好了),将镜像运行起来,把war包丢进去就好了。将Docker的镜像运行起来就是一两秒的事情而已,十分方便的。
- 2、解决应用之间隔离的问题
Docker 底层用的 Linux 的 cgroup 和 namespace 这两项技术来实现应用隔离。docker 通过隔离机制可以将服务器的资源利用到极致。
Linux 容器(LXC:Linux Containers):在Linux内核中,提供了cgroups功能,来达成资源的区隔化。它同时也提供了名称空间(namespace)区隔化的功能,使应用程序看到的操作系统环境被区隔成独立区间,包括进程树,网络,用户id,以及挂载的文件系统。简单来说就是:LXC是一个 Linux 内核包含特征的用户接口。通过强大的API和简单的工具,它可以让 Linux 用户轻松的创建和托管系统或者应用程序容器。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
可参考:https://zhuanlan.zhihu.com/p/54512286
1.2、docker和虚拟机的对比
虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。虚拟机技术是虚拟出一套硬件后,在该虚拟硬件之上运行一个完整的操作系统,再在该系统上运行所需的应用进程。
虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点:
(1)资源占用多。虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
(2)冗余步骤多。虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
(3)启动慢。启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。
由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
由于容器是进程级别的,相比虚拟机有很多优势:
(1)启动快。容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。
(2)资源占用少。容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。
(3)体积小。容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。
传统虚拟机技术是虚拟出一套硬件后,在该虚拟硬件之上运行一个完整的操作系统,再在该系统上运行所需的应用进程。而容器内的应用进程直接运行于本机宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟,因此容器要比虚拟机更为轻便。
容器可以理解为一个轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。
架构图对比:
容器架构:
虚拟机架构:
1.3、docker 的优点
Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
Docker是一个集开发、打包、运行应用于一体的开放式平台。Docker可以用来快速交付应用。使用Docker,你可以将应用程序从你的基础设施中分离出来,并将基础设施当做一个管理平台。Docker可以加快打包时间,加快测试,加快发布,缩短开发及运行代码之间的周期。
Docker 的主要用途,目前有三大类:
(1)提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
(3)组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
2、docker的三要素
docker由三部分组成:image(镜像)、容器(container)、仓库(repository)。
Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是 image 镜像文件。只有通过这个镜像文件才能生成Docker容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个image文件,可以生成多个同时运行的容器实例。
2.1、镜像(image)
Docker 镜像(Image)就是一个只读的模板,其中包含有关创建Docker容器的说明。镜像可以用来创建Docker容器,一个镜像可以创建多个容器。
Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,照样可以使用。
2.2、容器(container)
容器的基本介绍:
- 容器是通过镜像创建的运行实例
- Docker 利用容器(Container)来独立运行一个或一组应用
- 容器可以被启动、开始、停止、删除,每个容器之间都是相互隔离的,保证平台的安全。
- 可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
- 容器的定义和镜像几乎一摸一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
image 文件生成的容器实例,本身也是一个文件,称为容器文件。
2.3、仓库(repository)
仓库的基本介绍:
- 仓库(Repository)是集中存放镜像文件的场所。
- 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等。
仓库(Repository)很容易和Registry(仓库注册服务器)混淆。通俗地说,它们的关系是仓库注册服务器上往往存放着多个仓库,每个仓库又存放着一大堆Images,每个Image存在不同的标签(Tag);用户可以通过注册服务器从仓库拉取(带有某个tag的)Image,从而创建Container。
3、docker的安装
Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),一般用社区版就可以了。
如果是 centos 系统,则 Docker 支持以下CentOS 版本:CentOS 7(64-bit)、CentOS 8(64-bit)或者更高版本。Docker 运行在 centos7 上时,要求 CentOS 系统的内核版本高于 3.10 ;运行在 centos6.5或更高版本上时,要求系统内核版本为 2.6.32-431 或更高。
在不同版本系统上安装 docker 不太一样,下面我们在 centos7 版本的 Linux 系统上安装社区版 docker。
首先,我们需要通过 uname -r 命令查看你当前的内核版本:
也可以通过 cat /etc/redhat-release 命令来查看 centos 系统的版本:
如果之前安装过旧的版本的 docker,需要先卸载掉。较旧的Docker版本称为docker
或docker-engine
。如果已安装这些程序,请卸载它们以及相关的依赖项。
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的:
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
设置yum源。使用以下命令来设置稳定的仓库,官方的仓库比较慢,下面我们使用阿里云的仓库:
sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装docker。安装最新版本的 Docker Engine-Community 和 containerd:
sudo yum install docker-ce docker-ce-cli containerd.io
安装过程中可能会提示是否确认,可以直接选是。
上述过程中 docker 已安装完成,我们可以直接启动 Docker:
sudo systemctl start docker
安装完成后,我们可以通过查看 docker 的版本来验证是否安装成功:
docker version
效果如下:
3.1、配置加速器
下面配置阿里云加速器。
参考:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
3.2、试运行hello-world 镜像
Docker 允许你在容器内运行应用程序, 使用 docker run 命令来在容器内运行一个应用程序。下面我们试运行 hello-world 镜像:
sudo docker run hello-world
run
命令会从 image 文件,生成一个正在运行的容器实例。run
命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。
运行效果如下:
输出上面提示以后,hello world会自动
停止运行,容器自动终止。但有些容器是不会自动终止的,因为提供的是服务,比如安装运行 Ubuntu 的 image,就可以在命令行体验 Ubuntu 系统。对于那些不会自动终止的容器,必须使用docker container kill
命令手动终止。
4、docker的原理和架构
4.1、docker运行原理
Docker是一个Client-Server结构的系统,Docker守护进程(Docker daemon)运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。
4.2、docker架构
Docker使用了C/S体系架构,Docker客户端与Docker守护进程通信,Docker守护进程(docker daemon)负责构建,运行和分发Docker容器。Docker客户端和守护进程可以在同一个系统上运行,也可以将Docker客户端连接到远程Docker守护进程。Docker客户端和守护进程使用REST API通过UNIX套接字或网络接口进行通信。
5、Pod
Kubernetes 中的Pod是部署应用的最小单位。一个Pod封装一个应用容器,存储资源、一个独立的网络IP以及管理控制容器运行方式的策略选项。业务涉及到的所有应用均运行在容器云平台所管理的一个个的Pod当中。
当应用的Pod创建好之后,就要考虑建立完整的访问链路系统,最终实现对外提供服务的能力。建立网络访问的第一步,是实现各个Pod在所依赖宿主机——利用Kubernetes搭建的集群间内部的联通和访问。
集群内部Pod访问逻辑
实现集群内部访问有两种方案,分别如下:
1) 直接使用docker分配的IP地址
在Pod运行时,docker会为其分配一个独立的IP地址。拼接这个IP地址和服务的端口号,即可直接访问此Pod中的服务。但是这种方式有一个巨大的问题,即Pod在重启或销毁重建后,将会分配新的IP地址。
2) 通过ClusterIP方式访问
Pod的IP 地址实际存在于某个物理网卡或虚拟网卡上 ,而ClusterIP是创建Service的时候,由系统内部分配的一个全局唯一的虚拟IP.这个虚拟IP在网络中是无法寻址到的,它没有绑定到任何网卡设备上。这增加了它的访问的灵活性。我们可以通过服务的ClusterIP加应用容器的服务端口号来进行访问。
Service的业务入口访问机制
当Pod的访问有优选方案之后,我们接下来面临了下一个问题:Pod的IP和ClusterIP都是在每次创建后随之变化的,如何在应用的Pod重启后,保证服务的可访问性呢?此时,k8s中的Service可以解决此问题,它为应用的访问提供了一套业务的入口机制。