一、Docker 容器连接
前面我们实现了通过网络端口来访问运行在 docker 容器内的服务。 容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。 下面我们来实现通过端口连接到一个 docker 容器。
1.创建了一个 python 应用的容器
[root@a ~]# docker run -d -P training/webapp python app.py 52e8351ab9bc513105faf89ed5b5085eb04bb7ab7a7f147c4764058bf5ad4b54 我们也可以使用 -p 标识来指定容器端口绑定到主机端口。 两种方式的区别是: -P :是容器内部端口随机映射到主机的高端口。 -p : 是容器内部端口绑定到指定的主机端口。 [root@a ~]# docker run -d -p 5000:5000 training/webapp python app.py ed817fe4fbd66deec4ba606be2b8195461c1074f79e4d524967c579dee1720f9 [root@a ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ed817fe4fbd6 training/webapp "python app.py" 7 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp wizardly_lewin 52e8351ab9bc training/webapp "python app.py" 5 minutes ago Up 5 minutes 0.0.0.0:32768->5000/tcp stoic_chatterjee
2.指定容器绑定的网络地址
[root@a ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4ceb424eff78 training/webapp "python app.py" 15 seconds ago Up 15 seconds 127.0.0.1:5001->5000/tcp interesting_rhodes ed817fe4fbd6 training/webapp "python app.py" About a minute ago Up About a minute 0.0.0.0:5000->5000/tcp wizardly_lewin 52e8351ab9bc training/webapp "python app.py" 7 minutes ago Up 7 minutes 0.0.0.0:32768->5000/tcp stoic_chatterjee
3.如果要绑定UDP扩展,可以在端口后面加上/ udp
[root@a ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0156a6e359be training/webapp "python app.py" 10 seconds ago Up 10 seconds 5000/tcp, 127.0.0.1:5000->5000/udp dreamy_shaw 4ceb424eff78 training/webapp "python app.py" 13 minutes ago Up 13 minutes 127.0.0.1:5001->5000/tcp interesting_rhodes ed817fe4fbd6 training/webapp "python app.py" 14 minutes ago Up 14 minutes 0.0.0.0:5000->5000/tcp wizardly_lewin 52e8351ab9bc training/webapp "python app.py" 20 minutes ago Up 20 minutes 0.0.0.0:32768->5000/tcp stoic_chatterjee
4.docker port命令可以让我们快捷地查看端口的绑定情况。
[root@a ~]# docker port interesting_rhodes 5000 127.0.0.1:5001
5.Docker容器互联
进入映射并非唯一把docker连接到另一个容器的方法。 docker有一个连接系统允许将多个容器连接在一起,共享连接信息。 docker连接会创建一个父子关系,其中父容器可以看到子容器的信息。 容器命名 当我们创建一个容器的时候,docker会自动对它进行命名。另外,我们也可以使用--name标识来命名容器,例如: [root@a ~]# docker run -d -P --name caq training/webapp python app.py 5ccfa6094c72beca0f1bc28b460efd48d096721788648b6056d61b7b51d62c7d [root@a ~]# docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5ccfa6094c72 training/webapp "python app.py" 10 seconds ago Up 9 seconds 0.0.0.0:32769->5000/tcp caq 新建网络 下面先创建一个新的Docker网络。 [root@a ~]# docker network create -d bridge test-net d517093f0f3460bfa34a88544ad4f264b591b1849a0a4cd07332540a1fb89f4d [root@a ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 0923e7b2d104 bridge bridge local 7c3d205b3fbf host host local f74c9f9eb6ad none null local d517093f0f34 test-net bridge local -d:参数指定Docker网络类型,有网桥,覆盖。 连接容器 运行一个容器并连接到新建的test-net网络: [root@a ~]# docker run -itd --name test1 --network test-net ubuntu /bin/bash [root@a ~]# docker run -itd --name test2 --network test-net ubuntu /bin/bash root@1267f1a3c916:/# ping test2 PING test2 (172.18.0.2) 56(84) bytes of data. 64 bytes from test2.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.365 ms 64 bytes from test2.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.133 ms ^C --- test2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 0.133/0.249/0.365/0.116 ms [root@a ~]# docker exec -it test1 /bin/bash root@1267f1a3c916:/# ping test2 PING test2 (172.18.0.2) 56(84) bytes of data. 64 bytes from test2.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.291 ms 64 bytes from test2.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.131 ms 64 bytes from test2.test-net (172.18.0.2): icmp_seq=3 ttl=64 time=0.209 ms 64 bytes from test2.test-net (172.18.0.2): icmp_seq=4 ttl=64 time=0.143 ms ^C --- test2 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3002ms rtt min/avg/max/mdev = 0.131/0.193/0.291/0.065 ms [root@a ~]# docker exec -it test2 /bin/bash root@007730f40eae:/# ping test1 PING test1 (172.18.0.3) 56(84) bytes of data. 64 bytes from 1267f1a3c916 (172.18.0.3): icmp_seq=1 ttl=64 time=0.062 ms 64 bytes from 1267f1a3c916 (172.18.0.3): icmp_seq=2 ttl=64 time=0.092 ms 64 bytes from 1267f1a3c916 (172.18.0.3): icmp_seq=3 ttl=64 time=0.081 ms 64 bytes from 1267f1a3c916 (172.18.0.3): icmp_seq=4 ttl=64 time=0.096 ms 64 bytes from 1267f1a3c916 (172.18.0.3): icmp_seq=5 ttl=64 time=0.100 ms ^C --- test1 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4001ms rtt min/avg/max/mdev = 0.062/0.086/0.100/0.014 ms
6.配置 DNS
我们可以在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS: { "dns" : ["dns" : [ "114.114.114.114","114.114.114.114", "8.8.8.8""8.8.8.8" ]] }}
7.手动指定容器的配置
如果只想在指定的容器设置 DNS,则可以使用以下命令: [root@a ~]# docker run -it --rm -h ubuntu:15.10 --dns=114.114.114.114 --dns-search=test.com ubuntu:15.10 root@ubuntu:15:/# cat /etc/hostname ubuntu:15.10 root@ubuntu:15:/# 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 ubuntu:15.10 ubuntu:15 root@ubuntu:15:/# cat /etc/resolv.conf search test.com nameserver 114.114.114.114
8.参数说明:
- -h HOSTNAME 或者 --hostname=HOSTNAME: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。
- --dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
- --dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。
二、Dockerfile
什么是 Dockerfile?
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。 Docker执行Dockerfile的大致流程: 1、docker从基础镜像运行一个容器 2、执行一条指令并对容器做出修改 3、执行类似docker commit的操作提交一个新的镜像层 4、docker再基于刚提交的镜像运行一个新容器 5、执行dockerfile中的下一条指令直到所有指令都执行完成
使用 Dockerfile 定制镜像
定制一个 nginx 镜像
(构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件)
[root@a ~]# mkdir Dockerfile [root@a ~]# cd Dockerfile/ [root@a Dockerfile]# vi Dockerfile [root@a Dockerfile]# cat Dockerfile FROM nginx run echo "aaaa" > /usr/share/nginx/html/index.html
FROM 和 RUN 指令的作用
FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。 RUN:用于执行后面跟着的命令行命令。有以下俩种格式: shell 格式: RUN <命令行命令> #<命令行命令> 等同于,在终端操作的 shell 命令。 exec 格式: RUN ["可执行文件", "参数1", "参数2"] #例如: #RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline 注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如: FROM centos RUN yum install wget RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" RUN tar -xvf redis.tar.gz 以上执行会创建 3 层镜像。可简化为以下格式: FROM centos RUN yum install wget \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \ && tar -xvf redis.tar.gz 如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。
开始构建镜像
在 Dockerfile 文件的存放目录下,执行构建动作。 以下示例,通过目录下的 Dockerfile 构建一个 nginx:test(镜像名称:镜像标签)。 注:最后的 . 代表本次执行的上下文路径 [root@a Dockerfile]# docker build -t nginx:v1 .
上下文路径
上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。 如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。 注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢
保留关键字
FROM 基础镜像,当前镜像是基于那个镜像的 MAINTAINER 镜像维护者的姓名和邮件地址 RUN 容器构建时需要运行的命令 EXPOSE 当前容器对外暴露的端口 WORKDIR 工作目录,一个落脚点 ENY 用来构建镜像过程中设置环境变量 ADD 复制解压指令,从上下文目录中复制文件或者目录到容器里指定路径并解压。 COPY 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。 ----------------------------------------------------- CMD 类似于 RUN 指令,用于运行程序,但二者运行的时间点不同: CMD 在docker run 时运行。 RUN 是在 docker build。 作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。 注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。 格式: CMD <shell 命令> CMD ["<可执行文件或命令>","<param1>","<param2>",...] CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数 推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。 ENTRYPOINT 类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。 但是, 如果运行 docker run 时使用了 --entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。 优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。 注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。 格式: ENTRYPOINT ["<executeable>","<param1>","<param2>",...] 可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。 ----------------------------------------------- ONBUILD 当构建一个被继承的Dockerfile时运行命令,父镜像在被子镜像继承后父镜像的onbuild被触发
三、实例
实例1
更改进入容器后的根目录 启动容器时自动安装vim工具和net-tools 开放80端口 启动新的的shell进程 输出环境变量 输出successful 流程: 1、从docker hub上拉取centos镜像(我是在阿里云拉的) [root@docker1 dockerfile]# docker pull centos 2、编写dockerfile [root@docker1 dockerfile]# vim Dockerfile FROM centos ENV MYPATH /usr/local WORKDIR $MYPATH RUN yum install -y vim RUN yum install -y net-tools EXPOSE 80 CMD echo $MYPATH CMD echo sucess----------------------------ok CMD /bin/bash 3、建立容器 [root@docker1 dockerfile]# docker build -f /dockerfile/Dockerfile -t mycentos:1.3 . 4、启动容器 [root@docker1 dockerfile]# docker run -it mycentos:1.3 [root@ec8778c010e4 local]# pwd /usr/local [root@ec8778c010e4 local]# vim test.txt [root@ec8778c010e4 local]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet) RX packets 8 bytes 656 (656.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 可以看到容器能运行vim命令和ifconfig命令了 5、查看容器历史 [root@docker1 dockerfile]# docker history mycentos:1.3 IMAGE CREATED CREATED BY SIZE COMMENT 288eb5f68eab 3 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c " "/bin… 0B add473bae6fa 3 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c " "echo… 0B c218e96faf77 3 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c " "echo… 0B 3431b7fd8281 3 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B 399f38e480aa 3 minutes ago /bin/sh -c yum install -y net-tools 24MB fa72d883c903 3 minutes ago /bin/sh -c yum install -y vim 59.8MB 7e050cc9059e 4 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B 2246df8e7559 4 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/lo cal 0B 470671670cac 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 4 months ago /bin/sh -c #(nop) LABEL org.label-sc hema.sc… 0B <missing> 4 months ago /bin/sh -c #(nop) ADD file:aa54047c80 ba30064… 237MB
实例2
区别CMD和ENTRYPOINT CMD 1、编写dockerfile [root@docker1 dockerfile]# vim Dockerfile3 FROM centos RUN yum install -y curl CMD ["curl","-s","http://www.baidu.com"] 2、建立容器 [root@docker1 dockerfile]# docker build -f /dockerfile/Dockerfile3 -t test1 . Sending build context to Docker daemon 4.096kB Step 1/3 : FROM centos ---> 470671670cac Step 2/3 : RUN yum install -y curl ---> Using cache ---> 121fba3c88d0 Step 3/3 : CMD ["curl","-s","http://www.baidu.com"] ---> Running in 9d3c8e940c70 Removing intermediate container 9d3c8e940c70 ---> 370030919dc4 Successfully built 370030919dc4 Successfully tagged test1:latest 3、启动容器 [root@docker1 dockerfile]# docker run test1 <!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 id=wrapper> <div id=head> ... ... ... 可以看出不但运行了容器还运行了curl命令 那我在原来的基础上加上-i参数想查询网页报头,发现报错 这就是CMD参数,最后的命令会覆盖前面所有命令! [root@docker1 dockerfile]# docker run test1 -i docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown. ENTRYPOINT 1、编写dockerfile [root@docker1 dockerfile]# vim Dockerfile4 FROM centos RUN yum install -y curl ENTRYPOINT ["curl","-s","http://www.baidu.com"] 2、建立容器 [root@docker1 dockerfile]# docker build -f /dockerfile/Dockerfile4 -t test2 . Sending build context to Docker daemon 5.12kB Step 1/3 : FROM centos ---> 470671670cac Step 2/3 : RUN yum install -y curl ---> Using cache ---> 121fba3c88d0 Step 3/3 : ENTRYPOINT ["curl","-s","http://www.baidu.com"] ---> Running in b0830b190ecb Removing intermediate container b0830b190ecb ---> 473899186632 Successfully built 473899186632 Successfully tagged test2:latest 3、启动容器 [root@docker1 dockerfile]# docker run test2 <!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 id=wrapper> <div id=head> ......... ...... 下面加上-i参数也可以实现 [root@docker1 dockerfile]# docker run test2 -i HTTP/1.1 200 OK Accept-Ranges: bytes Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform Connection: keep-alive Content-Length: 2381 Content-Type: text/html Date: Wed, 20 May 2020 14:54:22 GMT Etag: "588604c8-94d" Last-Modified: Mon, 23 Jan 2017 13:27:36 GMT Pragma: no-cache Server: bfe/1.0.8.18 Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/ <!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 id=wrapper> <div id=head> ................. .............
实例3
1、编写父类dockerfile [root@docker1 dockerfile]# vim Dockerfile4 FROM centos RUN yum install -y curl ENTRYPOINT ["curl","-s","http://www.baidu.com"] ONBUILD RUN echo "father image onbuild---------------------886" 编写子类dockerfile [root@docker1 dockerfile]# vim Dockerfile5 FROM father RUN yum install -y curl CMD ["curl","-s","http://www.baidu.com"] 2、建立父容器 [root@docker1 dockerfile]# docker build -f /dockerfile/Dockerfile4 -t father . Sending build context to Docker daemon 5.12kB Step 1/4 : FROM centos ---> 470671670cac Step 2/4 : RUN yum install -y curl ---> Using cache ---> 121fba3c88d0 Step 3/4 : ENTRYPOINT ["curl","-s","http://www.baidu.com"] ---> Using cache ---> 473899186632 Step 4/4 : ONBUILD RUN echo "father image onbuild---------------------886" ---> Running in 55259036bd58 Removing intermediate container 55259036bd58 ---> 9c46a5e3585a Successfully built 9c46a5e3585a Successfully tagged father:latest 建立子容器 [root@docker1 dockerfile]# docker build -f /dockerfile/Dockerfile5 -t son . Sending build context to Docker daemon 6.144kB Step 1/3 : FROM father #Executing 1 build trigger ---> Running in 8b57d2b22be5 father image onbuild---------------------886 Removing intermediate container 8b57d2b22be5 ---> fc04a4561a38 Step 2/3 : RUN yum install -y curl ---> Running in afe9785ae3dd Last metadata expiration check: 0:34:57 ago on Wed May 20 14:31:22 2020. Package curl-7.61.1-11.el8.x86_64 is already installed. Dependencies resolved. Nothing to do. Complete! Removing intermediate container afe9785ae3dd ---> 55e7543cd3b9 Step 3/3 : CMD ["curl","-s","http://www.baidu.com"] ---> Running in f713b323d80a Removing intermediate container f713b323d80a ---> 38a42453cbb1 Successfully built 38a42453cbb1 Successfully tagged son:latest
可以发现建立子容器的时候父容器中的onbuild被触发 完成啦!