Docker详解--中

162 阅读52分钟

五、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 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。

因此在 COPYADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 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">
            &copy; 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/

注册地址:hub.docker.com/signup

注册完成登录之后就可以创建一个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