docker阶段03 docker容器hosts文件,DNS, 查docker空间占用, 部署自动化运维平台spug, 查docker run启动参数命令, Dockerfile多阶段构建, 容器数据管理, 容器通讯, 修改容器ip(网桥配置), 网络模型
容器内部的hosts文件
容器会自动将容器的ID加入自已的/etc/hosts文件中,并解析成容器的IP
范例: 修改容器的 hosts文件
[root@ubuntu1804 ~]#docker run -it --rm --add-host www.wangxiaochun.com:6.6.6.6 --add-host www.wang.org:8.8.8.8 busybox / # 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 6.6.6.6 www.wangxiaochun.com 8.8.8.8 www.wang.org 172.17.0.2 449bf0468efd
指定容器 DNS
容器的dns服务器,默认采用宿主机的dns 地址,可以用下面方式指定其它的DNS地址 修改宿主机的DNS地址址配置 在容器启动时加选项 --dns=x.x.x.x 在/etc/docker/daemon.json 文件中指定
查看docker空间磁盘占用情况, 清理不再使用的镜像
#查看docker空间磁盘占用情况 [root@ubuntu ~]#docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 2 0 85.25MB 85.25MB (100%) Containers 0 0 0B 0B Local Volumes 0 0 0B 0B #卷 Build Cache 0 0 0B 0B #清除不再使用的镜像 [root@ubuntu1804 ~]#docker system prune -f -a
实战案例: 利用 Docker 快速部署自动化运维平台
个人开发的小项目
[root@ubuntu1804 ~]# docker pull registry.aliyuncs.com/openspug/spug [root@ubuntu1804 ~]#docker run -d --restart=always --name=spug -p 80:80 registry.aliyuncs.com/openspug/spug #初始化(通过docker内部作者写的init_spug程序) [root@ubuntu1804 ~]#docker exec spug init_spug admin 123456
查看docker run启动参数命令
忘记之前启动一个容器的启动命令是什么,现在需要找回来 docker run 的运行参数,可以使用 runlike 工具实现
https://github.com/lavie/runlike
安装 runlike
#安装方式1: pip apt install -y python3-pip pip3 install runlike #安装方法2: by docker alias runlike="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike" #范例 [root@ubuntu ~]#runlike -p spug #根据返回选有用的启动参数 docker run --name=spug \ --hostname=cbd8c758f8a4 \ --mac-address=02:42:ac:11:00:02 \ --network=bridge \ -p 80:80 \ --restart=always \ --runtime=runc \ --detach=true \ registry.aliyuncs.com/openspug/spug
Dockerfile多阶段构建
属于dockerfile构建的优化,能节省空间
先构建个镜像,然后再构建个镜像把之前镜像生成的文件拷贝过来,前面那个镜像就不要了
go编译完的文件,不需要go就能运行 也就是说构建用一个镜像,运行用另一个镜像 这样可以节省空间 #优化 [root@ubuntu2004 go-hello]#vim Dockerfile FROM golang:1.18-alpine as builder COPY hello.go /opt WORKDIR /opt RUN go build hello.go FROM alpine:3.15.0 #FROM scratch COPY --from=builder /opt/hello /hello #只保留上面镜像的文件 #COPY --from=0 /opt/hello /hello #from=0可以指代前一次构建 CMD ["/hello"] [root@ubuntu2004 go-hello]#bash build.sh v2.0 [root@ubuntu2004 go-hello]#docker run --name hello2 go-hello:v2.0 hello, world #进一步优化 #实际上go编译后依赖内核就能运行,所以可以镜像内不放系统 [root@ubuntu2004 go-hello]#vim Dockerfile FROM golang:1.18-alpine as builder COPY hello.go /opt WORKDIR /opt RUN go build hello.go #FROM alpine:3.15.0 FROM scratch #空镜像 COPY --from=builder /opt/hello /hello #COPY --from=0 /opt/hello /hello CMD ["/hello"] [root@ubuntu2004 go-hello]#bash build.sh v3.0 [root@ubuntu2004 go-hello]#docker run --name hello2 go-hello:v3.0 hello, world #比较三次构建的结果 [root@ubuntu2004 go-hello]#docker images go-hello REPOSITORY TAG IMAGE ID CREATED SIZE go-hello v3.0 6b12922337d4 4 minutes ago 1.76MB go-hello v2.0 9d6b2abc190a 28 seconds ago 7.35MB go-hello v1.0 54c7203e3a60 5 minutes ago 330MB
范例: 多阶段构建 Vue 前端项目
FROM node:14.17.6 AS build COPY . /opt/vue WORKDIR /opt/vue RUN sed -i 's/config.headers/\/\/&/' src/api/request.js RUN npm install && npm run build FROM nginx:1.20.1 COPY --from=build /opt/vue/dist /opt/vue/dist COPY nginx.conf /etc/nginx/nginx.conf CMD ["nginx", "-g","daemon off;"]
1.LowerDir: image 镜像层,即镜像本身,只读 2.UpperDir: 容器的上层,可读写 ,容器变化的数据存放在此处 3.MergedDir: 容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和 upperdir 合并完成 后给容器使用,最终呈现给用户的统一视图 4.WorkDir: 容器在宿主机的工作目录,挂载后内容会被清空,且在使用过程中其内容用户不可见
容器数据持久保存方式
1. 卷(Volume) 2. 绑定挂载(Bind Mount) 3. tmpfs 挂载 #放入内存,做不到真正的持久化(容器停了或删了,数据就没了) #针对性能高要求,又不想真正持久化的时候用这种方式
#范例: tmpfs 挂载 #把容器的/data路径挂载的宿主机的内存文件中(写入/data速度快) [root@ubuntu2204 ~]#docker run -d --tmpfs /data --name test wangxiaochun/pod-test:v0.1
数据卷(data volume)
方式1: 绑定挂载(Bind Mount)
#指定宿主机目录或文件进行映射: -v <宿主机绝对路径的目录或文件>:<容器目录或文件> #将宿主机目录挂载容器目录,两个目录都可自动创建 #如果是文件进行的话,要写路径(相对路径),如: ./nginx.conf:/etc/nginx/nginx.conf
方式2: 匿名卷 (不推荐)
方式3: 命名卷
dockerfile中定义了匿名卷可以接用命名卷的方式启动(就不是匿名卷了)
#命名卷将固定的存放在/var/lib/docker/volumes/<卷名>/_data #注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录 -v <卷名>:<容器目录路径> #可以通过以下命令事先创建,如可没有事先创建卷名,docker run时也会自动创建卷 docker volume create <卷名> #示例: 会创建/var/lib/docker/volumes/vol1/_data目录进行映射 docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx #查看所有卷 docke volume ls
查看数据卷的挂载关系
docker inspect 容器名
看Mounts列
实现
使用数据卷容器
#第一个容器正常挂,后面的容器直接使用第一个容器的(挂几个就是几个),用命令--volumes-from --volumes-from <数据卷容器> 例: #创建一个数据卷容器 Server(即使没起来,也不影响后面镜像使用数据卷容器挂载) [root@ubuntu1804 ~]#docker run -d --name volume-server -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp tomcat-web:app1 #启动多个数据卷容器 Client [root@ubuntu1804 ~]#docker run -d --name client1 --volumes-from volume-server -p 8081:8080 tomcat-web:app1 fe6ce0548dfee924cd39a8d86d5ed0e8ce9ea65323742f1336fa3b002c1b4a8c [root@ubuntu1804 ~]#docker run -d --name client2 --volumes-from volume-server -p 8082:8080 tomcat-web:app1 10397838df9a489d7af2112850d4285bff5c2c262ea05cc9c5fb265af538f2c8
容器间的通信
修改默认docker0网桥的网络配置
默认docker后会自动生成一个docker0的网桥,使用的IP是172.17.0.1/16,可能和宿主机的网段发生冲突, 可以将其修改为其它网段的地址,避免冲突
#当内网地址刚好与网桥地址172.17一样,这时修改 #范例: 将docker0的IP修改为指定IP #方法1 [root@ubuntu1804 ~]#vim /etc/docker/daemon.json [root@ubuntu1804 ~]#cat /etc/docker/daemon.json { "bip": "192.168.100.1/24", "registry-mirrors": ["https://si7y70hh.mirror.aliyuncs.com"] } [root@ubuntu1804 ~]#systemctl restart docker.service #方法2 [root@ubuntu1804 ~]#vim /lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=192.168.100.1/24 [root@ubuntu1804 ~]#systemctl daemon-reload [root@ubuntu1804 ~]#systemctl restart docker.service #注意两种方法不可混用,否则将无法启动docker服务 #验证结果 [root@ubuntu1804 ~]#ip a
docker run 创建容器,可使用--link选项实现容器名称的引用,其本质就是在容器内的/etc/hosts中添加--link后指定的容器的IP和主机名的对应关系,从而实现名称解析 --link list #Add link to another container 格式: docker run --name <容器名称> #先创建指定名称的容器 docker run --link <目标通信的容器ID或容器名称> #再创建容器时引用上面容器的名称 #例 1. 先创建第一个指定容器名称的容器 [root@ubuntu1804 ~]#docker run -it --name server1 --rm alpine:3.11 sh / # cat /etc/hosts 2. 新建第二个容器时引用第一个容器的名称 [root@ubuntu1804 ~]#docker run -it --rm --name server2 --link server1 alpine:3.11 sh #--link server1: 把server1的名称ip地址放到host文件中 / # cat /etc/hosts #查看内部的host文件有server1 ... 172.17.0.2 server1 cdb5173003f5
通过自定义容器别名互联
名称有可能也会变,通过别名连接
docker run --name <容器名称> #先创建指定名称的容器 docker run --name <容器名称> --link <目标容器名称>:"<容器别名1> <容器别名2> ..." #给上面创建的容器起别名,来创建新容器 #范例: 创建第三个容器,引用前面创建的容器,并起别名 [root@ubuntu1804 ~]#docker run -it --rm --name server3 --link server1:server1-alias alpine:3.11 sh / # cat /etc/hosts 172.17.0.2 server1-alias cdb5173003f5 server1 #ping容器名或别名都行 / # ping server1 / # ping server1-alias
网络模式指定
docker会自动开启ip_forward, ip_forward为1。默认是bridge模式
1.Host 模式
效率高, 很少用, 相当于名称空间不隔离了, 容器和宿主机统一ip地址, 端口互相会冲突
2.None 模式
很少用, 没有外部网络
#例 (端口暴露也没用,无法和宿主机通讯) [root@ubuntu1804 ~]#docker run -d --network none --name web1-none nginx-centos7-base:1.6.1
3.Container 模式
容器和容器之间共享名称空间,容器ip地址相同(只是网络不独立,其他像进等都是独立的)
#方法1 [root@ubuntu2004 ~]#docker run -d -p 80:80 --name wordpress -v /data/wordpress:/var/www/html --restart=always wordpress:php7.4-apache [root@ubuntu2004 ~]#docker run --network container:wordpress -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name mysql -d -v /data/mysql:/var/lib/mysql --restart=always mysql:8.0.29-oracle #方法2 #MySQL提前为wordpress暴露端口 [root@ubuntu2004 ~]#docker run -d -p 80:80 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name mysql --restart=always mysql:8.0.29-oracle [root@ubuntu2004 ~]#docker run -d --network container:mysql --name wordpress --restart=always wordpress:php7.4-apache #注意:数据库主机地址为127.0.0.1或wordpress容器的IP,不支持localhost
4.自定义网络模式
本质是是bridge,但是是自定义网络
独有特性: 自定义网络可以通过容器名进行通讯
创建自定义网络: docker network create -d <mode> --subnet <CIDR> --gateway <网关> <自定义网络名称> #注意mode不支持host和none,默认是bridge模式 -d <mode> 可省略,默认为bridge #引用自定义网络 docker run --network <自定义网络名称> <镜像名称> docker run --net <自定义网络名称> --ip <指定静态IP> <镜像名称> #注意:静态IP只支持自定义网络模型 #指定自定义网络中的容器的别名 docker run --network <自定义网络名称> --network-alias list <镜像名称> #删除自定义网络 doccker network rm <自定义网络名称或网络ID> #例:创建自定义的网络 [root@ubuntu1804 ~]#docker network create -d bridge --subnet 172.27.0.0/16 -- gateway 172.27.0.1 test-net c90dee3b7937e007ed31a8d016a9e54c0174d0d26487b154db0aff04d9016d5b [root@ubuntu1804 ~]#docker network ls NETWORK ID NAME DRIVER SCOPE cabde0b33c94 bridge bridge local cb64aa83626c host host local 10619d45dcd4 none null local c90dee3b7937 test-net bridge local [root@ubuntu1804 ~]#docker inspect test-net [root@ubuntu1804 ~]#ip a #新加了一个网桥 [root@ubuntu1804 ~]#brctl show bridge name bridge id STP enabled interfaces br-00ab0f2d29e8 8000.024245e647ec no docker0 8000.0242cfd26f0a no #利用自定义的网络创建容器 [root@ubuntu1804 ~]#docker run -it --rm --network test-net alpine sh
同一个宿主机之间不同网络的容器通信
开两个容器,一个使用自定义网络容器,一个使用默认brideg网络的容器,默认因iptables规则导致无法通信
本质是iptables导致的
#通过解决docker network connect 实现同一个宿主机不同网络的容器间通信 #将CONTAINER连入指定的NETWORK中,使此CONTAINER可以与NETWORK中的其它容器进行通信 docker network connect [OPTIONS] NETWORK CONTAINER #本质上相当于又创建了一张网卡,ip在对方网段里 #例 #让默认网络中容器test1可以连通自定义网络test-net的容器test2 [root@ubuntu1804 ~]#docker network connect test-net test1 #让自定义网络中容器test2可以连通默认网络的容器test1 [root@ubuntu1804 ~]#docker network connect bridge test2
实现跨宿主机的容器之间网络互联
保证两个宿主机的容器网段不同, 了解原理, 生产中使用k8s解决