共享宿主机的内核(kernel)(容器的安全把宿主机安全管控好就好)
base镜像提供的是最小的linux发行版
同一docker主机支持运行多种linux发行版(因为linux内核一样,不一样的是文件系统)
采用分层结构的最大好处:共享资源(底层共享资源)
分层结构:降低了网络带宽和磁盘空间
(下载网上的镜像时候会判断你有该层没,你有相同层的话,你便不用下载了已经有的层。本机只要存储同样的镜像层一份,本机的镜像也可以共享)
Copy-on-Write(linux内核的写时复制技术)容器层可以写
容器层以下都不可以写(都是只读)
docker从上往下依次查找文件
容器层保存镜像变化的部分,并不会对镜像本身进行任何的修改
一个镜像最多127层(一般不会有那么大,因为优化就是减少镜像层)
容器常用用法:
-i交互式 | -t打开一个tty终端 |
---|---|
-d打入后台 | -name给容器起个名字 |
ctrl +d 退出容器终端并且容器停掉 | docker ps显示正在运行的容器 |
ctrl +pq 退出容器终端并且容器后台运行 | docker ps +a 显示所有的容器 |
docker rm +容器名字 删掉已经停止的容器 | 容器正在running状态需要先stop,在rm |
强制删除,不用停止容器直接删除rm-f + 容器名字 | docker -p 宿主机端口:容器内的端口,用宿主机的端口号映射容器内的端口 |
docker start + 容器名字,开启容器 | docker container attach +容器名字,进入该容器 |
docker images 查看容器已经有的镜像 | docker history +容器名字,查看之前打包的历史记录 |
docker rmi +镜像名字,删除镜像 | docker load 导入镜像 |
docker port + 容器名字,可以查看容器的端口映射 | –restart=always随着docker引擎的开启该容器自动启动 |
docker inspect + 容器名字或者id,查看容器具体信息 | docker inspect +镜像,查看镜像的具体信息 |
docker -v 宿主机路径:容器路径,数据持久化 | docker exec -it 容器名字 sh 重新进入交互式容器中不能tab补齐 |
– | docker -v 将宿主机资源挂载到容器内 |
docker exec -it 容器名字 bash 重新进入交互式容器中能tab补齐 |
[root@docker1 docker]# docker container
attach diff kill port rm stop wait
commit exec logs prune run top
cp export ls rename start unpause
create inspect pause restart stats update
镜像的名称是不能冲突的
不推荐该方法
运行容器 | docker run |
---|---|
修改容器 | |
将容器保存为新的镜像 | |
缺点:效率低、可重复性弱、容易出错 |
使用者无法对镜像进行审计,存在安全隐患
[root@docker1 docker]# docker run -it --name test busybox
/ # ls
bin dev etc home proc root sys tmp usr var
/ # touch file1
/ # touch file2
/ # touch file3
/ # ls
bin etc file2 home root tmp var
dev file1 file3 proc sys usr
ctrl +d
[root@docker1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@docker1 docker]# docker start test
test
[root@docker1 docker]# docker container attach test
/ #
ctrl +pq
[root@docker1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7d589bd8615e busybox "sh" 27 minutes ago Up 5 minutes test
[root@docker1 docker]# docker commit --help
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
[root@docker1 docker]# docker commit -m "v1" test test:v1
sha256:dc00eef3c58e0c21318a3a8bc9f4586f75e18b460211cb4bed1f565134924cf4
[root@docker1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v1 dc00eef3c58e 55 seconds ago 1.24MB
busybox latest beae173ccac6 3 weeks ago 1.24MB
missing 表示在别人机器上打包的你看不到,v1创建了3个文件
只能看到sh,不知道用户在其中具体干了什么,不便于用户审计和重用,安全隐患。
[root@docker1 docker]# docker history test:v1
IMAGE CREATED CREATED BY SIZE COMMENT
dc00eef3c58e 2 minutes ago sh 166B v1
beae173ccac6 3 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4… 1.24MB
[root@docker1 docker]# docker history busybox:latest
IMAGE CREATED CREATED BY SIZE COMMENT
beae173ccac6 3 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4… 1.24MB
分层结构的好处,共享资源。两个镜像都用了最后2层,但操作系统上面只用保存一份。彼此共享,镜像层是只读的
[root@docker1 docker]# docker run -it --name demo test:v1
/ # ls
bin dev etc file1 file2 file3 home proc root sys tmp usr var
删除原先docker三部曲创建的镜像
当前有容器占用镜像,所以删不了该镜像。删除镜像的时候,先保证没有容器占用该镜像
[root@docker1 docker]# docker rmi test:v1
Error response from daemon: conflict: unable to remove repository reference "test:v1" (must force) - container 1e534ccc1bb7 is using its referenced image dc00eef3c58e
docker ps-a 找到占用镜像的容器删掉,然后删除容器镜像
[root@docker1 docker]# docker rm -f demo
demo
[root@docker1 docker]# docker rmi test:v1
Untagged: test:v1
Deleted: sha256:dc00eef3c58e0c21318a3a8bc9f4586f75e18b460211cb4bed1f565134924cf4
Deleted: sha256:9e982fbda3a1bbb95e62de21d15b305daef87bab314f218a62afbfd3f9939c2e
创建一个Dockerfile (必须在一个空目录中创建) 因为一开始会发送内容给Docker引擎,如果在很大/目录上面构建,会把整个/发送去构建
构建镜像
[root@docker1 docker]# cd
[root@docker1 ~]# ls
[root@docker1 ~]# mkdir docker
[root@docker1 ~]# cd docker/
[root@docker1 docker]# ls
[root@docker1 docker]# vim Dockerfile
FROM 从那个基础镜像开始构建,给予它一个环境(构建方式,通过基础镜像,运行一个容器,(后台)在里面执行指令)
RUN在容器里面运行什么指令
.是从当前目录读取
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
里面有构建镜像的具体参数
[root@docker1 docker]# docker build --help
每一个RUN,一层。相当于,你每一次运行,执行了一次Docker commit
[root@docker1 docker]# docker build -t test:v1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM busybox
---> beae173ccac6
Step 2/4 : RUN touch file1
---> Running in 0121d9c6c701
Removing intermediate container 0121d9c6c701
---> 884a1d2fcbd4
Step 3/4 : RUN mkdir dir1
---> Running in f45072d8860b
Removing intermediate container f45072d8860b
---> 3f7259a48bfc
Step 4/4 : RUN mv file1 dir1
---> Running in 612208e6db5b
Removing intermediate container 612208e6db5b
---> b6b7f570cc47
Successfully built b6b7f570cc47
Successfully tagged test:v1
便于审计并且便于重用,因为这个文件Dockerfile已经写下来了
在Dockerfile中写的每一行通过Docker commit提交成镜像层
END
看docker官方的网站学习docker具体指令
常用指令:
FROM 指定base镜像,如果本地不存在会从远程仓库下载 | MAINTAINER 设置镜像的作者,比如用户邮箱等 |
---|---|
COPY 把文件从build context 复制到镜像 支持两种形式:COPY src dest 和 COPY 【“src,”“dest”】 src必须指定build context 中的文件或目录 |
src必须指定build context 中的文件或目录即当前目录
实验环境:
[root@docker1 docker]# vim index.html
[root@docker1 docker]# cat index.html
www.yan.com
编写Dockerfile:
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
前面镜像用的cache,因为之前编译过,Dockerfile没有改变都是用缓存来执行,加速构建过程
[root@docker1 docker]# docker build -t test:v2 .
Sending build context to Docker daemon 3.072kB
Step 1/5 : FROM busybox
---> beae173ccac6
Step 2/5 : RUN touch file1
---> Using cache
---> 884a1d2fcbd4
Step 3/5 : RUN mkdir dir1
---> Using cache
---> 3f7259a48bfc
Step 4/5 : RUN mv file1 dir1
---> Using cache
---> b6b7f570cc47
Step 5/5 : COPY index.html /
---> 317f587b99ce
Successfully built 317f587b99ce
Successfully tagged test:v2
拷贝文件到容器中
[root@docker1 docker]# docker history test:v2
IMAGE CREATED CREATED BY SIZE COMMENT
317f587b99ce 2 minutes ago /bin/sh -c #(nop) COPY file:d88a99d823b18eab… 12B
b6b7f570cc47 34 minutes ago /bin/sh -c mv file1 dir1 0B
3f7259a48bfc 34 minutes ago /bin/sh -c mkdir dir1 0B
884a1d2fcbd4 34 minutes ago /bin/sh -c touch file1 0B
beae173ccac6 3 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:6db446a57cbd2b7f4… 1.24MB
执行的指令,ls /接在镜像的后面会覆盖掉镜像原有的操作指令。
–rm帮助运行完容器指令后自动删除该容器
[root@docker1 docker]# docker run --rm test:v2 ls /
bin
dev
dir1
etc
home
index.html
proc
root
sys
tmp
usr
var
[root@docker1 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@docker1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ADD用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像:
实验环境:
[kiosk@foundation38 lamp]$ scp nginx-1.18.0.tar.gz [email protected]:/root/docker
[root@docker1 docker]# ls
Dockerfile index.html nginx-1.18.0.tar.gz
编写Dockefile:
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
[root@docker1 docker]# docker build -t test:v3 .
Sending build context to Docker daemon 1.043MB
Step 1/6 : FROM busybox
---> beae173ccac6
Step 2/6 : RUN touch file1
---> Using cache
---> 884a1d2fcbd4
Step 3/6 : RUN mkdir dir1
---> Using cache
---> 3f7259a48bfc
Step 4/6 : RUN mv file1 dir1
---> Using cache
---> b6b7f570cc47
Step 5/6 : COPY index.html /
---> Using cache
---> 317f587b99ce
Step 6/6 : ADD nginx-1.18.0.tar.gz /
---> 88b81d3d9f28
Successfully built 88b81d3d9f28
Successfully tagged test:v3
解压后放入,基础镜像没有解压命令,所以在构建前用ADD构建。
[root@docker1 docker]# docker run --rm test:v3 ls /
bin
dev
dir1
etc
home
index.html
nginx-1.18.0
proc
root
sys
tmp
usr
var
设置环境变量,变量可以被后续的指令使用:
变量名大写
Dockerfile写法:
[root@docker1 docker]# cat Dockerfile
ENV HOSTNAME docker1
用于应用容器,要发布一个服务的容器。
如果容器中运行应用服务,可以把服务端口暴露出去:
EXPOSE 80
[root@docker1 docker]# cat Dockerfile
EXPOSE 80
应用容器的数据需要持久化
申明数据卷,通常指定的是应用的数据挂载点:
[root@docker1 docker]# cat Dockerfile
VOLUME ["/data"]
为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建。即,容器内切换目录。
前面3个用法的运行结果:
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
运行结果就不写在代码块了
[root@docker1 docker]# docker build -t test:v4 .
data是前面定义的数据卷
[root@docker1 docker]# docker run -it test:v4
/dir1 # cd /data/
/data #
这个卷挂载你宿主机的上面
/data # mount |grep data
/dev/mapper/rhel-root on /data type xfs (rw,relatime,attr2,inode64,noquota)
docker里面看到的是宿主机的信息,磁盘、cpu、内存这些信息存在/proc,/proc是文件系统里面的不会被隔离。
/data # df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 17811456 1594500 16216956 9% /
tmpfs 65536 0 65536 0% /dev
tmpfs 1023468 0 1023468 0% /sys/fs/cgroup
shm 65536 0 65536 0% /dev/shm
/dev/mapper/rhel-root
17811456 1594500 16216956 9% /data
/dev/mapper/rhel-root
17811456 1594500 16216956 9% /etc/resolv.conf
/dev/mapper/rhel-root
17811456 1594500 16216956 9% /etc/hostname
/dev/mapper/rhel-root
17811456 1594500 16216956 9% /etc/hosts
tmpfs 1023468 0 1023468 0% /proc/asound
tmpfs 1023468 0 1023468 0% /proc/acpi
tmpfs 65536 0 65536 0% /proc/kcore
tmpfs 65536 0 65536 0% /proc/keys
tmpfs 65536 0 65536 0% /proc/timer_list
tmpfs 65536 0 65536 0% /proc/timer_stats
tmpfs 65536 0 65536 0% /proc/sched_debug
tmpfs 1023468 0 1023468 0% /proc/scsi
tmpfs 1023468 0 1023468 0% /sys/firmware
docker运行的主机:
[root@docker1 ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/rhel-root 17811456 1594500 16216956 9% /
devtmpfs 1011400 0 1011400 0% /dev
tmpfs 1023468 0 1023468 0% /dev/shm
tmpfs 1023468 17076 1006392 2% /run
tmpfs 1023468 0 1023468 0% /sys/fs/cgroup
/dev/vda1 1038336 135172 903164 14% /boot
tmpfs 204696 0 204696 0% /run/user/0
overlay 17811456 1594500 16216956 9% /var/lib/docker/overlay2/0d9fe361ffa5bec1667b38b2dc660e093a1dd319d69a23a6e498683163971fc5/merged
这个便是容器挂载在宿主机上面的卷
/dev/mapper/rhel-root 17811456 1594500 16216956 9% /
查看一下该容器的具体信息
docker inspect 后面可以加容器也可以镜像
[root@docker1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4c626bd8688f test:v4 "sh" 14 seconds ago Up 13 seconds 80/tcp romantic_bhabha
999fd34ca89f test:v4 "sh" 4 hours ago Exited (255) About a minute ago 80/tcp peaceful_wing
[root@docker1 ~]# docker inspect 4c626bd8688f
inspect查询部分信息:
Gateway是宿主机上面的地址,docker 0的地址。所以容器会通过桥接的模式到达宿主机的网关
所有容器的数据会放在宿主机的/var/lib/docker,如果把这个删掉,所有的镜像容器全没了。
[root@docker1 docker]# ls
builder buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes
容器的卷的目录在/var/lib/docker/volumes
"Gateway": "172.17.0.1"
"IPAddress": "172.17.0.2",
"Mounts": [
{
"Type": "volume",
"Name": "a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171",
"Source": "/var/lib/docker/volumes/a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
这个路径就被挂载在容器的目录里面
"Source": "/var/lib/docker/volumes/a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171/_data",
容器里面:
/data # touch file1
/data # ls
file1
宿主机上:
[root@docker1 ~]# cd /var/lib/docker/volumes/a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171/_data
[root@docker1 _data]# ls
file1
宿主机docker0的地址:
[root@docker1 ~]# ip addr | grep 172.17.0.1
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
容器里面查询ip的结果:
/dir1 # ip addr
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
1.容器的文件系统性能比宿主机差
2.数据过大放在容器不合适,不小心删掉容器,数据就丢失了
同理存在宿主机上面:
宿主机删掉该数据,容器里面的数据就没有了
宿主机:
[root@docker1 ~]# cd /var/lib/docker/volumes/a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171/_data
[root@docker1 _data]# ls
file1
[root@docker1 _data]# rm -fr file1
容器里面:
/data # ls
file1
/data # ls
/data #
在容器中运行命令并创建新的镜像层,常用于安装软件包:
RUN yum install -y vim
这两个指令都是用于设置容器后执行的命令,但CMD会被docker run 后面的命令行覆盖(比如docker run -it /ls会覆盖掉),而ENTRYPOINT不会被忽略,一定会被执行
docker run 后面的参数可以传递给ENTRYPOINT指令当作参数
Dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效
CMD当用户进入后自动执行的指令,像busybox这个镜像第二层,意思就是当用户进去后,自动执行sh
官方推荐CMD用exec格式写
[root@docker1 docker]# docker history test:v1
beae173ccac6 3 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量,而exec格式不会
而用exec格式写的话:[“/bin/sh”,“-c”,“echo $变量名”]
Shell格式:
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
CMD echo $HOSTNAME
[root@docker1 docker]# docker build -t test:v5 .
[root@docker1 docker]# docker run --rm test:v5
docker1
exec格式:
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
CMD ["echo $HOSTNAME"]
[root@docker1 docker]# docker build -t test:v6 .
[root@docker1 docker]# docker run --rm test:v6
docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: "echo $HOSTNAME": executable file not found in $PATH: unknown.
[root@docker1 docker]# docker rmi test:v6
[root@docker1 docker]# vim Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
CMD ["/bin/echo","echo $HOSTNAME"]
[root@docker1 docker]# docker build -t test:v6 .
[root@docker1 docker]# docker run --rm test:v6
echo $HOSTNAME
用exec格式写:
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
CMD ["/bin/echo","-c","echo $HOSTNAME"]
[root@docker1 docker]# docker build -t test:v7 .
[root@docker1 docker]# docker run --rm test:v7
docker1
Exec格式时,ENTRYPOINT可以通过CMD提供额外的参数,CMD的额外参数可以在容器启动时动态替换。在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数。即ENTRYPOINT与CMD混合使用
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM busybox
RUN touch file1
RUN mkdir dir1
RUN mv file1 dir1
COPY index.html /
ADD nginx-1.18.0.tar.gz /
ENV HOSTNAME docker1
EXPOSE 80
VOLUME ["/data"]
WORKDIR /dir1
ENTRYPOINT ["/bin/echo","hello"]
CMD ["world"]
[root@docker1 docker]# docker build -t test:v8 .
[root@docker1 docker]# docker run --rm test:v8
hello world
[root@docker1 docker]# docker run --rm test:v8 linux
hello linux
[root@docker1 docker]# docker run --rm test:v8 yan
hello yan
END
学会如何自己封装一个完整的镜像,将nginx容器化并且优化该镜像
容器启动后,宿主机能上网,容器也能上网。
[root@docker1 yum.repos.d]# docker search rhel7
STARS是下载量
前面带有名字的都不是官方镜像,有可能有问题。
[root@docker1 yum.repos.d]# docker search centos
这个是官方镜像
为了选择自己想要的版本,去docker官方仓库查看
https://hub.docker.com/
首先要清除前面测试的镜像
^以什么开头
[root@docker1 docker]# docker rmi `docker images | grep ^test | awk '{print $1":"$2}'`
在本地先下载一个系统的镜像
-i指定导入的系统镜像,加个bash进去交互。
[root@docker1 docker]# docker load -i rhel7.tar
e1f5733f050b: Loading layer [==================================================>] 147.1MB/147.1MB
[root@docker1 docker]# docker run -it --name demo rhel7:latest bash
版本是7.0的
bash-4.2# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.0 (Maipo)
现在容器里面里面安装Nginx,寻找需要什么资源后,在宿主机Dockerfile里面写相关资源提交镜像后,Nginx便容器化了。
容器中:
修改yum仓库
bash-4.2# cd /etc/yum.repos.d/
bash-4.2# vi rhel7.repo
bash-4.2# cat rhel7.repo
[dvd]
name = rhel7
baseurl = http://172.25.138.250/westos
gpgcheck=0
enabled =1
[root@docker1 docker]# ls
Dockerfile index.html nginx-1.18.0.tar.gz rhel7.tar
[root@docker1 docker]# tar zxf nginx-1.18.0.tar.gz
[root@docker1 docker]# docker container cp nginx-1.18.0 demo:/
[root@docker1 docker]# docker container attach demo
bash-4.2# ls
bin dev home lib64 mnt nginx-1.18.0.tar.gz proc run srv tmp var
boot etc lib media nginx-1.18.0 opt root sbin sys usr
nginx编译需要的包
bash-4.2# yum install gcc pcre-devel openssl-devel
Error: libselinux conflicts with fakesystemd-1-14.el7.x86_64
openssl与centos依赖性冲突了,所以就不装openssl
7.0升级到7.6
Rpmdb损坏了,要升级索引数据库。
bash-4.2# yum install gcc pcre-devel -y
Rpmdb checksum is invalid: dCDPT(pkg checksums): glibc-devel.x86_64 0:2.17-260.el7 - u
bash-4.2# rpmdb --rebuilddb
bash-4.2# cd nginx-1.18.0/
bash-4.2# ls
CHANGES CHANGES.ru LICENSE README auto conf configure contrib html man src
bash-4.2# ./configure --prefix=/usr/local/nginx
报错:缺少了zlib的库,这个库如果安装openssl便自动安装进去了
./configure: error: the HTTP gzip module requires the zlib library.
You can either disable the module by using --without-http_gzip_module
option, or install the zlib library into the system, or build the zlib library
statically from the source with nginx by using --with-zlib=<path> option.
编译成功
bash-4.2# ./configure --prefix=/usr/local/nginx
在宿主机里面:
/usr/local/nginx/html是nginx的默认发布目录
/usr/local/nginx/sbin/nginx运行nginx
查看一下nginx官方如何启动Nginx
[root@docker1 docker]# docker search nginx
nginx Official build of Nginx. 16202 [OK]
每次拉取都是镜像的最新版,详细看inspect,大概看history
[root@docker1 docker]# docker pull nginx
[root@docker1 docker]# docker inspect nginx:latest
自己写必须绝对路径而不是Nginx就行
Dockerfile写法:
[root@docker1 docker]# vim Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@docker1 docker]# docker build -t webserver:v1 .
Step 10/12 : RUN make
---> Running in 1871c9076c77
/bin/sh: make: command not found
所以需要修改一下Dockerfile
不要动上面好的部分,你在yum gcc那块添加的话,后面执行便不能用cache了,而且还可能改错。
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx
RUN yum install make -y
RUN make
RUN make install
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
继续安装
[root@docker1 docker]# docker build -t webserver:v1 .
Successfully tagged webserver:v1
[root@docker1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v1 bdd2829a42e3 31 seconds ago 346MB
可以删掉之前做实验的容器了
[root@docker1 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1871c9076c77 644179a9b6e3 "/bin/sh -c make" 6 minutes ago Exited (127) 6 minutes ago modest_buck
7aaa2d2b53da rhel7:latest "bash" 43 hours ago Exited (130) 16 minutes ago demo
[root@docker1 docker]# docker rm 1871c9076c77 7aaa2d2b53da
启动nginx的镜像
[root@docker1 docker]# docker run -d --name nginx webserver:v1
[root@docker1 docker]# docker inspect nginx
"IPAddress": "172.17.0.2",
"Source": "/var/lib/docker/volumes/70f0dede14b7c1978edb38eb49534509f1fcc205e750bb365254b75677b76005/_data",
发布失败,因为在inspect查到mount里面的宿主机挂载点覆盖点我们的卷了
[root@docker1 docker]# curl 172.17.0.2
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
重新测试:
[root@docker1 docker]# cd /var/lib/docker/volumes/70f0dede14b7c1978edb38eb49534509f1fcc205e750bb365254b75677b76005/_data
[root@docker1 _data]# vim index.html
[root@docker1 _data]# cat index.html
www.yan.org
[root@docker1 _data]# curl 172.17.0.2
www.yan.org
Nginx容器化成功
END
镜像的优化可以减少客户端下载时候的带宽
已经选择了精简的镜像,所以减少镜像的层数,可以把之前的RUN合并在一起。将WORKDIR切换成在RUN底下的cd
之前的Dockerfile:
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb
RUN yum install -y gcc pcre-devel zlib-devel
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx
RUN yum install make -y
RUN make
RUN make install
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
减少镜像层数后:
[root@docker1 docker]# cat Dockerfile
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.18.0.tar.gz /mnt
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel && cd /mnt/nginx-1.18.0 && ./configure --prefix=/usr/local/nginx && yum install make -y && make && make install
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
清理中间产物:/var/cache/yum yum的缓存路径,nginx安装包删掉,编译完了make gcc都可以删掉(安装make这些包时候会安装依赖性,但是删掉时候不删掉依赖性)
[root@docker1 docker]# cat Dockerfile
FROM rhel7
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.18.0.tar.gz /mnt
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make && cd /mnt/nginx-1.18.0 && ./configure --prefix=/usr/local/nginx && make && make install && rm -fr /mnt/nginx-1.18.0 /var/cache/yum
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@docker1 docker]# docker build -t webserver:v2 .
优化后镜像大小差距:
[root@docker1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v2 503439033877 24 seconds ago 258MB
webserver v1 bdd2829a42e3 36 minutes ago 346MB
[root@docker1 docker]# docker images rhel7
rhel7 latest 0a3eb3fde7fd 7 years ago 140MB
优化了:346-258,除去基础base镜像140,还是多了
可以看出多的主要在Yum里面111MB,该层主要安装包编译等
[root@docker1 docker]# docker history webserver:v2
IMAGE CREATED CREATED BY SIZE COMMENT
503439033877 6 minutes ago /bin/sh -c #(nop) CMD ["/usr/local/nginx/sb… 0B
c140026256fd 6 minutes ago /bin/sh -c rpmdb --rebuilddb && yum install … 111MB
8b163bc5912e 7 minutes ago /bin/sh -c #(nop) ADD file:a90dc1ecadbd423a5… 6.25MB
b17ef4913f9c 48 minutes ago /bin/sh -c #(nop) COPY file:fe5cdcb63a06bd4c… 67B
82ca7e5c38e6 49 minutes ago /bin/sh -c #(nop) VOLUME [/usr/local/nginx/… 0B
8aa3b0c110c7 49 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
0a3eb3fde7fd 7 years ago 140MB Imported from -
可以看出v1没有优化的时候,光yum代码。
[root@docker1 docker]# docker history webserver:v1
IMAGE CREATED CREATED BY SIZE COMMENT
bdd2829a42e3 44 minutes ago /bin/sh -c #(nop) CMD ["/usr/local/nginx/sb… 0B
9e03306cbffd 44 minutes ago /bin/sh -c yum install make -y 43.6MB
bf1e3007a03b 49 minutes ago /bin/sh -c yum install -y gcc pcre-devel zli… 133MB
源码编译后,真正只需要编译后的二进制代码
所以,我真正只需要/usr/local/nginx的二进制代码只有5M,其余的(中间产物)不需要,再把debug一关不到1M
可以只把操作系统编译好的源码(二进制代码)给其它操作系统直接使用
分两个部分,先在一个容器编译好二进制代码再拷贝给第二个容器使用
将debug注释掉
[root@docker1 nginx-1.18.0]# vim auto/cc/gcc
[root@docker1 nginx-1.18.0]# sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
全部替换完成
将该指令添加到Dockerfile中,并且多阶段构建(将上一个容器编译后的nginx的二进制编码拿来使用)
as build是给上面起名build
COPY --from=build /usr/local/nginx /usr/local/nginx 将上一个容器的二进制编码目录(/usr/local/nginx)放在下一个容器的/usr/local/nginx
暴露端口和卷都不是必须的所以删掉了,编一个最精简的Dockerfile
[root@docker1 docker]# cat Dockerfile
FROM rhel7 as build
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.18.0.tar.gz /mnt
RUN rpmdb --rebuilddb && yum install -y gcc pcre-devel zlib-devel make && cd /mnt/nginx-1.18.0 && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx && make && make install && rm -fr /mnt/nginx-1.18.0 /var/cache/yum
FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@docker1 docker]# docker build -t webserver:v3 .
除去140MB的基础镜像base,一个nginx 1MB
[root@docker1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v3 5cd6dd1fdc7f 31 seconds ago 141MB
webserver v2 503439033877 35 minutes ago 258MB
webserver v1 bdd2829a42e3 About an hour ago 346MB
docker 能运行肯定nginx没问题,有问题CMD都报错了
[root@docker1 docker]# docker run -d --name nginx webserver:v3
[root@docker1 docker]# docker container inspect nginx | grep "IPAddress"
"IPAddress": "172.17.0.2",
[root@docker1 docker]# curl 172.17.0.2
<h1>Welcome to nginx!</h1>
再优化都不可能比base镜像小
所以,在优化需要寻找更加精简的base,通过https://hub.docker.com/寻找或www.github.com或www.gitee.com上面寻找
nginx在运行时候,会调用系统的动态库文件
库文件必不缺的,缺了二进制文件就起不来了。
所以在拷贝二进制文件目录的时候把库文件也要考入,同时编译的nginx不同调用的库文件也不同,加了opensll还有lib-opensll库,所以如果换取更小的镜像的话,需要把库文件和二进制文件同时迁移。同时还要考虑这些库文件有无依赖性还要调用其他的库文件。
[root@docker1 docker]# docker run -it --rm webserver:v3 bash
bash-4.2# ldd /usr/local/nginx/sbin/nginx
linux-vdso.so.1 => (0x00007ffc06f6d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f3560c8a000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f3560a6e000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f3560837000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f35605d6000)
libz.so.1 => /lib64/libz.so.1 (0x00007f35603c0000)
libc.so.6 => /lib64/libc.so.6 (0x00007f355ffff000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3560e8e000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007f355fd80000)
在github上面搜索distroless,这个是谷歌的容器工具。
下载
我用的是debian10,导入镜像。
[root@docker1 docker]# docker load -i base-debian10.tar
这个base镜像小
[root@docker1 docker]# docker images
gcr.io/distroless/base-debian10 latest d48fcdd54946 52 years ago 19.2MB
首先在github搜索distroless nginx,进去
然后查看别人写好的Dockerfile,可以看出用的是官方提供的最小镜像并且是多阶段构建,以及将系统的库文件考入。
编写Dockerfile2
[root@docker1 docker]# cat Dockerfile2
FROM nginx:1.18.0 as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE
RUN mkdir -p /opt/var/cache/nginx && \
cp -a --parents /usr/lib/nginx /opt && \
cp -a --parents /usr/share/nginx /opt && \
cp -a --parents /var/log/nginx /opt && \
cp -aL --parents /var/run /opt && \
cp -a --parents /etc/nginx /opt && \
cp -a --parents /etc/passwd /opt && \
cp -a --parents /etc/group /opt && \
cp -a --parents /usr/sbin/nginx /opt && \
cp -a --parents /usr/sbin/nginx-debug /opt && \
cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime
FROM gcr.io/distroless/base-debian10
COPY --from=base /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx", "-g", "daemon off;"]
构建镜像,-f指定dockerfile2,要不然读取的是默认的Dockerfile
[root@docker1 docker]# docker build -t webserver:v4 -f Dockerfile2 .
效果
[root@docker1 docker]# docker images webserver
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v4 0fce27d5ac56 15 seconds ago 31.7MB
运行容器,运行成功。
[root@docker1 docker]# docker run -d --name nginx webserver:v4
[root@docker1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
79bc3e329d8f webserver:v4 "nginx -g 'daemon of…" 23 seconds ago Up 22 seconds 80/tcp, 443/tcp nginx
[root@docker1 docker]# docker inspect nginx | grep IP
"IPAddress": "172.17.0.2",
访问成功
[root@docker1 docker]# curl 172.17.0.2
<h1>Welcome to nginx!</h1>
END
在拉取镜像的时候通过官网(国外)拉取慢
所以通过阿里云的镜像加速器修改docker默认仓库位置后拉取镜像速度就快了
访问阿里云官网:www.aliyun.com
点击控制台
搜索容器,选择容器镜像服务
[root@server9 docker]# docker info
Registry Mirrors:
https://qwfrb104.mirror.aliyuncs.com/
Live Restore Enabled: false
END
默认使用的是官方的仓库,官方仓库的网址:https://hub.docker.com
可以在docker官方的公共仓库上面注册自己的账号并且分享自己的封装好的镜像
从私有仓库拉取以及上传的原理:
docker客户端通过Indx认证后,index给docker拉取仓库位置(仓库有很多个),以及token,docker访问仓库并给仓库token,仓库用docker客户端的token与index校验,校验成功后同意docker客户端拉取。
从私有仓库拉取以及上传的原理流程图
删除完镜像后,registry还要告诉indx要同步信息,在索引里面删掉该镜像。
END
在生产环境中,你的服务器可能不能上网
在内网集群里面,布置容器大量应用的时候,成千上万的,体量很大,分布密度比传统的虚拟化大很多,分布在不同节点上面跑,从远端仓库拉取很消耗网络资源和时间,所以必须在内网里面布置一个私有仓库,让其部署效率变快。
官方文档网址:https://docs.docker.com/registry/deploying/
在宿主机上面拉取registry镜像
[root@docker1 docker]# docker pull registry
重要信息
[root@docker1 docker]# docker history registry
<missing> 2 months ago /bin/sh -c #(nop) EXPOSE 5000 0B
<missing> 2 months ago /bin/sh -c #(nop) VOLUME [/var/lib/registry] 0B
–restart=always开机自启动
[root@docker1 ~]# docker run -d -p 5000:5000 --restart=always --name registry registry
向仓库上传自己的镜像,要先将镜像改名成仓库地址和端口加镜像名称,后面不加版本便为最新版
[root@docker1 ~]# docker tag webserver:v4 localhost:5000/webserver
上传
[root@docker1 ~]# docker push localhost:5000/webserver
删掉本地的镜像
[root@docker1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
webserver v4 0fce27d5ac56 47 hours ago 31.7MB
localhost:5000/webserver latest 0fce27d5ac56 47 hours ago 31.7MB
[root@docker1 ~]# docker rmi webserver:v4
Untagged: webserver:v4
[root@docker1 ~]# docker rmi localhost:5000/webserver:latest
Untagged: localhost:5000/webserver:latest
Untagged: localhost:5000/webserver@sha256:e1fcc49060c0bfcabaf933ce8ca297fc4df63b80113f5098d931f9fac65464fe
Deleted: sha256:0fce27d5ac5666a7fef70f3ad01b64177509acf11495c42ff85ef392b68e979f
Deleted: sha256:0bd5d2b3dad5cbee261f44fbe012ed0d0809990d75503463fb5d4ec17a582149
Deleted: sha256:5ee35935b3b95e4a02f7dde3541e235eb6d67421bd52157d29e13b2055dff9c2
Deleted: sha256:502cc226f58a8c7b0adf7a06b92a2d5156d08d7af7b21bc6e4d6a69
从仓库拉取镜像
[root@docker1 ~]# docker pull localhost:5000/webserver
[root@docker1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost:5000/webserver latest 0fce27d5ac56 47 hours ago 31.7MB
开启另一个虚拟机docker2,安装上docker就行,并且启动
[root@docker2 yum.repos.d]# yum install -y docker-ce
[root@docker2 yum.repos.d]# systemctl enable docker
没有ip,所以修改内核参数,docker1修改过,把内核文件复制给docker2就可以了
[root@docker2 yum.repos.d]# docker info
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
[root@docker1 sysctl.d]# scp docker.conf docker2:/etc/sysctl.d/
[root@docker2 yum.repos.d]# sysctl --system
docker2 拉取docker1仓库的镜像,默认仓库是不支持非加密访问远端仓库。
[root@docker2 sysctl.d]# docker info
Insecure Registries:
127.0.0.0/8
[root@docker2 sysctl.d]# docker pull 172.25.254.1:5000/webserver
Using default tag: latest
Error response from daemon: Get https://172.25.254.1:5000/v2/: http: server gave HTTP response to HTTPS client
[root@docker2 docker]# vim daemon.json
[root@docker2 docker]# cat daemon.json
{
"insecure-registries" : ["172.25.254.1:5000"]
}
[root@docker2 docker]# systemctl daemon-reload
[root@docker2 docker]# systemctl restart docker
支持非安全的仓库,本地的localhost和docker1的5000端口
[root@docker2 docker]# docker info
Insecure Registries:
172.25.254.1:5000
127.0.0.0/8
拉取成功
[root@docker2 docker]# docker pull 172.25.254.1:5000/webserver
[root@docker2 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
172.25.254.1:5000/webserver latest 0fce27d5ac56 47 hours ago 31.7MB
实验环境:
[root@docker1 sysctl.d]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bcb7e7682a99 registry "/entrypoint.sh /etc…" 53 minutes ago Up 53 minutes 0.0.0.0:5000->5000/tcp registry
[root@docker1 sysctl.d]# docker rm -f registry
registry
容器删掉了,但是卷存储了容器的操作,对存储没有影响
[root@docker1 sysctl.d]# docker volume ls
DRIVER VOLUME NAME
local 70f0dede14b7c1978edb38eb49534509f1fcc205e750bb365254b75677b76005
local a780ebed1de6be544711f18a254682fc4f018ce2f7058b0a23f6f16a3eb73171
local ab43717053b89c0a594289ab002bbc48ab9144075c88e5a230b78cb24250b305
local b76dc83d8ac88ed49f725bd014ec89e97c4f7b3cc2b75a803e9c66af1b5c101e
local f266d47b2c99eadd824c8d31ce921b03db558b0817b6bb20e36858e5000f8363
删掉本地没有被容器使用的卷
[root@docker1 sysctl.d]# docker volume prune
[root@docker1 sysctl.d]# docker volume ls
DRIVER VOLUME NAME
启动认证功能,首先开启其加密功能,保证认证的网络通信是加密的,不然alice截获,便成明文了,没有意义了。
读官方文档
要使用证书,所以我的证书是自签名的证书,在https://docs.docker.com/registry/insecure/有相关文档
在生产环境中的证书是从CA申请过来的,所以测试我用自签名证书
创建存放证书的目录,在linux中所以证书是通过openssl来运行的
[root@docker1 ~]# mkdir -p certs
[root@docker1 ~]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/yan.org.key -x509 -days 365 -out certs/yan.org.crt
Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:shanxi
Locality Name (eg, city) [Default City]:xi'an
Organization Name (eg, company) [Default Company Ltd]:yan
Organizational Unit Name (eg, section) []:linux
Common Name (eg, your name or your server's hostname) []:reg.yan.org 仓库的域名
Email Address []:
证书创建成功
[root@docker1 ~]# ls certs/
yan.org.crt yan.org.key
-v手动挂接,将生成的/certs挂入容器的/certs
容器启动443端口加密
由于官方的,没有加入卷,到时候删掉仓库容器后,还得重新上传镜像,麻烦,所以我多加了一条指令
在宿主机/opt/registry路径(自动帮忙生成,如果有直接挂载)挂载到/var/lib/registry这个目录上面(在容器内是仓库存放镜像的位置)
-v /opt/registry:/var/lib/registry
[root@docker1 ~]# docker run -d --restart=always --name registry -v "$(pwd)"/certs:/certs -v /opt/registry:/var/lib/registry -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/yan.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/yan.org.key -p 443:443 registry
后面所有镜像保存在该位置,可以在宿主机里面做持久化
[root@docker1 ~]# ll -d /opt/registry/
drwxr-xr-x 2 root root 6 Feb 6 21:10 /opt/registry/
[root@docker1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da3da9151ec0 registry "/entrypoint.sh /etc…" 2 minutes ago Up About a minute 0.0.0.0:443->443/tcp, 5000/tcp registry
[root@docker1 ~]# docker port registry
443/tcp -> 0.0.0.0:443
远程连接:
在docker2加入解析docker1的仓库
[root@docker2 docker]# sudo vim /etc/hosts
远程上传:
默认端口是443可以不用加
[root@docker2 docker]# docker tag nginx:latest reg.yan.org/nginx
[root@docker2 docker]# docker push reg.yan.org/nginx
The push refers to repository [reg.yan.org/nginx]
Get https://reg.yan.org/v2/: x509: certificate signed by unknown authority
缺少证书,需要添加证书,让docker引擎识别到证书
所有客户端都需要证书,一旦启动加密功能以后
后面端口不用加,默认是443,加个:443也可以
只要访问reg.yan.org这个仓库,它会自动去这个路经找证书,证书是docker1创建的
[root@docker2 docker]# mkdir /etc/docker/certs.d
[root@docker2 docker]# cd certs.d/
[root@docker2 certs.d]# mkdir reg.yan.org
/etc/docker/certs.d/reg.yan.org
[root@docker1 ~]# cd certs/
[root@docker1 certs]# ls
yan.org.crt yan.org.key
[root@docker1 certs]# scp yan.org.crt docker2:/etc/docker/certs.d/reg.yan.org/ca.crt
[root@docker2 reg.yan.org]# ls
ca.crt
有了证书,远程上传成功
[root@docker2 reg.yan.org]# docker push reg.yan.org/nginx
上传的镜像会保存到/opt/registry持久化了,实际是上传到容器内/var/lib/registry,但是你实际是把宿主机的目录挂在/var/lib/registry,所以会持久化
在官方文档https://docs.docker.com/registry/deploying/
官方是通过运行httpd镜像容器,然后使用htpasswd加密生成用户加密文件,所以我们安装了http-tools,不需要运行容器来加密,麻烦。
调用了htpasswd工具生成用户名密码的认证文件,这是http中的工具
[root@docker1 ~]# mkdir auth
[root@docker1 ~]# cd auth/
[root@docker1 auth]# yum install -y httpd-tools
[root@docker1 auth]# htpasswd --help
-B -b -n 都有
-B Force bcrypt encryption of the password (very secure).
-n Don't update file; display results on stdout.
-b Use the password from the command line rather than prompting for it.
-C Set the computing time used for the bcrypt algorithm
(higher is more secure but slower, default: 5, valid: 4 to 31).
第一次要加-c,第二次再使用就第一次的就被覆盖掉了
[root@docker1 auth]# cat htpasswd
ywq:$2y$05$fKzYdI2UoQqofccJO0F.VutAC4Akv7z9LdPoH7G2I134KxLD1l4fy
zrx:$2y$05$c77YlrPXP1oz6M6LRwydyOrywlVnvT28GGQWGwniscZe58F21mF6C
[root@docker1 ~]# docker rm -f registry
将认证目录挂载到容器内的/auth,告诉要启动htpasswd认证方式,读取该认证文件,修改仓库参数
–restart=always随着docker引擎的启动,该registry容器启动
[root@docker1 ~]# docker run -d --restart=always --name registry -v "$(pwd)"/certs:/certs -v /opt/registry:/var/lib/registry -v "$(pwd)"/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/yan.org.crt -e REGISTRY_HTTP_TLS_KEY=/certs/yan.org.key -p 443:443 registry
832e2bdb222d16ac3c25521d1a8420d48c4e4d6ce71028217f47a142d7709d01
远程上传拉取(缺少认证):
[root@docker2 reg.yan.org]# docker push reg.yan.org/nginx
no basic auth credentials
[root@docker2 reg.yan.org]# docker pull reg.yan.org/nginx
Using default tag: latest
Error response from daemon: Get https://reg.yan.org/v2/nginx/manifests/latest: no basic auth credentials
[root@docker2 reg.yan.org]# docker login reg.yan.org
Username: ywq
Password:
Login Succeeded
成功后会存储到这个文件
[root@docker2 ~]# cd .docker/
[root@docker2 .docker]# ls
config.json
[root@docker2 .docker]# cat config.json
{
"auths": {
"reg.yan.org": {
"auth": "eXdxOndlc3Rvcw=="
}
},
"HttpHeaders": {
"User-Agent": "Docker-Client/19.03.15 (linux)"
}
}
[root@docker2 .docker]# docker logout reg.yan.org
Removing login credentials for reg.yan.org
[root@docker2 .docker]# cat config.json
{
"auths": {},
"HttpHeaders": {
"User-Agent": "Docker-Client/19.03.15 (linux)"
}
}
所以只要就登陆一次就记录了,不需要重复登陆,除非你注销了。
认证后,远程上传成功
[root@docker2 .docker]# docker push reg.yan.org/nginx
END
harbor仓库作为企业级仓库:提供了镜像扫描、病毒扫描、签名
harbor仓库每次启动时会开启许多容器,可以提供web操作页面
docker-compose负责单个节点控制多个容器
软件下载位置:https://github.com/goharbor/harbor/releases
解压后里面的压缩包是所有harbor仓库的镜像,其余的是配置文件
[root@docker1 /]# tar zxf harbor-offline-installer-v1.10.1.tgz
[root@docker1 /]# cd harbor/
[root@docker1 harbor]# ls
common.sh harbor.v1.10.1.tar.gz harbor.yml install.sh LICENSE prepare
修改harbor配置文件
[root@docker1 harbor]# vim harbor.yml
[root@docker1 harbor]# mkdir /data
[root@docker1 harbor]# cp -r /root/certs/ /data/
[root@docker1 harbor]# vim harbor.yml
默认会开启80和443去发布,但是80是默认端口,如果443开启了会重定向到443
主机名必须与仓库生成证书的主机名保持一致
reg.yan.org就是服务器docker1,添加域名方便访问
设置一个管理员登陆的密码
安装harbor,提醒缺少docker-compose依赖性
[root@docker1 harbor]# ./install.sh
✖ Need to install docker-compose(1.18.0+) by yourself first and run this script again.
[root@docker1 /]# mv docker-compose-Linux-x86_64-1.27.0 /usr/local/bin/docker-compose
[root@docker1 /]# chmod +x /usr/local/bin/docker-compose
查看一下安装的参数
[root@docker1 harbor]# ./install.sh --help
--with-notary 镜像签名,只有签名过的镜像能部署,如果没有签名,拉取镜像是失败的(镜像安全)
--with-clair 镜像扫描器,扫描镜像里面的漏洞与缺陷
--with-chartmuseum k8s中的软件包管理,可以存放镜像还可以存放k8s的软件包。k8s有个应用ham应用专门把软件打成软件包来进行维护
最小化安装
[root@docker1 harbor]# ./install.sh
安装好以后,之后的管理都在/harbor(安装harbor的目录)交给docker-compose来管理
docker-compose会在/harbor读取.yml文件,它会通过脚本解析harbor.yml来生成docker-compose.yml
[root@docker1 harbor]# ls
docker-compose.yml harbor.yml
因为一共开启了9个容器,不好管理,也不知道容器之间的关系,所以用docker-compose来管理
[root@docker1 harbor]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
56f638dce977 goharbor/nginx-photon:v1.10.1 "nginx -g 'daemon of…" 19 seconds ago Up 12 seconds (health: starting) 0.0.0.0:80->8080/tcp, 0.0.0.0:443->8443/tcp nginx
8d7514279f86 goharbor/harbor-jobservice:v1.10.1 "/harbor/harbor_jobs…" 19 seconds ago Up 14 seconds (health: starting) harbor-jobservice
eba94647a312 goharbor/harbor-core:v1.10.1 "/harbor/harbor_core" 21 seconds ago Up 18 seconds (health: starting) harbor-core
85c0de195cd0 goharbor/harbor-db:v1.10.1 "/docker-entrypoint.…" 28 seconds ago Up 25 seconds (health: starting) 5432/tcp harbor-db
ca1121fe1d31 goharbor/registry-photon:v2.7.1-patch-2819-2553-v1.10.1 "/home/harbor/entryp…" 28 seconds ago Up 20 seconds (health: starting) 5000/tcp registry
89cf045cb2ea goharbor/redis-photon:v1.10.1 "redis-server /etc/r…" 28 seconds ago Up 23 seconds (health: starting) 6379/tcp redis
807ff92faad0 goharbor/harbor-registryctl:v1.10.1 "/home/harbor/start.…" 28 seconds ago Up 22 seconds (health: starting) registryctl
1fb2951ae681 goharbor/harbor-portal:v1.10.1 "nginx -g 'daemon of…" 28 seconds ago Up 20 seconds (health: starting) 8080/tcp harbor-portal
604de693ca95 goharbor/harbor-log:v1.10.1 "/bin/sh -c /usr/loc…" 30 seconds ago Up 28 seconds (health: starting) 127.0.0.1:1514->10514/tcp harbor-log
[root@docker1 harbor]# docker ps -aq
56f638dce977
8d7514279f86
eba94647a312
85c0de195cd0
ca1121fe1d31
89cf045cb2ea
807ff92faad0
1fb2951ae681
604de693ca95
[root@docker1 harbor]# docker ps -aq | wc -l
9
docker-compose管理
[root@docker1 harbor]# docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------
harbor-core /harbor/harbor_core Up (healthy)
harbor-db /docker-entrypoint.sh Up (healthy) 5432/tcp
harbor-jobservice /harbor/harbor_jobserv Up (healthy)
ice ...
harbor-log /bin/sh -c Up (healthy) 127.0.0.1:1514->10514
/usr/local/bin/ ... /tcp
harbor-portal nginx -g daemon off; Up (healthy) 8080/tcp
nginx nginx -g daemon off; Up (healthy) 0.0.0.0:80->8080/tcp,
0.0.0.0:443->8443/tcp
redis redis-server Up (healthy) 6379/tcp
/etc/redis.conf
registry /home/harbor/entrypoin Up (healthy) 5000/tcp
t.sh
registryctl /home/harbor/start.sh Up (healthy)
如果在启动的时候有容器没有启动起来
[root@docker1 harbor]# docker-compose start
Starting log ... done
Starting registry ... done
Starting registryctl ... done
Starting postgresql ... done
Starting portal ... done
Starting redis ... done
Starting core ... done
Starting jobservice ... done
Starting proxy ... done
使用web管理harbor
这个仓库,library是默认项目(仓库分类),可以匿名拉取的
给仓库上传镜像,先给解析
[root@docker1 harbor]# cat /etc/hosts
172.25.254.1 docker1 reg.yan.org
证书有问题给docker1添加一个证书
[root@docker1 harbor]# docker login reg.yan.org
Username: admin
Password:
Error response from daemon: Get https://reg.yan.org/v2/: x509: certificate signed by unknown authority
[root@docker1 harbor]# cd /etc/docker/
[root@docker1 docker]# mkdir certs.d
[root@docker1 docker]# cd certs.d/
[root@docker1 certs.d]# mkdir reg.yan.org
[root@docker1 certs.d]# cd reg.yan.org/
[root@docker1 reg.yan.org]# cp /data/certs/yan.org.crt ca.crt
所有harbor仓库的数据都持久化到/data了,添加不同的模块这个目录就记录下来了,可以看出harbor用了registry官方镜像
[root@docker1 reg.yan.org]# cd /data/
[root@docker1 data]# ls
ca_download certs database job_logs psc redis registry secret
[root@docker1 data]# docker login reg.yan.org
Username: admin
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
上传成功,上传的时候必须添加仓库的url+仓库的项目+镜像的名字+版本号
不加版本号的话,签名是不生效的,签名只针对你的标签(版本号)
[root@docker1 data]# docker tag busybox:latest reg.yan.org/library/busybox:latest
[root@docker1 data]# docker push reg.yan.org/library/busybox:latest
便可以在日志查看到上传成功
新建一个私有的项目
向私有项目上传镜像
[root@docker1 data]# docker tag yakexi007/game2048:latest reg.yan.org/yan/game2048:latest
[root@docker1 data]# docker push reg.yan.org/yan/game2048:latest
远程用户拉取本地仓库
由于docker2的指向仓库不是reg.yan.org所以拉取必须要加仓库名字,并且给docker2加解析拉取变快
[root@docker2 ~]# docker info
[root@docker2 ~]# docker pull reg.yan.org/library/busybox:latest
可以查看日志,匿名用户(docker2)拉取镜像了
太麻烦了,拉取镜像还得加仓库名字
修改docker2仓库定向(和阿里云加速器重定向修改一样,把阿里云加速器的地址换成自己的仓库的地址就行了)
[root@docker2 ~]# cd /etc/docker/
[root@docker2 docker]# vim daemon.json
[root@docker2 docker]# cat daemon.json
{
"registry-mirrors": ["https://reg.yan.org"]
}
[root@docker2 docker]# systemctl daemon-reload
[root@docker2 docker]# systemctl restart docker
[root@docker2 docker]# docker info
重定向生效
直接到本地仓库的默认library拉取,默认情况都会找library项目
[root@docker2 docker]# docker pull busybox
需要认证,因为不是公开的,不支持匿名下载。
[root@docker2 docker]# docker pull yan/game2048:latest
Error response from daemon: pull access denied for yan/game2048, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
创建用户
将用户添加到yan(私有仓库里)
拉取成功,并且私有仓库不暴露对外,不加本地仓库名字这个时候是去docker官方仓库拉取
[root@docker2 docker]# docker login reg.yan.org
Username: ywq
Password:
Login Succeeded
[root@docker2 docker]# docker pull yan/game2048:latest
[root@docker2 docker]# docker pull reg.yan.org/yan/game2048:latest
latest: Pulling from yan/game2048
534e72e7cedc: Pull complete
f62e2f6dfeef: Pull complete
fe7db6293242: Pull complete
3f120f6a2bf8: Pull complete
4ba4e6930ea5: Pull complete
Digest: sha256:8a34fb9cb168c420604b6e5d32ca6d412cb0d533a826b313b190535c03fe9390
Status: Downloaded newer image for reg.yan.org/yan/game2048:latest
reg.yan.org/yan/game2048:latest
[root@docker2 docker]# docker pull yan/game2048:latest
Error response from daemon: pull access denied for yan/game2048, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
扫描配合是否阻止潜在漏洞镜像来使用
官方文档地址:https://goharbor.io/docs/1.10/working-with-projects/working-with-images/pulling-pushing-images/
先停掉所有容器,并且删掉
由于之前导入过镜像,我们只删掉容器了,没有删镜像,所有这次加载快,因为镜像都导入过了
[root@docker1 docker]# cd /harbor
[root@docker1 harbor]# docker-compose stop
[root@docker1 harbor]# docker-compose rm
Are you sure? [yN] y
[root@docker1 harbor]# ./install.sh --with-notary --with-clair --with-chartmuseum
[root@docker1 harbor]# docker ps -aq|wc -l
14
这一次容器更多了,添加功能了
扫描镜像
项目配置里面有自动扫描,但是最好不开
它是会自动扫描的,每扫描一次相当于在虚拟机里面做了变更,虚拟机是属于copy-on-write的机制
它的一个子镜像,每次在子镜像做一次扫描,它的数据是变更的,一旦变更后它会大量复制母镜像的数据,只要会导致你子镜像越来越大,很快就塞满磁盘了,我的虚拟机设置是20G,一旦到了20G这个子镜像就不能使用了。这个扫描功能是你服务重启的时候,比如你重启你的虚拟机,这个扫描就会重新扫描一遍
[kiosk@foundation38 Pictures]$ cd /var/lib/libvirt/images/
[kiosk@foundation38 images]$ qemu-img info docker3
image: docker3
file format: qcow2
virtual size: 20G (21474836480 bytes)
disk size: 52M
cluster_size: 65536
backing file: new.qcow2
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
Helm Charts在k8s,由于在k8s要写大量的配置文件,为了便于管理Helm Charts将配置文件打包管理,类似操作系统的yum
删除镜像时候要垃圾清理,不然实际不会删掉的
镜像的签名部署
部署根证书,在/etc/docker,docker引擎就能识别到
开启docker内容信任变量后,上传下载镜像都要认证
[root@docker1 ~]# cd .docker/
[root@docker1 .docker]# ls
config.json
[root@docker1 .docker]# mkdir -p tls/reg.yan.org:4443/
[root@docker1 .docker]# cd tls/
[root@docker1 tls]# cd reg.yan.org\:4443/
[root@docker1 reg.yan.org:4443]# cp /etc/docker/certs.d/reg.yan.org/ca.crt .
[root@docker1 reg.yan.org:4443]# export DOCKER_CONTENT_TRUST=1
[root@docker1 reg.yan.org:4443]# export DOCKER_CONTENT_TRUST_SERVER=https://reg.yan.org:4443
开启认证功能后,必须带标签,因为签名就看标签
两个密码,如果镜像名字变更了,root key变更,镜像名不变,版本号变更,根key就不需要输入
每次只是变更v1 v2版本标签,你只需要输入仓库密码就行
[root@docker1 reg.yan.org:4443]# docker push reg.yan.org/library/busybox:latest
Enter passphrase for new root key with ID c9c5ba5:
Repeat passphrase for new root key with ID c9c5ba5:
Enter passphrase for new repository key with ID 38e3345:
Repeat passphrase for new repository key with ID 38e3345:
Successfully signed reg.yan.org/library/busybox:latest
远程部署:没有签名不能部署
[root@docker2 reg.yan.org]# docker pull reg.yan.org/library/webserver:v3
Error response from daemon: unknown: The image is not signed in Notary.
签名
[root@docker1 reg.yan.org:4443]# docker push reg.yan.org/library/webserver:v3
Enter passphrase for root key with ID c9c5ba5:
Enter passphrase for new repository key with ID 8b13ceb:
Repeat passphrase for new repository key with ID 8b13ceb:
Finished initializing "reg.yan.org/library/webserver"
Successfully signed reg.yan.org/library/webserver:v3
[root@docker2 reg.yan.org]# docker pull reg.yan.org/library/webserver:v3
删除签名
[root@docker1 reg.yan.org:4443]# docker trust revoke reg.yan.org/library/webserver:v3
Enter passphrase for repository key with ID 8b13ceb:
Successfully deleted signature for reg.yan.org/library/webserver:v3
重新安装harbor签名和认证功能不加了,加了消耗内存,只加chartmuseum为了以后的k8s实验
[root@docker1 reg.yan.org:4443]# cd /harbor/
[root@docker1 harbor]# docker-compose stop
[root@docker1 harbor]# docker-compose rm
[root@docker1 harbor]# unset DOCKER_CONTENT_TRUST 取消内容信任
[root@docker1 harbor]# ./install.sh --with-chartmuseum
但是不是所有的东西都被删除了,认证还有
[root@docker1 ~]# cd /data/
[root@docker1 data]# ls
ca_download chart_storage job_logs redis secret
certs database psc registry
如果要变更harbor的yml文件,在安装的时候要./prepare,重新生成docker-compose.yml文件
[root@docker1 data]# cd /harbor/
[root@docker1 harbor]# ls
docker-compose.yml harbor.yml
[root@docker1 harbor]# vim harbor.yml
[root@docker1 harbor]# ./prepare
END
本文章写的是单机的多容器之间网络通信以及跨主机容器的网络通信
删掉没有使用的网络,prune这个参数是docker每个指令都有的。
[root@docker1 harbor]# docker network prune
Are you sure you want to continue? [y/N] y
docker 原生网络 bridge host null ,默认情况用的是桥接
[root@docker1 harbor]# docker network ls
NETWORK ID NAME DRIVER SCOPE
8db80f123dd8 bridge bridge local
b51b022f8430 host host local
c386dbae12f0 none null local
docker0是当前这个主机所有容器的网关,容器地址在默认分配上是单调递增的,第一个是0.2,第二个是0.3…
当容器停掉后,它的ip资源就会被释放出来,然后分配给其它容器,ip地址在容器里面是会变的是动态的
首先安装一个工具,这个工具是来操作桥接口的,桥接网卡掉了后,可以通过这个工具加上去
[root@docker1 harbor]# yum install bridge-utils.x86_64 -y
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242b9a15b22 no
veth7c468c6是容器的虚拟网卡,容器桥接上去了,一头在容器内网络,一头在docker0
[root@docker1 harbor]# docker run -d --name nginx nginx
[root@docker1 harbor]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f28a0d105547 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 80/tcp nginx
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242b9a15b22 no veth7c468c6
虚拟网卡docker0上是39,容器里面可能就是40,一个网线的两端
veth7c468c6@if39:
[root@docker1 harbor]# ip addr show
40: veth7c468c6@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether aa:23:2b:fe:e9:94 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a823:2bff:fefe:e994/64 scope link
valid_lft forever preferred_lft forever
[root@docker1 harbor]# docker run -d --name nginx2 nginx
f640de1d58fe3628540618fc03aa8f4fe4259c55fec6d22eb41b741b94b6ae24
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242b9a15b22 no veth5af9c0d
veth7c468c6
ip是单调递增的
[root@docker1 harbor]# ip addr show docker0
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
所以
[root@docker1 harbor]# curl 172.17.0.2
<title>Welcome to nginx!</title>
[root@docker1 harbor]# curl 172.17.0.3
<h1>Welcome to nginx!</h1>
容器通过nat可以访问外网,开启了地址伪装MASQUERADE
容器通过虚拟网络对桥接到docker0上面,docker0通过eth0(打开内核路由功能 ipv4=1)来发送外网
[root@docker1 harbor]# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
默认是桥接,所以要用–network参数
[root@docker1 harbor]# docker run -d --name demo --network host nginx
里面只有一个虚拟网卡,host不会生成虚拟网卡,直接使用宿主机的网络就有公有ip了,外网可以访问容器,但是需要避免端口冲突,避免容器之间和容器与宿主机间的冲突
[root@docker1 harbor]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b9e64aaa49a nginx "/docker-entrypoint.…" About a minute ago Up About a minute demo
f28a0d105547 nginx "/docker-entrypoint.…" 14 minutes ago Up 14 minutes 80/tcp nginx
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242b9a15b22 no veth7c468c6
host网络模式
[root@docker1 harbor]# curl 172.25.254.1
<title>Welcome to nginx!</title>
端口冲突,启动不了
[root@docker1 harbor]# docker run -d --name demo2 --network host nginx
[root@docker1 harbor]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b9e64aaa49a nginx "/docker-entrypoint.…" 9 minutes ago Up 9 minutes demo
f28a0d105547 nginx "/docker-entrypoint.…" 21 minutes ago Up 21 minutes 80/tcp nginx
[root@docker1 harbor]# docker logs demo2
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
host直连不需要nat,但是
禁用网络模式,不给容器ip地址
[root@docker1 harbor]# docker run -d --name demo2 --network none nginx
[root@docker1 harbor]# docker run -it --rm --network none busybox
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
有些容器保存比较机密的东西如密码散列,不需要网络
overlay是互联网常用,三大运营商常用macvlan(底层硬件方式去解决网络跨主机通信)
自定义bridge可以自己固定某个容器的ip,而bridge在容器使用时会自动分配一个ip如1,关闭后重启,可能就不是ip1变成ip2等等。
自定义bridge还可以自定义ip网段和子网地址等
创建自定义网桥,默认自定义网络是bridge
[root@docker1 harbor]# docker network --help
[root@docker1 harbor]# docker network create my_net1
[root@docker1 harbor]# docker network ls
NETWORK ID NAME DRIVER SCOPE
8db80f123dd8 bridge bridge local
b51b022f8430 host host local
18fb73b3c2f5 my_net1 bridge local
c386dbae12f0 none null local
harbor也是自己创建的仓库,因为它可以自动DNS解析容器名称到IP,所以容器用bridge网桥的时候,建议使用自己创建的
不加网络参数默认会桥接到原生的bridge中
[root@docker1 harbor]# docker run -d --name web1 nginx
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
br-18fb73b3c2f5 8000.024231c9d512 no
docker0 8000.0242b9a15b22 no veth2e47b03
[root@docker1 harbor]# docker run -d --name web2 --network my_net1 nginx
[root@docker1 harbor]# brctl show
bridge name bridge id STP enabled interfaces
br-18fb73b3c2f5 8000.024231c9d512 no veth231ba38
docker0 8000.0242b9a15b22 no veth2e47b03
这个容器默认是原生bridge,是不提供解析的
[root@docker1 harbor]# docker run -it --rm busybox
ping: bad address 'web1'
/ # ping 172.17.0.2
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.059 ms
/ # ping web2
ping: bad address 'web2'
自定义网桥功能
通过DNS解析来通信
[root@docker1 harbor]# docker run -it --rm --network my_net1 busybox
/ # ping web2
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.120 ms
/ # ping web1
ping: bad address 'web1'
ping 不同web1,因为自定义的网段和docker0网段不同
[root@docker1 harbor]# ip addr show
43: br-18fb73b3c2f5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:31:c9:d5:12 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-18fb73b3c2f5
valid_lft forever preferred_lft forever
inet6 fe80::42:31ff:fec9:d512/64 scope link
valid_lft forever preferred_lft forever
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
容器的ip是动态的,用ip通信不行,ip是动态变更的,用容器名字来通信
[root@docker1 harbor]# docker stop web2
[root@docker1 harbor]# docker run -d --name web3 --network my_net1 nginx
[root@docker1 harbor]# docker start web2
[root@docker1 harbor]# docker run -it --rm --network my_net1 busybox
/ # ping web2
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.051 ms
[root@docker1 harbor]# docker network create --help
[root@docker1 harbor]# docker network create --subnet 172.20.0.0/24 --gateway 172.20.0.1 my_net2
3043c2a41e35d3a32a7a649fcaa3b4b0b2d3700121751b9a63415b6d31308bae
[root@docker1 harbor]# docker network ls
NETWORK ID NAME DRIVER SCOPE
8db80f123dd8 bridge bridge local
b51b022f8430 host host local
18fb73b3c2f5 my_net1 bridge local
3043c2a41e35 my_net2 bridge local
c386dbae12f0 none null local
[root@docker1 harbor]# ip addr show 生成桥接口了
58: br-3043c2a41e35: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:4f:90:95:90 brd ff:ff:ff:ff:ff:ff
inet 172.20.0.1/24 brd 172.20.0.255 scope global br-3043c2a41e35
valid_lft forever preferred_lft forever
[root@docker1 harbor]# docker run -d --name web4 --network my_net2 --ip 172.20.0.100 nginx
[root@docker1 harbor]# docker inspect web4
[root@docker1 harbor]# docker stop web4
[root@docker1 harbor]# docker start web4
[root@docker1 harbor]# docker inspect web4
必须是自定义的网段上面才能–ip
my_net1没有自定义网段
[root@docker1 harbor]# docker run -d --name web5 --network my_net1 --ip 172.18.0.100 nginx
docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.
docker为了安全,所以隔离不同的network,查看它的策略都是DROP丢弃的
不同网桥的容器通信不要调整它的策略这样会破坏它的隔离性
[root@docker1 ~]# iptables -L
Chain DOCKER-ISOLATION-STAGE-2 (3 references)
target prot opt source destination
DROP all -- anywhere anywhere
DROP all -- anywhere anywhere
DROP all -- anywhere anywhere
RETURN all -- anywhere anywhere
web3 my_net1 web4 my_net2,让其互通
给web3添加一块my_net1的网卡
[root@docker1 ~]# docker network connect my_net1 web4
[root@docker1 ~]# docker inspect web3
Joined容器:
共享一个容器的网络,两个不同容器可以通过localhost高速通信
比如lamp架构,nginx mysql php可以不同容器部署运行,但是通过localhost访问,只是通过不同的端口访问,比如mysql3306,php9000,nginx80。
web3是my_net1
[root@docker1 ~]# docker inspect web3
"Gateway": "172.18.0.1",
共享web3的ip地址
[root@docker1 ~]# docker pull radial/busyboxplus
[root@docker1 ~]# docker tag radial/busyboxplus:latest busyboxplus:latest
[root@docker1 ~]# docker run -it --rm --network container:web3 busyboxplus
/ # ip addr
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
/ # curl localhost
<title>Welcome to nginx!</title>
默认网络没有解析,给默认网路添加DNS解析,当容器重启后,ip变更,它的解析也自动变更。
主机名:别名,可以通信ping主机名也可以通信别名
这种不如直接用自定义的briege里面的DNS
[root@docker1 ~]# docker run -it --rm --link web3:nginx --network my_net1 busyboxplus
/ # ip addr
inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
/ # ping web3
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.057 ms
/ # env
HOSTNAME=ca85afe9651d
SHLVL=1
HOME=/
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
web1桥接的是docker0,env它将变量和解析加进去了
[root@docker1 ~]# docker inspect web1
"IPAddress": "172.17.0.2",
[root@docker1 ~]# docker run -it --rm --link web1:nginx busyboxplus
/ # ping web1
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.060 ms
/ # env
HOSTNAME=01ba3b877905
SHLVL=1
HOME=/
NGINX_ENV_PKG_RELEASE=1~bullseye
NGINX_PORT_80_TCP=tcp://172.17.0.2:80
NGINX_ENV_NGINX_VERSION=1.21.5
NGINX_ENV_NJS_VERSION=0.7.1
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_PORT=tcp://172.17.0.2:80
NGINX_NAME=/sad_benz/nginx
PWD=/
NGINX_PORT_80_TCP_ADDR=172.17.0.2
NGINX_PORT_80_TCP_PORT=80
NGINX_PORT_80_TCP_PROTO=tcp
/ # ping nginx
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.052 ms
/ # 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.2 nginx 2fc55fe8c1fd web1
172.17.0.3 01ba3b877905
加解析了
所以–link不能用于自定义网络,只能用于默认网络。
默认网络没有解析
/ # ping web1
ping: bad address 'web1'
并且它的解析会随着容器ip地址的变更而变更,但是它写入的变量env不会变更
将web1关闭启动变更它的ip,它的解析也变更了
/ # cat /etc/hosts
172.17.0.2 nginx 2fc55fe8c1fd web1
/ # cat /etc/hosts
172.17.0.4 nginx 2fc55fe8c1fd web1
数据如何进来以及如何出去
每加一个,开启一个虚拟伪装
[root@docker1 ~]# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.20.0.0/24 0.0.0.0/0
docker0与真实网卡,通过Linux内核实现数据包的路由
双冗余,一个通过iptable火墙转发,一种通过进程代理来实现,两种存在一个都可以生效(外网访问容器)
docker prox不能完全替代iptable DNAT因为转发还要用iptable
清空容器
[root@docker1 ~]# docker ps -aq
81cd079f1e08
f7d480524524
e305ecfdc237
7b5f010de330
ddcac54b82da
2fc55fe8c1fd
[root@docker1 ~]# docker rm -f `docker ps -aq`
当访问本机的80会重定向到容器的80端口
[root@docker1 ~]# docker run -d --name demo -p 80:80 nginx
[root@docker1 ~]# iptables -t nat -nL
Chain DOCKER (2 references)
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
[root@docker1 ~]# curl 172.25.254.1
<title>Welcome to nginx!</title>
专门的进程,监听80端口,双冗余
[root@docker1 ~]# netstat -antlp
tcp6 0 0 :::80 :::* LISTEN 7569/docker-proxy
证明外部访问容器是双冗余
宿主机直接走的网关docker0 ping通
[root@docker1 ~]# iptables -t nat -F
[root@docker1 ~]# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
Chain DOCKER (0 references)
target prot opt source destination
因为都在一个网桥不需要火墙和prox进程
/ # ping 172.17.0.2
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.079 ms
[root@docker1 ~]# ps ax
7569 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -contai
[root@docker1 ~]# kill 7569
/ # ping 172.17.0.2
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.065 ms
外部就不通了
没有火墙和proxy关了,外部进不来了
[kiosk@foundation38 Desktop]$ curl 172.25.254.1
curl: (7) Failed to connect to 172.25.254.1 port 80: Connection refused
关闭火墙外部数据还是能进入容器,通过docker proxy
[root@docker1 ~]# systemctl restart docker
[root@docker1 ~]# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.20.0.0/24 0.0.0.0/0
[root@docker1 ~]# docker start demo
[root@docker1 ~]# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.20.0.0/24 0.0.0.0/0
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:80
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
[kiosk@foundation38 Desktop]$ curl 172.25.254.1
<title>Welcome to nginx!</title>
[root@docker1 ~]# ps ax | grep proxy
8496 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
8572 pts/1 S+ 0:00 grep --color=auto proxy
[root@docker1 ~]# iptables -t nat -F
[root@docker1 ~]# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
Chain DOCKER (0 references)
target prot opt source destination
[kiosk@foundation38 Desktop]$ curl 172.25.254.1
<title>Welcome to nginx!</title>
关闭进程,留着火墙
[root@docker1 ~]# systemctl restart docker
[root@docker1 ~]# docker start demo
[root@docker1 ~]# ps ax | grep proxy
8896 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
8971 pts/1 R+ 0:00 grep --color=auto proxy
[root@docker1 ~]# kill 8896
[kiosk@foundation38 Desktop]$ curl 172.25.254.1
<title>Welcome to nginx!</title>
所以外部访问容器是双冗余机制
清理环境
[root@docker1 ~]# docker network prune
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
my_net1
my_net2
[root@docker1 ~]# docker rm -f demo
[root@docker1 ~]# docker volume prune
Are you sure you want to continue? [y/N] y
[root@docker1 ~]# docker image prune
删掉只有名字,id是none,用不了的镜像,其实是镜像的缓存
实验环境
[root@docker1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
3811ae4f8bf9 bridge bridge local
b51b022f8430 host host local
c386dbae12f0 none null local
[root@docker2 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
568aa16db709 bridge bridge local
20d505b4b669 host host local
09ed8d8227c2 none null local
overlay是docker swarm 使用,这次先用macvlan
第三方是k8s使用
docker官方给你网络库和模型了,你根据自己需要的网络自己开发
Endpoint相当于虚拟网卡
macvlan是底层解决,硬件来解决
给docker1和docker2各添加一个桥接在eth0的网卡
激活新加入网卡的混杂模式
[root@docker1 ~]# ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
46: enp7s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
[root@docker1 ~]# ip link set enp7s0 promisc on
[root@docker1 ~]# ip addr
46: enp7s0: <BROADCAST,MULTICAST,PROMISC> mtu 1500 qdisc noop state DOWN group default qlen 1000
[root@docker1 ~]# ip link set up enp7s0
[root@docker1 ~]# ip addr
46: enp7s0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
[root@docker2 ~]# ip link set enp7s0 promisc on
[root@docker2 ~]# ip link set up enp7s0
[root@docker2 ~]# ip addr
4: enp7s0: <NO-CARRIER,BROADCAST,MULTICAST,PROMISC,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
不能让两个ip冲突,两个网卡可以分配ip都为0.1,但是,因为是在一个vlan中,ip不要冲突。
[root@docker1 ~]# docker network create -d macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.1 -o parent=enp7s0 mynet1
[root@docker1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
3811ae4f8bf9 bridge bridge local
b51b022f8430 host host local
9e1d71bae46c mynet1 macvlan local
c386dbae12f0 none null local
[root@docker1 ~]# docker run -it --rm --network mynet1 --ip 10.0.0.10 busyboxplus
/ # ip addr
inet 10.0.0.10/24 brd 10.0.0.255 scope global eth0
[root@docker2 ~]# docker network create -d macvlan --subnet 10.0.0.0/24 --gateway 10.0.0.1 -o parent=enp7s0 mynet1
[root@docker2 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
568aa16db709 bridge bridge local
20d505b4b669 host host local
dd46171171cb mynet1 macvlan local
09ed8d8227c2 none null local
[root@docker2 ~]# docker run -it --rm --network mynet1 --ip 10.0.0.11 busybox
/ # ip addr
inet 10.0.0.11/24 brd 10.0.0.255 scope global eth0
/ # ping 10.0.0.10
64 bytes from 10.0.0.10: seq=0 ttl=64 time=0.814 ms
/ # ping 10.0.0.11
64 bytes from 10.0.0.11: seq=0 ttl=64 time=0.284 ms
通过底层的硬件去解决跨主机通信,由于容器是成千上万,可能需要很多网段需要大量的网卡,硬件可以插多少网卡呢?
没有新建linux bridge和虚拟网卡
[root@docker1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c27a3cac no
[root@docker1 ~]# ip addr
46: enp7s0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
[root@docker1 ~]# ip link set enp7s0 down
/ # ping 10.0.0.10
不通
[root@docker1 ~]# ip link set enp7s0 up
64 bytes from 10.0.0.10: seq=29 ttl=64 time=1002.343 ms
无需NAT和端口映射,所有这种macvlan只要把底层的物理链路通了,剩下的不用管了,docker不管
[root@docker2 ~]# docker run -d --name demo --network mynet1 --ip 10.0.0.11 webserver:v3
/ # curl 10.0.0.11
<title>Welcome to nginx!</title>
[root@docker2 ~]# netstat -antlp
[root@docker2 ~]# iptables -nL -t nat
没有DNAT,没有端口映射
由于不能接无限个网卡,所以可以创建子接口(.1 .2.3.4…)依此类推
二层
[root@docker2 ~]# docker network create -d macvlan --subnet 20.0.0.0/24 --gateway 20.0.0.1 -o parent=enp7s0.1 mynet2
[root@docker1 ~]# docker network create -d macvlan --subnet 20.0.0.0/24 --gateway 20.0.0.1 -o parent=enp7s0.1 mynet2
[root@docker1 ~]# docker run -d --network mynet2 nginx
[root@docker1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e91e508fdd2b nginx "/docker-entrypoint.…" 28 seconds ago Up 26 seconds practical_chandrasekhar
[root@docker1 ~]# docker inspect e91e508fdd2b
"IPAddress": "20.0.0.2",
[root@docker1 ~]# docker network create -d macvlan --subnet 30.0.0.0/24 --gateway 30.0.0.1 -o parent=enp7s0.2 mynet3
要使得二层通信,可以在三层做路由网关,将macvlan网络联通起来
inspect查看具体信息无论网络、卷、容器、镜像这些参数相通用的
[root@docker1 ~]# docker network inspect mynet2
[root@docker1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
3811ae4f8bf9 bridge bridge local
b51b022f8430 host host local
9e1d71bae46c mynet1 macvlan local
7450c8721d84 mynet2 macvlan local
2642e999f12a mynet3 macvlan local
c386dbae12f0 none null local
END
:以前宿主机路径,:以后容器路径
如果存在自动挂载,如果不存在默认会创建
对于镜像来说存储是一种需求,也可以通过这样的方式(挂载)修改容器的配置
/usr/share/nginx/html是Nginx默认的程序发布目录(数据挂载上)
[root@docker1 /]# mkdir /webdata
[root@docker1 /]# docker run -d --name demo -v /webdata:/usr/share/nginx/html nginx
[root@docker1 /]# cd /webdata/
[root@docker1 webdata]# vim index.html
[root@docker1 webdata]# cat index.html
www.yan.org
[root@docker1 webdata]# curl 172.17.0.2
www.yan.org
[root@docker1 webdata]# docker inspect demo
[root@docker1 webdata]# docker exec -it demo bash
root@a1f6892bc233:/# cd /usr/share/nginx/html/
root@a1f6892bc233:/usr/share/nginx/html# ls
index.html
root@a1f6892bc233:/usr/share/nginx/html# cat index.html
www.yan.org
/etc/nginx是nginx的配置文件(配置文件挂载)
[root@docker1 webdata]# docker rm -f demo
需要配置文件
[root@docker1 webdata]# mkdir conf
[root@docker1 webdata]# mkdir html
[root@docker1 webdata]# mv index.html html/
[root@docker1 webdata]# cd conf/
进去容器看看配置文件
[root@docker1 conf]# docker run -it --rm nginx bash
root@d775f75c01df:/# cd /etc/nginx/
root@d775f75c01df:/etc/nginx# ls
conf.d mime.types nginx.conf uwsgi_params
fastcgi_params modules scgi_params
root@d775f75c01df:/etc/nginx# cat nginx.conf
发现里面只有全局的http
root@d775f75c01df:/etc/nginx# cd conf.d/
root@d775f75c01df:/etc/nginx/conf.d# ls
default.conf
root@d775f75c01df:/etc/nginx/conf.d# cat default.conf
[root@docker1 conf]# vim www.conf
[root@docker1 conf]# cat www.conf
server {
listen 80;
server_name www.yan.org;
location / {
root /html;虚拟主机的发布目录(容器)
index index.html;
}
}
[root@docker1 conf]# cd ..
[root@docker1 webdata]# cd html/
[root@docker1 html]# ls
index.html
[root@docker1 ~]# docker run -d --name demo -v /webdata/html:/html -v /webdata/conf/www.conf:/etc/nginx/conf.d/www.conf:ro -p 80:80 nginx
[root@docker1 ~]# vim /etc/hosts
172.25.254.1 docker1 reg.yan.org www.yan.org
访问域名www.yan.org,容器占用宿主机的80端口,所以请求会重定向到容器内。
[root@docker1 ~]# curl www.yan.org
www.yan.org
访问宿主机的80会重定向到容器里面
[root@docker1 ~]# ps ax
[root@docker1 ~]# iptables -t nat -nL
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
[root@docker1 ~]# cd /webdata/
[root@docker1 webdata]# ls
conf html
[root@docker1 webdata]# cd html/
[root@docker1 html]# vim index.html
[root@docker1 html]# cat index.html
www.yan.org
123456789
[root@docker1 html]# curl www.yan.org
www.yan.org
123456789
nginx默认没有定义卷的挂载
[root@docker1 volumes]# docker history nginx:latest
IMAGE CREATED CREATED BY SIZE COMMENT
605c77e624dd 6 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 6 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 6 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 6 weeks ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB
<missing> 6 weeks ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB
<missing> 6 weeks ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB
<missing> 6 weeks ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB
<missing> 6 weeks ago /bin/sh -c set -x && addgroup --system -… 61.1MB
<missing> 6 weeks ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.1 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.5 0B
<missing> 7 weeks ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 7 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
registry定义了
[root@docker1 volumes]# docker history registry:latest
IMAGE CREATED CREATED BY SIZE COMMENT
<missing> 3 months ago /bin/sh -c #(nop) VOLUME [/var/lib/registry] 0B
_data就会被挂接到容器内
[root@docker1 docker]# cd /var/lib/docker/volumes/
[root@docker1 volumes]# ls
metadata.db
[root@docker1 volumes]# docker volume ls
DRIVER VOLUME NAME
local 489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9
[root@docker1 volumes]# ls
489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9 metadata.db
root@docker1 volumes]# cd 489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9/
[root@docker1 489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9]# ls
_data
[root@docker1 489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9]# cd _data/
[root@docker1 _data]# ls
由于自动生成的卷名字很长,而且看不出对应关系
删掉容器卷还保留着
[root@docker1 _data]# docker rm -f registry
registry
[root@docker1 _data]# docker volume ls
DRIVER VOLUME NAME
local 489ec21e4aa78de9ca13565361142459a460b1beb7f64124714299d4d4b964e9
[root@docker1 _data]# docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
[root@docker1 volumes]# cd /var/lib/docker/volumes/
[root@docker1 volumes]# ls
metadata.db
[root@docker1 volumes]# docker volume create registry
[root@docker1 _data]# docker volume ls
DRIVER VOLUME NAME
local registry
[root@docker1 volumes]# ls
metadata.db registry
[root@docker1 volumes]# docker run -d --name registry -v registry:/var/lib/registry registry
[root@docker1 volumes]# docker inspect registry
特点:
-v不是docker manager 的话(bind mount )是用宿主机内的数据覆盖掉容器内的,docker manager卷的话会把容器的挂载点里原有的文件考入物理节点上,持久化
[root@docker1 volumes]# docker volume create vol1
[root@docker1 volumes]# ls
metadata.db vol1
[root@docker1 volumes]# cd vol1/
[root@docker1 vol1]# ls
_data
[root@docker1 vol1]# cd _data/
[root@docker1 _data]# ls
[root@docker1 _data]# docker run -d --name nginx -v vol1:/usr/share/nginx/html nginx
[root@docker1 _data]# ls
50x.html index.html
mind mount是覆盖的(没有该目录自动创建),不会考入原有容器挂载点内的数据
[root@docker1 mnt]# cd /yan
-bash: cd: /yan: No such file or directory
[root@docker1 mnt]# docker run -d --name nginx2 -v /yan:/usr/share/nginx/html nginx
[root@docker1 ~]# docker run -d --name nginx4 nginx:latest
[root@docker1 ~]# docker exec -it nginx4 bash
root@f1177ba608b8:/# cd /usr/share/nginx/html/
root@f1177ba608b8:/usr/share/nginx/html# ls
50x.html index.html
[root@docker1 mnt]# cd /yan
[root@docker1 yan]# ls
[root@docker1 yan]# docker exec -it nginx2 bash
root@be477783aa8a:/# cd /usr/share/nginx/html/
root@be477783aa8a:/usr/share/nginx/html# ls
root@be477783aa8a:/usr/share/nginx/html#
host是主机
只能是目录,所以更改文件必须加-v重新写个
可以实现跨主机共享
客户端把请求给docker引擎,引擎再连接插件(存储),插件再连接底层的nfs.
插件就相当于一个接口,引擎不能直接连接Nfs。
Daemon docker引擎
/etc/docker/plugins/这个路径就是docker引擎缺省的扫描路径
直接使用nfs(网络文件共享系统),-v挂接也也可以,但是你需要写路径,不同主机路径不一样,不好操作,所以直接用插件便用使用
跨主机共享
将/mnt/nfs输出出去
底层文件nfs共享系统搭建
[root@docker1 ~]# mkdir /mnt/nfs
[root@docker1 ~]# vim /etc/exports
[root@docker1 ~]# cat /etc/exports
/mnt/nfs *(rw,sync)
[root@docker1 ~]# chmod 777 /mnt/nfs/
[root@docker1 ~]# ll -d /mnt/nfs/
drwxrwxrwx 2 root root 6 Feb 14 21:45 /mnt/nfs/
[root@docker1 ~]# yum install nfs-utils
[root@docker1 ~]# systemctl start nfs
[root@docker1 ~]# showmount -e
Export list for docker1:
/mnt/nfs *
[root@docker2 ~]# mkdir /mnt/nfs
[root@docker2 ~]# yum install nfs-utils -y
[root@docker2 ~]# mount 172.25.254.1:/mnt/nfs/ /mnt/nfs/
[root@docker2 ~]# cd /mnt/nfs/
[root@docker2 nfs]# ls
[root@docker2 nfs]# touch file
[root@docker2 nfs]# ls
file
[root@docker1 ~]# cd /mnt/nfs/
[root@docker1 nfs]# ls
file
[root@docker1 nfs]# rm -f file
[root@docker1 nfs]# ls
[root@docker2 nfs]# ls
安装插件
解压后里面就两个二进制程序,将它移动到/usr/local/bin/底下调用就行了
/etc/docker/plugins/这个路径就是docker引擎缺省的扫描路径(docker引擎会自动扫描这个路径上面的插件)
[root@docker1 ~]# ls
auth certs convoy.tar.gz docker
[root@docker1 ~]# tar zxf convoy.tar.gz
[root@docker1 convoy]# ls
convoy convoy-pdata_tools SHA1SUMS
[root@docker1 convoy]# mkdir -p /etc/docker/plugins/
启动插件服务
[root@docker1 convoy]# convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs &
[root@docker1 convoy]# ps ax
11173 pts/0 Sl 0:00 convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs
这个进程在后端
插件sock文件路径,只有程序启动后,sock才出现
[root@docker1 convoy]# cd /var/run/
[root@docker1 run]# cd convoy/
[root@docker1 convoy]# ls
convoy.sock
把sock路径写入docker缺省路径里面
[root@docker1 convoy]# echo "unix:///var/run/convoy/convoy.sock" > /etc/docker/plugins/convoy.spec
[root@docker1 convoy]# cat /etc/docker/plugins/convoy.spec
unix:///var/run/convoy/convoy.sock
给服务器2添加该插件
[root@docker2 ~]# ls
convoy.tar.gz
[root@docker2 ~]# tar zxf convoy.tar.gz
[root@docker2 convoy]# mv convoy* /usr/local/bin/
[root@docker2 convoy]# mkdir -p /etc/docker/plugins/
[root@docker2 convoy]# convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs &
[root@docker2 convoy]# cd /var/run/convoy/
[root@docker2 convoy]# ls
convoy.sock
[root@docker2 convoy]# echo "unix:///var/run/convoy/convoy.sock" > /etc/docker/plugins/convoy.spec
插件使用
[root@docker1 ~]# convoy --help
[root@docker1 ~]# convoy create vol1
[root@docker1 ~]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:15:16 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:15:16 +0800 2022",
"VolumeName": "vol1"
},
"Snapshots": {}
}
}
提示权限错误了
[root@docker2 convoy]# convoy list
{}
no_root_squash 超级用户挂载的时候不要去变更权限,要不然就变成nfs nobody
[root@docker1 ~]# vim /etc/exports
[root@docker1 ~]# cat /etc/exports
/mnt/nfs *(rw,no_root_squash)
[root@docker1 ~]# showmount -e
Export list for docker1:
/mnt/nfs *
[root@docker1 ~]# exportfs -rv 刷新
exporting *:/mnt/nfs
[root@docker2 config]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:15:16 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:15:16 +0800 2022",
"VolumeName": "vol1"
},
"Snapshots": {}
}
}
docker卷可以自动识别到
[root@docker2 config]# docker volume ls
DEBU[0707] Handle plugin activate: POST /Plugin.Activate pkg=daemon
DEBU[0707] Response: {
"Implements": [
"VolumeDriver"
]
} pkg=daemon
DEBU[0707] Handle plugin list volume: POST /VolumeDriver.List pkg=daemon
DEBU[0707] event=mountpoint object=volume pkg=daemon reason=prepare volume=vol1
DEBU[0707] event=mountpoint mountpoint= object=volume pkg=daemon reason=complete volume=vol1
DEBU[0707] Successfully got volume list for docker. pkg=daemon
DEBU[0707] Response: {
"Volumes": [
{
"Name": "vol1"
}
]
} pkg=daemon
ERRO[0707] Handler not found: POST /VolumeDriver.Capabilities pkg=daemon
DRIVER VOLUME NAME
convoy vol1
[root@docker2 config]# convoy create vol2
[root@docker1 ~]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:15:16 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:15:16 +0800 2022",
"VolumeName": "vol1"
},
"Snapshots": {}
},
"vol2": {
"Name": "vol2",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:25:28 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol2",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:25:28 +0800 2022",
"VolumeName": "vol2"
},
"Snapshots": {}
}
}
用convoy和docker volume都能操作
[root@docker1 ~]# docker volume rm -f vol2
[root@docker1 ~]# docker volume ls
DEBU[1386] Handle plugin list volume: POST /VolumeDriver.List pkg=daemon
DEBU[1386] event=mountpoint object=volume pkg=daemon reason=prepare volume=vol1
DEBU[1386] event=mountpoint mountpoint= object=volume pkg=daemon reason=complete volume=vol1
DEBU[1386] Successfully got volume list for docker. pkg=daemon
DEBU[1386] Response: {
"Volumes": [
{
"Name": "vol1"
}
]
} pkg=daemon
ERRO[1386] Handler not found: POST /VolumeDriver.Capabilities pkg=daemon
DRIVER VOLUME NAME
convoy vol1
[root@docker1 ~]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:15:16 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:15:16 +0800 2022",
"VolumeName": "vol1"
},
"Snapshots": {}
}
}
插件搭建成功
容器跨主机迁移以及数据跨主机迁移
docker 管理卷可以考出容器内数据
[root@docker1 ~]# docker run -d --name demo -v vol1:/usr/share/nginx/html nginx
[root@docker1 ~]# cd /mnt/nfs/vol1/
[root@docker1 vol1]# ls
50x.html index.html
[root@docker1 vol1]# vim test.html
[root@docker1 vol1]# cat test.html
www.yan.com
yan wei qiu
由于没有做端口映射
[root@docker1 vol1]# curl 172.17.0.2
<title>Welcome to nginx!</title>
[root@docker1 vol1]# curl 172.17.0.2/test.html
www.yan.com
yan wei qiu
正常情况下容器是自动迁移的,集群
[root@docker1 vol1]# docker rm -f demo
现在是手动迁移,容器跨主机迁移,数据也跨主机迁移
[root@docker2 config]# docker run -d --name demo -v vol1:/usr/share/nginx/html nginx
[root@docker2 config]# curl 172.17.0.2/test.html
www.yan.com
yan wei qiu
卸载该插件
[root@docker2 config]# fg
convoy daemon --drivers vfs --driver-opts vfs.path=/mnt/nfs (wd: ~/convoy)
^CCaught signal interrupt: shutting down.
[root@docker2 config]# cd /etc/docker/plugins/
[root@docker2 plugins]# ls
convoy.spec
[root@docker2 plugins]# rm -fr convoy.spec
[root@docker2 plugins]# systemctl stop docker
[root@docker2 plugins]# systemctl daemon-reload
[root@docker2 plugins]# systemctl start docker
不卸载的话,docker重启会慢
[root@docker1 vol1]# convoy list
{}
[root@docker1 vol1]# convoy create vol1
[root@docker1 vol1]# convoy list
{
"vol1": {
"Name": "vol1",
"Driver": "vfs",
"MountPoint": "",
"CreatedTime": "Mon Feb 14 22:47:43 +0800 2022",
"DriverInfo": {
"Driver": "vfs",
"MountPoint": "",
"Path": "/mnt/nfs/vol1",
"PrepareForVM": "false",
"Size": "0",
"VolumeCreatedAt": "Mon Feb 14 22:47:43 +0800 2022",
"VolumeName": "vol1"
},
"Snapshots": {}
}
}
[root@docker1 plugins]# systemctl stop docker
[root@docker1 plugins]# systemctl daemon-reload
[root@docker1 plugins]# systemctl start docker
启动会变慢
卷也看不到,因为docker引擎没有识别
[root@docker1 plugins]# docker volume ls
DRIVER VOLUME NAME
慢是因为有缓存信息
解决
docker所有数据在这里
重启恢复了(之前所有卷的数据也丢失了),所以卷的缓存信息都在元数据里面
[root@docker1 plugins]# cd /var/lib/docker
[root@docker1 docker]# cd volumes/
[root@docker1 volumes]# ls
metadata.db
[root@docker1 volumes]# mv metadata.db /mnt/
[root@docker1 volumes]# ls
[root@docker1 volumes]# systemctl restart docker
可以看出元数据缓存之前的卷,还可以重新将该服务写入docker缺省路径,按照正常的方式关闭
[root@docker1 volumes]# cd /mnt/
[root@docker1 mnt]# ls
metadata.db nfs
[root@docker1 mnt]# cat metadata.db
��
�
�������>[��
�
�z3Ff�l
nvolumes:vol1{"Name":"","Driver":"convoy","Labels":null,"Options":null}
volumes
END