五、Docker数据卷
1、数据挂载简介
在Docker中,容器的数据读写默认发生在容器的存储层,当容器被删除时,容器中的数据将会丢失。如果想实现数据的持久化,就需要将容器和宿主机建立联系(将数据从宿主机挂载到容器中),通俗的说,数据卷就是在容器和宿主机之间实现数据共享。
数据卷是宿主机(linux主机)中的一个目录或文件,当容器目录和数据卷目录绑定后,对方的修改会立即同步。可以不需要进入容器内部,就可以查看所需要的容器中的数据。
一个数据卷可以被多个容器同时挂载,一个容器也可以被挂载多个数据卷。
2、三种数据挂载方式
**volume:**挂载宿主机文件系统的固定位置(/var/lib/docker/volumes/卷名/_data
)。
**bind mounts:**挂载宿主机系统的任意位置。
**tmpfs mounts:**挂载存储在宿主机系统的内存中,不会写入宿主机的文件系统。容器关闭重启数据丢失。
3、三种挂载方式适用场景
(1)volume(固定目录数据卷挂载)
- 容器之间共享数据
(2)bind mounts(自定义目录挂载)
- 主机与容器共享数据
(3)tmpfs mounts(内存挂载)
- 既不想将数据存于主机,又不想存于容器中时(这可以是出于安全的考虑,或当应用需要写大量非持久性的状态数据时为了保护容器的性能)。
4、挂载方式命令详解
#卷管理命令说明
[root@bluecusliyou _data]# docker volume --help
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes
#volume 管理
docker volume ls #列出所有卷
docker volume create 卷名 #创建卷
docker volume inspect 卷名 #查看卷详细信息
docker volume rm 卷名1 卷名2 #删除卷
docker volume prune #删除未被使用的卷,容器停止的占用的卷也不会删除
#volume mounts(固定目录数据卷挂载)
docker run --mount [type=volume,]source=卷名,target=容器文件夹 镜像名
#bind mounts(自定义目录挂载)
docker run --mount type=bind,source=宿主机文件夹,target=容器内文件夹 镜像名
#tmpfs mounts(内存挂载)
docker run --mount type=tmpfs,target=容器内文件夹 镜像名
(1)volume(固定目录数据卷挂载)
- volume可以通过
docker volume
命令集被管理,创建的卷就是宿主机的固定文件夹/var/lib/docker/volumes/卷名/_data
- 手动创建卷,卷文件夹为空,挂载到容器,容器文件夹覆盖卷文件夹。
- 手动创建卷,卷文件夹不为空,挂载到容器,卷文件夹覆盖容器文件夹。
- 不存在的卷会自动创建,容器文件夹覆盖卷文件夹。
查看卷列表,创建volume数据卷,查看卷信息,查看卷详情,查看卷文件夹没有文件
#查看当前所有数据卷信息
#一大堆名字为很长字符的数据卷为匿名数据卷,是因为-v没有指定卷名,Docker就会自动创建匿名数据卷
[root@bluecusliyou ~]# docker volume ls
DRIVER VOLUME NAME
#创建volume数据卷
[root@bluecusliyou ~]# docker volume create nginx_v1
nginx_v1
#查看卷信息
[root@bluecusliyou ~]# docker volume inspect nginx_v1
[
{
"CreatedAt": "2021-12-03T15:28:04+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/nginx_v1/_data",
"Name": "nginx_v1",
"Options": {},
"Scope": "local"
}
]
#查看卷文件夹没有文件
[root@bluecusliyou ~]# cd /var/lib/docker/volumes/nginx_v1/_data
[root@bluecusliyou _data]# ls
手动创建卷,卷文件夹为空,挂载到容器,容器文件夹覆盖卷文件夹。
#运行容器挂载到卷
[root@bluecusliyou _data]#docker run -d -p 8561:80 --name nginx_cv1 --mount source=nginx_v1,target=/usr/share/nginx/html nginx
e8158f0887ec6f515b8ca0a752c30c99fc1ac3323fd47a32c45a83bfff9c621d
#查看容器卷信息
[root@bluecusliyou _data]# docker inspect nginx_cv1
[
{
...
"Mounts": [
{
"Type": "volume",
"Name": "nginx_v1",
"Source": "/var/lib/docker/volumes/nginx_v1/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
...
}
]
#查看卷文件夹,容器目录文件挂载出来了
[root@bluecusliyou _data]# ls
50x.html index.html
卷里面添加文件,进入容器查看,容器文件夹也能看到该文件,且可以访问成功
#卷里面添加文件
[root@bluecusliyou _data]# echo v1test>>test.html
[root@bluecusliyou _data]# ls
50x.html index.html test.html
#进入容器查看,容器文件夹也添加了该文件
[root@bluecusliyou _data]# docker exec -it nginx_cv1 /bin/bash
root@c601ab5eb057:/# cd /usr/share/nginx/html
root@c601ab5eb057:/usr/share/nginx/html# ls
50x.html index.html test.html
root@c601ab5eb057:/usr/share/nginx/html# read escape sequence
#访问成功
[root@bluecusliyou _data]# curl localhost:8561/test.html
v1test
关闭删除容器,宿主机文件夹文件依然存在,持久化成功
#关闭容器,文件依然在卷里
[root@bluecusliyou _data]# docker stop nginx_cv1
nginx_cv1
[root@bluecusliyou _data]# ls
50x.html index.html test.html
#删除容器,文件依然在卷里
[root@bluecusliyou _data]# docker rm -f nginx_cv1
nginx_cv1
[root@bluecusliyou _data]# ls
50x.html index.html test.html
手动创建卷,卷文件夹不为空,挂载到容器,卷文件夹覆盖容器文件夹。
#运行容器挂载到卷
[root@bluecusliyou _data]# docker run -d -p 8562:80 --name nginx_cv2 --mount source=nginx_v1,target=/usr/share/nginx/html nginx
4ed35df5bfa7f6a7a897e453fe284c0f6b918cce2aa7853d10d00616194bc9d2
#测试文件还在
[root@bluecusliyou _data]# ls
50x.html index.html test.html
#访问测试文件成功
[root@bluecusliyou _data]# curl localhost:8562/test.html
v1test
不创建卷,直接挂载不存在的卷到容器,会自动创建卷,容器文件夹覆盖卷文件夹。
#查看当前的所有卷
[root@bluecusliyou _data]# docker volume ls
DRIVER VOLUME NAME
local nginx_v1
#运行容器,挂载未创建的卷,自动创建卷
[root@bluecusliyou _data]# docker run -d -p 8563:80 --name nginx_cv3 --mount source=nginx_v3,target=/usr/share/nginx/html nginx
5d5671155661b165085a65f26d674ca91d9cdf84d90b34fcf074a15362a0eb87
[root@bluecusliyou _data]# docker volume ls
DRIVER VOLUME NAME
local nginx_v1
local nginx_v3
#查看卷信息,卷文件夹,容器文件夹覆盖卷文件夹
[root@bluecusliyou _data]# docker volume inspect nginx_v3
[
{
"CreatedAt": "2022-01-08T19:59:23+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginx_v3/_data",
"Name": "nginx_v3",
"Options": null,
"Scope": "local"
}
]
[root@bluecusliyou _data]# cd /var/lib/docker/volumes/nginx_v3/_data
[root@bluecusliyou _data]# ls
50x.html index.html
(2)bind mounts(自定义目录挂载)
-
运行容器挂载宿主机文件夹,宿主机文件夹不存在报错
-
宿主机文件夹为空,运行容器挂载宿主机文件夹,宿主机文件夹覆盖容器文件夹
-
宿主机文件夹非空,运行容器挂载宿主机文件夹,宿主机文件夹覆盖容器文件夹
运行容器挂载宿主机文件夹,宿主机文件夹不存在报错
[root@bluecusliyou _data]# docker run -d -p 8571:80 --name nginx_cb1 --mount type=bind,source=/var/lib/mydocker/nginx_b1,target=/usr/share/nginx/html nginx
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /var/lib/mydocker/nginx_b1.
See 'docker run --help'.
宿主机文件夹为空,运行容器挂载宿主机文件夹,宿主机文件夹覆盖容器文件夹
#创建宿主机文件夹
[root@bluecusliyou _data]# cd /var/lib
[root@bluecusliyou lib]# mkdir -p mydocker/nginx_b1
#运行容器挂载宿主机文件夹
[root@bluecusliyou lib]# docker run -d -p 8571:80 --name nginx_cb1 --mount type=bind,source=/var/lib/mydocker/nginx_b1,target=/usr/share/nginx/html nginx
53d66a64ba1dea4b6ba3c17230cd2e73d09b0820c697c0a47cf00cc943d091dd
#查看容器挂载信息,类型bind
[root@bluecusliyou lib]# docker inspect nginx_cb1
[
{
...
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/mydocker/nginx_b1",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
...
}
]
#查看宿主机文件夹,文件还是空
[root@bluecusliyou lib]# cd /var/lib/mydocker/nginx_b1
[root@bluecusliyou nginx_b1]# ls
#进入容器,查看文件为空,被宿主机覆盖
[root@bluecusliyou nginx_b1]# docker exec -it nginx_cb1 /bin/bash
root@53d66a64ba1d:/# cd /usr/share/nginx/html
root@53d66a64ba1d:/usr/share/nginx/html# ls
root@53d66a64ba1d:/usr/share/nginx/html# read escape sequence
宿主机文件夹添加文件,进入容器查看,容器文件夹也能看到该文件,且可以访问成功
#宿主机文件夹添加文件文件
[root@bluecusliyou nginx_b1]# echo b1test>>test.html
[root@bluecusliyou nginx_b1]# ls
test.html
#进入容器查看,容器文件夹也能看到该文件,且可以访问成功
[root@bluecusliyou nginx_b1]# docker exec -it nginx_cb1 /bin/bash
root@53d66a64ba1d:/# cd /usr/share/nginx/html
root@53d66a64ba1d:/usr/share/nginx/html# ls
test.html
root@53d66a64ba1d:/usr/share/nginx/html# read escape sequence
[root@bluecusliyou nginx_b1]# curl localhost:8571/test.html
b1test
关闭删除容器,宿主机文件夹文件依然存在,持久化成功
#关闭容器,宿主机文件夹文件依然存在
[root@bluecusliyou nginx_b1]# docker stop nginx_cb1
nginx_cb1
[root@bluecusliyou nginx_b1]# ls
test.html
#删除容器,文件依然在卷里
[root@bluecusliyou nginx_b1]# docker rm -f nginx_cb1
nginx_cb1
[root@bluecusliyou nginx_b1]# ls
test.html
宿主机文件夹非空,运行容器挂载宿主机文件夹,宿主机文件夹覆盖容器文件夹
#运行容器挂载宿主机文件夹
[root@bluecusliyou nginx_b1]# docker run -d -p 8572:80 --name=nginx_cb2 --mount type=bind,source=/var/lib/mydocker/nginx_b1,target=/usr/share/nginx/html nginx
f5ef981caaca239ae14a261595ec26b99ea6a30e7dd5778330d23e940101a72a
#宿主机文件还在,访问成功ls
[root@bluecusliyou nginx_b1]# ls
test.html
[root@bluecusliyou nginx_b1]# curl localhost:8572/test.html
b1test
(3)tmpfs mounts(内存挂载)
挂载到宿主机内存,容器文件夹文件被覆盖掉了
#创建容器
[root@bluecusliyou nginx_v3]# docker run -d -p 8581:80 --name=nginx_ct1 --mount type=tmpfs,target=/usr/share/nginx/html nginx
6031e0ffc52d876a789c05473434e787641a14d84580fe74eac0df97b6212b07
#查看容器挂载详情
[root@bluecusliyou nginx_v3]# docker inspect nginx_ct1
[
{
...
"Mounts": [
{
"Type": "tmpfs",
"Source": "",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
...
}
]
#进入容器,添加测试文件,访问成功
[root@bluecusliyou nginx_b1]# docker exec -it nginx_ct1 /bin/bash
root@09593f4c5c55:/# cd /usr/share/nginx/html
root@09593f4c5c55:/usr/share/nginx/html# echo tmpfstest>test.html
root@09593f4c5c55:/usr/share/nginx/html# ls
test.html
root@09593f4c5c55:/usr/share/nginx/html# read escape sequence
[root@bluecusliyou nginx_b1]# curl localhost:8581/test.html
tmpfstest
停止容器再启动,重新访问失败,文件无法持久化
#停止容器再启动,重新访问失败,文件无法持久化
[root@bluecusliyou nginx_b1]# docker stop nginx_ct1
nginx_ct1
[root@bluecusliyou nginx_b1]# docker start nginx_ct1
nginx_ct1
[root@bluecusliyou nginx_b1]# curl localhost:8581/test.html
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>
5、-v灵活的挂载方式
-v 等价于 --volume,可以实现三种类型的挂载,匿名挂载、具名挂载、指定路径挂载 。
docker run -v 容器内目录 镜像名 #匿名挂载,volume类型
docker run -v 卷名:容器内目录 镜像名 #具名挂载,volume类型
docker run -v 宿主机路径:容器内路径 镜像名 #指定路径挂载,bind类型
(1)匿名挂载,volume类型
-
docker run -v 容器内目录 镜像名
-
自动创建卷,容器文件夹覆盖宿主机文件夹
自动创建卷,容器文件夹覆盖宿主机文件夹
#查看已有的卷
[root@bluecusliyou nginx_b1]# docker volume ls
DRIVER VOLUME NAME
local nginx_v1
local nginx_v3
#运行容器,-v匿名挂载
[root@bluecusliyou nginx_b1]# docker run -d --name nginx_c_v1 -p 8591:80 -v /usr/share/nginx/html nginx
5a2f59d8b4619e229ca4aeed31c15d98b9da0e54a666790c2a7f556d64f7b7b0
#自动生成新的卷
[root@bluecusliyou nginx_b1]# docker volume ls
DRIVER VOLUME NAME
local 25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04
local nginx_v1
local nginx_v3
#查看容器详情,类型volume
[root@bluecusliyou nginx_b1]# docker inspect nginx_c_v1
[
{
...
"Mounts": [
{
"Type": "volume",
"Name": "25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04",
"Source": "/var/lib/docker/volumes/25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
}
]
#进入卷文件夹,容器文件夹覆盖宿主机文件夹
[root@bluecusliyou nginx_b1]# cd /var/lib/docker/volumes/25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04/_data
[root@bluecusliyou _data]# ls
50x.html index.html
(2)具名挂载,volume类型
- docker run -v 卷名:容器内目录 镜像名
- 手动创建卷,宿主机文件夹为空,容器文件夹覆盖宿主机文件夹
- 手动创建卷,宿主机文件夹非空,宿主机文件夹覆盖容器文件夹
- 不存在的卷会自动创建,容器文件夹覆盖宿主机文件夹
手动创建卷,宿主机文件夹为空,容器文件夹覆盖宿主机文件夹
#创建新卷
[root@bluecusliyou _data]# docker volume create nginx_v_v2
nginx_v_v2
[root@bluecusliyou _data]# docker volume ls
DRIVER VOLUME NAME
local 25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04
local nginx_v1
local nginx_v3
local nginx_v_v2
#运行容器,-v 具名挂载
[root@bluecusliyou _data]# docker run -d --name nginx_c_v2 -p 8592:80 -v nginx_v_v2:/usr/share/nginx/html nginx
7697a36778b7503028401eb8e175330353577c3b220bdab72936f1bdeff89fcf
#查看容器详情
[root@bluecusliyou _data]# docker inspect nginx_c_v2
[
{
"Mounts": [
{
"Type": "volume",
"Name": "nginx_v_v2",
"Source": "/var/lib/docker/volumes/nginx_v_v2/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
}
]
[root@bluecusliyou _data]# cd /var/lib/docker/volumes/nginx_v_v2/_data
[root@bluecusliyou _data]# ls
50x.html index.html
手动创建卷,宿主机文件夹非空,宿主机文件夹覆盖容器文件夹
#文件夹添加测试文件
[root@bluecusliyou _data]# echo -vtest>>test.html
[root@bluecusliyou _data]# ls
50x.html index.html test.html
#运行容器,-v 具名挂载
[root@bluecusliyou _data]# docker run -d --name nginx_c_v3 -p 8593:80 -v nginx_v_v2:/usr/share/nginx/html nginx
a916f1d3c625bb49e6591fb04e44372059d8131ce6ce4cdd406c30a89b314cdf
#宿主机文件夹覆盖容器文件夹
[root@bluecusliyou _data]# ls
50x.html index.html test.html
不存在的卷会自动创建,容器文件夹覆盖宿主机文件夹
#查看现有卷
[root@bluecusliyou _data]# docker volume ls
DRIVER VOLUME NAME
local 25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04
local nginx_v1
local nginx_v3
local nginx_v_v2
#运行容器,-v 具名挂载,卷不存在,自动创建成功
[root@bluecusliyou _data]# docker run -d --name nginx_c_v4 -p 8594:80 -v nginx_v_v4:/usr/share/nginx/html nginx
bf4bcd3a37adaab8ad6d342088bc47297ff533b4e8a069e11b296f0d55d3f49
[root@bluecusliyou _data]# docker volume ls
DRIVER VOLUME NAME
local 25e37107425180f45d3ea8182ee738eb4e7434717bf0f4d7ef05aeef4ecb2c04
local nginx_v1
local nginx_v3
local nginx_v_v2
local nginx_v_v4
#容器文件夹覆盖宿主机文件夹
[root@bluecusliyou _data]# docker volume inspect nginx_v_v4
[
{
"CreatedAt": "2022-01-08T23:08:49+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginx_v_v4/_data",
"Name": "nginx_v_v4",
"Options": null,
"Scope": "local"
}
]
[root@bluecusliyou _data]# cd /var/lib/docker/volumes/nginx_v_v4/_data
[root@bluecusliyou _data]# ls
50x.html index.html
(3)指定路径挂载,bind类型
- docker run -v 宿主机路径:容器内路径 镜像名
- 创建宿主机文件夹,宿主机文件夹为空,宿主机文件夹覆盖容器文件夹
- 创建宿主机文件夹,宿主机文件夹非空,宿主机文件夹覆盖容器文件夹
- 不创建宿主机文件夹,自动创建空文件夹,宿主机文件夹覆盖容器文件夹
创建宿主机文件夹,宿主机文件夹为空,宿主机文件夹覆盖容器文件夹
[root@bluecusliyou ~]# cd /var/lib/mydocker
[root@bluecusliyou mydocker]# ls
nginx_b1 nginx_crun
[root@bluecusliyou mydocker]# mkdir nginx_v_b1
[root@bluecusliyou mydocker]# ls
nginx_b1 nginx_crun nginx_v_b1
[root@bluecusliyou mydocker]# docker run -d --name nginx_c_b1 -p 8601:80 -v /var/lib/mydocker/nginx_v_b1:/usr/share/nginx/html nginx
fe489f13bf5a94fdbe9a0593a2eb26e97f350a19ba152f7c1bae4880e495ecd9
[root@bluecusliyou mydocker]# ls /var/lib/mydocker/nginx_v_b1
[root@bluecusliyou mydocker]#
创建宿主机文件夹,宿主机文件夹非空,宿主机文件夹覆盖容器文件夹
[root@bluecusliyou mydocker]# echo test>/var/lib/mydocker/nginx_v_b1/test.html
[root@bluecusliyou mydocker]# ls /var/lib/mydocker/nginx_v_b1
test.html
[root@bluecusliyou mydocker]# docker run -d --name nginx_c_b2 -p 8602:80 -v /var/lib/mydocker/nginx_v_b1:/usr/share/nginx/html nginx
209c8b095680b443b576ae51fd7a09ada8d37c51bd166fbbf561f27be46f7671
[root@bluecusliyou mydocker]# ls /var/lib/mydocker/nginx_v_b1
test.html
不创建宿主机文件夹,自动创建空文件夹,宿主机文件夹覆盖容器文件夹
[root@bluecusliyou mydocker]# ls
nginx_b1 nginx_crun nginx_v_b1
[root@bluecusliyou mydocker]# docker run -d --name nginx_c_b3 -p 8603:80 -v /var/lib/mydocker/nginx_v_b3:/usr/share/nginx/html nginx
c326680b684c815c88632b92329a4667b4ff35786b241b84622ca63356c2fe62
[root@bluecusliyou mydocker]# ls
nginx_b1 nginx_crun nginx_v_b1 nginx_v_b3
[root@bluecusliyou mydocker]# ls /var/lib/mydocker/nginx_v_b3
[root@bluecusliyou mydocker]#
(4)指定文件夹的可读可写性质
# 通过 -v 容器内路径: ro rw 改变读写权限
ro #readonly 只读
rw #readwrite 可读可写
docker run -d -v 卷名:容器内目录:ro -p 主机端口:容器内端口 --name 容器名称 镜像名
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
#运行容器挂载宿主机目录,宿主机可写,容器内只读
[root@bluecusliyou mydocker]# docker run -d --name nginx_c_b4 -p 8604:80 -v /var/lib/mydocker/nginx_v_b4:/usr/share/nginx/html:ro nginx
623798a666f981067afede23825db9de2e7d4a7a25870d85f56f98db9ec9d1b7
[root@bluecusliyou mydocker]# echo test>/var/lib/mydocker/nginx_v_b4/test.html
[root@bluecusliyou mydocker]# ls /var/lib/mydocker/nginx_v_b4
test.html
[root@bluecusliyou mydocker]# docker exec -it nginx_c_b4 /bin/bash
root@623798a666f9:/# cd /usr/share/nginx/html
root@623798a666f9:/usr/share/nginx/html# ls
test.html
root@623798a666f9:/usr/share/nginx/html# echo testsss>test2.html
bash: test2.html: Read-only file system
root@623798a666f9:/usr/share/nginx/html# read escape sequence
[root@bluecusliyou mydocker]#
6、实战:mysql数据库持久化
(1)运行容器,将文件挂载到宿主机
#运行容器
[root@bluecusliyou _data]# docker run --name mysqlserver -v /data/mysql/conf:/etc/mysql/conf.d -v /data/mysql/logs:/logs -v /data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306 mysql:latest --lower_case_table_names=1
f0afabacbf2e580c311b05fd2ec1ce5fda8f533abe8fddd1d69a6df0a0f7ab19
参数 | 说明 |
---|---|
--name mysqlserver | 容器运行的名字 |
-v /data/mysql/conf:/etc/mysql/conf.d | 将宿主机/data/mysql/conf映射到容器/etc/mysql/conf.d |
-v /data/mysql/logs:/logs | 将宿主机/data/mysql/logs映射到容器/logs |
-v /data/mysql/data:/var/lib/mysql | 将宿主机/data/mysql/data映射到容器 /var/lib/mysql |
-e MYSQL_ROOT_PASSWORD=123456 | 数据库初始密码123456 |
-p 3306:3306 | 将宿主机3306端口映射到容器的3306端口 |
--lower_case_table_names=1 | 设置表名忽略大小写,只能首次修改,后续无法修改 |
(2)客户端连接数据库,添加数据库表数据
宿主机上文件已经生成
(3)删除容器,宿主机文件还在
[root@bluecusliyou _data]# docker rm -f mysqlserver
mysqlserver
(4)重新运行容器,数据持久化成功
[root@bluecusliyou _data]# docker run --name mysqlserver -v /data/mysql/conf:/etc/mysql/conf.d -v /data/mysql/logs:/logs -v /data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306 mysql:latest --lower_case_table_names=1
2bf89cba601dbcc6a297ec7446a98b8cd6435e514b20d50334101fb286f3c6e0
六、Docker网络
1、网络简介
Docker网络的实现主要是依赖Linux网络有关的技术,这些技术有网络命名空间(Network Namespace)、Veth设备对、网桥、ipatables和路由。
(1)网络命名空间,实现网络隔离。
(2)Veth设备对,实现不同网络命名空间之间的通信。
(3)网桥,实现不同网络之间通信。
(4)ipatables,实现对数据包进行过滤和转发。
(5)路由,决定数据包到底发送到哪里。
2、网络模式
[root@bluecusliyou ~]# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
安装完docker,就默认创建了三个网络
[root@bluecusliyou ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
04f2eb0d05b9 bridge bridge local
9697f77248e1 host host local
abc08bbf4c8b none null local
运行容器时,你可以使用该-–net标志来指定容器应连接到哪些网络
-
host模式:使用 --net=host 指定。
host模式,容器和宿主机共用一个Network Namespace。容器将使用宿主机的网络。
-
none模式:使用 --net=none 指定。
none模式,就是没有网络。
-
container模式:使用 --net=container:容器名称或者ID 指定。
container模式,当前容器和指定容器共用一个Network Namespace。当前容器将使用指定容器的网络。
-
bridge模式:使用 --net=bridge 指定,默认设置。
bridge模式,桥接模式,默认的模式。
3、Bridge模式
bridge模式是Docker默认的网络模式,此模式会为每一个容器分配独立的Network Namespace。
(1)实现原理
- 我们只要在宿主机上安装了docker,就会创建一个虚拟网桥docker0。
- 我们每启动一个docker容器,docker就会给容器分配一个docker0的子网的ip,同时会创建了一对 veth pair 接口,一端连接容器内部,一端连接docker0网桥。
- 通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。
运行容器前系统信息
#查看系统网络信息,安装了docker就会有docker0
[root@bluecusliyou ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:16:fa:95 brd ff:ff:ff:ff:ff:ff
inet 172.27.45.106/20 brd 172.27.47.255 scope global dynamic noprefixroute eth0
valid_lft 315324622sec preferred_lft 315324622sec
inet6 fe80::216:3eff:fe16:fa95/64 scope link
valid_lft forever preferred_lft forever
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:84:89:96:a4 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:84ff:fe89:96a4/64 scope link
valid_lft forever preferred_lft forever
容器运行
#运行两个网络模式是bridge的容器,默认就是bridge
[root@bluecusliyou ~]# docker run -d --name nginx_c_b1 -p 8561:80 nginx
c19004406443ed36417ece5a29f855feeea1e1fa745d61c1df356cba1874b58f
[root@bluecusliyou ~]# docker run -d --name nginx_c_b2 -p 8562:80 nginx
b01797d4cc20969f10c2310edf031d6ca50448205715ffc6a050339eb1d0fc6f
容器运行后系统信息
[root@bluecusliyou ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:16:fa:95 brd ff:ff:ff:ff:ff:ff
inet 172.27.45.106/20 brd 172.27.47.255 scope global dynamic noprefixroute eth0
valid_lft 315324281sec preferred_lft 315324281sec
inet6 fe80::216:3eff:fe16:fa95/64 scope link
valid_lft forever preferred_lft forever
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:84:89:96:a4 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:84ff:fe89:96a4/64 scope link
valid_lft forever preferred_lft forever
99: vethb22303b@if98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether f2:65:40:31:c5:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::f065:40ff:fe31:c503/64 scope link
valid_lft forever preferred_lft forever
101: veth770c276@if100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 72:aa:84:01:7e:c8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::70aa:84ff:fe01:7ec8/64 scope link
valid_lft forever preferred_lft forever
再查看bridge信息,容器已经加入到bridge里面,ip地址就是子网的ip
[root@bluecusliyou ~]# docker network inspect bridge
[
{
...
"Containers": {
"62766e60bf80b0d60090fa857313645fc5e9817c50cc65358bd82052a20e0d14": {
"Name": "nginx_c_b2",
"EndpointID": "cdd96b9b6c643c25ee85c17b65d70ecaa2eb663651f1130bd92a2c1957d11a51",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"88fe91f8d2a51f357a1bbc7c00e7025f02deea2fa3dfa4884d1d98b47d8b1b7e": {
"Name": "nginx_c_b1",
"EndpointID": "501826389e3cc55943cc9abcce93b4529b496e98f342a9e920b6b045611dcdba",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}
...
}
]
(2)宿主机访问容器,容器之间访问
宿主机请求容器成功
[root@bluecusliyou ~]# curl 172.17.0.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
进入一个容器,请求另外的容器成功
[root@bluecusliyou ~]# docker exec -it nginx_c_b1 /bin/bash
root@c19004406443:/# curl 172.17.0.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
(3)容器访问外部网络的过程
#容器内访问外网地址成功
[root@bluecusliyou ~]# docker exec -it nginx_c_b1 /bin/bash
root@41abcb9be1b3:/# curl www.baidu.com
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div
...
主机有一块网卡为eth0,从主机上一个IP为172.17.0.2的容器中访问百度。
IP包首先判断要访问的地址非本子网,从容器发往自己的默认网关docker0,然后会查询主机的路由表,发现包应该从主机的eth0发往主机的网关。IP包就会转发给eth0,并从eth0发出去。
#查看系统路由表
[root@bluecusliyou ~]# ip route
default via 172.27.47.253 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.27.32.0/20 dev eth0 proto kernel scope link src 172.27.45.106 metric 100
# 容器要想访问外部网络,需要本地系统的转发支持。
# 在Linux 系统中,检查转发是否打开。如果为 0,说明没有开启转发,则需要手动打开。
[root@bluecusliyou ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
(4)外部访问容器的过程
#通过暴露的外部接口访问成功
[root@bluecusliyou ~]# curl localhost:8561
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
在创建完两个带暴露端口的容器后,查看Iptable规则的变化,发现多了两个网址规则,这些规则就是对主机eth0收到的目的端口为8563/8564的tcp流量进行DNAT转换,将流量发往172.17.0.2:80/172.17.0.3:80,也就是我们创建的Docker容器。所以,外界只需访问宿主机地址:8561/8562就可以访问到容器中的服务。
#查看系统iptables,新增加两个地址匹配规则
[root@bluecusliyou ~]# iptables-save|grep -i docker0
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8561 -j DNAT --to-destination 172.17.0.2:80
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8562 -j DNAT --to-destination 172.17.0.3:80
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
(5)–link实现容器名访问
IP地址可以实现互联互通,但是直接能用名称访问是更好的方式。
# 直接名称访问是不行的
[root@bluecusliyou ~]# docker exec -it nginx_c_b1 curl nginx_c_b2
curl: (6) Could not resolve host: nginx_c_b2
# 容器运行的时候给容器创建连接
[root@bluecusliyou ~]# docker run -d --name nginx_c_b3 --link nginx_c_b2 nginx
c234f762227516399a081707bce0f17710f1efee0d36d0cbc949ddb1f5f4e292
# 重新用名称访问成功
[root@bluecusliyou ~]# docker exec -it nginx_c_b3 curl nginx_c_b2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 反过来用名称访问也是不行的
[root@bluecusliyou ~]# docker exec -it nginx_c_b2 curl nginx_c_b3
curl: (6) Could not resolve host: nginx_c_b3
原理探究:–link本质就是在hosts配置中添加映射。
# 查看容器详情,有一个连接记录
[root@bluecusliyou ~]# docker inspect nginx_c_b3
[
{
"Links": [
"/nginx_c_b2:/nginx_c_b3/nginx_c_b2"
],
}
]
# 查看容器内部IP映射文件,link就是加了一条映射 ID和容器名称都是可以直接访问的
[root@bluecusliyou ~]# docker exec -it nginx_c_b3 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 nginx_c_b2 edb46469326c
172.17.0.4 c234f7622275
[root@bluecusliyou ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c234f7622275 nginx "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 80/tcp nginx_c_b3
edb46469326c nginx "/docker-entrypoint.…" 35 minutes ago Up 35 minutes 0.0.0.0:8562->80/tcp, :::8562->80/tcp nginx_c_b2
41abcb9be1b3 nginx "/docker-entrypoint.…" 35 minutes ago Up 35 minutes 0.0.0.0:8561->80/tcp, :::8561->80/tcp nginx_c_b1
4、自定义网络
我们直接启动一个容器,不适用--net指定网络,默认指定的是bridge的docker0,但是容器名不能直接访问,需要使用link实现,比较麻烦。而且默认网络是自动分配IP的,不能自己指定,在实际部署中,我们需要指定容器ip,不允许其自行分配ip,尤其是搭建集群时,固定ip是必须的。
我们可以自定义bridge网络,不需要link也可以实现容器名称访问,创建容器的时候可以使用--ip指定容器的IP,专网专用也可以隔离容器之间的网络互相干扰。
自定义网络,bridge类型,子网,网关
# 创建自定义网络
[root@bluecusliyou ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
c5e99afee05debf1c1bb212ebe98e63630713bd4337b8f8bd39ac085f9001adc
[root@bluecusliyou ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
04f2eb0d05b9 bridge bridge local
9697f77248e1 host host local
c5e99afee05d mynet bridge local
abc08bbf4c8b none null local
#查看网络详情
[root@bluecusliyou ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "c5e99afee05debf1c1bb212ebe98e63630713bd4337b8f8bd39ac085f9001adc",
"Created": "2022-01-09T20:21:03.072892903+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
启动两个容器,连接到自定义网络,使用容器名称可以访问成功
[root@bluecusliyou ~]# docker run -d --name nginx_c_b4 --net mynet --ip 192.168.0.6 nginx
4a981bf3c9976ce538582964bf879538c1d34e046b4b7742332cd46c4f6686a9
[root@bluecusliyou ~]# docker run -d --name nginx_c_b5 --net mynet --ip 192.168.0.7 nginx
1bda6e711e7cf31c0597f0c023a7317fbd8b576c9297408b0826d810e58b7760
[root@bluecusliyou ~]# docker exec -it nginx_c_b4 curl nginx_c_b5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
5、网络连通
不同网络的容器之间需要互相访问,可以将一个网络的容器连接到另一个网络。
[root@bluecusliyou ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 将容器加入网络,容器名访问成功
[root@bluecusliyou ~]# docker network connect mynet nginx_c_b1
[root@bluecusliyou ~]# docker exec -it nginx_c_b1 curl nginx_c_b4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
七、DockerFile
1、Dockerfile简介
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。
2、DockerFile文件格式
Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。
## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
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
# 4、容器启动指令
CMD /usr/sbin/nginx
3、DockerFile指令详解
基础知识
- 每个保留关键字(指令)都是必须是大写字母
- 执行从上到下顺序 执行
- “#”表示注释
- 每一个指令都会创建提交一个新的镜像层
FROM 指定基础镜像
通过 FROM 指定的镜像,可以是任何有效的基础镜像。FROM 有以下限制:
- Dockerfile 中第一条非注释命令必须是FROM
- 在一个 Dockerfile 文件中创建多个镜像时,FROM 可以多次出现。只需在每个新命令 FROM 之前,记录提交上次的镜像 ID。
- tag 或 digest() 是可选的,如果不使用这两个值时,会使用 latest 版本的基础镜像
选择基础镜像的三个原则:
- 官方镜像优于非官方的镜像;
- 固定版本的Tag,而不是每次都使用latest;
- 功能满足,选择体积小的镜像;
格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
示例:
FROM mysql:5.6
MAINTAINER 维护者信息
格式:
MAINTAINER <name>
示例:
MAINTAINER Jasper Xu
MAINTAINER sorex@163.com
MAINTAINER Jasper Xu <sorex@163.com>
COPY 复制文件
COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的<目标路径>
位置。
<源路径>
可以是多个,甚至可以是通配符,<目标路径>
可以是容器内的绝对路径,也可以是相对于工作目录的相对路径,目标路径不存在会自动创建。
此外,源文件的各种元数据都会保留。如读、写、执行权限、文件变更时间等。
格式:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
示例:
COPY package.json /usr/src/app/
COPY hom* /mydir/
COPY hom?.txt /mydir/
ADD 更高级的复制文件
ADD 指令和 COPY 的格式和性质基本一致。tar类型文件会自动解压,可以访问网络资源。
在 Docker 官方的 [Dockerfile 最佳实践文档] 中要求,尽可能的使用 COPY
,因为 COPY
的语义很明确,就是复制文件而已,而 ADD
则包含了更复杂的功能,其行为也不一定很清晰。而且ADD
指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
因此在 COPY
和 ADD
指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY
指令,仅在需要自动解压缩的场合使用 ADD
。
格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/ # 添加 "test" 到WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
ENV 设置环境变量
ENV指令就是设置环境变量,后面的其它指令,还是运行时的应用,都可以直接使用这里定义的环境变量。
这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法。
docker run
的时候可以使用 -e
覆盖或者添加变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
ARG 构建参数
ARG用于指定传递给构建镜像时的变量,使用 docker build
构建镜像时,可以通过 --build-arg <varname>=<value>
参数来指定或重设置这些变量的值。
格式:
ARG <name>[=<default value>]
示例:
ARG site
ARG build_user=IT笔录
EXPOSE 暴露端口
EXPOSE 指令并不会让容器监听 host 的端口,如果需要,需要在 docker run
时使用 -p
、-P
参数来发布容器端口到 host 的某个端口上。
格式:
EXPOSE <port> [<port>...]
VOLUME 定义匿名卷
VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。
通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。
格式:
VOLUME ["/data"]
USER 指定当前用户
USER 用于指定运行镜像所使用的用户,使用USER指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。
使用USER指定用户后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT 都将使用该用户。镜像构建完成后,通过 docker run
运行容器时,可以通过 -u
参数来覆盖所指定的用户。
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
WORKDIR 指定工作目录
WORKDIR用于在容器内设置一个工作目录,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。WORKDIR 指定的工作目录,必须是提前创建好的。可以看成cd
命令。
在使用 docker run
运行容器时,可以通过-w
参数覆盖构建时所设置的工作目录。
WORKDIR /a
WORKDIR b
WORKDIR c
#pwd 最终将会在 /a/b/c 目录中执行
RUN pwd
LABEL 为镜像添加元数据
LABEL用于为镜像添加元数据,元数以键值对的形式指定,一条LABEL可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL version="1.0" description="这是一个Web服务器" by="Docker"
ONBUILD 镜像触发器
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
格式:
ONBUILD [INSTRUCTION]
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
RUN 构建执行命令
在镜像的构建过程中执行特定的命令,并生成一个中间镜像。
- RUN 命令将在当前 image 中执行任意合法命令并提交执行结果。
- RUN 指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定
--no-cache
参数,如:docker build --no-cache
。
shell执行
格式:
RUN <command>
exec执行
格式:
RUN ["executable", "param1", "param2"]
示例:
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg1"]
CMD 容器启动命令
CMD用于指定在容器启动时所要执行的命令。CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
一个Dockerfile仅仅最后一个CMD起作用,docker run
命令如果指定了命令会覆盖CMD命令。
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。
格式:
CMD <shell 命令>
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
示例:
CMD echo "This is a test."
CMD ["/usr/bin/wc","--help"]
#编写dockerfile
[root@bluecusliyou dockerfile-cmd]# vim dockerfile-test-cmd
[root@bluecusliyou dockerfile-cmd]# cat dockerfile-test-cmd
FROM centos
CMD ["ls","-a"]
#构建镜像
[root@bluecusliyou dockerfile-cmd]# docker build -f dockerfile-test-cmd -t cmd-test:0.1 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 5d0da3dc9764
Step 2/2 : CMD ["ls","-a"]
---> Running in fe15de5e3126
Removing intermediate container fe15de5e3126
---> fb5f1364201a
Successfully built fb5f1364201a
Successfully tagged cmd-test:0.1
#启动一个容器
[root@bluecusliyou dockerfile-test-cmd]# docker run cmd-test:0.1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
# cmd的情况下 -l 替换了CMD["ls","-l"]。 -l 不是命令所以报错
[root@bluecusliyou dockerfile-test-cmd]# docker run cmd-test:0.1 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.
ERRO[0000] error waiting for container: context canceled
ENTRYPOINT 容器启动命令
ENTRYPOINT指定这个容器启动的时候要运行的命令,可以追加命令。
ENTRYPOINT 与 CMD 非常类似,不同的是通过docker run
执行的命令不会覆盖 ENTRYPOINT,而是追加。且会覆盖 CMD 命令指定的参数。
Dockerfile 中只允许有一个 ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的 ENTRYPOINT 指令。
格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
示例:
ENTRYPOINT ["/usr/bin/nginx"]
#编写dockerfile
[root@bluecusliyou dockerfile-entrypoint]# vim dockerfile-test-entrypoint
[root@bluecusliyou dockerfile-entrypoint]# cat dockerfile-test-entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
#构建镜像
[root@bluecusliyou dockerfile-test-entrypoint]# docker build -f dockerfile-test-entrypoint -t entrypoint-test:0.1 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 5d0da3dc9764
Step 2/2 : ENTRYPOINT ["ls","-a"]
---> Running in 47b91532e6c3
Removing intermediate container 47b91532e6c3
---> 86bb562cb0c1
Successfully built 86bb562cb0c1
Successfully tagged entrypoint-test:0.1
#启动一个容器
[root@bluecusliyou dockerfile-test-entrypoint]# docker run entrypoint-test:0.1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
#命令是直接拼接在ENTRYPOINT命令后面效果
[root@bluecusliyou dockerfile-test-entrypoint]# docker run entrypoint-test:0.1 -l
total 0
drwxr-xr-x 1 root root 6 Dec 22 09:21 .
drwxr-xr-x 1 root root 6 Dec 22 09:21 ..
-rwxr-xr-x 1 root root 0 Dec 22 09:21 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Dec 22 09:21 dev
drwxr-xr-x 1 root root 66 Dec 22 09:21 etc
drwxr-xr-x 2 root root 6 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 6 Sep 15 14:17 lost+found
drwxr-xr-x 2 root root 6 Nov 3 2020 media
drwxr-xr-x 2 root root 6 Nov 3 2020 mnt
drwxr-xr-x 2 root root 6 Nov 3 2020 opt
dr-xr-xr-x 134 root root 0 Dec 22 09:21 proc
dr-xr-x--- 2 root root 162 Sep 15 14:17 root
drwxr-xr-x 11 root root 163 Sep 15 14:17 run
lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x 2 root root 6 Nov 3 2020 srv
dr-xr-xr-x 13 root root 0 Dec 22 09:21 sys
drwxrwxrwt 7 root root 171 Sep 15 14:17 tmp
drwxr-xr-x 12 root root 144 Sep 15 14:17 usr
drwxr-xr-x 20 root root 262 Sep 15 14:17 var
4、实战:构建.Net项目
创建一个Net6项目,选择docker支持,生成一个dockerfile,上传项目到服务器上
#查看Dockerfile文件
[root@bluecusliyou dockerfile-WebAppTest]# cat Dockerfile
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["dockerfile-WebAppTest.csproj", "."]
RUN dotnet restore "./dockerfile-WebAppTest.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "dockerfile-WebAppTest.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "dockerfile-WebAppTest.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
#构建镜像
[root@bluecusliyou dockerfile-WebAppTest]# docker build -t webapptest:0.1 .
Sending build context to Docker daemon 8.2MB
Step 1/17 : FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
6.0: Pulling from dotnet/aspnet
a2abf6c4d29d: Pull complete
08af7dd3c640: Pull complete
742307799914: Pull complete
a260dbcd03fc: Pull complete
96c3c696f47e: Pull complete
Digest: sha256:7696d5b456eede87434c232b9070f40659ff0c4b71ca622cf197815ccaee661d
Status: Downloaded newer image for mcr.microsoft.com/dotnet/aspnet:6.0
---> 8d32e18b77a4
Step 2/17 : WORKDIR /app
---> Running in 51c96a80e840
Removing intermediate container 51c96a80e840
---> 83c90e297231
Step 3/17 : EXPOSE 80
---> Running in 869ab4f326c2
Removing intermediate container 869ab4f326c2
---> d6085b7c2120
Step 4/17 : EXPOSE 443
---> Running in fd855df4c7fd
Removing intermediate container fd855df4c7fd
---> c702c8de57e7
Step 5/17 : FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
6.0: Pulling from dotnet/sdk
a2abf6c4d29d: Already exists
08af7dd3c640: Already exists
742307799914: Already exists
a260dbcd03fc: Already exists
96c3c696f47e: Already exists
d81364490ceb: Pull complete
3e56f7c4d95f: Pull complete
9939dbdaf4a7: Pull complete
Digest: sha256:a7af03bdead8976d4e3715452fc985164db56840691941996202cea411953452
Status: Downloaded newer image for mcr.microsoft.com/dotnet/sdk:6.0
---> e86d68dca8c7
Step 6/17 : WORKDIR /src
---> Running in 14fef285f572
Removing intermediate container 14fef285f572
---> 001d6270e584
Step 7/17 : COPY ["dockerfile-WebAppTest.csproj", "."]
---> 7cd3e5caf89c
Step 8/17 : RUN dotnet restore "./dockerfile-WebAppTest.csproj"
---> Running in bc35234d137d
Determining projects to restore...
Restored /src/dockerfile-WebAppTest.csproj (in 2.14 sec).
Removing intermediate container bc35234d137d
---> c067bc6dfbae
Step 9/17 : COPY . .
---> 78dab1650a45
Step 10/17 : WORKDIR "/src/."
---> Running in d643773f4d58
Removing intermediate container d643773f4d58
---> 026250d9327d
Step 11/17 : RUN dotnet build "dockerfile-WebAppTest.csproj" -c Release -o /app/build
---> Running in 8e1eb8e0879b
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
dockerfile-WebAppTest -> /app/build/dockerfile-WebAppTest.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:08.85
Removing intermediate container 8e1eb8e0879b
---> c1c5553c89b7
Step 12/17 : FROM build AS publish
---> c1c5553c89b7
Step 13/17 : RUN dotnet publish "dockerfile-WebAppTest.csproj" -c Release -o /app/publish
---> Running in 8114015e5016
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
dockerfile-WebAppTest -> /src/bin/Release/net6.0/dockerfile-WebAppTest.dll
dockerfile-WebAppTest -> /app/publish/
Removing intermediate container 8114015e5016
---> 55c4cd5cbd12
Step 14/17 : FROM base AS final
---> c702c8de57e7
Step 15/17 : WORKDIR /app
---> Running in 5dd1282931ec
Removing intermediate container 5dd1282931ec
---> ac0ef9e1e60d
Step 16/17 : COPY --from=publish /app/publish .
---> 32484d82d784
Step 17/17 : ENTRYPOINT ["dotnet", "dockerfile-WebAppTest.dll"]
---> Running in 6fc2496b06da
Removing intermediate container 6fc2496b06da
---> 1c02f8c29e8a
Successfully built 1c02f8c29e8a
Successfully tagged webapptest:0.1
#运行容器
[root@bluecusliyou dockerfile-WebAppTest]# docker run -id -p 8888:80 --name mywebapp webapptest:0.1
c989d336314bab82ebf29ef777b386e7db5731d34a415cae5c41b3408aa6e8bc
#访问接口成功
[root@bluecusliyou dockerfile-WebAppTest]# curl localhost:8888
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home Page - dockerfile_WebAppTest</title>
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="/css/site.css?v=AKvNjO3dCPPS0eSU1Ez8T2wI280i08yGycV9ndytL-c" />
<link rel="stylesheet" href="/dockerfile_WebAppTest.styles.css" />
</head>
<body>
<header>
<nav b-bke7gorqge class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div b-bke7gorqge class="container-fluid">
<a class="navbar-brand" href="/">dockerfile_WebAppTest</a>
<button b-bke7gorqge class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span b-bke7gorqge class="navbar-toggler-icon"></span>
</button>
<div b-bke7gorqge class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul b-bke7gorqge class="navbar-nav flex-grow-1">
<li b-bke7gorqge class="nav-item">
<a class="nav-link text-dark" href="/">Home</a>
</li>
<li b-bke7gorqge class="nav-item">
<a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div b-bke7gorqge class="container">
<main b-bke7gorqge role="main" class="pb-3">
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
</main>
</div>
<footer b-bke7gorqge class="border-top footer text-muted">
<div b-bke7gorqge class="container">
© 2021 - dockerfile_WebAppTest - <a href="/Home/Privacy">Privacy</a>
</div>
</footer>
<script src="/lib/jquery/dist/jquery.min.js"></script>
<script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
</body>
</html>
八、Docker仓库
1、Docker Hub(公共镜像仓库)
Docker Hub公共仓库就类似于GitHub,这是一个公共的共享的镜像仓库平台,我们可以像在GitHub上随意得clone公共的开源项目一样pull镜像到本地。
(1)注册和创建仓库
官网地址:hub.docker.com/
注册完成登录之后就可以创建一个Repository,可以创建公有仓库,也可以创建私有仓库。免费版本只能创建一个私有库。
(2)客户端上登录dockerhub账号
这里的账号就是dockerhub上注册的账号
[root@bluecusliyou ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: bluecusliyou
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[root@bluecusliyou ~]# cat /root/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "Ymx1ZWN1c2xpeW91OmxpeW91QGRvY2tlckBxNTkuY29t"
}
}
}
(3)客户端上传镜像
只有dockerhub的主机名是可以省略的,其他仓库的主机名必须写上
#给镜像打标签,上传镜像到仓库
[root@bluecusliyou ~]# docker tag nginx bluecusliyou/nginx:0.1
[root@bluecusliyou ~]# docker push bluecusliyou/nginx:0.1
The push refers to repository [docker.io/bluecusliyou/nginx]
d874fd2bc83b: Layer already exists
32ce5f6a5106: Layer already exists
f1db227348d0: Layer already exists
b8d6e692a25e: Layer already exists
e379e8aedd4d: Layer already exists
2edcec3590a4: Layer already exists
0.2: digest: sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3 size: 1570
#查看仓库镜像版本
[root@bluecusliyou ~]# curl https://registry.hub.docker.com/v1/repositories/bluecusliyou/nginx/tags
[{"layer": "", "name": "0.1"}]
(4)另一台客户端下载镜像
[root@bluecusliyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@bluecusliyou ~]# docker pull bluecusliyou/nginx:0.1
0.2: Pulling from bluecusliyou/mynginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3
Status: Downloaded newer image for bluecusliyou/mynginx:0.2
docker.io/bluecusliyou/nginx:0.1
[root@bluecusliyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
bluecusliyou/nginx 0.1 605c77e624dd 10 days ago 141MB
2、Registry(私有镜像仓库)
Docker Hub是Docker默认官方公共镜像,如果想要自己搭建私有镜像仓库,官方也提供Registry镜像,使得我们搭建私有仓库变得非常简单。
所谓私有仓库,也就是在本地(局域网)搭建的一个类似公共仓库的东西,搭建好之后,我们可以将镜像提交到私有仓库中。这样我们既能使用 Docker 来运行我们的项目镜像,也避免了商业项目暴露出去的风险。
(1)服务器搭建镜像仓库并启动
#拉取仓库的镜像
[root@bluecusliyou ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
79e9f2f55bf5: Pull complete
0d96da54f60b: Pull complete
5b27040df4a2: Pull complete
e2ead8259a04: Pull complete
3790aef225b9: Pull complete
Digest: sha256:169211e20e2f2d5d115674681eb79d21a217b296b43374b8e39f97fcf866b375
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
#运行仓库镜像
[root@bluecusliyou ~]# docker run -d -v /opt/images/registry:/var/lib/registry -p 5000:5000 --restart=always --name bluecusliyou-registry registry
e2761519cd538d028622cc6ff406257712d1077b6ecc6e8f545f1e580ae49471
(2)客户端修改本地域名文件
这里的“your-server-ip”请换为你的仓库的服务器的IP地址
[root@blueculiyou ~]# vim /etc/hosts
[root@blueculiyou ~]# cat /etc/hosts
127.0.0.1 VM-0-13-centos VM-0-13-centos
127.0.0.1 localhost.localdomain localhost
127.0.0.1 localhost4.localdomain4 localhost4
::1 VM-0-13-centos VM-0-13-centos
::1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
your-server-ip registery
(3)客户端修改Docker的配置文件
为了让客户端服务器能够快速地访问搭建的镜像仓库,在客户端配置一下私有仓库的可信任设置让我们可以通过HTTP直接访问
[root@blueculiyou ~]# vim /etc/docker/daemon.json
[root@blueculiyou ~]# cat /etc/docker/daemon.json
{
"insecure-registries": ["registery:5000"]
}
#重新加载配置文件
[root@blueculiyou harbor]# systemctl daemon-reload
[root@blueculiyou harbor]# systemctl restart docker
(4)客户端上传镜像
#给镜像打标签,上传镜像到仓库
[root@blueculiyou ~]# docker tag nginx registery:5000/bluecusliyou/nginx:0.1
[root@blueculiyou ~]# docker push registery:5000/bluecusliyou/nginx:0.1
The push refers to repository [registery:5000/bluecusliyou/nginx]
2bed47a66c07: Layer already exists
82caad489ad7: Layer already exists
d3e1dca44e82: Layer already exists
c9fcd9c6ced8: Layer already exists
0664b7821b60: Layer already exists
9321ff862abb: Layer already exists
0.1: digest: sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47 size: 1570
#查看仓库镜像列表
[root@blueculiyou ~]# curl http://registery:5000/v2/_catalog
{"repositories":["bluecusliyou/nginx"]}
#查看仓库镜像有哪些版本
[root@blueculiyou ~]# curl http://registery:5000/v2/bluecusliyou/nginx/tags/list
{"name":"bluecusliyou/nginx","tags":["0.1"]}
(5)另一台客户端下载镜像
#下载镜像
[root@blueculiyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f652ca386ed1 3 weeks ago 141MB
[root@blueculiyou ~]# docker pull registery:5000/bluecusliyou/nginx:0.1
0.1: Pulling from bluecusliyou/nginx
Digest: sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47
Status: Downloaded newer image for registery:5000/bluecusliyou/nginx:0.1
registery:5000/bluecusliyou/nginx:0.1
[root@blueculiyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f652ca386ed1 3 weeks ago 141MB
registery:5000/bluecusliyou/nginx 0.1 f652ca386ed1 3 weeks ago 141MB
3、Harbor(企业级镜像仓库)
Harbor是VMware公司开源的一个企业级Docker Registry项目,项目地址:github.com/goharbor/ha…
Harbor作为一个企业级私有Registry服务器,提供了更好的性能和安全,提升了用户使用Registry构建和运行环境传输镜像的效率。虽然Harbor和Registry都是私有镜像仓库的选择,但是Harbor的企业级特性更强,因此也是更多企业级用户的选择。
Harbor实现了基于角色的访问控制机制,并通过项目来对镜像进行组织和访问权限的控制,也常常和K8S中的namespace结合使用。此外,Harbor还提供了图形化的管理界面,我们可以通过浏览器来浏览,检索当前Docker镜像仓库,管理项目和命名空间。
有关Harbor的架构,可以参考阅读这一篇《Harbor整体架构》一文,里面讲述了Harbor的6大核心组件构成,有兴趣的朋友可以一读。
下面列出了Harbor的搭建过程,主要参考自Harbor的github文档
(1)安装准备
下载安装包,解压缩
Harbor提供了两种安装方式:一种是在线安装包,因此包很小;另一种是离线安装包,因此包很大(>=570MB)。这里选择下载离线安装包,下载地址:github.com/goharbor/ha…
#下载离线安装包
[root@bluecusliyou harbor]# wget https://github.com/goharbor/harbor/releases/download/v2.3.5/harbor-offline-installer-v2.3.5.tgz
#下载之后,解压缩
[root@bluecusliyou harbor]# tar zvxf harbor-offline-installer-v2.3.5.tgz
harbor/harbor.v2.3.5.tar.gz
harbor/prepare
harbor/LICENSE
harbor/install.sh
harbor/common.sh
harbor/harbor.yml.tmpl
安装Docker,参考安装Docker章节
安装Docker-compose
#下载
[root@bluecusliyou harbor]# curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 423 100 423 0 0 403 0 0:00:01 0:00:01 --:--:-- 403
100 12.1M 100 12.1M 0 0 2228k 0 0:00:05 0:00:05 --:--:-- 2949k
#添加可执行权限
[root@bluecusliyou harbor]# sudo chmod +x /usr/local/bin/docker-compose
#查看版本号成功,表示安装完成
[root@bluecusliyou harbor]# docker-compose version
docker-compose version 1.29.1, build c34c88b2
docker-py version: 5.0.0
CPython version: 3.7.10
OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019
如果要卸载直接删除文件
sudo rm /usr/local/bin/docker-compose
修改配置文件配置信息
hostname:服务器IP
https先注释掉,不配置证书,生产环境需要配置证书保证安全性。
密码:harbor_admin_password: admin
每次修改配置文件的后都要重新执行prepare
否则配置文件不生效,第一次安装可以不执行
#先拷贝一个配置文件模板
[root@bluecusliyou harbor]# cp harbor.yml.tmpl harbor.yml
#修改配置文件
[root@bluecusliyou harbor]# vim harbor.yml
[root@bluecusliyou harbor]# ./prepare
prepare base dir is set to /home/test/harbor
WARNING:root:WARNING: HTTP protocol is insecure. Harbor will deprecate http protocol in the future. Please make sure to upgrade to https
Clearing the configuration file: /config/portal/nginx.conf
...
(2)安装仓库
[root@bluecusliyou harbor]# ./install.sh
[Step 0]: checking if docker is installed ...
Note: docker version: 20.10.7
[Step 1]: checking docker-compose is installed ...
Note: docker-compose version: 1.29.1
[Step 2]: loading Harbor images ...
1e3f0dc884e2: Loading layer [==================================================>] 39.45MB/39.45MB
...
Loaded image: goharbor/chartmuseum-photon:v2.3.5
[Step 3]: preparing environment ...
[Step 4]: preparing harbor configs ...
prepare base dir is set to /home/test/harbor
WARNING:root:WARNING: HTTP protocol is insecure. Harbor will deprecate http protocol in the future. Please make sure to upgrade to https
Generated configuration file: /config/portal/nginx.conf
...
[Step 5]: starting Harbor ...
Creating network "harbor_harbor" with the default driver
...
✔ ----Harbor has been installed and started successfully.----
如果需要修改Harbor的配置文件,修改完成之后需要重启运行harbor,因为Harbor是基于docker-compose服务编排的,我们可以使用docker-compose命令重启Harbor。docker-compose start | stop | restart,这边的命令需要在docker-compose.yml文件的路径下执行。
#停止harbor
[root@bluecusliyou harbor]# docker-compose down
Stopping harbor-jobservice ... done
Stopping nginx ... done
Stopping harbor-core ... done
Stopping redis ... done
Stopping registryctl ... done
Stopping registry ... done
Stopping harbor-portal ... done
Stopping harbor-db ... done
Stopping harbor-log ... done
Removing harbor-jobservice ... done
Removing nginx ... done
Removing harbor-core ... done
Removing redis ... done
Removing registryctl ... done
Removing registry ... done
Removing harbor-portal ... done
Removing harbor-db ... done
Removing harbor-log ... done
Removing network harbor_harbor
#启动harbor -d后台运行
[root@bluecusliyou harbor]# docker-compose up -d
Creating network "harbor_harbor" with the default driver
Creating harbor-log ... done
Creating registryctl ... done
Creating harbor-db ... done
Creating harbor-portal ... done
Creating redis ... done
Creating registry ... done
Creating harbor-core ... done
Creating harbor-jobservice ... done
Creating nginx ... done
将 harbor 配成 systemd 的 service,使之能开机自启动,添加配置文件/usr/lib/systemd/system/harbor.service
其中 {{ harbor_install_path }}
换成自己的 harbor 安装路径。还有 docker-compose 的绝对路径,请通过 which docker-compose
查看。
[Unit]
Description=Harbor
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=http://github.com/vmware/harbor
[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/local/bin/docker-compose -f {{ harbor_install_path }}/harbor/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f {{ harbor_install_path }}/harbor/docker-compose.yml down
[Install]
WantedBy=multi-user.target
修改完成之后设置harbor开机启动,重启验证harbor自启动成功
[root@bluecusliyou system]# sudo systemctl enable harbor
Created symlink /etc/systemd/system/multi-user.target.wants/harbor.service → /usr/lib/systemd/system/harbor.service.
[root@bluecusliyou system]# sudo systemctl start harbor
(3)图形界面管理
Harbor的管理平台登录页面
新建项目
添加一个管理账号
为测试项目添加创建的管理员
(4)客户端修改本地域名文件
这里的“your-server-ip”请换为你的仓库的服务器的IP地址
[root@blueculiyou ~]# vim /etc/hosts
[root@blueculiyou ~]# cat /etc/hosts
127.0.0.1 VM-0-13-centos VM-0-13-centos
127.0.0.1 localhost.localdomain localhost
127.0.0.1 localhost4.localdomain4 localhost4
::1 VM-0-13-centos VM-0-13-centos
::1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
your-server-ip registery
your-server-ip2 harbor.io
(5)客户端修改Docker的配置文件
为了让客户端服务器能够快速地访问搭建的镜像仓库,在客户端配置一下私有仓库的可信任设置让我们可以通过HTTP直接访问
[root@blueculiyou ~]# vim /etc/docker/daemon.json
[root@blueculiyou ~]# cat /etc/docker/daemon.json
{
"insecure-registries": ["registery:5000","harbor.io"]
}
#重新加载配置文件
[root@blueculiyou ~]# systemctl daemon-reload
[root@blueculiyou ~]# systemctl restart docker
(6)客户端登陆服务器
[root@blueculiyou ~]# docker login harbor.io
Username: bluecusliyou
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[root@blueculiyou ~]# cat /root/.docker/config.json
{
"auths": {
"harbor.io": {
"auth": "Ymx1ZWN1c2xpeW91OkxpeW91MTIz"
}
}
}
(7)客户端上传镜像
#给镜像打标签,上传镜像到仓库
[root@blueculiyou ~]# docker tag nginx harbor.io/bluecusliyou/nginx:0.1
[root@blueculiyou ~]# docker push harbor.io/bluecusliyou/nginx:0.1
The push refers to repository [harbor.io/bluecusliyou/nginx]
2bed47a66c07: Pushed
82caad489ad7: Pushed
d3e1dca44e82: Pushed
c9fcd9c6ced8: Pushed
0664b7821b60: Pushed
9321ff862abb: Pushed
0.1: digest: sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47 size: 1570
图形界面查看上传的镜像
(8)另一台客户端下载镜像
#下载镜像
[root@blueculiyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registery:5000/bluecusliyou/nginx 0.1 f652ca386ed1 3 weeks ago 141MB
nginx latest f652ca386ed1 3 weeks ago 141MB
[root@blueculiyou ~]# docker pull harbor.io/bluecusliyou/nginx:0.1
0.1: Pulling from bluecusliyou/nginx
Digest: sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47
Status: Downloaded newer image for harbor.io/bluecusliyou/nginx:0.1
harbor.io/bluecusliyou/nginx:0.1
[root@blueculiyou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f652ca386ed1 3 weeks ago 141MB
harbor.io/bluecusliyou/nginx 0.1 f652ca386ed1 3 weeks ago 141MB
registery:5000/bluecusliyou/nginx 0.1 f652ca386ed1 3 weeks ago 141MB