☕
0.前言
文章封面来自涌潮悲歌-KR
最近发的文章里大部分都有容器相关的东西,就考虑把以前学习时候的文档拿出来水一水,这是我旧的博客(已经停了)里翻出来的,大概两年前的东西了,虽然有点旧但是内容是挺多的,适合学习使用
文章的内容是我在一位优秀的前辈 编程不良人 的笔记之上,整合自己的学习和理解添加补充修改后形成
文章分为上下两部分,本篇是上半,下半部分在这里(点我)
(说起来真的非常感谢这位老师的JAVA和一些其他的基础视频,在我还在写该死的Oracle Package时,是这位老师的课程让我成功转型,真的非常感谢这位老师,如果觉得这篇文章不错可以多多关注编程不良人老师的B站账号!
一些链接:
- 官方文档地址
- Docker Hub
- 参考中文文档地址(中文文档也不太好用,网页点不动…)
- 离线Docker二进制文件包下载
1.什么是 Docker
1.1 简介
# 官方介绍
- We have a complete container solution for you - no matter who you are and where you are on your containerization journey.
- 翻译: 我们为你提供了一个完整的容器解决方案,不管你是谁,不管你在哪,你都可以开始容器的的旅程。
- 官方定义: docker是一个容器技术。
# 底层
- Docker使用Google公司推出的 Go语言 进行开发实现,基于 Linux 内核的 cgroup、namespace 以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
- Docker是一个CS架构,有分客户端和服务器端,详细你可以docker info 可以看出来 client和server
2.Docker和虚拟机区别
关于Docker与虚拟机的区别下面的图已经说的很清楚了。
比较上面两张图,我们发现虚拟机是携带操作系统,本身很小的应用程序却因为携带了操作系统而变得非常大,很笨重
。Docker是不携带操作系统的,所以Docker的应用就非常的轻巧。另外在调用宿主机的CPU、磁盘等等这些资源的时候,拿内存举例,虚拟机是利用Hypervisor去虚拟化内存,整个调用过程是虚拟内存->虚拟物理内存->真正物理内存,但是Docker是利用Docker Engine去调用宿主的的资源,这时候过程是虚拟内存->真正物理内存。
传统虚拟机 | Docker容器 | |
---|---|---|
磁盘占用 | 几个GB到几十个GB左右 | 几十MB到几百MB左右 |
CPU内存占用 | 虚拟操作系统非常占用CPU和内存 | Docker引擎占用极低 |
启动速度 | (从开机到运行项目)几分钟 | (从开启容器到运行项目)几秒 |
安装管理 | 需要专门的运维技术 | 安装、管理方便 |
应用部署 | 每次部署都费时费力 | 从第二次部署开始轻松简捷 |
耦合性 | 多个应用服务安装到一起,容易互相影响 | 每个应用服务一个容器,达成隔离 |
系统依赖 | 无 | 需求相同或相似的内核,目前推荐是Linux |
3.Docker的安装
3.1 相关网站
3.2 在线安装Docker(centos7.x)
注意:Docker只兼容centos7以及之后的版本,centos6?对不起不行,不支持
-
卸载原始docker
$ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
-
安装docker依赖
$ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
-
设置docker的yum源
$ sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
-
安装最新版的docker
$ sudo yum install docker-ce docker-ce-cli containerd.io
-
指定版本安装docker
$ yum list docker-ce --showduplicates | sort -r $ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io $ sudo yum install docker-ce-18.09.5-3.el7 docker-ce-cli-18.09.5-3.el7 containerd.io
-
启动docker
# 开机自启 $ sudo systemctl enable docker $ sudo systemctl start docker
-
关闭docker
$ sudo systemctl stop docker
-
测试docker安装
$ sudo docker run hello-world
3.3 在线bash安装(通用所有平台)
-
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过
--mirror
选项使用国内源进行安装:执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定(stable)版本安装在系统中。# 下载脚本文件 $ curl -fsSL get.docker.com -o get-docker.sh # 安装 $ sudo sh get-docker.sh --mirror Aliyun
-
启动docker
$ sudo systemctl enable docker $ sudo systemctl start docker
-
创建docker用户组并将当前用户加入docker组
$ sudo groupadd docker $ sudo usermod -aG docker $USER
注意:创建docker的专用用户和用户组并不是必须的操作,只是官方的建议(我自己就从来没有建过,直接拿root耍,hahah)
为什么要建立docker的专用用户?
默认情况下,docker命令会使用Unix socket与docker 引擎通信,而只有 root 用户和 docker 组的用户才能访问docker引擎的 Unix socket。出于安全考虑,一般Linux系统上不会直接使用 root 用户。因此,更好的方法是将需要使用的 docker的用户 加入docker 用户组~
-
测试docker安装是否正确
$ docker run hello-world
3.4 离线安装Docker
-
查看内核 内核版本需要3.10.0以上
uname -a cat /proc/version
-
关闭selinux 修改SELINUX的值为 disabled
vi /etc/selinux/config
-
关闭防火墙
systemctl status firewalld systemctl stop firewalld systemctl disable firewalld reboot
-
创建安装的位置(目录根据实际来调整 最好放在最大的挂载下)
df -h mkdir -p /app/docker
-
复制文件到服务器的/usr/bin目录,也可以使用xftp传送到/usr/bin
注意:较高版本 docker二进制包里面的文件不一定全是docker开头 根据安装版本来定 需要全部给执行权限
cd docker-18.06.1-ce/ cp docker/* /usr/bin/ chmod +x /usr/bin/docker*
-
配置docker的 daemon.json
mkdir -p /etc/docker cd /etc/docker vi /etc/docker/daemon.json
-
配置举例
{ "data-root":"docker目录位置,自行设置,磁盘要够大", "insecure-registries": ["不含证书的镜像仓库地址可以是多个", "如: 192.168.137.111:4000"] }
-
配置样例
{ "data-root":"/app/docker" }
-
将docker设置为开机自启并查看docker
cp docker.service /usr/lib/systemd/system/ systemctl daemon-reload systemctl enable docker systemctl start docker docker -v docker info
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
- Docker 用户创建(用root可以不做,创建参考在线安装)
4.Docker 的核心架构
镜像:
一个镜像代表一个应用环境,他是一个只读的文件,如 mysql镜像,tomcat镜像,nginx镜像等容器:
镜像每次运行之后就是产生一个容器,就是正在运行的镜像,特点就是可读可写仓库:
用来存放镜像的位置,类似于maven仓库,也是镜像下载和上传的位置dockerFile:
docker生成镜像配置文件,用来书写自定义镜像的一些配置tar:
一个对镜像打包的文件,日后可以还原成镜像
5.Docker 配置阿里镜像加速服务
5.1 docker 运行流程
5.2 docker配置阿里云镜像加速(可选)
-
访问阿里云登录自己账号查看docker镜像加速服务
# 设置 仓库地址替换成自己的~
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://your.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
验证docker的镜像加速是否生效
[root@localhost ~]# docker info
..........
127.0.0.0/8
Registry Mirrors:
'https://your.mirror.aliyuncs.com/'
Live Restore Enabled: false
Product License: Community Engine
6.Hello-world
6.1 docker 的第一个容器
docker run hello-world
[root@localhost ~]# docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
7.常用命令
7.1 辅助命令
# 1.安装完成辅助命令
docker version ---- 查看docker的信息
docker info ---- 查看更详细的信息
docker --help ---- 帮助命令
7.2 Images 镜像命令
# 1.查看本机中所有镜像
docker images ---- 列出本地所有镜像
-a 列出所有镜像(包含中间映像层)
-q 只显示镜像id
# 2.搜索镜像
docker search [options] 镜像名 ---- 去dockerhub上查询当前镜像
-s 指定值 列出收藏数不少于指定值的镜像
--no-trunc 显示完整的镜像信息
# 3.从仓库下载镜像
docker pull 镜像名[:TAG|@DIGEST] ---- 下载镜像
# 4.删除镜像
docker rmi 镜像名 ---- 删除镜像
-f 强制删除
7.3 Container 容器命令
# 1.运行容器
docker run 镜像名 ---- 镜像名新建并启动容器
--name 别名为容器起一个名字(自定义了dns解析,同一网桥下可以使用名称访问ip)
-d 启动守护式容器(在后台启动容器)
-p 映射端口号:原始端口号 指定端口号启动
--ip 172.18.0.2 指定ip(网桥下)
--network bridge 指定网络模式
-it 直接以交互模式启动(进容器里了)
例:
docker run -it --name myTomcat -p 8888:8080 tomcat
docker run -d --name myTomcat -P tomcat
# 2.docker run --privileged=true 参数作用
使用该参数,container内的root拥有真正的root权限。
否则,container内的root只是外部的一个普通用户权限。
privileged启动的容器,可以看到很多host上的设备,并且可以执行mount。
甚至允许你在docker容器中启动docker容器。
# 3.docker run -d 参数作用
启动后直接返回容器id,不以终端的形式运行容器(终端 ctrl+c 就直接退出了)
# 4.查看运行的容器
docker ps ---- 列出所有正在运行的容器
-a 正在运行的和历史运行过的容器
-q 静默模式,只显示容器编号
docker ps -qa
# 5.停止|关闭|重启容器
docker start 容器名字或者容器id ---- 开启容器
docker restart 容器名或者容器id ---- 重启容器
docker stop 容器名或者容器id ---- 正常停止容器运行
docker kill 容器名或者容器id ---- 立即停止容器运行
# 6.删除容器
docker rm -f 容器id和容器名
docker rm -f $(docker ps -aq) ---- 删除所有容器
# 7.查看容器内进程
docker top 容器id或者容器名 ---- 查看容器内的进程
# 8.查看查看容器内部细节
docker inspect 容器id ---- 查看容器内部细节
docker inspect --format="{{.Id}}" name ---- 获取运行容器id
# 9.查看容器的运行日志
docker logs [OPTIONS] 容器id或容器名 ---- 查看容器日志
-t 加入时间戳
-f 跟随最新的日志打印
--tail 数字 显示最后多少条
docker logs -t -f --tail=100 rabbitmq
# 10.进入容器内部
docker exec [options] 容器id 容器内命令 ---- 进入容器执行命令
-i 以交互模式运行容器,通常与-t一起使用
-t 分配一个伪终端 shell窗口 bash
docker exec -it rabbitmq bash
# 11.容器和宿主机之间复制文件
docker cp 文件|目录 容器id:容器路径
---- 将宿主机复制到容器内部
docker cp 容器id:容器内资源路径 宿主机目录路径
---- 将容器内资源拷贝到主机上
# 12.数据卷(volum)实现与宿主机共享目录
docker run -v 宿主机的路径|任意别名:/容器内的路径 镜像名
docker run -v 宿主机的路径|任意别名:/容器内的路径:ro 镜像名
:ro 表示容器内目录只读(容器不能操作这个目录)
注意(这个非常重要):
1.如果是宿主机路径必须是绝对路径,宿主机目录会覆盖容器内目录内容
2.如果是别名则会在docker运行容器时自动在宿主机中创建一个目录,并将容器目录文件复制到宿主机中
理解上面的注意点还是非常有用的。
# 13.打包镜像
docker save 镜像名 -o 名称.tar
# 14.载入镜像
docker load -i 名称.tar
# 15.容器打包成新的镜像(容器会暂停)
docker commit -m "描述信息" -a "作者信息" 容器id或者名称 打包的镜像名称:标签
# 16.打包(dockerfile) 不要忘了后面的 . 这里指定的上下文目录,上下文目录为当前目录
docker build -t vuenginxcontainer .
docker build -t myctr:01 .
8.Docker的镜像原理
8.1 镜像是什么?
镜像是一种轻量级的,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时所需的库、环境变量和配置文件。
8.2 为什么一个镜像会那么大?
镜像就是花卷
-
UnionFS(联合文件系统):
Union文件系统是一种分层,轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。这种文件系统特性:就是一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录 。
8.3 Docker镜像原理
docker的镜像实际是由一层一层的文件系统组成(由一层一层的镜像构成)。
-
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统。在docker镜像的最底层就是bootfs。这一层与Linux/Unix 系统是一样的,包含boot加载器(bootloader)和内核(kernel)。当boot加载完,后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时会卸载bootfs。
-
rootfs(root file system),在bootfs之上,包含的就是典型的linux系统中的/dev,/proc,/bin,/etc等标准的目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu/CentOS等等。
-
我们平时安装进虚拟机的centos都有1到几个GB,为什么docker这里才200MB?对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令,工具,和程序库就可以了,因为底层直接使用Host的Kernal,自己只需要提供rootfs就行了。由此可见不同的linux发行版,他们的bootfs是一致的,rootfs会有差别。因此不同的发行版可以共用bootfs。
当你在拉取镜像的时候也可以看出,拉取的时候是一层层拉取的,最上层则是最基础最公共的基础镜像
8.4 为什么docker镜像要采用这种分层结构呢?
最大的一个好处就是资源共享
- 比如:有多个镜像都是从相同的base镜像构建而来的,那么宿主机只需在磁盘中保存一份base镜像。同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。Docker镜像都是只读的。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称为容器层,容器层之下都叫镜像层。
9.高级网络配置(非常重要)
9.1 说明
当 Docker 启动时,会自动在主机上创建一个 bridge
虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。
同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 中定义)中的一个地址给 docker0
接口。比如典型的 172.17.42.1
,掩码为 255.255.0.0
。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16
)的地址。
当创建一个 Docker 容器的时候,同时会创建了一对 veth pair
接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0
;另一端在本地并被挂载到 bridge
网桥,名称以 veth
开头(例如 vethAQI2QT
)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。
9.2 查看网络信息
# docker network ls
上面的图中可以看到docker的三种网络模式,bridge、host(容器的网络配置与host完全一样,在容器中可以看到host的所有网卡,而且hostname也和宿主机保持一致)、none(封闭网络,自闭模式hhh),默认容器都是使用bridge。实际应用中并不是所有容器都挂到默认的bridge网桥上就行了,如果有其中一个容器与外部交互非常频繁,会给默认的bridge带来较大的压力,由于别的容器也是公用这一个网桥,这样也就会影响到别的容器和外部的通信。我们要根据实际情况来决定使用一个默认的网桥是否合理。
9.3 创建一个网桥
# docker network create -d bridge 网桥名称
9.4 删除一个网桥
# docker network rm 网桥名称
9.5 容器之间使用网络通信
# 1.查询当前网络配置
docker network ls
NETWORK ID NAME DRIVER SCOPE
8e424e5936b7 bridge bridge local
17d974db02da docker_gwbridge bridge local
d6c326e433f7 host host local
# 2.创建桥接网络
docker network create -d bridge info
[root@centos ~]# docker network create -d bridge info
6e4aaebff79b1df43a064e0e8fdab08f52d64ce34db78dd5184ce7aaaf550a2f
[root@centos ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
8e424e5936b7 bridge bridge local
17d974db02da docker_gwbridge bridge local
d6c326e433f7 host host local
6e4aaebff79b info bridge local
# 3.启动容器指定使用网桥
docker run -d -p 8890:80 --name nginx001 --network info nginx
docker run -d -p 8891:80 --name nginx002 --network info nginx
# 注意:一旦指定网桥后--name指定名字就是主机名,多个容器指定在同一个网桥时,可以在任意一个容器中使用主机名与容器进行互通
[root@centos ~]# docker run -d -p 8890:80 --name nginx001 --network info nginx
c315bcc94e9ddaa36eb6c6f16ca51592b1ac8bf1ecfe9d8f01d892f3f10825fe
[root@centos ~]# docker run -d -p 8891:80 --name nginx002 --network info nginx
f8682db35dd7fb4395f90edb38df7cad71bbfaba71b6a4c6e2a3a525cb73c2a5
[root@centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8682db35dd7 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:8891->80/tcp nginx002
c315bcc94e9d nginx "/docker-entrypoint.…" 7 minutes ago Up 7 minutes 0.0.0.0:8890->80/tcp nginx001
b63169d43792 mysql:5.7.19 "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 3306/tcp mysql_mysql.1.s75qe5kkpwwttyf0wrjvd2cda
[root@centos ~]# docker exec -it f8682db35dd7 /bin/bash
root@f8682db35dd7:/# curl http://nginx001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
.....
9.6 查看网桥信息
# 可以直接查看当前网桥的ip(网关gatwway)和在这个ip下活跃的容器 Containers标签存在容器即活跃
docker inspect 网桥名称
举例:
[root@worker2 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
ef209b3f55ac bridge bridge local
d5d7fb7c733a host host local
9617f81ea7ed metersphere_ms-network bridge local
b8fbeb5755fc none null local
[root@worker2 ~]# docker inspect metersphere_ms-network
[
{
"Name": "metersphere_ms-network",
"Id": "9617f81ea7edc6e38e711dfea23ffaa0c8dc17df458c597213a52b389a3c480e",
"Created": "2021-05-31T14:27:40.186875973+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"73cd659e5bd5f772b6481319aba934043121329e0edf9a2e96e9ddc7e64d69b8": {
"Name": "ms-node-controller",
"EndpointID": "c787b3b08d33088df519b879a0cb4f96f56e1ac2c0647c4dfd8adaa6450d9bf4",
"MacAddress": "02:42:ac:12:00:05",
"IPv4Address": "172.18.0.5/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "ms-network",
"com.docker.compose.project": "metersphere"
}
}
]
[root@worker2 ~]#
10.高级数据卷配置
10.1 说明
数据卷
是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷
可以在容器之间共享和重用- 对
数据卷
的修改会立马生效 - 对
数据卷
的更新,不会影响镜像 数据卷
默认会一直存在,即使容器被删除
注意:
数据卷
的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。
10.2 创建数据卷
创建时不需要指定目录,这个数据卷会在docker root 目录下面生成~
[root@centos ~]# docker volume create my-vol
my-vol
10.3 查看数据卷
[root@centos ~]# docker volume inspect my-vol
[
{
"CreatedAt": "2020-11-25T11:43:56+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
10.4 挂载数据卷
[root@centos ~]# docker run -d -P --name web -v my-vol:/usr/share/nginx/html nginx
[root@centos ~]# docker inspect web
"Mounts": [
{
"Type": "volume",
"Name": "my-vol",
"Source": "/var/lib/docker/volumes/my-vol/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
10.5 删除数据卷
docker volume rm my-vol
Q.E.D.