Docker

Docker简介

引入

在docker没有出现以前,我们开发了一款产品,开发和上线使用的是两套不同的环境。比如我们的项目需要依赖java、tomcat的环境,我们需要在服务器上进行配置,如果是分布式应用,那么每台服务器都需要配置。还可能出现在开发人员的电脑上能运行,到运维人员手中就出现问题,产生矛盾,那么我们能不能将项目带上安装环境一起打包呢?这样运维部署将变得十分方便。于是Docker就可以实现,在Docker中将带环境的项目打包在一起,叫做镜像。

什么是Docker

Docker是一个开项目,诞生于2013年初,最初是dotCloud公司内部的一个业余项目。它基于Google公司推出的Go语言实现。项目后来加入了Linux基金会,遵从了Apache2.0协议,项目代码在GitHub上进行维护。Docker自开源后受到广范的关注和讨论,以至于dotCloud公司后来都改名为Docker Inc。RedHat已经在其RHEL6.5中集中支持Docker;Google也在其PaaS产品中广泛应用。Docker的目标是实现经量级的操作系统虚拟化解决方案。Docker的基础是Linux容器(LXC)等技术。在LXC的基础上Docker进行了进一步的封装,让用户不需要关心容器的管理,使得操作更加简单。用户操作Docker的容器就像操作一个快速轻量级的虚拟机一样简单。

下图比较了Docker和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现的虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层现实现。

Docker设计思想

Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来。并且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一艘大船把他们都运走。docker就是类似的理念。现在都流行云计算了,云计算就好比大货轮。docker就是集装箱。

1、不同的应用程序可能会有不同的应用环境,比如.net开发的网站和php开发的网站依赖的软件就不一样,如果把他们依赖的软件都安装在一个服务器上就要调试很久,而且很麻烦,还会造成一些冲突。比如IIS和Apache访问端口冲突。这个时候你就要隔离.net开发的网站和php开发的网站。常规来讲,我们可以在服务器上创建不同的虚拟机在不同的虚拟机上放置不同的应用,但是虚拟机开销比较高。docker可以实现虚拟机隔离应用环境的功能,并且开销比虚拟机小,小就意味着省钱了。

2、你开发软件的时候用的是Ubuntu,但是运维管理的都是centos,运维在把你的软件从开发环境转移到生产环境的时候就会遇到一些Ubuntu转centos的问题,比如:有个特殊版本的数据库,只有Ubuntu支持,centos不支持,在转移的过程当中运维就得想办法解决这样的问题。这时候要是有docker你就可以把开发环境直接封装转移给运维,运维直接部署你给他的docker就可以了。而且部署速度快。

3、在服务器负载方面,如果你单独开一个虚拟机,那么虚拟机会占用空闲内存的,docker部署的话,这些内存就会利用起来。

Docker的优点

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个Docker 容器。

容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。

传统虚拟机方式运行10 个不同的应用就要起10 个虚拟机,而Docker 只需要启动10 个隔离的应用即可。具体说来,Docker 在如下几个方面具有较大的优势。

1、更快速的交付和部署

对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间.

2、更轻松的迁移和扩展

Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器

等。这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

3、更简单的管理

使用Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。

docker的局限性

1、Docker是基于Linux 64bit的,无法在32bit的linux/Windows/unix环境下使用

2、LXC是基于cgroup等linux kernel功能的,因此container的guest系统只能是linux base的

3、隔离性相比KVM之类的虚拟化方案还是有些欠缺,所有container公用一部分的运行库

4、网络管理相对简单,主要是基于namespace隔离

5、cgroup的cpu和cpuset提供的cpu功能相比KVM的等虚拟化方案相比难以度量(所以dotcloud主要是按内存收费)

6、Docker对disk的管理比较有限

7、container随着用户进程的停止而销毁,container中的log等用户数据不便收集

docker在开发、测试、部署中的定位

1、尝试新软件

对开发者而言,每天会催生出的各式各样的新技术都需要尝试,然而开发者却不太可能为他们一一搭建好环境并进行测试。时间非常宝贵,正是得益于 Docker,让我们有可能在一条或者几条命令内就搭建完环境。Docker 有一个傻瓜化的获取软件的方法,Docker 后台会自动获得环境镜像并且运行环境。

并不仅仅是新技术环境搭建用得到 Docker。如果你想快速在你的笔记本上运行一个 MySQL 数据库,或者一个 Redis 消息队列,那么使用 Docker 便可以非常容易地做到。例如 Docker 只需要一条命令便可以运行 MySQL 数据库:docker run -d -p 3306:3306 tutum/mysql

2、进行演示

工作中自己开发的成果对客户或者别人做一两个演示。搭建演示环境的过程非常麻烦。Docker是演示这些工具的最合理的方式。同时,对于客户来说,可以直接将 Docker 镜像提供给他们,而不必去做任何环境配置的工作,工作的效果也会和在他们演示中所看到的一模一样,同时不必担心他们的环境配置会导致我们的产品无法运行。

3、避免“我机器上可以运行”

因为环境配置不同,很多人在开发中也会遇到这个情况(我的机器可以运行,你的机器不能),甚至开发的软件到了测试人员的机器上便不能运行。但这都不是重点。重点是,如果我们有一个可靠的、可分发的标准开发环境,那么我们的开发将不会像现在这么痛苦。Docker 便可以解决这个问题。Docker 镜像并不会因为环境的变化而不能运行,也不会在不同的电脑上有不同的运行结果。可以给测试人员提交含有应用的 Docker 镜像,这样便不再会发生“在我机器上是可以运行的”这种事情,很大程度上减轻了开发人员测试人员互相检查机器环境设置带来的时间成本。

4、更好地利用资源

虚拟机的粒度是“虚拟出的机器”,而 Docker 的粒度则是“被限制的应用”,相比较而言 Docker 的内存占用更少,更加轻量级。对我来说这是 Docker 的一个优势:因为在如果在电脑中运行多个 Docker 应用,使用 Docker 比使用虚拟机更加简单,方便,粒度更细,也能持续地跟踪容器状态。

5、为微服务定制

Docker 可以很好地和微服务(Microservices)结合起来。从概念上来说,一个微服务便是一个提供一整套应用程序的部分功能,Docker 便可以在开发、测试和部署过程中一直充当微服务的容器。甚至生产环境也可以在 Docker 中部署微服务。

6、在云服务提供商之间移植

大多数的云主机提供商已经全面支持 Docker。对于开发人员来说,这表示你可以很方便地切换云服务提供商,当然也可以很方便地将你本地的开发环境移动到云主机上,不需要本地上配置一次运行环境、在云主机上还配置一次运行环境。全面部署 Docker (Docker here and Docker there) 作为标准运行环境可以极大地减轻应用上线时的工作量和产生 BUG。

7、技术的创新

Docker 正在快速发展,工具也在不断更新,没有人能预见到未来 Docker 会是什么样子的。你在复杂的系统中 Docker 使用的越多,越是可能会发现技术上的空白和未来技术发展的方向。现在还处在 Docker 的发展期,任何你使用 Docker 创建的工具都有可能成为社区关注的热点。

Docker与虚拟机比较

Docker利用的是宿主机的内核,VM需要的是Guest OS。

虚拟机的缺点: 资源占用多、冗余步骤多、启动慢。

由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。

Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。

docker有着比虚拟机更少的抽象层。由亍docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。

docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个docker容器只需要几秒钟。

Docker安装

官网:https://www.docker.com/

文档地址:https://docs.docker.com/

仓库地址:https://hub.docker.com/

Docker 安装前提说明

Docker支持以下的CentOS版本:

CentOS 7 (64-bit)、CentOS 6.5 (64-bit) 或更高的版本。

目前,CentOS 仅发行版本中的内核支持 Docker。

Docker 运行在 CentOS 7 上,要求系统为64位、系统内核版本为 3.10 以上。

Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为 2.6.32-431 或者更高版本。

#查看自己的内核
#uname命令用于打印当前系统相关信息(内核版本号、硬件架构、主机名称和操作系统类型等)。
[root@localhost ~]# uname -r 
3.10.0-1127.19.1.el7.x86_64

Docker的基本组成

  • 镜像( image) :docker镜像就好比是一个模板 ,可以通过这个模板来创建容器服务, tomcat镜像===> run ==> tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
  • 容器( container) :Docker利用容器技术,独立运行一个或者一 个组应用 ,通过镜像来创建的。
  • 仓库( repository) :仓库就是存放镜像的地方。仓库分为公有仓库和私有仓库。Docker Hub (默认是国外的)
    ,我们可以配置容器服务器的镜像加速。

CentOS7下安装Docker

CentOS安装Docker 文档:https://docs.docker.com/install/linux/docker-ce/centos/

# 确定是centos7的版本
[root@localhost ~]# cat /etc/redhat-release 
CentOS Linux release 7.8.2003 (Core)

#yum安装gcc相关(确定centos7能上外网)
yum -y install gcc
yum -y install gcc-c++

#卸载旧版本
sudo yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine
    
#安装docker(不指定版本会默认下载最新版)
yum install docker

# 启动docker
systemctl start docker

# 设置开机自启
systemctl enable docker

#查看docker版本
docker version

测试运行hello-world:docker run hello-world(由于本地没有hello-world这个镜像,所以会下载一个hello-world的镜像,并在容器内运行。)

如果报178错误,说明SELinux为启动状态,需要临时关闭

setenforce 0表示临时关闭,setenforce 1表示临时开启。

正常运行hello-world

测试运行nginx:

#将80端口映射为8080,或者80:80还是原先的80端口,不可以不写。
docker run -p 8080:80 -d docker.io/nginx

卸载docker

systemctl stop docker
yum -y remover docker
rm -rf /var/lib/docker

配置阿里云镜像加速

Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从镜像仓库下载(默认是Docker Hub 公共注册服务器中的仓库)。

通过网址可以找到目标镜像 https://hub.docker.com/explore/

可以使用docker pull 命令来从仓库获取所需要的镜像。

下面的例子将从Docker Hub 仓库下载一个Ubuntu 18.04 操作系统的镜像。

# 该命令实际上相当于sudo docker pull registry.hub.docker.com/ubuntu:18.04
docker pull ubuntu:18.04

我们发现下载速度很慢。因为hub.docker.com这个网站是国外的,我们可以配置阿里云的镜像加速。

1、登录阿里云,搜索 容器镜像服务 找到后如下图:

2、编辑配置文件

#编辑配置文件
vi /etc/docker/daemon.json

#拷贝下面的内容/etc/docker/daemon.json中
{ "registry-mirrors": ["https://32e0cwh7.mirror.aliyuncs.com"] }

#刷新docker的配置
sudo systemctl daemon-reload
sudo systemctl restart docker

docker镜像的相关常用命令

#列出所有镜像
docker images 

#根据镜像id删除镜像
docker rmi 镜像id

docker run

Docker底层原理

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。

Docker命令

帮助命令

docker version #查看docker版本
docker info #显示全系统信息
docker --help #显示docker相关的所有命令及功能说明

镜像命令

1、docker images :列表本机上的镜像

同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。

如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,docker 将默认使用 ubuntu:latest 镜像。

docker images -[options]的说明:

-a 列表本地的所有镜像及子镜像 ,-q 只显示镜像ID ,--digests 显示镜像的摘要信息, --no-trunc 显示完整的镜像信息。

2、docker search: 镜像搜索命令

搜索网站:https://hub.docker.com

语法:docker search 镜像名称
docker search 镜像名称  -[options]  说明
--no-trunc 显示完整的镜像描述
-s 列出收藏数不少于指定值的镜像
--automated 只列出 automated build类型的镜像

3、docker pull :镜像下载命令

docker pull 镜像名称:[TAG]
例如:docker pull tomcat:8.5  下载8.5的镜像版本
     dokcer pull tomcat 默认下载最新的tomcat镜像版本 【latest】     

4、docker rmi: 镜像删除命令

删除单个  docker rmi -f 镜像ID
删除多个  docker rmi -f 镜像ID1 镜像ID2
         docker rmi -f 镜像名:[tag]  镜像名:[tag]
删除全部
         docker rmi -f $(docker images -qa)

容器命令

容器镜像是一个软件的轻量级独立可执行软件包,包含运行它所需的一切:代码,运行时的系统工具、系统库、设置。不管环境如何,集装箱化软件都可以运行相同的Linux和Windows应用程序。容器将软件与其周围环境隔离开来,例如开发环境和生产环境之间的差异,并有助于减少在同一基础架构上运行不同软件的团队之间的冲突。

有镜像才能创建容器,这个是根本要求

创建并启动容器

docker run [options] images [command][args] 

options说明(有些是一个减号,有些是两个减号)

--name="容器新名字":为容器指定一个名称;
-d:后台运行容器,并返回容器ID,也即启动守护式容器;
-i:以交互模式运行容器,通常与 -t 同时使用;
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
-P: 随机端口映射;
-p: 指定端口映射,有以下四种格式
   ip:hostPort:containerPort
   ip::containerPort
   hostPort:containerPort
   containerPort

交互式运行

#使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it centos /bin/bash 

列出当前所有正在运行的容器

docker ps [options]
-a :列出当前所有正在运行的容器+历史上运行过的
-l :显示最近创建的容器。
-n:显示最近n个创建的容器。
-q :静默模式,只显示容器编号。
--no-trunc :不截断输出。

其它命令

退出容器
1、exit:停止容器并退出
2、ctrl+P+Q  容器不停止退出

启动容器
docker start 容器ID或容器名称

重启容器
docker restart 容器ID或容器名称

停止容器
docker stop 容器ID或容器名称

强行停止容器
docker kill 容器ID或容器名称

删除已停止的容器
#单个删除
docker rm 容器ID

#多个删除
docker rm -f $(docker ps -aq)
docker ps -aq xargs docker rm


启用守护式容器

使用镜像centos:latest以后台模式启动一个容器

docker run -d centos

问题:docker ps -a 进行查看, 会发现容器已经退出

很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程。

容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。

这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动是应的service即可。例如 service nginx start

但这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀,因为他觉得他没事可做了。

所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行。

查看容器日志

如果是后台运行的程序,那么如何查看运行的日志呢?

1、先运行一个程序,在后台每隔两秒输出[hello]
docker run -d centos /bin/sh -c "while true;do echo hello;sleep 2;done"

2、查看日志
docker logs -tf --tail 10 容器ID

-t 是加入时间戳
-f 跟随最新的日志打印
--tail 数字 显示最后多少条

3、查看容器运行进程 
docker top 容器ID

4、查询容器内部细节
docker inspect 容器ID

5、进入正在进行的容器并以命令行交互
#方式1 重新打开一个新的终端,如果以这种方式进入容器,可以使用exit退出而不会关闭容器
docker exec -it 容器ID /bin/bash
#方式2 这种方式exit退出会关闭容器
docker attach 容器ID

#两种方式的区别
exec 在容器中打开新的终端 并且可以启动新的进程
attch 直接进行容器终端,不会启动新的进程

6、进入容器内拷贝文件到主机
docker cp 容器ID:容器内的路径  主机目录

Docker命令图示

Docker镜像

UnionFS

UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

平时我们安装进虚拟机的CentOS都是好几个G,为什么docker里的CentOS才几百M?

对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。

以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载。这样分层的最大的一个好处就是共享资源。

比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

镜像的特点

Docker镜像都是只读的,当容器启动时,一个新的可写的镜像被加载到镜像层的顶部。这一层通常被叫做容器层,容器层之下的都叫镜像层。

镜像的commit

作用:当镜像运行之后可以修改容器里面的内容,再提交成一个新的镜像

命令语法

docker commit -m='新的镜像的描述信息' -a='作者' 容器ID 要创建的目标镜像名:[标签名]

案例

docker commit -a='test' -m='del tomcat docs' dbebc1893880 coydone/tomcatnodocs:1.0

# 从hub上拉一下tomcat镜像当运行
docker run -it -p 8080:8080 tomcat 
p 主机端口:容器端口
P 随机分配端口
i 交互
t 终端

# 删除所有容器
docker rm -f $(docker ps -aq)

# 启动之前的镜像
docker run -d -p 8888:8080 镜像ID或仓库ID+版本

#启动自己的镜像
docker run -d -p 9999:8080 镜像ID 

Docker容器卷

将应用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的。容器之间希望有可能共享数据。

Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了。

为了能保存数据在docker中我们使用卷。

容器数据卷的作用:容器数据的持久化;容器之间继承和共享数据。

添加数据卷

直接使用命令添加

docker run -it -v /宿主机目录:/容器内目录 centos /bin/bash

#查看容器卷是否挂载成功
docker inspect 容器ID

查看容器卷和宿主机的数据共享:在宿主机的mycentos001中创建hello.txt文件并写入数据mycentos001,

进入容器查看container001里面有hello.txt文件数据和mycentos001里面的一样。

注意:在以上的例子中,默认的只能在宿主机里面写数据。

如果出以下的问题

解决办法:在挂载目录后多加一个--privileged=true参数即可

查看容器停止后,主机修改数据是否同步

带权限的处理方式

使用DockerFile添加

1、在宿主机的根目录下创建mydocker文件夹并进入

2、在当前目录创建一个DockerFile的文件

3、编写DockerFile

FROM centos
VOLUME ["/dataContainer1","/dataContainer2"]
CMD echo "finished,--------success1"
CMD /bin/bash

4、build生成一个新的镜像

docker build -f /mydocker/DockerFile -t coydone/centos .  #注意后面有一个点

5、启动容器

docker run -it --name='mycentos' coydone/centos

6、查看容器里面有两个容器卷

数据卷容器

作用:实现容器之间的数据共享。

操作思路

#以上面的coydone/centos为镜像,里面有dataContainer1和dataContailer2
#启动一个容器dc1  在dataContailer1里面添加dc1.txt
docker run -it --name='dc1' coydone/centos
cd /dataContailer1
touch dct.txt

#启动一个容器dc2 继承dc1  在dataContailer1里面添加dc2.txt
#启动一个容器dc3 继承dc2  在dataContailer1里面添加dc3.txt
#发现在dc3里面可以看到dc1.txt dc2.txt dc3.txt
#     dc1里面可以看到dc1.txt dc2.txt dc3.txt

#删除dc1容器之后在dc2和dc3里面还是可以看到dc1.txt dc2.txt dc3.txt
#说明数据卷容器的生命周期一直持续到没有容器使用它为止

Dockerfile

Dockerfile是用来构建Docker镜像的构建文件,是由一系列的命令和参数构成的脚本。

Dokcerfile的构建步骤

1、编写Dokcerfile文件

2、docker build 生成新的镜像

3、docker run 运行镜像

以centos的镜像为例来说明 https://hub.docker.com/_/centos

#centos的Dockerfile
FROM scratch
ADD centos-7.8.2003-x86_64-docker.tar.xz /

LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20200504" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-05-04 00:00:00+01:00"

CMD ["/bin/bash"]

DockerFile构建过程解析

基础知识

1、每条保留字指令都必须为大写字母后面要跟随至少一个参数
2、指令从上到下顺序执行
3、#表示注释
4、每条指令都会创建一个新的镜像层,并对镜像进行提交

大致流程

1、docker从基础镜像运行一个容器

2、执行一条指令并对容器进行修改

3、执行类似于docker commit的操作提交一个新的镜像

4、docker再基于刚提交的新的镜像运行一个新的容器

5、执行dockerfile的下一个指令再人生第2点直到没有指令

总结

从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:

  • Dockerfile是软件的原材料,
  • Docker镜像是软件的交付品,

  • Docker容器则可以认为是软件的运行态。

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

1、Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。

Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;

2、Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;

3、Docker容器,容器是直接提供服务的。

DockerFile体系结构

FROM:基础镜像,当前新镜像是基于哪个镜像的。

MAINTAINER:镜像维护者的姓名和邮箱地址。

RUN:容器构建时需要运行的命令。

EXPOSE:当前容器对外暴露的端口。

WORKDIR:指定在创建容器后,终端默认登陆进来的工作目录。

ENV:用来在构建镜像过程中设置环境变量。

ADD:将宿主机目录下的文件拷贝进镜像并且ADD命令会自动处理URL和解压tar包。

COPY:类似ADD,拷贝文件和目录到镜像中,语法COPY src dest COPY [''src","dest"]

VOLUME:容器数据卷,用于数据保存和持久化工作。

CMD:指定一个容器启动时要运行的命令格式。shell:CMD <命令>exec CMD ['可执行文件',"参数1","参数2"],DockerFile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换。

ENTEYPONT:指定一个容器启动时要运行的命令。ENTRYPOINT的目地和CMD一样,都是在指定容器启动程序及参数。

OBBUILD:当构建一个被继承的Dockerfile时运行命令,父镜像在被子镜像继承后触发父镜像的onbuild。

自定义mycentos镜像

docker Hub上99%的镜像都是通过base镜像中安装和配置需要的软件构建出来的。

目的:设置登陆后的默认路径、vim编辑器。

# 进入mydocker目录创建DockerFile文件编写
[root@localhost ~]# mkdir dockerfile
[root@localhost ~]# cd dockerfile/
[root@localhost dockerfile]# pwd
/root/dockerfile
[root@localhost dockerfile]# mkdir mycentos
[root@localhost dockerfile]# cd mycentos/
[root@localhost mycentos]# pwd
/root/dockerfile/mycentos
[root@localhost mycentos]# touch Dockerfile
[root@localhost mycentos]# chmod 777 Dockerfile
[root@localhost mycentos]# vim Dockerfile

#基于centos镜像
FROM centos
#设置作者和邮箱
MAINTAINER coydone<coydone@qq.com>
#声明变量
ENV MYPATH /usr/local
#设置工作目录
WORKDIR $MYPATH
#安装VIM -y表示所有的确认的地方全部yes
RUN yum -y install vim
#设置对外暴露的端口,提示作用
EXPOSE 80
#打开一个终端
CMD echo $MYPATH
CMD echo "success---------ok"
CMD /bin/bash

#使用docker build命令构建镜像
[root@localhost mycentos]# docker build -t mycentos:1.0 .
#使用docker run命令运行
[root@localhost mycentos]# docker run -it mycentos:1.0 /bin/bash

#列出镜像的变更历史
docker history 镜像名:TAG

Dockerfile 中可以有多个CMD的命令,但只有最后一个生效,CMD会被docker run之后的参数替换掉。

使用ENTRYPOINT来运行命令,在run 运行的参数会追加到新的命令后面。

自定义项目Docker

打jar包镜像

准备工作:创建一个springboot项目并打jar包,把jar包拷贝到linux的/mydocker/myjarproject目录下。

# 编写Dockerfile
FROM openjdk:8u181-jdk-alpine
ARG workdir=/app
VOLUME ${workdir}
WORKDIR ${workdir}
ADD coydone.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]

#构建镜像
docker build -t myapp:1.0 .

#运行镜像
docker run -it --name 'myapp' -p 8888:8080 myapp:1.0

打war包镜像

准备工作:把上面的项目改成war项目打war包放到linux。

#编写Dockerfile

#基于tomcat
FROM tomcat:alpine
#作者信息
MAINTAINER  coydone<coydone@qq.com>
#声明环境变量
ENV TOMCATPATH=/usr/local/tomcat
#设置工作目录
WORKDIR ${TOMCATPATH}/webapps
#删除webapps下面的所有项目
RUN  rm -rf * 
#添加war包到当前的工作目录下的webapps
COPY coydone.war ./coydone.war
#更改war包的名字
RUN mv ./coydone.war ./ROOT.war
#显示webapps下面的文件
RUN ls -lh ./
#解压war包
RUN unzip ./ROOT.war -d ./
#删除ROOT.war包
RUN rm -rf ./ROOT.war
EXPOSE 8080
CMD ["bin/catalina.sh", "run"]

#构建镜像
docker build -t mywarapp:1.0 .

#运行镜像
docker run -it -p 8888:8080 mywarapp:1.0 

Docker安装常用镜像

安装Tomcat

docker hub查找tomcat镜像 docker search tomcat,从docker hub上拉取tomcat镜像到本地 docker pull tomcat,使用docker images查看是否有拉取到tomcat,使用tomcat镜像运行镜像成为容器:

docker run -it -p 8080:8080 tomcat 
    -p 主机端口:容器端口
    -P 随机分配端口
    -i  交互
    -t 终端  

安装MySQL

1、docker hub上查找mysql镜像 docker search mysql

2、docker hub上拉取mysql镜像到本地:docker pull mysql:5.7

使用mysql5.7镜像创建容器运行存在问题
[root@localhost ~]docker run -it -p 3306:3306 mysql:5.7
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_PASSWORD
这是因为mysql镜像启动需要加载设置用户名和密码

docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

远程连接问题

mysql远程连接发现报错,这是因为docker 的mysql里面的localhost  -root 的密码是启动时设置的是123456而%-root的没有设置

解决方法:
1、进入mysql的docker 容器
docker exec -it mysql /bin/bash

2、登陆mysql
mysql -u root -p
输入密码

3、使用mysql数据库
use mysql

4、执行修改密码的命令
update user set password=password(123456) where host='%'
5、重启mysql的docker 容器
exit #退出
#重启mysql
docker restart mysql

主机IP无法访问:

如果出现使用宿主机IP无法访问的情况 在宿主机里面执行如下命令
请顺序运行以下命令:

nmcli connection modify docker0 connection.zone trusted

systemctl stop NetworkManager.service

firewall-cmd --permanent --zone=trusted --change-interface=docker0

systemctl start NetworkManager.service

nmcli connection modify docker0 connection.zone trusted

systemctl restart docker.service

再重启mysql和tomcat的容器

相关文件地址配置

可以在运行容器时设置 :
docker run -p 3306:3306 --name mysql -v $PWD/conf/my.cnf:/etc/mysql/my.cnf -v $PWD/logs:/logs -v $PWD/data:/mysql_data -e MYSQL_ROOT_PASSWORD=123456 -d mysql
但要求对应路径对应文件已存在,才能成功挂载相内容到对应位置。

命令说明:
-p 3306:3306:将容器的3306端口映射到主机的3306端口
-v $PWD/conf/my.cnf:/etc/mysql/my.cnf:将主机当前目录下的conf/my.cnf挂载到容器的/etc/mysql/my.cnf
-v $PWD/logs:/logs:将主机当前目录下的logs目录挂载到容器的/logs
-v $PWD/data:/mysql_data:将主机当前目录下的data目录挂载到容器的/mysql_data
-e MYSQL_ROOT_PASSWORD=123456:初始化root用户的密码

本地镜像发布到阿里云

1、登录阿里云搜索容器镜像服务,创建镜像仓库。

2、设置镜像仓库的访问密码

3、打开镜像仓库,查看基本信息,按照操作指南来做。

#将镜像推送到Registry
$ sudo docker login --username=xxx registry.cn-beijing.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-beijing.aliyuncs.com/coydone/mywar:[镜像版本号]
$ sudo docker push registry.cn-beijing.aliyuncs.com/coydone/mywar:[镜像版本号]

#从Registry中拉取镜像
$ sudo docker pull registry.cn-beijing.aliyuncs.com/coydone/mywar:[镜像版本号]

Docker的四种网络模式

我们安装Docker之后,使用ifconfig或者ip addr命令查看网络信息时,会发现有一个docker0。

docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式:

  • bridge模式:使--net =bridge指定,默认设置;

  • host模式:使--net =host指定;

  • none模式:使--net =none指定;

  • container模式:使--net =container:NAMEorID指定。

可以使用docker network ls来查看:

bridge模式

bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将并将一个主机上的Docker容器连接到一个虚拟网桥上。当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理端口,在宿主机上作为一块虚拟网卡使用)。

具体操作:

启动容器:(由于是默认设置,这里没指定网络–net =bridge,可以看到容器内创建了eth0)

使用ping命令连接Host网络发现,容器与Host网络是连通的:

eth0实际上是veth pair的一端,另一端(veth945c)连接在docker0网桥上:

host模式

如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

使用host模式启动容器:

使用host模式启动容器后可以发现,使用ip addr查看网络环境时,看到的都是宿主机上的信息。这种方式创建出来的容器,可以看到host上的所有网络设备。

容器中,对这些设备有全部的访问权限。因此docker提示我们,这种方式是不安全的。如果在隔离良好的环境中(比如租户的虚拟机中)使用这种方式,问题不大。

none模式

在none模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

使用--net =none模式启动容器:

container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。

使用--net =container模式启动容器:

通过该例子可以看出来,两者的网络完全相同。

posted @ 2020-10-27 08:57  coydone  阅读(81)  评论(0编辑  收藏  举报