说实话关于 Docker 是什么并太好说,下面我通过四点向你说明 Docker 到底是个什么东西。
官网地址:https://www.docker.com/ 。
docker是一个用Go语言实现的开源项目,可以让我们方便的创建和使用容器,docker将程序以及程序所有的依赖都打包到docker container,这样你的程序可以在任何环境都会有一致的表现,这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口,程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系。
因此我们可以看到docker可以屏蔽环境差异,也就是说,只要你的程序打包到了docker中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华了,不会再有“在我的环境上可以运行”,真正实现“build once, run everywhere”。
此外docker的另一个好处就是快速部署,这是当前互联网公司最常见的一个应用场景,一个原因在于容器启动速度非常快,另一个原因在于只要确保一个容器中的程序正确运行,那么你就能确信无论在生产环境部署多少都能正确运行。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
传统的开发流程中,我们的项目通常需要使用 MySQL、Redis、FastDFS 等等环境,这些环境都是需要我们手动去进行下载并配置的,安装配置流程极其复杂,而且不同系统下的操作也不一样。
Docker 的出现完美地解决了这一问题,我们可以在容器中安装 MySQL、Redis 等软件环境,使得应用和环境架构分开,它的优势在于:
另外,《Docker 从入门到实践》[1] 这本开源书籍中也已经给出了使用 Docker 的原因。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6wfvt2g-1631610680293)(Docker 介绍.assets/640-1625468324110.webp)]
接下来对 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
即可:
安装完成后,我们仍然需要重启计算机,重启后,若提示如下内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWcNX6NI-1631610680301)(Docker 介绍.assets/640-1625451265686.png)]
它的意思是询问我们是否使用 WSL2,这是基于 Windows 的一个 Linux 子系统,这里我们取消即可,它就会使用我们之前勾选的Hyper-V
虚拟机。
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn/"
],
"insecure-registries": [],
"debug": false,
"experimental": true
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B75MO0tJ-1631610680301)(Docker 介绍.assets/image-20210531213119735.png)]
直接使用 Homebrew 安装即可
brew install --cask docker
下面来看看 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
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
在正式学习 Docker 之前,我们需要了解 Docker 中的几个核心概念:
镜像就是一个只读的模板,镜像可以用来创建 Docker 容器,一个镜像可以创建多个容器
容器是用镜像创建的运行实例,Docker 利用容器独立运行一个或一组应用。它可以被启动、开始、停止、删除,每个容器都是相互隔离的、保证安全的平台。可以把容器看作是一个简易的 Linux 环境和运行在其中的应用程序。容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的
仓库是集中存放镜像文件的场所。仓库和仓库注册服务器是有区别的,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签。仓库分为公开仓库和私有仓库两种形式,最大的公开仓库是 DockerHub,存放了数量庞大的镜像供用户下载,国内的公开仓库有阿里云、网易云等
通俗点说,一个镜像就代表一个软件;而基于某个镜像运行就是生成一个程序实例,这个程序实例就是容器;而仓库是用来存储 Docker 中所有镜像的。
其中仓库又分为远程仓库和本地仓库,和 Maven 类似,倘若每次都从远程下载依赖,则会大大降低效率,为此,Maven 的策略是第一次访问依赖时,将其下载到本地仓库,第二次、第三次使用时直接用本地仓库的依赖即可,Docker 的远程仓库和本地仓库的作用也是类似的。
看完上面的例子,你可能仍然不会觉得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个运行着不同操作系统的虚拟服务器,而对于用户来说,这些是透明的––用户则认为自己拥有一台完整的服务器。据我推测,阿里云的服务器就是这么干的。这充分利用了闲置的硬件资源。
一般在下载镜像前我们需要搜索一下镜像有哪些版本才能对指定版本进行下载,使用指令:
$ 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]
在左上角的搜索框中输入MySQL
并回车:
可以看到相关 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的安装)
拉取镜像的标准命令:
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
具体的选项可以通过 docker pull --help
命令看到,这里我们说一下镜像名称的格式。
<域名/IP>[:端口号]
。默认地址是 Docker Hub(docker.io
)。<用户名>/<软件名>
。对于 Docker Hub,如果不给出用户名,则默认为 library
,也就是官方镜像。若想查看 Docker 中当前拥有哪些镜像,则可以使用 docker images
或 docker 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...
删除镜像使用指令:
$ 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 镜像就都被删除了。
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
在 Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx
、redis
、mongo
、mysql
、httpd
、php
、tomcat
等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node
、openjdk
、python
、ruby
、golang
等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu
、debian
、centos
、fedora
、alpine
等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
如果你以 scratch
为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。GO语言开发的应用常用此方法构建镜像。
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/*
指定启动容器时执行的命令,每个 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指定的默认的执行命令执行。
EXPOSE [...]。
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P
,Docker 主机会自动分配一个端口转发到指定的端口。
注意: EXPOSE 有点鸡肋。
1、真正的暴露端口是在创建容器 run 的时候指定的 -p 或者 -P 参数,先来说说 -p 参数后面跟的是【主机端口:容器端口】,那么问题就来了既然在运行的时候还需要指定端口那么 EXPOSE还要什么用呢!
2、当我们创建容器 run 的时候指定参数是 -P,那么在运行之后 会把 EXPOSE 的端口随机映射到主机的不同端口,这时问题又来了既然映射到不同的端口那么容器的端口就是是随机的不确定的,那就要在运行之后才能知道端口,这样使用起来是极其不便的。
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
指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
因此在 COPY
和 ADD
指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY
指令,仅在需要自动解压缩的场合使用 ADD
。
WORKDIR /path/to/workdir。
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
MAINTAINER , #指定维护者信息。
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
# 声明容器中的 /root/httpServer/log 目录为挂载点
VOLUME /root/httpServer/log
$ docker build -t <仓库名/镜像名> <dockerfile路径>
$ sudo docker build -t myrepo/myapp /tmp/test1/
-t, --tag list
掌握了镜像的相关指令之后,我们需要了解一下容器的指令,容器是基于镜像的。
若需要通过镜像运行一个容器,则使用:
$ 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
接下来是容器的启动、停止、重启指令。 (操作针对已经创建好的容器)
$ docker start c2f5d78c5d1a
通过该指令能够将已经停止运行的容器运行起来,可以通过容器的 id 启动,也可以通过容器的名称启动。
$ docker restart c2f5d78c5d1a
该指令能够重启指定的容器。
$ docker stop c2f5d78c5d1a
该指令能够停止指定的容器。
$ docker kill c2f5d78c5d1a
该指令能够直接杀死指定的容器。
以上指令都能够通过容器的 id 和容器名称两种方式配合使用。
容器的状态可以通过以下命令查看:
$ docker container ls -a
或
$ docker ps -a
当容器被停止之后,容器虽然不再运行了,但仍然是存在的,若是想删除它,则使用指令:
$ docker rm d5b6c177c151
需要注意的是容器的 id 无需全部写出来,只需唯一标识即可。
若是想删除正在运行的容器,则需要添加-f
参数强制删除:
$ docker rm -f d5b6c177c151
若是想删除所有容器,则可以使用组合指令:
$ docker rm -f $(docker ps -qa)
先通过docker ps -qa
查询出所有容器的 id,然后通过docker rm -f
进行删除。
当容器以后台的方式运行时,我们无法知晓容器的运行状态,若此时需要查看容器的运行日志,则使用指令:
$ 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)]
若是想与容器进行交互,则使用指令:
$ 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
即可。
镜像载入/导出: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
当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit
命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
$ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
$ docker commit \
--author "xiao ming " \
--message "修改了默认网页" \
webserver \
nginx:v2
注意: 使用docker commit可能会让镜像变成黑箱,因为对容器的修改难以追溯。多次的commit也可能会让镜像变得更加臃肿。最好别用 commit。 建议用Dockerfile。
第一,commit+save模式:保留了一个镜像的所有历史文件和元数据,它把容器的内容作为一个新的层覆盖在原有镜像之上,然后实现持久化。原有的镜像没有改变。
当开发、测试中,对容器内容有修改后,可以使用commit提交。提交后可以保存为一个镜像,但不改变基础镜像的内容。换言之,在基础镜像上再包裹了一层容器修改后的文件系统。
这样的好处是,当我发现这个commit+save的镜像包有问题的时候,可以快速回滚到之前的镜像版本。
你想,之前我们是怎么做产品版本管理的?每发一个版本就要生成一个安装部署包,久而久之版本越来越多,部署包的文件夹越来越大。部署实施人员在客户单位想回滚版本的时候可能只能够重新部署。
删除新版本–>拷贝旧版本–>安装旧版本…
几个小时过去了。
有了docker这个回滚特性,那就直接回滚到镜像的某个层,然后再实例化一个容器就可以了。
几秒钟的工作量。
第二,export模式:打扫干净屋子再请客。把之前镜像的层级删干净,只保留最新的快照文件。
这种情况适用于我不需要回滚,当前的状态即是最佳状态的时候。这样做的好处是,删除了没有用处的历史数据,同时保留了镜像具有的所有功能,关键是镜像文件还小的很
选项 | 描述 |
---|---|
-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 |
在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就可以访问宿主机端口了。
$ 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网络的默认网关即是宿主机。
需要注意的是,这种情况下,经由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)]
数据卷:“其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的”。感觉像是由一个容器定义的一个数据挂载信息。其他的容器启动可以直接挂载数据卷容器中定义的挂载信息。
简单来说,它就是一个用来实现宿主机和容器共享数据的和持久化的技术,它不会随着容器关闭而消失,
书写Dockerfile文件时,指定VALUME保留字(这种方式构建出来的容器数据库一致性更好,推荐使用这种方式,
现在我们知道,一个运行的容器有一个或多个只读层和一个读写层。在容器运行过程中,若产生了一些重要的数据或是更改了一些文件,这些更改我们应该怎么保存呢?容器关闭或重启,这些数据不受影响;但删除Docker容器,则数据将会全部丢失。除此之外也还有其他的一些问题。
存在的问题:
为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供以下便利。
使用 --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
当修改共享的数据卷中,本地主机,多个容器都会接收到修改后的结果。一个地方改了,其他地方被同步修改。
$ 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
解析:
现在我们已经能够进入容器终端执行相关操作了,那么该如何向 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 复制到容器内,此时启动容器即可。
$ docker run -d -v D:\dockertest:/usr/local/tomcat/webapps -p 8089:8080 36ef
-v 本地文件路径:容器路径
-d 后台运行
-p 宿主机端口:容器端口映射
36ef为运行镜像的ID
在Dockerfile 内添加 ADD 或者 COPY。 war包要和dockerfile在同一路径下。
ADD test.war /usr/local/tomcat/webapps/CMD ["catalina.sh", "run"]
由于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"]
# 拉取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
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
Compose 使用的三个步骤:
前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知。利用Docker-Compose可以简化Docker容器启停的工作量。
使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
Windows 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Windows 用户不需要单独安装 Compose。Docker 安装说明可以参阅 Windows Docker 安装。
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
在自定的工作目录中创建一个名为 docker-compose.yml 的文件,然后粘贴以下内容:
# yaml 配置
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
该 Compose 文件定义了两个服务:web 和 redis。
在测试目录中,执行以下命令来启动应用程序:
docker-compose up
如果你想在后台执行该服务可以加上 -d 参数:
docker-compose up -d
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
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
设置依赖关系。
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:配置如何在退出容器时重新启动容器。
rollback_config:配置在更新失败的情况下应如何回滚服务。
update_config:配置应如何更新服务,对于配置滚动更新很有用。
注:仅支持 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
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"
容器之间通信的主要方式
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"
容器之间通信的主要方式
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] 各种博客