Docker 介绍

1.Docker 介绍

什么是 Docker?

说实话关于 Docker 是什么并太好说,下面我通过四点向你说明 Docker 到底是个什么东西。

  • Docker 是世界领先的软件容器平台,基于 Go 语言 进行开发实现。
  • Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放开发人员。
  • 用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
  • Docker可以对进程进行封装隔离,属于操作系统层面的虚拟化技术。 由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。

官网地址:https://www.docker.com/ 。

docker是一个用Go语言实现的开源项目,可以让我们方便的创建和使用容器,docker将程序以及程序所有的依赖都打包到docker container,这样你的程序可以在任何环境都会有一致的表现,这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口,程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系

因此我们可以看到docker可以屏蔽环境差异,也就是说,只要你的程序打包到了docker中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华了,不会再有“在我的环境上可以运行”,真正实现“build once, run everywhere”。

此外docker的另一个好处就是快速部署,这是当前互联网公司最常见的一个应用场景,一个原因在于容器启动速度非常快,另一个原因在于只要确保一个容器中的程序正确运行,那么你就能确信无论在生产环境部署多少都能正确运行。

Docker 介绍_第1张图片什么是Docker

为什么要用 Docker?

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

传统的开发流程中,我们的项目通常需要使用 MySQL、Redis、FastDFS 等等环境,这些环境都是需要我们手动去进行下载并配置的,安装配置流程极其复杂,而且不同系统下的操作也不一样。

Docker 的出现完美地解决了这一问题,我们可以在容器中安装 MySQL、Redis 等软件环境,使得应用和环境架构分开,它的优势在于:

  1. 一致的运行环境,能够更轻松地迁移
  2. 对进程进行封装隔离,容器与容器之间互不影响,更高效地利用系统资源
  3. 可以通过镜像复制多个一致的容器

另外,《Docker 从入门到实践》[1] 这本开源书籍中也已经给出了使用 Docker 的原因。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6wfvt2g-1631610680293)(Docker 介绍.assets/640-1625468324110.webp)]

2.Docker 的安装

Windows

接下来对 Docker 进行安装,以 Windows 系统为例,访问 Docker 的官网:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VCVu3tNW-1631610680295)(Docker 介绍.assets/640-1625451275342.png)]

然后点击Get Started

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8pOqPgU8-1631610680296)(Docker 介绍.assets/640-1625451279175.png)]

在此处点击Download for Windows即可进行下载。

如果你的电脑是Windows 10 64位专业版的操作系统,则在安装 Docker 之前需要开启一下Hyper-V,开启方式如下。打开控制面板,选择程序:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KxMBGyJ-1631610680297)(Docker 介绍.assets/640-1625451281862.png)]

点击启用或关闭Windows功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FqZvvcFR-1631610680298)(Docker 介绍.assets/640-1625451283684.png)]

勾选上Hyper-V,点击确定即可:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IGG8BvC8-1631610680299)(Docker 介绍.assets/640-1625451285532.png)]

完成更改后需要重启一下计算机。

开启了Hyper-V后,我们就可以对 Docker 进行安装了,打开安装程序后,等待片刻点击Ok即可:

Docker 介绍_第2张图片

安装完成后,我们仍然需要重启计算机,重启后,若提示如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWcNX6NI-1631610680301)(Docker 介绍.assets/640-1625451265686.png)]

它的意思是询问我们是否使用 WSL2,这是基于 Windows 的一个 Linux 子系统,这里我们取消即可,它就会使用我们之前勾选的Hyper-V虚拟机。

注意 修改docker daemon配置文件

{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn/"
  ],
  "insecure-registries": [],
  "debug": false,
  "experimental": true
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B75MO0tJ-1631610680301)(Docker 介绍.assets/image-20210531213119735.png)]

Mac

直接使用 Homebrew 安装即可

brew install --cask docker

Linux

下面来看看 Linux 中如何安装 Docker,这里以 CentOS7 为例。

在测试或开发环境中,Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,执行这个脚本后就会自动地将一切准备工作做好,并且把 Docker 的稳定版本安装在系统中。

curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun

安装完成后直接启动服务:

$ systemctl start docker

推荐设置开机自启,执行指令:

$ systemctl enable docker

Linux 无Internet安装Docker

1、查看内核:
官方文档要求Linux kernel至少3.8以上,且docker只能运行在64位的系统中。由于RHEL6和CentOS6的内核版本为2.6,因此必须要先升级内核。建议使用centos7及以上版本的系统。

执行查看内核的命令:uname -r

Centos6内核如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kiDSuush-1631610680302)(Docker 介绍.assets/20210516132127799.png)]

Centos7内核如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1aTbrLl9-1631610680302)(Docker 介绍.assets/2021051613213413.png)]

2、无网安装docker:
(1)关闭selinux:
编辑/etc/selinux/config文件。然后重新启动服务器。

(2)下载软件包:
软件下载地址:https://download.docker.com/linux/static/stable/

选择自己需要的版本,ce版本是社区版本(免费),ee版本是商业版本(付费)。

(3)上传软件包:
将下载的软件包上传到服务器中指定的文件夹中。

(4)解压软件包:
将服务器指定文件夹中的软件包进行解压。

(5)移动文件:
移动解压后的docker中的所有文件到/usr/bin/中。

(6)编写docker服务脚本:
编写docker.service服务文件;

docker.service

docker.service
 
 
 
[Unit]
 
Description=Docker Application Container Engine
 
Documentation=https://docs.docker.com
 
After=network-online.target firewalld.service
 
Wants=network-online.target
 
 
 
[Service]
 
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
 
ExecStart=/usr/bin/dockerd
 
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
 
LimitNOFILE=infinity
 
LimitNPROC=infinity
 
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
 
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
 
Delegate=yes
# kill only the docker process, not all processes in the cgroup
 
KillMode=process
# restart the docker process if it exits prematurely
 
Restart=on-failure
 
StartLimitBurst=3
 
StartLimitInterval=60s
 
 
 
[Install]
 
WantedBy=multi-user.target

(7)上传服务脚本及授权:
将docker.service文件上传到服务器/etc/systemd/system/中,并授权:chmod 777 /etc/systemd/system/docker.service

(8)系统重新加载配置文件:
由于增加了docker.service,因此需要让系统重新加载配置文件。执行命令:systemctl daemon-reload

3、开机自启docker服务:
执行开机自启添加docker服务命令:

systemctl enable docker

4、启动docker服务:
执行启动docker服务命令:

systemctl start docker

5、关闭docker服务:
执行关闭docker服务命令:

systemctl stop docker

6、查看docker服务:
执行查看docker服务命令:

systemctl status docker

7、查看docker版本:
执行查看docker版本命令:

docker version

3. Docker 中的几个概念

在正式学习 Docker 之前,我们需要了解 Docker 中的几个核心概念:

镜像

镜像就是一个只读的模板,镜像可以用来创建 Docker 容器,一个镜像可以创建多个容器

容器

容器是用镜像创建的运行实例,Docker 利用容器独立运行一个或一组应用。它可以被启动、开始、停止、删除,每个容器都是相互隔离的、保证安全的平台。可以把容器看作是一个简易的 Linux 环境和运行在其中的应用程序。容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的

仓库

仓库是集中存放镜像文件的场所。仓库和仓库注册服务器是有区别的,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签。仓库分为公开仓库和私有仓库两种形式,最大的公开仓库是 DockerHub,存放了数量庞大的镜像供用户下载,国内的公开仓库有阿里云、网易云等

总结

通俗点说,一个镜像就代表一个软件;而基于某个镜像运行就是生成一个程序实例,这个程序实例就是容器;而仓库是用来存储 Docker 中所有镜像的。

其中仓库又分为远程仓库和本地仓库,和 Maven 类似,倘若每次都从远程下载依赖,则会大大降低效率,为此,Maven 的策略是第一次访问依赖时,将其下载到本地仓库,第二次、第三次使用时直接用本地仓库的依赖即可,Docker 的远程仓库和本地仓库的作用也是类似的。

3.1Docker带来了哪些好处

看完上面的例子,你可能仍然不会觉得Docker有什么好处,其实真正使用了Docker之后,你会觉得简直爱不释手。粗略来说,Docker好处有:

1.保证了线上线下环境的一致性
我们在线下的开发环境使用Docker构建好weaapp的镜像后,可以直接在线上使用一个镜像,保证了线上线下环境的一致性,再也不会有在线下开发环境中运行正常,而部署到线上各种错误了。

2.极大的简化了webapp的部署流程
在不使用Docker时,我们部署app时,要先搭建好app运行所需环境,这个过程做过的人都知道多么枯燥繁琐,一不小心还出错。而有了Docker,我们只需要直接构建一个我们webapp的镜像然后将其运行即可,无论在多少台服务器中部署,都是如此。再比如,使用Docker之前要搭建一个WordPress对于新手来说是有些困难的,而有了Docker,只需要从DockerHub上pull一个WordPress镜像并启动就可以了,非常非常方便。

3.实现了沙盒机制,提高了安全性
由于webapp运行在容器中,与操作系统隔离开了,从而使操作系统基本不可能受到破坏,另外如果webapp因为攻击而瘫痪,并不需要重启服务器,直接重启容器或者再启动一个镜像就可以了。

4.实现了模块化,提高了复用性
在二中使用Docker的第二种方式就可以看出,我们将数据库和Tomcat运行在不同的容器中,从某种角度来说,这也降低了模块之间的耦合性,便于拓展。比如我们要把MySQL替换为oracle,只需要再构建一个oracle镜像并启动与Tomcat连接即可,非常方便。对于我们构建的镜像,在其他app中直接拿来用就可以了,不必重复劳动。

5.实现了虚拟化,提高硬件利用率
有了Docker,我们可以在一台服务器上运行很多webapp,充分利用闲置资源。这时候,服务器的操作系统就类似于货轮,而一个个Docker容器就相当于货轮上的一个个集装箱。现在大热的云服务市场,不少就用了Docker。举个例子来说,现在我们有一台操作系统为Ubuntu14.04的服务器,我们构建不同版本的ubuntu镜像并启动,并且为不同的用户分配不同的容器。这样,用一台服务器
可以虚拟出n个运行着不同操作系统的虚拟服务器,而对于用户来说,这些是透明的––用户则认为自己拥有一台完整的服务器。据我推测,阿里云的服务器就是这么干的。这充分利用了闲置的硬件资源。

4.Docker 操作

4.1 Docker 镜像操作

4.1.0 搜索镜像

一般在下载镜像前我们需要搜索一下镜像有哪些版本才能对指定版本进行下载,使用指令:

$ docker search MySQL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f951irt8-1631610680303)(Docker 介绍.assets/640-1625453244914.png)]

不过该指令只能查看 MySQL 相关的镜像信息,而不能知道有哪些版本,若想知道版本,则只能这样查询:

$ docker search MySQL:5.5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQC4oCCO-1631610680304)(Docker 介绍.assets/640-1625453243166.png)]

若是查询的版本不存在,则结果为空:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNNX4KOf-1631610680304)(Docker 介绍.assets/640-1625453241932.png)]

除了命令行搜索外,也可以在网页上搜索。

和 GitHub 一样,Docker 也提供了一个 DockerHub 用于查询各种镜像的地址和安装教程,为此,我们先访问 DockerHub:https://hub.docker.com/[2]

Docker 介绍_第3张图片

在左上角的搜索框中输入MySQL并回车:

Docker 介绍_第4张图片

可以看到相关 MySQL 的镜像非常多,若右上角有OFFICIAL IMAGE标识,则说明是官方镜像,所以我们点击第一个 MySQL 镜像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5o2tLUg-1631610680306)(Docker 介绍.assets/640-1625451256352.png)]

右边提供了下载 MySQL 镜像的指令为docker pull MySQL,但该指令始终会下载 MySQL 镜像的最新版本。

若是想下载指定版本的镜像,则点击下面的View Available Tags

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EpVOCdkR-1631610680306)(Docker 介绍.assets/640-1625451258159.png)]

这里就可以看到各种版本的镜像,右边有下载的指令,所以若是想下载 5.7.32 版本的 MySQL 镜像,则执行:

$ docker pull MySQL:5.7.32

然而下载镜像的过程是非常慢的,所以我们要确保配置镜像源加速下载。(见2.Docker的安装)

4.1.1 拉取镜像

拉取镜像的标准命令:

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

具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。

  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub(docker.io)。
  • 仓库名:这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。

4.1.2 查看本地镜像

若想查看 Docker 中当前拥有哪些镜像,则可以使用 docker imagesdocker image ls 命令。

[root@izrcf5u3j3q8xaz ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
MySQL         5.7.32    f07dfa83b528   11 days ago     448MB
tomcat        latest    feba8d001e3f   2 weeks ago     649MB
nginx         latest    ae2feff98a0c   2 weeks ago     133MB
hello-world   latest    bf756fb1ae65   12 months ago   13.3kB

其中REPOSITORY镜像名TAG版本标志IMAGE ID镜像 id(唯一的)CREATED创建时间,注意这个时间并不是我们将镜像下载到 Docker 中的时间,而是镜像创建者创建的时间,SIZE为镜像大小。

该指令能够查询指定镜像名:

$ docker image MySQL

若如此做,则会查询出 Docker 中的所有 MySQL 镜像:

[root@izrcf5u3j3q8xaz ~]# docker images MySQL
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
MySQL        5.6       0ebb5600241d   11 days ago     302MB
MySQL        5.7.32    f07dfa83b528   11 days ago     448MB
MySQL        5.5       d404d78aa797   20 months ago   205MB

该指令还能够携带-p参数:docker images -q-q表示仅显示镜像的 id:

[root@izrcf5u3j3q8xaz ~]# docker images -q
0ebb5600241d
f07dfa83b528
feba8d001e3f
d404d78aa797

可以通过 docker system df 命令来便捷的查看镜像、容器、数据卷所占用的空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7seEArQn-1631610680308)(Docker 介绍.assets/1625453888136.png)]

除此以外,docker image ls 还支持强大的过滤器参数 --filter,或者简写 -f。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 mongo:3.2 之后建立的镜像,可以用下面的命令:

$ docker image ls -f since=mongo:3.2
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redis               latest              5f515359c7f8        5 days ago          183 MB
nginx               latest              05a60462f8ba        5 days ago          181 MB

想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可。

此外,如果镜像构建时,定义了 LABEL,还可以通过 LABEL 来过滤。

$ docker image ls -f label=com.example.version=0.1...

4.1.3 删除镜像

删除镜像使用指令:

$ docker image rm MySQL:5.5

若是不指定版本,则默认删除的也是最新版本。

还可以通过指定镜像 id 进行删除:

$ docker image rm bf756fb1ae65

然而此时报错了:

[root@izrcf5u3j3q8xaz ~]# docker image rm bf756fb1ae65
Error response from daemon: conflict: unable to delete bf756fb1ae65 (must be forced) - image is being used by stopped container d5b6c177c151

这是因为要删除的hello-world镜像正在运行中,所以无法删除镜像,此时需要强制执行删除:

$ docker image rm -f bf756fb1ae65

该指令会将镜像和通过该镜像执行的容器全部删除,谨慎使用。

Docker 还提供了删除镜像的简化版本:docker rmi 镜像名:版本标志

此时我们即可借助rmi-p进行一些联合操作,比如现在想删除所有的 MySQL 镜像,那么你需要查询出 MySQL 镜像的 id,并根据这些 id 一个一个地执行docker rmi进行删除,但是现在,我们可以这样:

$ docker rmi -f $(docker images MySQL -q)

首先通过docker images MySQL -q查询出 MySQL 的所有镜像 id,-q表示仅查询 id,并将这些 id 作为参数传递给docker rmi -f指令,这样所有的 MySQL 镜像就都被删除了。

4.1.4 制作镜像

Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),**每一条指令构建一层,**因此每一条指令的内容,就是描述该层应当如何构建。

如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

一般的,Dockerfile 分为四部分 :

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时执行指令

支持以#为开头的注释。

例子:

FROM ubuntu
MAINTAINER docker_user [email protected]
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
CMD /usr/sbin/nginx
4.1.4.1 FROM 指定基础镜像

在 Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginxredismongomysqlhttpdphptomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 nodeopenjdkpythonrubygolang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntudebiancentosfedoraalpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

FROM scratch

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。GO语言开发的应用常用此方法构建镜像。

4.1.4.2 RUN 执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。 其格式有两种:

  • shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。

  • RUN echo 'hello world'
    
  • exec 格式:RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。 别使用单引号。

注意: 不要在一个Dockerfile中运行多个RUN,这要会导致建立多个层。应该只写一个RUN指令。

RUN echo 'hello world'
	&& rm -rf /var/lib/apt/lists/*

4.1.4.3 CMD 或 ENTRYPOINT 启动容器运行命令

指定启动容器时执行的命令,每个 Dockerfile 只能有一条CMD
命令。如果指定了多条命令,只有最后一条会被执行。

支持三种格式

CMD ["executable","param1","param2"] # 使用 exec 执行,推荐方式;
CMD command param1 param2 # 在 /bin/sh 中执行,提供给需要交互的应用;
CMD ["param1","param2"] # 提供给 ENTRYPOINT 的默认参数;

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2(shell中执行)。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD指定的命令。

CMD给出的是一个容器的默认的可执行体。即容器启动以后,默认的执行的命令。若docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么,将使用cmd指定的默认的执行命令执行。

4.1.4.6 EXPOSE 暴露端口
EXPOSE  [...]。

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P
,Docker 主机会自动分配一个端口转发到指定的端口。

注意: EXPOSE 有点鸡肋。

1、真正的暴露端口是在创建容器 run 的时候指定的 -p 或者 -P 参数,先来说说 -p 参数后面跟的是【主机端口:容器端口】,那么问题就来了既然在运行的时候还需要指定端口那么 EXPOSE还要什么用呢!

2、当我们创建容器 run 的时候指定参数是 -P,那么在运行之后 会把 EXPOSE 的端口随机映射到主机的不同端口,这时问题又来了既然映射到不同的端口那么容器的端口就是是随机的不确定的,那就要在运行之后才能知道端口,这样使用起来是极其不便的。

4.1.4.7 COPY 或 ADD 宿主机文件拷贝进容器

COPY指令和ADD指令的唯一区别在于是否支持从远程URL获取资源。COPY指令只能从执行docker build所在的主机上读取资源并复制到镜像中。而ADD指令还支持通过URL从远程服务器读取资源并复制到镜像中。

COPY 复制文件

格式:

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]

COPY package.json /usr/src/app/
COPY hom?.txt /mydir/
COPY hom* /mydir/

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

<源路径> 可以是多个,甚至可以是通配符。

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

此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。

ADD 复制后可自动解压 (稍许鸡肋)

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。

比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用

如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。

在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu 中:

FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz  /

但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 ADD 命令了。

在 Docker 官方的最佳实践文档中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。

另外需要注意的是,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。

因此在 COPYADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

4.1.4.8 WORKDIR 指定工作路径
WORKDIR /path/to/workdir。

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c。

4.1.4.9 MAINTAINER
MAINTAINER  #指定维护者信息。
4.1.4.10 VOLUMN

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

# 声明容器中的 /root/httpServer/log 目录为挂载点
VOLUME /root/httpServer/log
4.1.4.11 Build 构建镜像
$ docker build -t <仓库名/镜像名> <dockerfile路径>
$ sudo docker build -t myrepo/myapp /tmp/test1/

-t, --tag list          

4.2 Docker 容器操作

4.2.1 容器的创建

掌握了镜像的相关指令之后,我们需要了解一下容器的指令,容器是基于镜像的。

若需要通过镜像运行一个容器,则使用:

$ docker run --name tomcat -p 8080:8080 tomcat:8.0-jre8

运行后查看一下当前运行的容器:docker ps

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-grpROhQn-1631610680308)(Docker 介绍.assets/640-1625456256674.png)]

其中

CONTAINER_ID容器的 id

IMAGE镜像名

COMMAND容器内执行的命令

CREATED为容器的创建时间

STATUS容器的状态

PORTS容器内服务监听的端口

NAMES容器的名称

通过该方式运行的 tomcat 是不能直接被外部访问的,因为容器具有隔离性,若是想直接通过 8080 端口访问容器内部的 tomcat,则需要对宿主机端口与容器内的端口进行映射

$ docker run -p 8080:8080 tomcat:8.0-jre8

解释一下这两个端口的作用(8080:8080),第一个 8080 为宿主机端口,第二个 8080 为容器内的端口,外部访问 8080 端口就会通过映射访问容器内的 8080 端口。

此时外部就可以访问 Tomcat 了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbpWfVVi-1631610680309)(Docker 介绍.assets/640-1625456253303.png)]

若是这样进行映射:

$ docker run -p 8089:8080 tomcat:8.0-jre8

则外部需访问 8089 端口才能访问 tomcat,需要注意的是,每次运行的容器都是相互独立的,所以同时运行多个 tomcat 容器并不会产生端口的冲突。

容器还能够以后台的方式运行,这样就不会占用终端。 命令中加上 -d :

$ docker run -d -p 8080:8080 tomcat:8.0-jre8

启动容器时默认会给容器一个名称,但这个名称其实是可以设置的,使用指令:

$ docker run -d -p 8080:8080 --name tomcat01 tomcat:8.0-jre8

此时的容器名称即为 tomcat01,容器名称必须是唯一的。

再来引申一下docker ps中的几个指令参数,比如-a

$ docker ps -a

该参数会将运行和非运行的容器全部列举出来:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QecJHKnx-1631610680309)(Docker 介绍.assets/640-1625456249443.png)]

-q参数将只查询正在运行的容器 id:docker ps -q

[root@izrcf5u3j3q8xaz ~]# docker ps -q
f3aac8ee94a3
074bf575249b
1d557472a708
4421848ba294

若是组合使用,则查询运行和非运行的所有容器 id:docker ps -qa

[root@izrcf5u3j3q8xaz ~]# docker ps -aq
f3aac8ee94a3
7f7b0e80c841
074bf575249b
a1e830bddc4c
1d557472a708
4421848ba294
b0440c0a219a
c2f5d78c5d1a
5831d1bab2a6
d5b6c177c151

4.2.3 容器的启停

接下来是容器的启动、停止、重启指令。 (操作针对已经创建好的容器)

$ docker start c2f5d78c5d1a

通过该指令能够将已经停止运行的容器运行起来,可以通过容器的 id 启动,也可以通过容器的名称启动。

$ docker restart c2f5d78c5d1a

该指令能够重启指定的容器。

$ docker stop c2f5d78c5d1a

该指令能够停止指定的容器。

$ docker kill c2f5d78c5d1a

该指令能够直接杀死指定的容器。

以上指令都能够通过容器的 id 和容器名称两种方式配合使用

容器的状态可以通过以下命令查看:

 $ docker container ls -a 

$ docker ps -a 

4.2.3 容器的删除

当容器被停止之后,容器虽然不再运行了,但仍然是存在的,若是想删除它,则使用指令:

$ docker rm d5b6c177c151

需要注意的是容器的 id 无需全部写出来,只需唯一标识即可。

若是想删除正在运行的容器,则需要添加-f参数强制删除:

$ docker rm -f d5b6c177c151

若是想删除所有容器,则可以使用组合指令:

$ docker rm -f $(docker ps -qa)

先通过docker ps -qa查询出所有容器的 id,然后通过docker rm -f进行删除。


4.2.4 查看容器运行状态(日志)

当容器以后台的方式运行时,我们无法知晓容器的运行状态,若此时需要查看容器的运行日志,则使用指令:

$ docker logs 289cc00dc5ed

这样的方式显示的日志并不是实时的,若是想实时显示,需要使用-f参数:

$ docker logs -f 289cc00dc5ed

通过-t参数还能够显示日志的时间戳,通常与-f参数联合使用:

$ docker logs -ft 289cc00dc5ed

查看容器内运行了哪些进程,可以使用指令:

$ docker top 289cc00dc5ed

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJ0HZAWR-1631610680310)(Docker 介绍.assets/640-1625468162513.png)]

4.2.5 进入容器(向Linux一样操作容器)

若是想与容器进行交互,则使用指令:

$ docker exec -it 289cc00dc5ed bash

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9R1c8HDK-1631610680311)(Docker 介绍.assets/640-1625468159692.png)]

-i -t 参数

docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。

只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。

-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

此时终端将会进入容器内部,执行的指令都将在容器中生效,在容器内只能执行一些比较简单的指令,如:ls、cd 等,若是想退出容器终端,重新回到 CentOS 中,则执行exit 或者 ctrl+D即可。

4.2.7 容器\镜像的导出、导入

镜像载入/导出:docker load/docker save;将一个镜像导出为文件,再使用docker load命令将文件导入为一个镜像,会保存该镜像的的所有历史记录。比docker export命令导出的文件大,很好理解,因为会保存镜像的所有历史记录。

docker save的应用场景是,如果你的应用是使用docker-compose.yml编排的多个镜像组合,但你要部署的客户服务器并不能连外网。这时,你可以使用docker save将用到的镜像打个包,然后拷贝到客户服务器上使用docker load载入。

docker save如果指定的是container,docker save将保存的是容器背后的image。

docker 如果将多个镜像打包, 导入也将导入多个镜像。

$ docker load [OPTIONS] image.tar
$ docker load -i image.tar

从标准输入或文件导入镜像

Options:
  -i, --input string   从tar文件读取
  -q, --quiet          抑制导入结果输出

$ docker save [OPTIONS] IMAGE [IMAGE...]
$ docker save -o image.tar imageID #保存为文件
$ docker save imageID > image.tar #将标准输出定向到文件

Save one or more images to a tar archive (streamed to STDOUT by default)
保存一个或多个镜像为tar机构文件,但其默认为标准输出

Options:
  -o, --output string   输出为文件,而不是标准输出

容器导入/导出:docker import/docker export;将一个容器导出为文件,再使用docker import命令将容器导入成为一个新的镜像,但是相比docker save命令,容器文件会丢失所有元数据和历史记录,仅保存容器当时的状态,相当于虚拟机快照。

另外一点是,docker import可以指定IMAGE[:TAG],说明我们可以为镜像指定新名称。如果本地镜像库中已经存在同名的镜像,则原有镜像的名称将会被剥夺,赋给新的镜像。原有镜像将成为孤魂野鬼,只能通过IMAGE ID进行操作。

docker export的应用场景主要用来制作基础镜像,比如你从一个ubuntu镜像启动一个容器,然后安装一些软件和进行一些设置后,使用docker export保存为一个基础镜像。然后,把这个镜像分发给其他人使用,比如作为基础的开发环境。

$ docker export [OPTIONS] CONTAINER
$ docker export -o 导出文件名 容器名
$ docker export -o postgres-export.tar postgres
将容器的文件系统导出为tar文件打包。

Options:
  -o, --output string  输出为文件,而不是标准输出
$ docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
$ docker import postgres-export.tar postgres:latest

导入tar包来创建镜像

Options:
  -c, --change list       使用Dockerfile指令
  -m, --message string    Set commit message for imported image设置提交信息来导入image
      --platform string   Set platform if server is multi-platform capable

4.2.8 容器提交成为镜像

当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

$ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

$ docker commit \
    --author "xiao ming " \
    --message "修改了默认网页" \
    webserver \
    nginx:v2

注意: 使用docker commit可能会让镜像变成黑箱,因为对容器的修改难以追溯。多次的commit也可能会让镜像变得更加臃肿。最好别用 commit。 建议用Dockerfile。

4.2.9 组合使用

第一,commit+save模式:保留了一个镜像的所有历史文件和元数据,它把容器的内容作为一个新的层覆盖在原有镜像之上,然后实现持久化。原有的镜像没有改变。

当开发、测试中,对容器内容有修改后,可以使用commit提交。提交后可以保存为一个镜像,但不改变基础镜像的内容。换言之,在基础镜像上再包裹了一层容器修改后的文件系统。

这样的好处是,当我发现这个commit+save的镜像包有问题的时候,可以快速回滚到之前的镜像版本。

你想,之前我们是怎么做产品版本管理的?每发一个版本就要生成一个安装部署包,久而久之版本越来越多,部署包的文件夹越来越大。部署实施人员在客户单位想回滚版本的时候可能只能够重新部署。

删除新版本–>拷贝旧版本–>安装旧版本…

几个小时过去了。

有了docker这个回滚特性,那就直接回滚到镜像的某个层,然后再实例化一个容器就可以了。

几秒钟的工作量。

第二,export模式:打扫干净屋子再请客。把之前镜像的层级删干净,只保留最新的快照文件。

这种情况适用于我不需要回滚,当前的状态即是最佳状态的时候。这样做的好处是,删除了没有用处的历史数据,同时保留了镜像具有的所有功能,关键是镜像文件还小的很

4.2.10 容器资源配置

选项 描述
-m,--memory 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
--memory-swap 内存+交换分区大小总限制。格式同上。必须比-m设置的大
--memory-reservation 内存的软性限制。格式同上
--oom-kill-disable 是否阻止 OOM killer 杀死容器,默认没设置
--oom-score-adj 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
--memory-swappiness 用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
--kernel-memory 核心内存限制。格式同上,最小为 4M

4.2.11 容器网络配置

在docker容器中访问宿主机端口有时候会显示连接失败。

宿主机明明有8080的服务,容器内确访问不到。

localhost:8080
Docker有四种网络模式:

1.host模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rcw6VdJZ-1631610680311)(Docker 介绍.assets/20160216134403551.jpg)]

Host模式下,容器的网络接口不与宿主机网络隔离。在容器中监听相应端口的应用能够直接被从宿主机访问。host网络仅支持Linux。

该模式下的Docker Container会和host宿主机共享同一个网络namespace.

故Docker Container可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。

换言之,Docker Container的IP地址即为宿主机eth0的IP地址。

容器和宿主机共享network,这时候localhost就可以访问宿主机端口了。

  • host网络没有与宿主机网络隔离,可能引发安全隐患或端口冲突。
  • 仅适用于Linux。
$ docker run -d --network host --name nginx

2.container模式

容器A和容器B共享network,就是说容器之间可以通过localhost直接访问。

$ docker run -d --network container:容器名 --name nginx

3.none模式

容器与宿主机隔绝,不能联网,安全性最高,一般很少用到。

 $ docker run -d --network none --name nginx

4.bridge模式(默认模式)

Bridge是Docker默认使用的网络类型。如图,网络中的所有容器可以通过IP互相访问。Bridge网络通过网络接口docker0 与主机桥接,可以在主机上通过ifconfig docker0查看到该网络接口的信息。

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

在默认的bridge模式下,docker0网络的默认网关即是宿主机。

  • 在Linux下,docker0网络通常会分配一个172.17.0.0/16的网段,其网关通常为172.17.0.1;
  • 在macOS下的网段则为192.168.65.0/24,网关为192.168.65.1。在容器中使用该IP地址即可访问宿主机上的各种服务。
  • 在windows下,使用ipconfig命令查看vEthernet的宿主机IP地址。容器去访问vEthernet宿主机的IP地址即可。

需要注意的是,这种情况下,经由docker0网桥而来的流量不经过宿主机的本地回环,因此需要将宿主机上的应用(MySQL,Redis等)配置为监听0.0.0.0。

每个容器有自己的network,通过localhost访问不到宿主机

$ docker run -d --name nginx

如果在bridge模式下,容器需要访问宿主机的端口,需要访问虚拟网卡的IPV4地址。在windows下,使用ipconfig命令查看vEthernet的宿主机IP地址。容器去访问vEthernet宿主机的IP地址即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBTeb9xy-1631610680312)(Docker 介绍.assets/1626227661478.png)]

虽然使用 Docker 启动软件环境非常简单,但同时也面临着一个问题,我们无法知晓容器内部具体的细节,比如监听的端口、绑定的 ip 地址等等。只需使用指令:

$ docker inspect 923c969b0d91

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PcFhBwX0-1631610680313)(Docker 介绍.assets/640-1625467863955.png)]

5.Docker数据管理

数据卷:“其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的”。感觉像是由一个容器定义的一个数据挂载信息。其他的容器启动可以直接挂载数据卷容器中定义的挂载信息。

简单来说,它就是一个用来实现宿主机和容器共享数据的和持久化的技术,它不会随着容器关闭而消失,

书写Dockerfile文件时,指定VALUME保留字(这种方式构建出来的容器数据库一致性更好,推荐使用这种方式,

现在我们知道,一个运行的容器有一个或多个只读层和一个读写层。在容器运行过程中,若产生了一些重要的数据或是更改了一些文件,这些更改我们应该怎么保存呢?容器关闭或重启,这些数据不受影响;但删除Docker容器,则数据将会全部丢失。除此之外也还有其他的一些问题。

存在的问题:

  • 存储于联合文件系统中,不易于宿主机访问
  • 容器间数据共享不便

为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供以下便利。

  • volume在容器创建时就会初始化,在容器运行时就可以使用其中的文件。
  • volume能在不同的容器之间共享和重用。
  • 对volume中数据的操作会马上生效。
  • 对volume中数据的操作不会影响到镜像本身
  • volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的
  • volume也不会被Docker删除。

5.1 挂载宿主机目录

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

上面的命令加载主机的 /src/webapp 目录到容器的 /usr/share/nginx/html目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读。(可选)

$ docker run -d -p 8080:8080 --name ipswitch1 --mount type=bind,source=D:\dockertest,target=/user/local/tomcat/webapps,readonly tomcat:9.0

$ docker run -d -p 8080:8080 --name ipswitch1 -v D:\dockertest:/user/local/tomcat/webapps   tomcat:9.0

然后通过–volumes-from指令参数来挂载 已存在容器中的数据卷,相当于引用已有容器中已经挂载好的数据卷。

这可以实现多个容器共同挂载一个数据卷。

$ docker run -d  -p 8081:8080 --volumes-from ipswitch1 --name ipswitch2 tomcat:9.0

当修改共享的数据卷中,本地主机,多个容器都会接收到修改后的结果。一个地方改了,其他地方被同步修改。

5.1.1 挂载MySQL数据

$ docker run --name mysql -d -p 3310:3306 -v /home/mysql/conf/conf.d:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7

解析:

  • –name mysql01 # 对容器的命名
  • -d #后台运行
  • -p 3310:3306 #对外暴露端口号3310
  • -v /home/mysql/conf:/etc/mysql/conf.d #配置文件挂载到当前宿主机的/home/mysql/conf
  • -v /home/mysql/data:/var/lib/mysql #数据挂载到当前宿主机的 /home/mysql/data
  • -e MYSQL_ROOT_PASSWORD=123456 #设置mysql的root用户的密码是:·123456

5.2 DockerFile VALUME 指定容器数据卷

6. Docker 部署WEB项目

6.1 向tomcat 容器中部署项目

6.1.1 cp复制文件到容器

现在我们已经能够进入容器终端执行相关操作了,那么该如何向 tomcat 容器中部署一个项目呢?

$ docker cp ./test.html 289cc00dc5ed:/usr/local/tomcat/webapps

通过docker cp指令能够将文件从 CentOS 复制到容器中,./test.html为 CentOS 中的资源路径,289cc00dc5ed为容器 id,/usr/local/tomcat/webapps为容器的资源路径,此时test.html文件将会被复制到该路径下。

[root@izrcf5u3j3q8xaz ~]# docker exec -it 289cc00dc5ed bash
root@289cc00dc5ed:/usr/local/tomcat# cd webapps
root@289cc00dc5ed:/usr/local/tomcat/webapps# ls
test.html
root@289cc00dc5ed:/usr/local/tomcat/webapps#

若是想将容器内的文件复制到 CentOS 中,则反过来写即可:

$ docker cp 289cc00dc5ed:/usr/local/tomcat/webapps/test.html ./

所以现在若是想要部署项目,则先将项目上传到 CentOS,然后将项目从 CentOS 复制到容器内,此时启动容器即可。

6.1.2 挂载本地文件路径(与容器共享文件)

$ docker run -d  -v D:\dockertest:/usr/local/tomcat/webapps  -p 8089:8080 36ef  

-v 本地文件路径:容器路径

-d 后台运行

-p 宿主机端口:容器端口映射

36ef为运行镜像的ID

6.1.3 Dockerfile 添加进镜像内

在Dockerfile 内添加 ADD 或者 COPY。 war包要和dockerfile在同一路径下。

  ADD test.war /usr/local/tomcat/webapps/CMD ["catalina.sh", "run"]

6.2 部署SpringBoot

由于SpringBoot不需要Tomcat容器(其拥有内置tomcat web服务器)所以无需依赖tomcat镜像。

# Docker image for springboot file run
# VERSION 0.0.1
# Author: xiaoming
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER xiaoming 
# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp 
# 将jar包添加到容器中并更名为app.jar
ADD demo-0.0.1-SNAPSHOT.jar app.jar 

# 也可以直接写成ADD microservice-discovery-eureka-0.0.1-SNAPSHOT.jar /app.jar
# touch命令的作用是修改这个文件的访问时间和修改时间为当前时间。
RUN bash -c 'touch /app.jar'

# 运行jar包
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

6.3 Docker 部署Oracle

# 拉取oracle-xe-11g镜像 
$ docker pull oracleinanutshell/oracle-xe-11g

# 启动第一个 oracle 容器 
$ docker run -d  --name oracle-1  oracleinanutshell/oracle-xe-11g

# 将oracle 中自带的数据文件拷贝到宿主机中 根据操作系统写入不同的宿主机路径, 路径可以自定。
$ docker cp determined_driscoll:/u01/app/oracle/ /mnt/d/project/oracle/oracle11g-data/
$ docker cp determined_driscoll:/u01/app/oracle/ d:\project\oracle\oracle11g-data\

# 启动挂载宿主机文件系统的 oracle 容器 (真正使用的oracle容器)
$ docker docker run -d -p 1521:1521 -v /mnt/d/project/oracle/oracle11g-data/oracle:/u01/app/oracle  --name oracle_XE_11g  oracleinanutshell/oracle-xe-11g

# 可以删除第一个启动的oracle容器
$ docker rm -f oracle-1
# 数据库的默认服务名 XE 和用户名密码
sid: xe
username: system
password: oracle

7.Docker-Compose

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Compose 使用的三个步骤:

  • 使用 Dockerfile 定义应用程序的环境。
  • 使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
  • 最后,执行 docker-compose up 命令来启动并运行整个应用程序。

前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知。利用Docker-Compose可以简化Docker容器启停的工作量。

使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具

7.1 Docker-Compose 安装

7.1.1 Windows PC

Windows 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Windows 用户不需要单独安装 Compose。Docker 安装说明可以参阅 Windows Docker 安装。

7.1.2 Linux

Linux 上我们可以从 Github 上下载它的二进制包来使用,最新发行的版本地址:https://github.com/docker/compose/releases。

运行以下命令以下载 Docker Compose 的当前稳定版本:

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

要安装其他版本的 Compose,请替换 1.24.1。

将可执行权限应用于二进制文件:

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

创建软链:

$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

测试是否安装成功:

$ docker-compose --version
cker-compose version 1.24.1, build 4667896b

7.2 Docker-Compose使用

在自定的工作目录中创建一个名为 docker-compose.yml 的文件,然后粘贴以下内容:

7.2.1 docker-compose.yml 配置文件

# yaml 配置
version: '3'
services:
 web:
  build: .
  ports:

   - "5000:5000"
 redis:
  image: "redis:alpine"

该 Compose 文件定义了两个服务:web 和 redis。

  • web:该 web 服务使用从 Dockerfile 当前目录中构建的镜像。然后,它将容器和主机绑定到暴露的端口 5000。此示例服务使用 Flask Web 服务器的默认端口 5000 。
  • redis:该 redis 服务使用 Docker Hub 的公共 Redis 映像。

7.2.1 使用 Compose 命令构建和运行您的应用

在测试目录中,执行以下命令来启动应用程序:

docker-compose up

如果你想在后台执行该服务可以加上 -d 参数:

docker-compose up -d

7.3 yml 配置

version

指定本 yml 依从的 compose 哪个版本制定的。 指定 docker-compose.yml 文件的写法格式

build

指定为构建镜像上下文路径:

例如 webapp 服务,指定为从上下文路径 ./dir/Dockerfile 所构建的镜像:

version: "3.7"
services:
  webapp:
    build: ./dir

或者,作为具有在上下文指定的路径的对象,以及可选的 Dockerfile 和 args:

version: "3.7"
services:
  webapp:
    build:
      context: ./dir
      dockerfile: Dockerfile-alternate
      args:
        buildno: 1
      labels:
        - "com.example.description=Accounting webapp"
        - "com.example.department=Finance"
        - "com.example.label-with-empty-value"
      target: prod
  • context:上下文路径。
  • dockerfile:指定构建镜像的 Dockerfile 文件名。
  • args:添加构建参数,这是只能在构建过程中访问的环境变量。
  • labels:设置构建镜像的标签。
  • target:多层构建,可以指定构建哪一层。

cap_add,cap_drop

添加或删除容器拥有的宿主机的内核功能。

cap_add:
  - ALL # 开启全部权限

cap_drop:
  - SYS_PTRACE # 关闭 ptrace权限

cgroup_parent

为容器指定父 cgroup 组,意味着将继承该组的资源限制。

cgroup_parent: m-executor-abcd

command

覆盖容器启动的默认命令。

command: ["bundle", "exec", "thin", "-p", "3000"]

container_name

指定自定义容器名称,而不是生成的默认名称。

container_name: my-web-container

depends_on

设置依赖关系。

  • docker-compose up :以依赖性顺序启动服务。在以下示例中,先启动 db 和 redis ,才会启动 web。
  • docker-compose up SERVICE :自动包含 SERVICE 的依赖项。在以下示例中,docker-compose up web 还将创建并启动 db 和 redis。
  • docker-compose stop :按依赖关系顺序停止服务。在以下示例中,web 在 db 和 redis 之前停止。
version: "3.7"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

注意:web 服务不会等待 redis db 完全启动 之后才启动。

deploy

指定与服务的部署和运行有关的配置。只在 swarm 模式下才会有用。

version: "3.7"
services:
  redis:
    image: redis:alpine
    deploy:
      mode:replicated
      replicas: 6
      endpoint_mode: dnsrr
      labels: 
        description: "This redis service label"
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

可以选参数:

endpoint_mode:访问集群服务的方式。

endpoint_mode: vip 
# Docker 集群服务一个对外的虚拟 ip。所有的请求都会通过这个虚拟 ip 到达集群服务内部的机器。
endpoint_mode: dnsrr
# DNS 轮询(DNSRR)。所有的请求会自动轮询获取到集群 ip 列表中的一个 ip 地址。

labels:在服务上设置标签。可以用容器上的 labels(跟 deploy 同级的配置) 覆盖 deploy 下的 labels。

mode:指定服务提供的模式。

  • replicated:复制服务,复制指定服务到集群的机器上。

  • global:全局服务,服务将部署至集群的每个节点。

  • 图解:下图中黄色的方块是 replicated 模式的运行情况,灰色方块是 global 模式的运行情况。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K65CncCb-1631610680314)(Docker 介绍.assets/docker-composex.png)]

replicas:mode** 为 replicated 时,需要使用此参数配置具体运行的节点数量。

resources:配置服务器资源使用的限制,例如上例子,配置 redis 集群运行需要的 cpu 的百分比 和 内存的占用。避免占用资源过高出现异常。

restart_policy:配置如何在退出容器时重新启动容器。

  • condition:可选 none,on-failure 或者 any(默认值:any)。
  • delay:设置多久之后重启(默认值:0)。
  • max_attempts:尝试重新启动容器的次数,超出次数,则不再尝试(默认值:一直重试)。
  • window:设置容器重启超时时间(默认值:0)。

rollback_config:配置在更新失败的情况下应如何回滚服务。

  • parallelism:一次要回滚的容器数。如果设置为0,则所有容器将同时回滚。
  • delay:每个容器组回滚之间等待的时间(默认为0s)。
  • failure_action:如果回滚失败,该怎么办。其中一个 continue 或者 pause(默认pause)。
  • monitor:每个容器更新后,持续观察是否失败了的时间 (ns|us|ms|s|m|h)(默认为0s)。
  • max_failure_ratio:在回滚期间可以容忍的故障率(默认为0)。
  • order:回滚期间的操作顺序。其中一个 stop-first(串行回滚),或者 start-first(并行回滚)(默认 stop-first )。

update_config:配置应如何更新服务,对于配置滚动更新很有用。

  • parallelism:一次更新的容器数。
  • delay:在更新一组容器之间等待的时间。
  • failure_action:如果更新失败,该怎么办。其中一个 continue,rollback 或者pause (默认:pause)。
  • monitor:每个容器更新后,持续观察是否失败了的时间 (ns|us|ms|s|m|h)(默认为0s)。
  • max_failure_ratio:在更新过程中可以容忍的故障率。
  • order:回滚期间的操作顺序。其中一个 stop-first(串行回滚),或者 start-first(并行回滚)(默认stop-first)。

:仅支持 V3.4 及更高版本。

devices

指定设备映射列表。

devices:
  - "/dev/ttyUSB0:/dev/ttyUSB0"

dns

自定义 DNS 服务器,可以是单个值或列表的多个值。

dns: 8.8.8.8

dns:
  - 8.8.8.8
  - 9.9.9.9

dns_search

自定义 DNS 搜索域。可以是单个值或列表。

dns_search: example.com

dns_search:
  - dc1.example.com
  - dc2.example.com

entrypoint

覆盖容器默认的 entrypoint。

entrypoint: /code/entrypoint.sh

也可以是以下格式:

entrypoint:
    - php
    - -d
    - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
    - -d
    - memory_limit=-1
    - vendor/bin/phpunit

env_file

从文件添加环境变量。可以是单个值或列表的多个值。

env_file: .env

也可以是列表格式:

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

environment

添加环境变量。您可以使用数组或字典、任何布尔值,布尔值需要用引号引起来,以确保 YML 解析器不会将其转换为 True 或 False。

environment:
  RACK_ENV: development
  SHOW: 'true'

expose

暴露端口,但不映射到宿主机,只被连接的服务访问。

仅可以指定内部端口为参数:

expose:
 - "3000"
 - "8000"

ports:对外暴露的端口定义,和 expose 对应

ports:   # 暴露端口信息  - "宿主机端口:容器暴露端口"
- "8763:8763"
- "8763:8763"

extra_hosts

添加主机名映射。类似 docker client --add-host。

extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:

162.242.195.82  somehost
50.31.209.229   otherhost

healthcheck

用于检测 docker 服务是否健康运行。

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"] # 设置检测程序
  interval: 1m30s # 设置检测间隔
  timeout: 10s # 设置检测超时时间
  retries: 3 # 设置重试次数
  start_period: 40s # 启动后,多少秒开始启动检测程序

image

指定容器运行的镜像。以下格式都可以:

image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd # 镜像id

logging

服务的日志记录配置。

driver:指定服务容器的日志记录驱动程序,默认值为json-file。有以下三个选项

driver: "json-file"
driver: "syslog"
driver: "none"

仅在 json-file 驱动程序下,可以使用以下参数,限制日志得数量和大小。

logging:
  driver: json-file
  options:
    max-size: "200k" # 单个文件大小为200k
    max-file: "10" # 最多10个文件

当达到文件限制上限,会自动删除旧得文件。

syslog 驱动程序下,可以使用 syslog-address 指定日志接收地址。

logging:
  driver: syslog
  options:
    syslog-address: "tcp://192.168.0.42:123"

network_mode

设置网络模式。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

networks

配置容器连接的网络,引用顶级 networks 下的条目 。

services:
  some-service:
    networks:
      some-network:
        aliases:
         - alias1
      other-network:
        aliases:
         - alias2
networks:
  some-network:
    # Use a custom driver
    driver: custom-driver-1
  other-network:
    # Use a custom driver which takes special options
    driver: custom-driver-2

aliases :同一网络上的其他容器可以使用服务名称或此别名来连接到对应容器的服务。

restart

  • no:是默认的重启策略,在任何情况下都不会重启容器。
  • always:容器总是重新启动。
  • on-failure:在容器非正常退出时(退出状态非0),才会重启容器。
  • unless-stopped:在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

注:swarm 集群模式,请改用 restart_policy。

secrets

存储敏感数据,例如密码:

version: "3.1"
services:

mysql:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my_secret
  secrets:
    - my_secret

secrets:
  my_secret:
    file: ./my_secret.txt

security_opt

修改容器默认的 schema 标签。

security-opt:
  - label:user:USER   # 设置容器的用户标签
  - label:role:ROLE   # 设置容器的角色标签
  - label:type:TYPE   # 设置容器的安全策略标签
  - label:level:LEVEL  # 设置容器的安全等级标签

stop_grace_period

指定在容器无法处理 SIGTERM (或者任何 stop_signal 的信号),等待多久后发送 SIGKILL 信号关闭容器。

stop_grace_period: 1s # 等待 1 秒
stop_grace_period: 1m30s # 等待 1 分 30 秒 

默认的等待时间是 10 秒。

stop_signal

设置停止容器的替代信号。默认情况下使用 SIGTERM 。

以下示例,使用 SIGUSR1 替代信号 SIGTERM 来停止容器。

stop_signal: SIGUSR1

sysctls

设置容器中的内核参数,可以使用数组或字典格式。

sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

tmpfs

在容器内安装一个临时文件系统。可以是单个值或列表的多个值。

tmpfs: /run

tmpfs:
  - /run
  - /tmp

ulimits

覆盖容器默认的 ulimit。

ulimits:
  nproc: 65535
  nofile:
    soft: 20000
    hard: 40000

volumes

将主机的数据卷或着文件挂载到容器里。

version: "3.7"
services:
  db:
    image: postgres:latest
    volumes:
      - "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
      - "/localhost/data:/var/lib/postgresql/data"

8. 容器间通信

容器之间通信的主要方式

1.通过容器ip访问

容器重启后,ip会发生变化。通过容器ip访问不是一个好的方案。

2.通过宿主机的ip:port访问

通过宿主机的ip:port访问,只能依靠监听在暴露出的端口的进程来进行有限的通信。

3.通过link建立连接(官方不推荐使用)

 运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。

源容器:mysql

docker run -itd --name test-mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7

被链接容器 centos

docker run -itd --name test-centos --link test-mysql:mysql centos /bin/bash

进入test-centos

docker exec -it test-centos /bin/bash
直接通过 link的名字或者link时候取的别名就能进入:

通过link建立连接的容器,被链接的容器能 ping 通源容器,反过来不行。

在被链接的容器上查看环境变量

被链接容器会继承源容器的环境变量信息。

与/etc/hosts中的主机条目不同,如果重新启动源容器,则不会自动更新存储在环境变量中的IP地址。我们建议使用主机条目 /etc/hosts来解析链接容器的IP地址。

除了环境变量之外,Docker还将源容器的主机条目添加到/etc/hosts文件中。

如果重新启动源容器,/etc/hosts链接容器上的文件将使用源容器的新IP地址自动更新,从而允许链接通信继续。

4.通过 User-defined networks(推荐)

 docker network来创建一个桥接网络,在docker run的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以通过互相访问。

创建网络

$ docker network create test-network

启动容器时,加入创建的网络

docker run -it --network test-network --network-alias mysql -e MYSQL_ROOT_PASSWORD=123 mysql:5.7
启动被链接的容器

$ docker run -it --network test-network --network-alias oracle  oracleinanutshell/oracle-xe-11g

将启动的容器加入网络

$ docker network connect network_name container_name

参考资料

[1]《Docker 从入门到实践》: https://yeasy.gitbook.io/docker_practice/introduction/why

[2]https://hub.docker.com/: https://hub.docker.com/

[3] https://www.runoob.com/docker/docker-compose.html

中的内核参数,可以使用数组或字典格式。

sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

tmpfs

在容器内安装一个临时文件系统。可以是单个值或列表的多个值。

tmpfs: /run

tmpfs:
  - /run
  - /tmp

ulimits

覆盖容器默认的 ulimit。

ulimits:
  nproc: 65535
  nofile:
    soft: 20000
    hard: 40000

volumes

将主机的数据卷或着文件挂载到容器里。

version: "3.7"
services:
  db:
    image: postgres:latest
    volumes:
      - "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
      - "/localhost/data:/var/lib/postgresql/data"

8. 容器间通信

容器之间通信的主要方式

1.通过容器ip访问

容器重启后,ip会发生变化。通过容器ip访问不是一个好的方案。

2.通过宿主机的ip:port访问

通过宿主机的ip:port访问,只能依靠监听在暴露出的端口的进程来进行有限的通信。

3.通过link建立连接(官方不推荐使用)

 运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。

源容器:mysql

docker run -itd --name test-mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7

被链接容器 centos

docker run -itd --name test-centos --link test-mysql:mysql centos /bin/bash

进入test-centos

docker exec -it test-centos /bin/bash
直接通过 link的名字或者link时候取的别名就能进入:

通过link建立连接的容器,被链接的容器能 ping 通源容器,反过来不行。

在被链接的容器上查看环境变量

被链接容器会继承源容器的环境变量信息。

与/etc/hosts中的主机条目不同,如果重新启动源容器,则不会自动更新存储在环境变量中的IP地址。我们建议使用主机条目 /etc/hosts来解析链接容器的IP地址。

除了环境变量之外,Docker还将源容器的主机条目添加到/etc/hosts文件中。

如果重新启动源容器,/etc/hosts链接容器上的文件将使用源容器的新IP地址自动更新,从而允许链接通信继续。

4.通过 User-defined networks(推荐)

 docker network来创建一个桥接网络,在docker run的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以通过互相访问。

创建网络

$ docker network create test-network

启动容器时,加入创建的网络

docker run -it --network test-network --network-alias mysql -e MYSQL_ROOT_PASSWORD=123 mysql:5.7
启动被链接的容器

$ docker run -it --network test-network --network-alias oracle  oracleinanutshell/oracle-xe-11g

将启动的容器加入网络

$ docker network connect network_name container_name

参考资料

[1]《Docker 从入门到实践》: https://yeasy.gitbook.io/docker_practice/introduction/why

[2]https://hub.docker.com/: https://hub.docker.com/

[3] https://www.runoob.com/docker/docker-compose.html

[4] 各种博客

你可能感兴趣的:(docker)