docker,听说过但也仅限于听说过。
本文借鉴大佬博客进行docker学习,链接放在文末,此文为整合篇,分篇可以参观原帖。
(1):初识docker
先来聊聊什么是docker。
在没有接触docker之前,我去网上搜索“什么是docker”,有很多文章说docker是一种容器虚拟化技术,看到“虚拟化”几个字,我自然而然的就想到了虚拟机,于是我就想要通过虚拟机的概念,类比着去理解docker。
我开始搜索,docker和虚拟机的区别,但是我发现,这样去理解似乎不太容易,主要有两个原因,第一个原因是我压根没有使用过docker,完全没有任何概念,第二个原因是docker与传统的虚拟化不同,有人说docker是容器虚拟化技术,有人说docker压根不能算作虚拟化的范畴。
看到这些争论,我越来越搞不明白了,于是我放弃抵抗,开始直接使用docker,在实际的使用过程中,我才有了自己的理解,现在,我从一个使用者的角度,去描述一下docker是什么,它和虚拟机有什么不同,如果你看完我的描述,还是没有理解,最好和我一样,先用上再说。
咱们还是以传统意义上的“虚拟机”作为切入点,假设,有一台物理服务器,我们把这台服务器当做宿主机,在宿主机上虚出了虚机A、虚机B等虚拟机,对于这些虚拟机来说,它们各自都独占了一套完整的操作系统,准确的说,应该是一套完整的“操作系统发行版”。
注意,这里强调一下“发行版”三个字,以linux系统为例,组成一个“linux系统发行版”,首先需要有“linux内核”,其次还需要有相关软件依赖的运行环境软件包,比如说一些类库包、命令行工具等等,不同的linux发行版基于相同的linux内核开发,但是由于组成运行环境的包的不同,所以产生了不同的发行版,比如ubuntu和centos就是不同的linux发行版,对于虚机A和虚机B来说,它们各自拥有一套完整的操作系统(即使A和B都安装了centos7.9,本质上也是两个相互独立的操作系统),从隔离层面上来说,A和B之间是“linux系统发行版”的完整隔离,A和B都各自使用自己的内核和运行环境依赖包,各自使用自己的虚拟硬件,这是虚拟机在操作系统层面的隔离,理解上述概念,是避免搞混docker和传统虚拟化的关键。
现在再来聊聊docker,我们知道docker是一种容器引擎,docker运行容器的本质目的是去运行某个程序,docker通过应用程序镜像,安装并运行对应的程序,比如,我需要运行nginx,只需要下载对应的nginx镜像,docker就可以把这个nginx镜像运行成一个容器,容器把内部的nginx服务端口暴露出来,我们就可以访问nginx服务了,其他服务软件同理。
在没有使用docker之前,看到“镜像”这个词,我又情不自禁的想到了虚拟机,只不过,虚拟机是通过“操作系统镜像”安装运行系统,docker是通过“应用程序镜像”安装运行对应的软件,当我搜索docker和虚拟机有什么不同的时候,我看到了下面这张图。
从上图来看,每个虚拟机都有自己的完整的操作系统(上图的GUEST OS就是虚拟机的操作系统,但是此处,我们应该把它拆成两部分理解,GUEST OS是由其内核和运行环境包组成的)。
VM1虚拟机上的所有APP使用的是同一套运行库和内核,其他虚拟机同理,Hypervisor层对硬件资源进行划分,各个虚拟机在整个操作系统发行版层面进行了隔离。
而容器则不同,容器是将APP和其需要依赖的运行库打包成一个镜像,在APP进程和运行库层面进行了隔离(上图中CONTAINER中的APP就是应用程序镜像,这个镜像中包含应用本身和其需要的运行时依赖包),不同的APP各自使用自己的运行时环境,容器所运行的镜像中不需要内核,所有容器共用所在主机HostOS的内核,统一通过HostOS的内核与硬件进行交互,docker就是运行在HostOS中的容器引擎。
理解了上图以后,我们就能明显感觉到它们的区别了,上图中最凸显的区别就是隔离层面的不同,虚拟机是针对整个系统发行版层面的隔离,而容器则是对程序进程和其依赖的运行环境的隔离,虚拟机的隔离性更强,隔离范围大,容器的隔离性没有虚拟机强,但是隔离范围更小,容器并不涉及Hypervisor层,它是一种进程隔离技术。
从这个方面讲,它不应该算做“传统虚拟化”的范围,人们愿意把它称之为容器“虚拟化”技术,我觉得是因为容器更偏向于“java虚拟机”或者“python虚拟环境”这种软件意义上的“虚拟”,所以说,容器并不会取代虚拟机,它们之间也并不冲突,它们应该是相辅相成的。
把应用程序和其运行时依赖库打包隔离的好处有哪些呢?首先,由于隔离的范围更小了,所以当出现问题时,影响的范围也会更小,只会影响这个应用程序容器的进程本身,而不是其他程序的容器和HOST操作系统,其次,由于应用程序和其依赖的运行时环境是打包在一起的(打包成一个docker镜像),这就表示,如果一个程序的镜像在我的centos7上能够运行,那么在你的centos7上用也能够正常运行,不会出现某个程序在开发人员的笔记本中可以运行,但是放在服务器上就不能正常运行的情况(因为服务器上可能没有安装程序所依赖的包),这就是把程序和依赖打包在一起的好处。
当我们想要通过docker运行一个软件,首先要有对应软件的镜像,我们可以自己制作对应的镜像,也可以从镜像仓库中下载别人制作好的镜像,下载后直接运行,在刚开始学习docker时,最好使用别人创建的镜像,因为这样学习的难度会降低很多,docker官方提供了镜像仓库,供大家分享镜像,官方的镜像仓库叫做docker hub,我们可以从这里面下载别人的镜像,或者上传自己的镜像,docker hub的地址是https://registry.hub.docker.com ,我们可以在仓库中搜索需要的镜像,下载到本地使用
docker还有很多概念,我们放在后面慢慢聊,现在我们来动动手,实际操作一下,先安装一下docker,我的实验环境是centos7.9,我们可以创建一个虚拟机,专门用来学习docker。
为了更加快速的安装较新的docker版本,此处配置阿里云的yum源,阿里云的docker-ce源配置操作地址如下,这个页面中记录了在ubuntu和centos下安装docker的方法:
https://developer.aliyun.com/mirror/docker-ce
下面的安装命令就是从上面的链接中粘贴过来的,安装过程如下
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加yum软件源信息
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 替换yum源中的url,如果没有download.docker.com这个url,可省略此步骤
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新yum源并安装Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务,设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
安装完成后,使用docker version命令,可以查看到对应的docker版本。
现在,我们就可以开始使用docker了,比如从官方的仓库拉取一个镜像,然后跑一个容器,但是由于网络原因,从国内去官方的镜像仓库下载镜像有时会遇到问题,所以最好配置一个加速服务地址,通过加速服务地址获取镜像,会比较快,这里使用阿里云的镜像加速地址,配置过程如下:
- 打开国内阿里云网页,登录自己阿里云的账号,点击“控制台”,在搜索栏中搜索“镜像加速器”,如下图所示,点击搜索出的控制台入口:
进入对应控制台页面后,如下图所示,找到“镜像工具–镜像加速器”菜单栏,即可找到对应的加速服务地址,此页面中还给出了不同系统中,配置加速地址的方法,点击下图中的centos页签,按照提示的命令操作即可。
按照上图中的提示进行操作,配置加速地址过程如下:
#创建配置文件目录和配置文件
mkdir -p /etc/docker
touch /etc/docker/daemon.json
#编辑daemon.json配置文件,将如下内容写入配置文件,加速地址的ID替换成自己网页中的ID
{
"registry-mirrors": ["https://加速地址ID.mirror.aliyuncs.com"]
}
#配置文件设置完成后,重载配置
systemctl daemon-reload
systemctl restart docker
完成上述步骤后,就可以开始顺畅的使用docker了,还有很多关于docker的知识点需要总结,咱们后面慢慢聊。
安装完成查看version
(2)docker:仓库、镜像、容器
前文说过,如果想要用docker运行一个程序,则必须先获取到这个程序的镜像,比如,如果我想要运行一个nginx容器,就必须先有一个nginx镜像,我们可以基于同一个nginx镜像,启动多个nginx容器,就好比基于同一套nginx模板,启动了多个nginx服务一样,此处我们也可以类比着“虚拟机”去理解,程序镜像就好比虚拟机的“模板机镜像”,我们可以通过“模板机镜像”快速的创建出可运行的“虚拟机(虚拟机容器)”,docker也差不多,只不过docker是通过“程序镜像”创建出可运行的“程序容器”,最终对外提供服务,镜像是磁盘中的文件,基于镜像创建出容器,运行的容器就是内存中的进程。
现在我们就来动动手,下载一个镜像,并运行第一个docker容器吧。在docker主机上运行如下命令,即可从docker hub仓库拉取一个别人制作好的镜像到本地。
docker pull docker.io/library/nginx:latest
如上所示,docker pull命令就是用来拉取镜像到本地的,从哪里拉取镜像呢?就是从下面这个url拉取。
docker.io/library/nginx:latest
那么我们来看一下这个url各个部分代表的含义,如下图所示,我们把这个url分成4个部分去理解
第1部分:代表从哪个仓库服务中心下载镜像,docker.io就是官方的仓库服务中心(其实就是docker hub,官方的仓库服务器),这个仓库服务器中有很多组织,也有很多个人,每个组织或者个人都可以在这个仓库服务器创建仓库,然后把镜像存放在仓库中,前文说过,docker hub的官网地址是registry.hub.docker.com,这个地址是方便我们从web页面查找镜像使用的,使用命令行从docker hub拉取镜像时,默认使用的是docker.io这个地址。除了docker官方的docker hub,还有一些其他仓库服务器,我们也可以自建仓库服务器,当使用其他仓库服务器或者自建仓库服务器时,要把第1部分改为对应的域名或IP,如果我们就是想要从官方的docker hub下载镜像,上图中的第1部分可以不写,当省略第1部分时,默认就是使用docker.io这个仓库服务器的,仓库服务或者仓库服务中心的英文原文是registry,简单来说,第1部分就是registry的地址。
第2部分:代表组织或者个人的“命名空间”,命名空间是什么意思呢?举个例子,我们每个人都可以docker hub上注册自己的账号,然后使用自己的账号在docker hub上创建仓库,比如,我在docker hub上的账号id是zsythink,那么我就可以在docker hub的zsythink命名空间下创建仓库,如果我创建了一个名为nginx的仓库,那么我就可以通过如下url,获取到这个nginx仓库中的镜像
docker.io/zsythink/nginx
同理,如果你在docker hub上的id是zhangsan,你也创建了一个名为nginx的仓库,那么你就可以通过下面这个url获取仓库中的镜像
docker.io/zhangsan/nginx
当然,你也可以创建出多个仓库,比如再创建一个名为redis的仓库,专门用来存放redis镜像,那么你就可以通过如下url来获取这个redis仓库中的镜像
docker.io/zhangsan/redis
聪明如你一定看出来了,上例中的nginx或者redis,才是真正的仓库名,也就是我们要说的第3部分,第2部分命名空间是用来区分组织或者个人创建出的仓库的,每个人都在自己的命名空间下创建仓库,即使仓库名重复,也可以看出是由不同的人创建出的仓库,命名空间可以代表个人,也可以代表一个组织,比如,“技术菜责任有限公司”的“jenkins”镜像仓库可能就长成下面这个样子
docker.io/jishucai/jenkins
图例中的第2部分是library,这个命名空间比较特殊,library是专门给上游软件提供商官方使用的命名空间,比如,nginx官方(或者说nginx组织)的nginx的仓库,并不会使用docker.io/nginx/nginx, 而是会使用docker.io/library/nginx ,同理,redis官方的redis仓库,也不会使用docker.io/redis/redis ,而是会使用docker.io/library/redis ,官方仓库中的镜像被称之为官方镜像,docker有一个专门的团队,负责审核和发布官方镜像,这个团队会与软件提供商、社区以及安全专家合作,共同维护这些官方仓库中的镜像,官方镜像的更多内容,可以参考如下链接https://docs.docker.com/docker-hub/official_images ,当我们想要从官方仓库下载镜像时,是可以省略library的,比如,我想要直接从docker hub的nginx官方仓库中下载镜像,可以直接使用docker pull nginx命令,此命令等效于docker pull docker.io/library/nginx ,需要注意,只有官方仓库可以省略(即只有library可以省略),其他组织或者个人的命名空间不能省略。
第3部分:代表仓库名,在介绍第2部分时,我们已经描述的比较清楚了,我们可以在自己的命名空间下创建很多仓库,比如nginx仓库、redis仓库等,nginx仓库专门用来存放nginx镜像,redis仓库专门用来存放redis镜像,创建仓库时,我们可以选择是否公开仓库,如果不公开,只有通过认证后,才能够获取仓库中的镜像,仓库的英文原文为repository,简单来说,第3部分就是repository的名字。
第4部分:代表镜像对应的标签(tag),通常情况下,我们会通过标签对镜像的版本进行区分,比如,nginx仓库中有十个nginx镜像,第一个nginx镜像对应的版本为nginxV1.1,第二个nginx镜像对应的版本为nginxV1.2,那么第一个镜像的tag就是1.1,第二个镜像的tag就是1.2,以此类推,当我们在拉取镜像时,如果没有指定tag,默认会使用”latest”作为tag,也就是图例中第4部分的标签,”latest”表示最新版,当省略标签或者标签为latest时,会从仓库中下载最新版本的镜像,但是今天的最新版可能就是明天的旧版本,所以,在生产环境中,不要使用latest这个tag,或者说,不要省略tag,最好是指定一个明确的版本tag,这样做是为了防止”latest”这个特殊的tag对应的镜像发生了更新。tag和仓库名之间需要用冒号隔开,比如,nginx:1.21 表示nginx库中的tag为1.21的镜像。
了解完上述每个部分的含义后,我们再来回顾一下之前下载镜像命令的含义
docker pull docker.io/library/nginx:latest
上述命令表示从docker hub的nginx官方仓库中拉取tag为latest的镜像(即从nginx官方仓库中拉取最新版本的nginx镜像),上述命令与如下命令等效。
docker pull nginx
刚才说过,我们可以直接访问registry.hub.docker.com,通过web页面搜索我们想要使用的镜像,现在我们就来看看通过网页是怎么操作的,打开docker hub官网,搜索nginx
搜索结果如下图,可以看到,搜索出了很多包含nginx关键字的仓库,这些nginx仓库由不同的组织或者个人创建,带有”Official Image”标识的仓库就是上文所说的官方仓库,也就是说,下图中的第一条搜索结果就是nginx的官方仓库(即library/nginx,而且展示时省略了library),如果你不信任其他发布者,或者没有特殊要求,应该首选官方仓库使用,毕竟这样更加安全,从下图可以看出,官方仓库中的镜像下载次数和星星的数量也是最多的,不过,话不能说死,其他发布者的仓库中也会有很好用的镜像,但前提是需要确保安全,或者你可以完全信任他。
点击进入nginx官方仓库的详情页,如下图所示,可以看到,页面中提示我们,运行”docker pull nginx”命令,以便拉取这个镜像,详情页中还有多个页签,默认展示的是”Description”页签,其中描述了这个镜像的详细信息,比如镜像的版本标签、使用方法和命令等等,除了”Description”页签,还有”Reviews”页签和”Tags”页签,”Reviews”页签中展示了大家对这个镜像的评价,”Tags”页签中详细的列出的镜像相关的“标签”。
点击上图中的”Tags”页签,可以看到一个镜像列表(或者说可以看到一个tag列表,每个tag对应一个镜像),如下图所示,在每个tag的后面,docker都很贴心的给出了拉取对应镜像的命令,方便我们复制使用,在下载镜像时,我们指定的tag必须是在这个列表里面存在的,否则是无法下载对应的镜像的。
除了通过web页面,我们也可以使用命令行去搜索对应的仓库,以nginx为例,使用docker search nginx命令,即可搜索出包含nginx关键字的仓库,但是使用命令搜索出来的信息可读性和详细程度都不如web页面,最明显的就是使用docker search命令搜索出的结果没有对应的tag信息可以查看,而且docker没有专门的子命令可以搜索远程仓库的tag信息,所以我个人还是比较喜欢使用web页面查看需要下载的镜像,如果想要通过命令行去查看仓库的tag信息,可以安装skopeo命令,通过skopeo inspect命令可以查看到对应仓库的tag列表,或者,在命令行中直接使用curl命令访问如下链接,也可以获得对应仓库的tag信息。
https://registry.hub.docker.com/v1/repositories/命名空间/仓库名/tags
我已经执行了拉取镜像的命令,也就是说,我本地已经有nginx的镜像可以使用了,我们可以使用docker images
命令,查看本地都有哪些镜像
如上所示,我的本地目前只有一个镜像,这个镜像来自nginx库,tag为latest。
执行如下命令,即可基于上面的镜像,运行一个nginx容器
docker run --name nginx-demo -d -p 80:80 nginx:latest
上述命令表示,基于nginx:latest 这个镜像,创建一个名为nginx-demo的容器,并启动这个容器,-d表示后台运行这个容器,-p 80:80表示将docker主机的80端口和容器内nginx服务的80端口进行映射,映射后,访问docker主机的80端口,即相当于访问容器内的nginx服务的80端口,我们先不用纠结这些参数的含义,因为这些参数涉及的话题过多,后面的文章我们再详细的讨论,现在只需要能把这个容器运行起来即可,执行上述命令后,查看docker主机的80端口,是已经监听的,访问docker主机的ip地址,就可以看到nginx的默认页面了,如下图
访问到上述页面,就能证明我们的nginx容器已经正常运行了,我们可以使用docker ps命令,查看正在运行中的容器,如下:
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4cf7518d3afb nginx:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp nginx-demo
可以看到,当前只运行了一个容器,容器的ID是55e0b2020fc0,容器使用的镜像是nginx:latest,容器的名称是nginx-demo,我们可以使用docker stop nginx-demo命令和docker start nginx-demo命令来停止或者启动这个容器,动手操作一下吧。
虽然文章啰嗦了很多,但是实际的操作很少,我们只是下载了镜像,然后通过镜像启动了一个容器而已,当然,这只是我们的第一步,剩下的就在之后的文章中讨论吧。
(3)docker:容器中的进程
前文中,我们运行了一个名为nginx-demo的容器,如果我们想要进入这个容器内部,查看一下容器中的nginx配置文件内容,该怎么办呢?想进入容器很简单,我们只需要执行如下命令即可。
docker exec -it nginx-demo /bin/bash
执行上述命令后,命令提示符会从host主机的提示符变成了容器的提示符(host主机就是容器所在的主机),如下
[root@localhost docker]# docker exec -it nginx-demo /bin/bash
root@4cf7518d3afb:/#
root@4cf7518d3afb:/#
容器的提示符是root@4cf7518d3afb:/# ,细心如你肯定已经发现了,4cf7518d3afb正是容器的ID,也就是执行docker ps命令时看到的nginx-demo的容器ID。
提示符变化后,所有操作就是在容器中进行了,我们可以进入某个目录,查看一些配置文件的内容,如下
[root@localhost simple]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost simple]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a72860cb95fd 7 weeks ago 188MB
[root@localhost simple]# docker start nginx-demo
nginx-demo
[root@localhost simple]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4cf7518d3afb nginx:latest "/docker-entrypoint.…" 4 hours ago Up 8 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx-demo
[root@localhost simple]# docker exec -it nginx-demo /bin/bash
root@4cf7518d3afb:/# cd /etc/nginx/
root@4cf7518d3afb:/etc/nginx# ls
conf.d mime.types nginx.conf uwsgi_params
fastcgi_params modules scgi_params
root@4cf7518d3afb:/etc/nginx# cat nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
root@4cf7518d3afb:/etc/nginx#
如果我们尝试在nginx-demo容器中使用vim或者vi命令去修改配置文件,则会报错
bash: vim: command not found
很明显,我们下载的nginx镜像中并没有包含vim和vi,所以,通过nginx镜像启动的容器中也不会有对应的命令,这种情况在容器内操作时是很常见的,因为在创建镜像时,制作者为了尽量减小镜像的体积,会只将必要的程序打包进镜像,显然,编辑器对于nginx来说,不是必须依赖的程序,没有编辑器,怎样修改文件内容呢?
有两种方法,一种方法是先将容器内的文件拷贝出来,在host主机中修改完成后再拷贝回去,还有一种方法是通过docker卷的方式,将容器中的配置文件目录与host主机中的某个目录映射挂载,映射后,直接在host主机中即可修改对应的配置文件了。第二种方法是较为常用的,但是由于docker卷的话题后面会单独总结,所以此处咱们先使用第一种方法,直接把文件拷贝出来修改,然后再拷贝回去,操作如下
#注:拷贝操作均在host主机中完成
#先查看一下当前目录,只有一个文件
[root@localhost docker]# ls
daemon.json
#通过docker cp命令,将nginx-demo容器中的/etc/nginx/nginx.conf文件拷贝到当前路径下
[root@localhost docker]# docker cp nginx-demo:/etc/nginx/nginx.conf ./
Successfully copied 2.56kB to /etc/docker/./
#再次查看当前目录,已经多出了一个nginx.conf文件
[root@localhost docker]# ls
daemon.json nginx.conf
#修改配置文件内容,在配置文件的最后一行添加一行注释
[root@kvm32docker2 ~]# vim nginx.conf
#通过docker cp命令,将当前目录中修改过的nginx.conf文件拷贝回容器中
[root@localhost docker]# docker cp ./nginx.conf nginx-demo:/etc/nginx/nginx.conf
Successfully copied 2.56kB to nginx-demo:/etc/nginx/nginx.conf
完成上述操作后,再次回到容器中查看对应的文件,会发现文件内容已经是修改过的内容了。
如你所见,借助docker cp命令,可以在host主机和容器间互相拷贝文件,即使容器停止了,也可以进行拷贝操作。
进入容器后,如果想要退出,在容器中执行exit命令即可,如下:
[root@localhost docker]# docker exec -it nginx-demo /bin/bash
root@4cf7518d3afb:/# exit
exit
[root@localhost docker]#
在容器的shell中执行exit命令会结束当前shell,如果不想结束当前shell,只是想要从当前shell中剥离出来,可以使用组合键Ctrl + p + q
其实,docker exec命令不仅可以帮助我们进入容器,还能让我们在不进入容器的情况下,直接运行容器内的命令,我们先来看一个示例,然后再详细的讨论docker exec命令参数的具体作用,示例如下:
[root@localhost docker]# docker exec nginx-demo ls -l /etc/nginx/
total 24
drwxr-xr-x. 1 root root 26 Aug 12 03:19 conf.d
-rw-r--r--. 1 root root 1007 May 28 13:22 fastcgi_params
-rw-r--r--. 1 root root 5349 May 28 13:22 mime.types
lrwxrwxrwx. 1 root root 22 May 29 16:45 modules -> /usr/lib/nginx/modules
-rw-r--r--. 1 root root 674 Aug 12 07:31 nginx.conf
-rw-r--r--. 1 root root 636 May 28 13:22 scgi_params
-rw-r--r--. 1 root root 664 May 28 13:22 uwsgi_params
上例中,我们没有进入容器,而是通过docker exec命令指定在nginx-demo中运行ls -l /etc/nginx/命令,并将容器内的运行结果返回到当前的命令行中,从而查看到了容器内部/etc/nginx/目录中的信息。
你肯定已经发现了,如下两条命令的主要区别在于是否使用了-it参数
命令一:
docker exec -it nginx-demo /bin/bash
命令二
docker exec nginx-demo ls -l /etc/nginx/
如果我们把命令一中的-it参数去掉,其实它们两个的含义是一样的,都是在指定的容器中运行对应的命令,并将返回值显示在当前的命令行中,只不过,当我们直接执行如下命令时,是没有任何返回值的
[root@localhost docker]# docker exec nginx-demo /bin/bash
[root@localhost docker]#
上例表示在nginx-demo容器中执行/bin/bash命令,并将返回值显示到当前命令行中,但是由于容器中的bash没有任何输出,所以给人的感觉好像没有变化一样。
那么,-it参数是什么意思呢?其实,-it是两个参数,-i和-t。
-i参数表示interactive,即交互模式。
-t参数表示分配一个伪终端。
通常情况下,这两个参数会结合在一起使用,方便我们在终端中以交互模式运行指定的程序,所以,现在再回过头来看下面的命令,就很容易理解其意义了
docker exec -it nginx-demo bash
上述命令表示,在终端中以交互模式运行nginx-demo容器中的bash程序,最终呈现的效果就是在容器中敲命令,需要注意的是,有的镜像中没有bash程序,只有sh程序或者其他shell,所以通过上述方法进入容器内部时,需要先确定镜像中的shell是什么。
除了在容器中运行shell需要利用到-it参数,还有哪些场景会用到-it参数呢?比如,当你的容器中有vi编辑器时,可以直接使用如下命令,编辑容器中的文件。
#如下命令表示在交互终端下,执行容器中的vi命令,编辑容器内的/etc/hosts文件
docker exec -it 容器名 vi /etc/hosts
刚才说过,为了尽量减少镜像的体积,镜像的制作者通常只会将必要依赖软件打包到镜像中,所以,在做实验时,没有对应工具命令的情况非常常见,没有关系,我们可以直接在镜像中安装对应的软件,方便我们进行实验,例如,当前nginx-demo容器所使用的镜像是基于debian操作系统建立的,这一点通过容器内的/etc/os-release文件内容可以确认,所以,我们可以使用apt-get的软件源安装需要的软件,就像在debian系统下安装软件一样。
此处在nginx-demo容器中安装procps包,以便可以在容器中执行ps命令,方便之后的测试。同理,也可以安装vim,方便在容器中直接编辑文件。
root@4cf7518d3afb:/# apt-get update && apt-get install procps
如果在容器中执行apt-get install命令时对应的软件下载缓慢,可以修改容器的/etc/apt/sources.list文件中的下载源,就跟平常debian系统下的操作别无二致。
安装完procps后,即可在nginx-demo容器中执行ps命令了,执行结果如下: