
重启docker服务
systemctl restart docker

查看docker服务状态
systemctl status docker
开机启动docker服务
systemctl enable docker

### 2.2、docker 镜像相关命令
查看docker全部命令
docker

你会发现docker的命令基本都是docker打头
查看镜像(对于本地):查看本地是否有要搜索的镜像
docker images

在什么都没安装的情况下:我们会发现docker为我们提供了一个helo-world的镜像。
我们来看一下它的属性:
REPOSITORY:装有软件及其它所需要的文件系统
TAG:版本号
IMAGE\_ID:镜像ID
CREATED:什么时候创建的
SIZE:大小
我们还可以在命令后加-q,查找仓库全部的image\_id

搜索镜像(由于需要去本地或者远程仓库查找,所以需要网络)
docker search xxx
比方我找redis的镜像

同样的,我们来看一下表头属性:
NAME:镜像名
DESCRIPTION:简介
STARS:类似github的欢迎数
OFFICIAL:是否为官方认证
AUTOMATED:是否自动构建的
拉取镜像
`docker pull xxxx(: xx)` 冒号后为版本号,不加则默认latest.


可是有时我想要具体使用某个版本的docker,但又不知道docker仓库到底有没有我要的具体版本的镜像,怎么办呢?
我们可以去hub.docker.com去查找


点进去,我们可以看到它所支持的tags.然后找到适合自己的版本拉取下来就行了

ps:一个镜像可以有多个版本名,比方这里的6.2.5,6.2,6你可以理解为就是redis 6.2

比方我现在下载一个redis5.0


删除镜像
`docker rmi (IMAGE ID)`:括号参数为镜像ID

你会发现我并没有把全部的镜像ID输入,只输入了前3位,那么此时docker会匹配你的镜像id前3位为这个的镜像,将其全部删掉。
当然你也可以通过docker rmi REPOSITORY:TAG删除相应版本的镜像也是可以的。

同linux,你可以在后面加上-f表示强制删除。
那么我们是否可以一次性删除全部的镜像呢,是可以的。
我们既然删除的参数是镜像id,那么我将全部的镜像id作为参数传进去就好了啊
`docker rmi $(docker images -q)`就可以了。
### 2.3、docker容器相关命令
查看容器
`docker ps`:查看所有当前正在运行的容器,如果是通过-i创建的,那么已经因exit退出而关闭的容器不会显示出来。

`docker ps -a`:查看所有的容器,包括已经exit的容器。

`docker ps -aq`:查看所有的容器id,包括已经exit的容器。
创建容器
docker run -it(d) --name=xx REPOSITORY:TAG /bin/bash
-i
表示容器交互式操作,保持容器运行。通常与 -t 同时使用。加入it这两个参数后,容器创建后自动进入容器中,退出容器后,容器自动关闭。
`-t`表示给容器赋予伪终端,能够实施一些命令
`-d`:以守护(后台)模式运行容器。创建一个容器在后台运行,需要使用docker exec 进入容器。退出后,容器不会关闭。
`-it` 创建的容器一般称为交互式容器,-id 创建的容器一般称为守护式容器
`--name`表示给容器起名字,可以用等号,也可以用空格。
REPOSITORY:TAG指定对应版本号的镜像
`/bin/bash`:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
为确保测试能够正常进行
我提前下载了一个redis 5.0

我们通过 `docker run -it --name=redis5 redis:5.0 /bin/bash`测试一下
我们发现我们就已经进入了我们创建的容器中,且是一个redis的伪终端。

我们可以用exit退出终端,我们发现我们又回到我们的linux上了。

我们刚刚通过参数-it创建了终端式的,现在我们通过-id参数创建一个后端运行的容器。
docker run -id --name=redis2 redis:5.0

我们发现我们并没有进入终端,而是给了一个很长的id,我们可以**用命令进入这个容器**。
docker exec -it redis2 /bin/bash

进入容器
docker exec 参数 容器名or容器id /bin/bash
`-i`:分配一个伪终端
`-t`:表示给容器赋予伪终端,能够实施一些命令
`-d`:分离模式: 在后台运行
**ps:docker exec 进入容器后通过exit退出,容器不会停止!**

停止及启动容器
docker stop 容器名or容器id

那么有停止容器就有启动容器
docker start 容器名or容器id

删除容器
docker rm 容器名or容器id

ps:一般情况下,你不能关闭一个已经启动的容器
当然,你可以通过`rm后加-f`强制删除这个启动的容器

那同删除所有镜像一样,你也可以将命令作为参数传给docker rm,
那删除全部容器的命令就是
docker rm $(docker ps -aq)

查看容器信息
docker inspect 容器名or容器id

同样的,你可以通过docker inspect $(docker ps -aq)查看所有容器信息
## 三、docker数据卷
我们先来看下第一个问题:比方我现在有一个通过mysql5.7镜像创建的容器,假设我在这个容器创建了数据库,并往里面加入了数据,这时候容器挂了,里面的数据还在吗?
答案:当然不在了,容器都没了,数据又没存在额外的地方。那岂不是容器非常的不安全。
那想想如果我的容器能够和外部交换文件的话,岂不就可以解决上述困难了:
比方我的一个web项目放到tomcat的容器去运行,比方我将mysql容器的数据文件拿出来等等。
可惜,外部机器和容器之间网络并不互通。这一点我们目前也做不到。但外部机器可以和宿主机互通。

那容器和容器之间能交互数据吗,其实这些问题引出我们的主角—数据卷
### 3.1、数据卷概念
1、数据卷是宿主机中的一个目录或文件,当然单独的宿主机文件并不是数据卷,需要将数据卷的目录和容器的目录挂载绑定起来,那么此时的宿主机目录才可称之为数据卷。
2、当容器目录和数据卷目录绑定后,双方的修改会立即同步
3、一个数据卷可以被多个容器同时挂载
4、一个容器也可以被挂载多个数据卷

数据卷的作用
1、容器数据持久化
2、外部机器和容器间接通信
3、容器之间数据交换
### 3.2、配置数据卷
* 创建启动容器时,使用 –v 参数 设置数据卷 docker run … –v 宿主机目录(文件):容器内目录(文件)
…
ps:
1. 目录必须是绝对路径
2. 如果目录不存在,会自动创建
3. 可以挂载多个数据卷

我们现在演示一下,为了更加清晰,我已经提前删除了全部容器。然后安装了centos7的镜像

我们通过`docker run -id -v /root/docker/data:/root/data --name=mycentos centos:7 /bin/bash`创建centos7的容器并且进行挂载,将宿主机的/root/docker/data(没有会创建)挂载到我们容器的/root/data下,然后创建容器。
接着我们docker exec进入容器,然后我们进入root的目录下ll可以看到我们创建的/data目录。同样的我们在宿主机的root目录下可以看到我们创建的/docker/data目录

接着我们来演示一下数据卷的作用
我们进入容器的/data目录,touch一个txt文件。

这个时候看下宿主机的data下,发现这个文件同样被同步进去了。

同样的,我们对文件进行的数据操作,也会被同步
我们执行`echo mbw > mbw.txt`

回到宿主机这里再看看

然后现在我们模拟下假设容器挂掉了或者已经删了,宿主机的数据还有吗?

然后回到宿主机,我们发现数据还在

这个时候我们如果再次创建一个docker,然后通过
docker run -id -v ~/docker:/root/data --name=mycentos centos:7 /bin/bash
创建一个容器,并且挂载我们已经存在的数据文件。
我们看看新的容器是否同步已有的数据呢
我们可以很清楚的看到,内容完全同步了进去,即/root/docker下的所有数据挂载到容器的/root/data中。

当然,我们的容器创建时可以挂载多个数据卷
docker run -id
-v /root/docker/data1:/root/data1
-v /root/docker/data2:/root/data2
–name=centos1 centos:7 /bin/bash
我们可以看到这样就挂载了两个数据卷
且在容器和宿主机都可以看到

我们还可以实现两个容器同时挂载同一个数据卷,我们以宿主机的/root/docker为数据卷,
然后实现两个容器的数据交互:
我们创建两个容器,同时挂载/root/docker目录

然后我们在centos3创建文件


这个时候进入centos2和宿主机看看文件是否被创建:发现均同步了


### 3.3、数据卷容器
多容器进行数据交换
第一种办法就是我们刚才做的一样多个容器挂载同一个数据卷,但是这样操作就比较麻烦。
第二种就是通过数据卷容器
我们可以创建一个数据卷容器c3,这个c3和其他容器c1,c2没什么区别,但是c3挂载了一个数据卷,那么如果让c1,c2挂载这个数据卷容器,就简介挂在了数据卷。这也同时意味着c1,c2,c3都可以和数据进行交互 ,即使c3挂了,c1,c2仍然可以继续和数据卷进行数据交互。

那么我们就来创建数据卷。
首先将c1,c2,c3,c4全部给删了

然后创建启动c3数据卷容器,这个其实和之前的创建容器一样
docker run –it --name=c3 –v /root/docker/data:/volume centos:7 /bin/bash
然后创建c1,c2并挂载c3数据卷容器,我们使用 –-volumes-from 参数 设置数据卷
docker run -id --name=c1 --volumes-from c3 centos:7
docker run -id --name=c2 --volumes-from c3 centos:7
然后我们进入c2,看看有没有/volume这个文件夹:

这个时候我创建一个文件,然后退出c2进入c1,c3



发现同样同步成功。
假设我现在将c3删了,然后我在宿主机增加一个文件,这个时候回到c2,看看数据有没有同步:
证明就算数据卷容器没有了,我的c1,c2也已经简介挂载了宿主机的数据卷,且可以同步进行数据交互。

## 四、docker的应用
### 4.1、docker部署mysql
在Docker容器中部署MySQL,并通过外部mysql客户端操作MySQL Server。
实现步骤其实很简单,而且基本其他的都很相似,比起我们当时在linux自己安装要简单很多。
实现步骤:
1、搜索mysql镜像
2、拉取mysql镜像
3、创建容器
4、操作容器中的mysql
但是有一个问题,我们之前讲过,外部机器和容器之间网络是不通的,那么我们怎么让外部的mysql客户端连接上我们的mysql呢?
容器内的网络服务和外部机器不能直接通信
外部机器和宿主机可以直接通信
宿主机和容器可以直接通信
当容器中的网络服务需要被外部机器访问时,可以将容器中提供服务的端口映射到宿主机的端口上。外部机器访问宿主机的该端口,从而间接访问容器的服务。
这种操作称为:端口映射。

那我们现在就开始实操:
访问 MySQL 镜像库地址:<https://hub.docker.com/_/mysql?tab=tags> 。
可以通过 Sort by 查看其他版本的 MySQL,默认是最新版本 mysql:latest 。

我们这里下载mysql5.7的`docker pull mysql:5.7`,下载成功和可以看到。

然后就是关键了,我们要创建容器同时进行端口映射
docker run -d -p 3306:3306 --privileged=true -v /opt/mysql/conf/my.cnf:/etc/my.cnf -v /opt/mysql/data:/var/lib/mysql -v /opt/mysql/logs:/logs -e MYSQL_ROOT_PASSWORD=your password --name mysql mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
我们来解释一下,首先我们通过宿主机的3306端口映射容器的3306端口。
然后通过数据卷将mysql的data数据文件,conf配置文件分别挂载到宿主机自己创建的三个目录,将来我们就可以直接同步数据,非常的方便。
**最后需要在防火墙开放我们宿主机的3306端口!**
那么下面我们就进入容器验收成果:
我们通过`mysql -uroot -p`输入密码登入我们的mysql

然后在mysql客户端试试能否远程登录,连接前要保证容器是开启的状态,没有记得docker start xxx

连接成功,docker安装mysql成功.
### 4.2、docker安装tomcat
这个比较复杂,我们需要用到一些新知识。
首先还是之前的老两步:
search–pull
我这里pull的是和我本地一致的版本:tomcat:8.5.63-jdk8-openjdk
pull好后,我们就需要创建容器,学过javaweb知识的肯定知道tomcat有两个比较重要的文件夹,webapps以及conf这两个文件夹,为了方便,我们自然而然能想到通过挂载的方式解决。
我们通过以下命令:
docker run -d --name tomcat -p 8080:8080 -v /opt/tomcat/conf:/usr/local/tomcat/conf -v /opt/tomcat/webapps:/usr/local/tomcat/webapps -v /opt/tomcat/logs/:/usr/local/tomcat/logs --restart=always tomcat:8.5.63-jdk8-openjdk
直接创建后,发现启动不起来,一直是restart状态,将 --restart=always 去掉重新run后发现状态变成了exit,就是启动不起来。
这是因为上面的命令中,我们也挂载了conf文件夹。这个是tomcat的配置文件夹。必须把tomcat中的conf中的文件复制到挂载文件上(**即/home/supervision\_app/app/conf目录下需要有配置文件**)
**而我们将空的数据卷与我们的容器同步,结果全是空的,自然启动不起来了**。
所以我们暂时有一个思路,\*\*创建一个不挂载数据卷的tomcat容器,如果能正常启动起来,就将它这里的数据移动到我们原来的数据卷中。\*\*其实这个思路是可行的。
我们docker rm -f tomcat后再次通过
docker run -d --name tomcat -p 8080:8080 --restart=always tomcat:8.5.63-jdk8-openjdk
**然后防火墙开放8080端口。**
创建tomcat,现在通过docker ps -a发现状态变成正常的了,那么我们通过
`docker exec -it tomcat /bin/bash`进入容器,进入成功。

这个时候exit退出后,由于我们做了端口映射,我们试试远程能不能访问:
一输入发现404

这里其实已经说明,我们的tomcat已经起了。端口什么的都配好了。
使用命令: `docker exec -it 运行的tomcat容器ID /bin/bash` 进入到tomcat的目录
进入webapps文件夹,发现里面是空的(tomcat默认的欢迎页面实际上放在的路径应该是:webapps/ROOT/index.jsp或者index.html)
**发现下边还有个webapps.dist的文件,进入才发现原本应该在webapps文件中的文件都在webapps.dist文件中**

**那么很简单,我们将webapps给删了,然后将webapps.dist改为webapps就行了。**
rmdir webapps
mv webapps.dist/ webapps
exit后`docker restart tomcat`后就可以发现启动成功

但其实,我们解决的并不完美,**因为再另启动一个Tomcat容器时又会回到之前的窘境,既实际上问题的根源是在生成容器的镜像上,只有将镜像修改了,再生成的容器才不会再出现这个问题。**
所以我们得通过将我们的容器映射为新的镜像,然后通过这个镜像创建容器,就无后患了。
我们可以使用`docker commit`命令将运行着的容器映射成新的镜像
格式:`docker commit -a='作者-这个随意写' -m='‘修改内容--随意写' 容器名称或者ID 新生成镜像的名称:自己定义的版本号`
比方我以我现在这个可以正常运行的tomcat容器映射成镜像,
docker commit -a=‘孟博文’ -m=‘修改404’ tomcat tomcat:v1
我们映射完后通过docker images可以看到我们的镜像生成成功了。

此时还没完,我们现在肯定想的是用这个新镜像创建新容器,但是我们之前那个tomcat容器还没有利用完:
因为你想想,我这个时候创建新容器,挂载还是空的,照样不行,所以我们将之前正常的tomcat容器文件复制到我们的挂载的数据卷上。
那怎么移动呢,docker自己提供了cp命令。
docker cp 容器名or id:容器目录or 文件 数据卷目录
我们将我们的conf以及webapps目录拷贝到/opt/tomcat下,在移动之前将以前空的文件删掉就行。
docker cp tomcat:/usr/local/tomcat/conf/ /opt/tomcat/
docker cp tomcat:/usr/local/tomcat/webapps/ /opt/tomcat/
然后发现我们的数据卷此时就有数据了:

这个时候tomcat容器光荣退休,我们把它删了。
docker rm -f tomcat
然后用我们的新镜像创建新的容器并挂载我们数据卷
docker run -d --name tomcat -p 8080:8080 -v /opt/tomcat/conf:/usr/local/tomcat/conf -v /opt/tomcat/webapps:/usr/local/tomcat/webapps -v /opt/tomcat/logs/:/usr/local/tomcat/logs --restart=always tomcat:v1

如果发现status为up就代表成功!去浏览器输入发现同样能进入主页!

### 4.3、docker部署nginx
如果tomcat安装成功,其实nginx就非常简单了。
首先我们pull nginx的镜像,我这里拉取的是1.19.0的

然后通过docker run创建一个正常的nginx的容器
docker run -d --name=nginx -p 80:80 --restart=always nginx:1.19.0

创建成功后,我们可以直接访问192,168.200.130:80访问
但是我们现在还没挂载,借用之前安装tomcat的思路,我们同样先在/opt创建nginx文件夹,然后在这下面创建三个文件夹:conf,logs,html分别存放我们的配置文件,日志文件,以及项目。
而nginx的这三份对应的文件在:
1、日志位置:**/var/log/nginx/**
2、配置文件位置:**/etc/nginx/nginx.conf**
3、项目位置:**/usr/share/nginx/html**
我们通过cp命令拷贝到我们的数据卷即可:
docker cp /var/log/nginx/ /opt/nginx/logs
docker cp nginx:/usr/share/nginx/html /opt/nginx/
docker cp nginx:/etc/nginx/nginx.conf /opt/nginx/conf

‘
这个时候通过`docker rm -f nginx`
然后创建新容器并挂载数据卷
docker run -d --name=nginx --restart=always -p 80:80 -v /opt/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /opt/nginx/html/:/usr/share/nginx/html -v /opt/nginx/logs:/var/logs/nginx nginx:1.19.0
此时通过`docker ps -a`

这个时候访问192.168.200.130:80即可访问到我们的nginx,docker安装nginx成功!

### 4.4、docker安装redis5
拉取镜像redis:5:`docker pull redis:5`

然后创建/opt/redis/conf和/opt/redis/data两个目录

然后去<https://download.redis.io/redis-stable/redis.conf>下载redis.conf文件传到/opt/redis/conf

vim这个文件,需要注意修改的地方:
`bind 127.0.0.1` #注释掉这部分,这是限制redis只能本地访问
`protected-mode no` #默认yes,开启保护模式,限制为本地访问
`daemonize no`#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
`databases 16` #数据库个数(可选),我修改了这个只是查看是否生效。。
`dir ./` #输入本地redis数据库存放文件夹(可选)
`appendonly yes` #redis持久化(可选)
修改完后,就可以创建容器并挂载了
docker run --restart=always -d --privileged=true -p 6379:6379 -v /opt/redis/conf/redis.conf:/redis.conf -v /opt/redis/data:/data --name redis redis:5 redis-server --appendonly yes
这里介绍一些参数的意义:
`--privileged=true` 提升容器内权限
`redis-server` redis-server就是redis的启动命令
`--appendonly yes` 开启数据持久化

然后在redis客户端发现一样能登陆成功,redis安装成功!

## 五、dockerfile
提前剧透:dockerfile就是用来制作镜像的
思考一下:
Docker 镜像本质是什么?
Docker 中一个centos镜像为什么只有200MB,而一个centos操作系统的iso文件要几个个G?
Docker 中一个tomcat镜像为什么有500MB,而一个tomcat安装包只有70多MB?

在讲原理之前,大家不妨先了解一下操作系统和linux的文件系统。
操作系统组成部分:
1.进程调度子系统
2.进程通信子系统
3.内存管理子系统
4.设备管理子系统
**5.文件管理子系统**
6.网络通信子系统
7.作业控制子系统
Linux文件系统由bootfs和rootfs两部分组成
bootfs:包含bootloader(引导加载程序)和 kernel(内核)
rootfs: root文件系统,包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc等标准目录和文件
不同的linux发行版,bootfs基本一样,而rootfs不同,如ubuntu,centos等

### 5.1、docker镜像原理
Docker镜像是由特殊的文件系统叠加而成
最底端是 `bootfs`,**并使用宿主机的bootfs,比方linux操作的时候会加载一次内核,然后docker加载的时候就不会再次重复加载,而是直接使用之前linux加载的内核。所以docker启动非常快。实现一种复用效果**。
第二层是 root文件系统`rootfs`,称为**base image**
然后再往上可以叠加其他的镜像文件,比方我们可以在基础镜像上放上jdk镜像,然后在jdk镜像上接着放tomcat的镜像。这三部分组合起来,我们就把它称作tomcat镜像。这种组合性的镜像叫做**只读镜像**!那么这样的好处,仙子啊假设我在tomcat这样一个组合镜像基础上,再安装一个nginx的镜像,只读镜像已经有的,如果nginx也刚好需要,它就不需要再去下载了,即我**下载的每一层镜像都可以被复用**!
统一文件系统(Union File System)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,**这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。** 这也就解释了为什么tomcat这样一个镜像就有500多Mb,centos镜像反而只要200Mb.
一个镜像可以放在另一个镜像的上面。**位于下面的镜像称为父镜像,最底部的镜像成为基础镜像**。
**只读镜像不能修改**,如果我想修改的话,就有了下面的方法,当从一个镜像启动容器时,Docker会在最顶层加载一个读写文件系统作为容器。

而我们可以通过容器创建新的镜像,由此对镜像去做出一些改变,而可读镜像仍然是固定的,可复用的!

那么我们现在就来回答一下刚才的思考题:
1.Docker 镜像本质是什么?
是一个分层文件系统
2.Docker 中一个centos镜像为什么只有200MB,而一个centos操作系统的iso文件要几个个G?
Centos的iso镜像文件包含`bootfs`和`rootfs`,而docker的centos镜像复用操作系统的bootfs,只有rootfs和其他镜像层
3.Docker 中一个tomcat镜像为什么有500MB,而一个tomcat安装包只有70多MB?
由于docker中镜像是分层的,tomcat虽然只有70多MB,但他需要依赖于父镜像和基础镜像,所有整个对外暴露的tomcat镜像大小500多MB
我们可以通过`docker inspect tomcat:v`1看一下我们的tomcat到底被分为多少层:你可以看到被分成了特别多层:

### 5.2、容器转化为镜像
命令:`docker commit -a’xxxx作者信息’ -m’修改信息’ 容器id/容器名称 自定义镜像名称:自定义版本号`
比方我们将修改好的tomcat转化为镜像:
docker commit -a=‘孟博文’ -m=‘修改404’ tomcat tomcat:v1

这样就生成了我们想要的镜像,这在今后是非常方便的,我们比方在tomcat部署了一个web项目,然后我们就可以将它转化为我们的镜像,然后将这个镜像通过某种方式转给其他人,就ok了。
那我们该怎么将镜像分享出去呢,docker有一个压缩命令可以将镜像压缩为文件。
docker save -o 压缩文件名称(可以加路径,不加默认当前目录) 镜像名称:版本号
我们可以将这个压缩文件发给其他人,然后其他人可以把这个压缩文件转化为镜像,然后使用就是我们自己写改好的东西了。
那怎么把压缩文件还原成镜像:
docker load -i 压缩文件名称
那我们现在从头到尾演示一遍:
为了一个需要注意的知识点,我现在新建一个test文件夹然后写一个页面放进我们的webapps叫做test.html!

然后由于**挂载**,它肯定是放在了我们的容器中的,我们可以看一下演示一下:

现在我将这个生成镜像叫做tomcat:v2
docker commit -a ‘孟博文’ -m ‘挂载页面’ tomcat tomcat:v2

然后我们先进去创建一个v2的容器看看我们挂载的test.html还在不在,假设我创建的tomcat容器我不挂载任何一个目录,因为你发给别人,别人肯定不能挂载到你这里的文件。
docker run -d --name=tomcat2 -p 8888:8080 --restart=always tomcat:v2
运行后进去这个容器,发现我们的test文件夹不见了!访问肯定也是404

**docker commit是只会保留容器的数据,不会保留任何挂载在宿主机的数据。**
如果我们在我们刚才生成的镜像里创建文件,那么毫无疑问会被保留下来,这也是为什么我们将空的webapps删了,将webapps.dist改为webapps能够在新镜像保留下来的原因。那怎么保留我们的数据,我们在之后的dockerfile会讲解。
然后我们现在尝试着将我们这个镜像生成压缩文件,然后删除我们这个v2镜像,再把这个压缩文件还原,看是否能重新生成该镜像。
docker save -o /opt/tomcat/tomcat.tar tomcat:v2

docker load -i /opt/tomcat/tomcat.tar

镜像重新生成成功,演示完毕!
### 5.3、dockerfile概述以及相关关键字
dockerfile概念:
Dockerfile 是一个文本文件
1、包含了一条条的指令
2、每一条指令构建一层,基于基础镜像,最终构建出一个新的镜像
3、对于开发人员:可以为开发团队提供一个完全一致的开发环境
4、对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了
5、对于运维人员:在部署时,可以实现应用的无缝移植

我们可以在docker官网搜到一些dockerFile.
Dochub网址:<https://hub.docker.com>
| 关键字 | 作用 | 备注 |
| --- | --- | --- |
| | | |
| FROM | 指定父镜像 | 指定dockerfile基于那个image构建 |
| MAINTAINER | 作者信息 | 用来标明这个dockerfile谁写的 |
| LABEL | 标签 | 用来标明dockerfile的标签 可以使用Label代替Maintainer 最终都是在docker image基本信息中可以查看 |
| RUN | 执行命令 | 执行一段命令 默认是/bin/sh 格式: RUN command 或者 RUN [“command” , “param1”,“param2”] |
| CMD | 容器启动命令 | 提供启动容器时候的默认命令 和ENTRYPOINT配合使用.格式 CMD command param1 param2 或者 CMD [“command” , “param1”,“param2”] |
| ENTRYPOINT | 入口 | 一般在制作一些执行就关闭的容器中会使用 |
| COPY | 复制文件 | build的时候复制文件到image中 |
| ADD | 添加文件 | build的时候添加文件到image中 不仅仅局限于当前build上下文 可以来源于远程服务 |
| ENV | 环境变量 | 指定build时候的环境变量 可以在启动的容器的时候 通过-e覆盖 格式ENV name=value |
| ARG | 构建参数 | 只在构建的时候使用的参数 如果有ENV 那么ENV的相同名字的值始终覆盖arg的参数 |
| VOLUME | 定义外部可以挂载的数据卷 | 指定build的image那些目录可以启动的时候挂载到文件系统中 启动容器的时候使用 -v 绑定 格式 VOLUME [“目录”] |
| EXPOSE | 暴露端口 | 定义容器运行的时候监听的端口 启动容器的使用-p来绑定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp |
| WORKDIR | 工作目录 | 指定容器内部的工作目录 如果没有创建则自动创建 如果指定/ 使用的是绝对地址 如果不是/开头那么是在上一条workdir的路径的相对路径 |
| USER | 指定执行用户 | 指定build或者启动的时候 用户 在RUN CMD ENTRYPONT执行的时候的用户 |
| HEALTHCHECK | 健康检查 | 指定监测当前容器的健康监测的命令 基本上没用 因为很多时候 应用本身有健康监测机制 |
| ONBUILD | 触发器 | 当存在ONBUILD关键字的镜像作为基础镜像的时候 当执行FROM完成之后 会执行 ONBUILD的命令 但是不影响当前镜像 用处也不怎么大 |
| STOPSIGNAL | 发送信号量到宿主机 | 该STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。 |
| SHELL | 指定执行脚本的shell | 指定RUN CMD ENTRYPOINT 执行命令的时候 使用的shell |
### 5.4、dockerfile案例—自定义centos
我们自定义一个centos镜像要实现以下功能:
1、默认登陆路径为/usr
2、可以使用vim
实现步骤:
定义父镜像:`FROM centos:7`
定义作者信息:`MAINTAINER mbw <mbw@mbw.com>`
执行安装vim命令: `RUN yum install -y vim`
定义默认的工作目录:`WORKDIR /usr`
定义容器启动执行的命令:`CMD /bin/bash`
通过dockerfile构建镜像:`docker bulid –f dockerfile文件路径 –t 镜像名称:版本 URL`
我们现在在/opt创建dockerfile/centos目录,在其下vim一个dockerfile文件,写上以下内容:

然后通过`docker build -f /opt/docker-files/centos/dockerfile -t mycentos:v1` .
`-f`后接的是dockerfile的路径
`-t`是自定义的镜像名:版本号
`.`表示当前路径,一般都是这个
生成后,我们通过docker images 查看

然后通过`docker run -it --name=mycentos mycentos:v1`创建并进入我们的容器
发现我们的默认工作目录已经变成了/usr

然后试下vim,发现也可以成功。那么自定义centos就算完成了。

### 5.5、dockerfile实例二:测试一个springboot的项目
我们创建一个非常简单的springboot的项目来测试是否能运行


在保证项目没问题的前提下,我们通过`mvn package`将我们的项目打成jar包


我们可以通过`java -jar xxx.jar`正常运行

然后来到docker这边
首先pull一个jdk8的镜像
docker pull abrtech/alpine-oraclejdk8

一切都成功的情况下,我们可以创建dockerfile。
在`/opt/docker-files`创建`project/docker_springboot`目录,将生成的jar包放入并创建dockerfile

Dockerfile内容如下:
FROM 基础镜像:`FROM abrtech/alpine-oraclejdk8`
ADD 类似于linux命令中的copy:
`ADD docker_springboot-0.0.1-SNAPSHOT.jar app.jar`
暴露项目的服务端口:`EXPOSE 8080`
启动命令:`CMD java -jar app.jar`
编写完后**cd 到/opt/docker-files/project/docker\_springboot**执行
`docker build -f ./Dockerfile -t app .`创建镜像
执行完后可以通过`docker images`看到我们生成的镜像:

然后运行容器启动jar包
docker run -it -p 9090:8080 --name=springboot_docker app

发现项目成功运行起来,输入192.168.200.130:9090/hello,访问成功!

### 六、docker compose
微服务架构的应用系统中一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,维护的工作量会很大。
例如要从Dockerfile build image 或者去dockerhub拉取image
要创建多个container
要管理这些container(启动停止删除)
这样的工作量是非常恐怖且浪费时间的!
服务编排:按照一定的业务规则批量管理容器
### 6.1、服务编排
Docker Compose概念
Docker Compose是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建,启动和停止。使用步骤:
1、利用 Dockerfile 定义运行环境镜像
2、使用 docker-compose.yml 定义组成应用的各服务
3、运行 docker-compose up 启动应用

安装docker Compose
curl -L “https://get.daocloud.io/docker/compose/releases/download/1.27.3/docker-compose- ( u n a m e − s ) − (uname -s)- (uname−s)−(uname -m)” -o /usr/local/bin/docker-compose

然后设置文件可执行权限
chmod +x /usr/local/bin/docker-compose
验证是否安装成功可通过`docker-compose -v`,显示版本号就说明docker-compoe安装成功!

要是想删除其实docker-compose也很简单,因为我们是通过二进制包方式安装的,直接删除这个文件就好
rm /usr/local/bin/docker-compose
### 6.2、docker-compose编排nginx+springboot的项目
我们首先通过docker images查看
我们的镜像
之前我们直接通过run我们的app容器启动我们的springboot项目,但是假设现在我们需要nginx反向代理我们的项目,你可能会想,我再启动我的nginx就好了,是可以这样,但万一你的项目又要很多种数据库,再加上消息队列,还需要布置集群,你还愿意一个一个启动吗?
所以我们通过docker compose将这些全部放一起:
我们在/opt下新建docker-compose/docker-compose.yml,这个yml名字不能改变。
然后编辑docker-compose.yml,**语法格式注意是yml**
version: ‘3’
services:
#第一个要编排的服务
nginx:
#基础镜像
image: nginx:1.19.0
#端口映射
ports:
- 9090:80
#链接到其它服务的中的容器,这里可以是别名也可以是服务名称
links:
- app
#数据卷挂载,挂载我们宿主机自己nginx的配置文件
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
#第二个服务,其次也是被nginx链接的服务app
app:
image: app
#暴露的端口号
expose:
- “8080”
然后就是nginx的配置文件
我们在docker-compose.yml处于的当前目录新建当时我们挂载的目录
`mkdir -p /nginx/conf.d`,然后在这下面创建mbw.conf这个配置文件

这样我们写的配置文件自然会被挂载进去,配置文件内容如下:
server {
listen 80;
access_log off;
location / {
proxy_pass http://app:8080;
}
}
意思就是当访问192.168.200.130,就会自动代理到app这个镜像的8080端口。
这个app其实是我们刚才links里链接的app镜像。
然后:wq保存退出,**cd …/…/返回装有yml的目录**
通过`docker-compose up`启动我们的docker-compose即可

启动成功!我们访问192.168.200.130:9090/hello(9090是映射的nginx的80端口)