平常的产品发布的时候,我们根据不同平台需要配置不同的环境,大大增加了我们的工作。而Docker就可以简化我们的工作,Docker镜像的设计,使得Docker得以打破过去**「程序即应用」**的观念。透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
可以想象成Docker就是简单的虚拟机。
Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
那么docker和虚拟机有什么区别呢?
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口
虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。
Docker与虚拟机架构对比图:
Docker 支持以下的 64 位 CentOS 版本:
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum install docker-ce
# 如果不能成功就先更新yum软件包索引
# sudo yum makecache fast
# 开机自启
sudo systemctl enable docker
# 启动docker服务
sudo systemctl start docker
docker version
接下来是一些可选配置,建议设置!
# 1. 建立 Docker 用户组
sudo groupadd docker
# 2.添加当前用户到 docker 组
sudo usermod -aG docker $USER
# 加速器地址 :
# 阿里云控制台搜索容器镜像服务
# 进入容器镜像服务, 左侧最下方容器镜像服务中复制加速器地址
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["你的加速器地址"]
}
EOF
# 重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
1.镜像是什么?
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件
(1)UnionFS(联合文件系统)
UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,Union文件系统是Dokcer镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统加载起来,这样最终的文件系统会包含所有的底层文件和目录
(2)Docker镜像加载原理
docker的镜像实际上是由一层一层的文件系统构成,这种层级的文件系统UnionFS。
主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的linux/unix系统是一样的,包含boot加载器内核。当boot加载完之后整个内核就都在内存中了,此时内存的使用权已经由bootfs交给内核了,此时系统也会卸载bootfs
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M
对以一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就行,因为底层直接用host和kernel,自己只需要提供rootfs就行。由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
(3)分层的镜像
以pull为例,在下载的过程中我么可以看到docker的镜像好像是在一层一层的下载
(4)为什么Docker镜像要采用这种分层的结构
最大一个好处就是——共享资源
比如:有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一份base镜像,同时内存中也需要加载一份base镜像,就可以为所有服务器服务了。而且镜像的每一层都可以被共享。
2.特点
docker镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下都叫“镜像层”
最大的一个好处就是 :共享资源,节约空间
比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,
同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
# 可以查看当前的 docker 版本信息
[zztroot@localhost ~]$ docker version
Client: Docker Engine - Community
Version: 19.03.12
API version: 1.40
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:46:54 2020
OS/Arch: linux/amd64
Experimental: false
# 后面省略不显示
# 查看docker系统信息
[zztroot@localhost ~]$ docker info
Client:
Debug Mode: false
Server:
Containers: 1
Running: 0
# 后面省略不显示
# 查询docker的指令
[zztroot@localhost ~]$ docker --help
# 下面会显示多个指令的意思,也会有子指令的提示
# 还可以通过下面方式查看某个指令的用途
zztroot@localhost ~]$ docker pull --help
Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
Pull an image or a repository from a registry
Options:
-a, --all-tags Download all tagged images in the repository
--disable-content-trust Skip image verification (default true)
--platform string Set platform if server is multi-platform capable
-q, --quiet Suppress verbose output
# 查询目前的镜像
[zztroot@localhost ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0d120b6ccaa8 7 days ago 215MB
mysql latest 0d64f46acfd1 13 days ago 544MB
hello-world latest bf756fb1ae65 7 months ago 13.3kB
# 搜索指定镜像,举例搜索mysql
[zztroot@localhost ~]$ docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9854 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3599 [OK]
percona Percona Server is a fork of the MySQL relati… 502 [OK]
# 拉取镜像,冒号输入的是版本号,也可以不输入,默认拉取lastest标签的镜像
docker pull name:tag
[zztroot@localhost ~]$ docker pull java
Using default tag: latest
latest: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:latest
docker.io/library/java:latest
# 移除某个镜像
docker rmi bf756fb1ae65 #通过docker images查看要移除镜像的id
# 如果报错,可能是该镜像还有容器再使用,建议看一下是否要删除
docker rmi -f bf756fb1ae65 # 强制删除指令
# 显示目前正在运行的容器
[zztroot@localhost ~]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41515b7bc337 centos "/bin/bash" About a minute ago Exited (0) About a minute ago mycentos
# 如果不输入-a就不能看见历史记录,只能看见当前运行的
# 实例化某个镜像,-p代表 主机端口:容器端口 --name 设置名字
docker run -p 8080:8080 --name mycentos centos
# 实例化完我们会发现,采用ps指令没有出现该容器,原因是机制认为该容器未被使用,所以自己暂停了
docker run -d centos #后台运行,守护进程的方式
docker run -it centos #进入交互界面
#以上两种可以进入容器
# exit可以关闭并退出容器
exit
# CTRL + P + Q ,可以不关闭,退出容器(无所谓大小写)
# attach、exec都需要保证容器在运行才可以
docker attach 容器id
docker exec -it 容器id [指令]
# 区别在于前者进入执行,后再可以在外层发出指令,让容器执行,并保持在外层
# 删除容器 docker rm 容器id
zztroot@localhost ~]$ docker rm $(docker ps -aq)
41515b7bc337
bda10e7bec92
# $(docker ps -aq) 的意思是删除所有容器,你也可以指定容器
# 查看容器内部细节
docker inspect 容器id
# 查看容器的日志,-f代表实时更新,--tail后面要加数字(选择后面第n条开始)
docker logs -f -t --tail 3 [容器id]
# 启动容器
docker start 容器id
# 关闭容器
docker stop 容器id
# 强制关闭
docker kill 容器id
# 复制容器内的文件到主机
docker cp 容器ID:容器内路径 目的主机路径
docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]
# 假设你的容器修改完了,你想保存这个镜像
[zztroot@localhost ~]$ docker commit -a="Gnight" -m="My Centos create zzt.txt" 92867844cfef mycentos_gnight:1.0
# 会返回id,可以通过docker images查询
sha256:82e54145035a497352cb97d79ac087e2e4c00991421ade9f77e76720f93c09a7
在Docker中,要想实现数据的持久化(Docker的数据持久化 == 数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中。
如果你学习过Redis,虽然它是内存数据库,但是也有RDB、AOF的持久化方式,可以再重启后自动恢复数据,不会因为关闭而导致数据丢失。
数据卷的使用
# 采用 -v 的方式创建数据卷
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
# 方法2,先创建好数据卷,不采用目录的方式
docker volume create mycentos-vol # 创建一个自定义容器卷
docker volume ls # 查看所有容器卷
docker volume inspect mycentos-vol # 查看指定容器卷详情信息
# 如果不再使用自定义数据卷了,那么可以手动清理掉:
docker stop 容器id # 暂停容器实例
docker rm 容器id # 移除容器实例
docker volume rm mycentos-vol # 删除自定义数据卷
# 补充说明,如果Docker挂载主机目录Docker访问出现cannot open directory .: Permission denied
# 解决办法:在挂载目录后多加一个 --privileged=true 参数即可
操作示例:
假设我们要设置一个目录叫contact_vol
,作为容器数据持久化的目录。
# 首先在 HOST 主机 操作实例化一个容器,并设置了数据卷目录
[zztroot@localhost myworkspace]$ docker run -it -v /home/zztroot/myworkspace/contact_vol:/docker_workspace/contact_vol centos
查看二者是否出现了一样的目录:
可以看到二者都出现了一样的 contact_vol
目录
# 主机和容器都可以修改该文件内容,并且都能查看到!
# 主机修改
[zztroot@localhost contact_vol]$ touch contact_vol
[zztroot@localhost contact_vol]$ ls
contact_vol
# 容器也看见
[root@b4d4a1c741b8 contact_vol]# ls
contact_vol
# 容器修改
[root@b4d4a1c741b8 contact_vol]# touch docker_add.txt
# 主机看见
[zztroot@localhost contact_vol]$ ls
contact_vol docker_add.txt
补充:
当我们多个容器都和宿主机某个目录有数据卷的时候,那么他们相互之间都是互通的,数据卷的寿命周期一直到没有容器使用它为止。
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。 平常我们通过pull拉取镜像,那么如果我们需要对镜像定制的时候咋办呢?我们可以通过编写DockerFile来实现镜像定制,这就是一个文件告诉我们该如何配置这个镜像,最后通过 build 指令创建镜像,可以通过 run 启动我们的镜像。
DockerFile语法知识:
#
注释单纯的说肯定没什么感觉,那就让我们讲一个示例,把每个保留字都用一下!
假设我们需要创建一个centos容器,由于这是精简的centos所以是没有vim,我们就给他装上vim。
# 首先创建DockerFile文件
vim DockerFile
# 编辑内容
# 从centos出发
FROM centos
# 设置作者和邮箱
MAINTAINER Gnight<[email protected]>
# 构建的时候要安装 vim
RUN yum -y install vim
# 对外暴露端口(指的是容器暴露的)
EXPOSE 80
# 配置环境变量
ENV START_PATH /usr
# 一开始进入终端后的位置,这里用环境变量替代了,也可以直接写目录
WORKDIR $START_PATH
# 设置容器卷
VOLUME /MyCentosWorkSpace
# CMD,只有最后一条生效,如果我们run的时候设置了别的指令,那么这条也不会执行
CMD /bin/bash
# 如果有容器继承(FROm)他,那么就打出来这句话
ONBUILD echo "hello , son !"
镜像创建指令:
docker build -f DockerFile -t mycentos:1.0 .
# 注意结尾有一个 . 一定不能缺失!!!!这是代表工作目录为当前!
# 当我们看见
Successfully built 21707a7bd77d
Successfully tagged mycentos:1.0
# 就代表创建成功了
# 查看我们创建好的镜像
[zztroot@localhost myworkspace]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 21707a7bd77d About a minute ago 272MB
cento satest 0d120b6ccaa8 8 days ago 215MB
那么实例化一下我们的镜像吧!
docker run -it --name father_centos -p 80:80 mycentos:1.0
ENTRYPOINT和CMD指令的区别在哪?
ENTRYPOINT属于不会被覆盖的,我们可以在run指令后继续添加完善这个指令,而cmd会被覆盖,如果DockerFile有多个CMD则只有最后一个生效,如果run指令后面添加了其他指令,那么原本的CMD也会失效。
# 创建DockerFile2
FROM centos
# 免得缺少curl
RUN yum -y install curl
# 设置entrypoint指令
ENTRYPOINT ["curl","-s","http://ip.cn"]
#------------------------------------------------------
#------------------------------------------------------
# 制造镜像
docker build -f DockerFile2 -t ipcheck:1.0 .
#------------------------------------------------------
# 可以直接在后面续上指令 -i 输出头信息
[zztroot@localhost myworkspace]$ docker run -it ipcheck:1.0 -i
# 以下是输出的信息
HTTP/1.1 403 Forbidden
Date: Wed, 19 Aug 2020 09:09:22 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 16
Connection: keep-alive
#后面省略……
ADD指令和COPY指令区别?
我们找到了Docker hub中centos7的Dockerfile:(创建centos7的DockerFile详情)
# scratch是基础镜像,大部分镜像都是这个发展起来的
FROM scratch
# 要保证含有该压缩包
ADD centos-7-docker.tar.xz /
LABEL org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20181006"
CMD ["/bin/bash"]
ADD会拉取并自动处理(解压),而COPY仅仅是复制
我们如果想要把SpringBoot打包成镜像怎么办?
target
中可以找到FROM java:8
# 要让这条指令实现,前提是:将我们的jar包复制出来,放到同一目录
COPY *.jar /lzzh.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/lzzh.jar"]
docker build -f DockerFile -t mylzzh:1.0 .
# 运行
docker run -d -p 8080:8080 mylzzh:1.0
# 后续打开浏览器访问8080即可