Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
构建步骤:
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
下面以定制一个 nginx 镜像(构建好的镜像内会有一个 /usr/share/nginx/html/index.html 文件)
在一个空目录下,新建一个名为 Dockerfile 文件,并在文件内添加以下内容:
FROM nginx
RUN echo "这是一个本地构建的nginx镜像" > /usr/share/nginx/html/index.html
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning ‹system› <master*>
╰─$ mkdir Dockerfile
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning ‹system› <master*>
╰─$ cd Dockerfile/
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/Dockerfile ‹system› <master*>
╰─$ vi Dockerfile
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/Dockerfile ‹system› <master*>
╰─$ cat Dockerfile
FROM nginx
RUN echo "这是一个本地构建的nginx镜像" > /usr/share/nginx/html/index.html
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 -y 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 -y 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:v3(镜像名称:镜像标签)。
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/Dockerfile ‹system› <master*>
╰─$ docker build -t nginix:v3 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM nginx
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
---> 605c77e624dd
Step 2/2 : RUN echo "这是一个本地构建的nginx镜像" > /usr/share/nginx/html/index.html
---> Running in a0a47e30bbdd
Removing intermediate container a0a47e30bbdd
---> 47090d8be9da
Successfully built 47090d8be9da
Successfully tagged nginix:v3
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/Dockerfile ‹system› <master*>
╰─$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginix v3 47090d8be9da About a minute ago 141MB
其中有一个地方需要说明
docker build -t nginix:v3 .
该指令最后一个 .
是上下文路径
上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
格式:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
[–chown=
<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
格式:
CMD <shell 命令>
CMD ["<可执行文件或命令>","" ,"" ,...]
CMD ["" ,"" ,...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
格式:
ENTRYPOINT ["" ,"" ,"" ,...]
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。
示例:
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
1、不传参运行
$ docker run nginx:test
容器内会默认运行以下命令,启动主进程。
nginx -c /etc/nginx/nginx.conf
2、传参运行
$ docker run nginx:test -c /etc/nginx/new.conf
容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
nginx -c /etc/nginx/new.conf
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
格式:
ARG <参数名>[=<默认值>]
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。
docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
格式:
WORKDIR <工作目录路径>
用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。
格式:
USER <用户名>[:<用户组>]
用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
格式:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
格式:
ONBUILD <其它指令>
LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
比如我们可以添加镜像的作者:
LABEL org.opencontainers.image.authors="runoob"
1️⃣ 使用vscode或者IDEA建立一个springboot项目
2️⃣ 编写controller代码
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Value("${server.port}")
private String port;
@RequestMapping("/order/docker")
public String helloDOcker(){
return "hello docker \t"+port;
}
@RequestMapping(value="/order/index",method = RequestMethod.GET)
public String index(){
return "服务端口号: \t"+port;
}
}
yaml文件中的内容如下:
server:
port: 8080
3️⃣ 浏览器测试结果如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCelFiMK-1667363431972)(./assets/image-20221031162752425.png)]
4️⃣ 打包成jar包
在target目录下得到jar包
5️⃣ 编写Dockerfile文件
FROM openjdk:17-oracle
# 添加作者
LABEL org.opencontainers.image.authors="yijunquan"
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下
# 创建了一个临时文件并链接到容器的/tmp
VOLUME [ "/tmp" ]
# 将jar包添加到容器中并更名为yijunquan_docker.jar
ADD demo-0.0.1-SNAPSHOT.jar yijunquan_docker.jar
# 运行jar包
RUN bash -c 'touch /yijunquan_docker.jar'
ENTRYPOINT ["java","-jar","/yijunquan_docker.jar"]
#暴露8080端口作为微服务
EXPOSE 8080
6️⃣ 将jar包和dockerfile放在同一个目录下,构建镜像
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/example/mydocker ‹system› <master*>
╰─$ docker build -t yijunquan_docker:v1.0.0 . ↵ 1
Sending build context to Docker daemon 18.52MB
Step 1/7 : FROM openjdk:17-oracle
17-oracle: Pulling from library/openjdk
155aced26663: Pull complete
ac5901c58ecb: Pull complete
6b1076e441ff: Pull complete
Digest: sha256:af48c65da151f90303f86ce0e82de11d4c7960d4320c948d85912e27b17aa724
Status: Downloaded newer image for openjdk:17-oracle
---> 5f94f53bbced
Step 2/7 : LABEL org.opencontainers.image.authors="yijunquan"
---> Running in 87a8ed08e983
Removing intermediate container 87a8ed08e983
---> ab347a8fc080
Step 3/7 : VOLUME [ "/tmp" ]
---> Running in 6abe2c327d22
Removing intermediate container 6abe2c327d22
---> e42162ea7076
Step 4/7 : ADD demo-0.0.1-SNAPSHOT.jar yijunquan_docker.jar
---> 7e93d1b258f3
Step 5/7 : RUN bash -c 'touch /yijunquan_docker.jar'
---> Running in 8c967adf42da
Removing intermediate container 8c967adf42da
---> 5a946b2d2a5b
Step 6/7 : ENTRYPOINT ["java","-jar","/yijunquan_docker.jar"]
---> Running in ebbbcf65dc8d
Removing intermediate container ebbbcf65dc8d
---> 16807e9e6313
Step 7/7 : EXPOSE 8080
---> Running in 0370514b4e32
Removing intermediate container 0370514b4e32
---> 880d488a0ea2
Successfully built 880d488a0ea2
Successfully tagged yijunquan_docker:v1.0.0
7️⃣ 运行容器
╭─yjq@yi-junquan /home/yjq/mynote/cloud-learning/docker-learning/example/mydocker ‹system› <master*>
╰─$ docker run -it -p 8080:8080 yijunquan_docker:v1.0.0
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.0-SNAPSHOT)
2022-10-31T09:40:12.668Z INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT using Java 17.0.1 on 3e0292193652 with PID 1 (/yijunquan_docker.jar started by root in /)
2022-10-31T09:40:12.672Z INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2022-10-31T09:40:13.712Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-10-31T09:40:13.725Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-10-31T09:40:13.726Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.0.27]
2022-10-31T09:40:13.828Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-10-31T09:40:13.831Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1075 ms
2022-10-31T09:40:14.258Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-10-31T09:40:14.278Z INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 2.16 seconds (process running for 2.609)
8️⃣ 访问测试
docker启动以后,运行ifconfig
命令,会发现产生了一个名为docker0的虚拟网桥
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:31ff:fe05:761f prefixlen 64 scopeid 0x20<link>
ether 02:42:31:05:76:1f txqueuelen 0 (以太网)
RX packets 12 bytes 1157 (1.1 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 51 bytes 7611 (7.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
查看docker网络模式命令
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a1bbeaba0d0d bridge bridge local
c0fa24253dbb host host local
679c5afa718a none null local
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
docker network ls
docker network inspect <网络名>
docker network rm <网络名>
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network create test
025dc6ec43f18a8e5af645045fd5d530e43f3bd89fa183657e278728827685b8
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a1bbeaba0d0d bridge bridge local
c0fa24253dbb host host local
679c5afa718a none null local
025dc6ec43f1 test bridge local
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network rm test
test
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a1bbeaba0d0d bridge bridge local
c0fa24253dbb host host local
679c5afa718a none null local
网络模式 | 参数 | 说明 |
---|---|---|
host模式 | -–net=host | 容器和宿主机共享 Network namespace。 |
container模式 | –-net={id} | 容器和另外一个容器共享 Network namespace。 kubernetes 中的pod就是多个容器共享一个 Network namespace。 |
none模式 | –-net=none | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,配置IP等。 |
bridge模式 | –net=bridge | 默认为该模式,通过 -p 指定端口映射。 |
启动两个ubuntu容器实例
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker run -it --name u1 ubuntu bash
root@1701fcb00f23:/# exit
exit
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker run -it --name u2 ubuntu bash
root@04a40ebc8189:/# exit
exit
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
04a40ebc8189 ubuntu "bash" About a minute ago Exited (0) About a minute ago u2
1701fcb00f23 ubuntu "bash" About a minute ago Exited (0) About a minute ago u1
docker inspect
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker inspect u1 | tail -n 20
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "573ab509fdad70c28b8d3a0c386c2a81f2adc74bf12d5db076d2ad3b01599440",
"EndpointID": "36ff12bdd4ff98f204357316b66259bba6c719ef9b3d5d3e2c184f96f7972d92",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker inspect u2 | tail -n 20
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "573ab509fdad70c28b8d3a0c386c2a81f2adc74bf12d5db076d2ad3b01599440",
"EndpointID": "7bc0480d39c868e169df33ea1d2e11d5a80b0a72774051e2077dfd165691bbb6",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null
}
}
}
}
]
关闭u2实例,新建u3,查看ip变化
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker inspect u3 | tail -n 20 ↵ 127
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "573ab509fdad70c28b8d3a0c386c2a81f2adc74bf12d5db076d2ad3b01599440",
"EndpointID": "5f21b9e46fc4d427b77f6341e72e56bd595558a92cc24ce67cc232e1bf47e3c3",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null
}
}
}
}
]
结论:docker容器内部的ip会发生变化,并不是固定的
Docker 服务默认会创建一个 docker0
网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
查看 bridge 网络的详细信息,并通过 grep 获取名称项
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker network inspect bridge | grep name
"com.docker.network.bridge.name": "docker0",
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ ifconfig | grep docker
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的networketh0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址。
网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker run -d -p --network host --name tomcat83 billygoo/tomcat8-jdk8
52fd4b6247722371b8d834ee301269f4796005fdc101b9248f699b5c91931004
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
52fd4b624772 billygoo/tomcat8-jdk8 "catalina.sh run" 5 minutes ago Up 5 minutes tomcat83
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker inspect tomcat83 | tail -n 20
"Networks": {
"host": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "c0fa24253dbbe683bec206d6abcc2ede94bb0f72264aee3e101271612c5efe88",
"EndpointID": "aef4cecc28aff4c611a06b9ec0bf03e40e9b32075278c45d860e1167ae0f77b0",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
}
}
]
容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
在none模式下,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo,需要我们自己为Docker容器添加网卡、配置IP等。
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
6a9deff836ca126c07da5bae506c74f7af7fc38014a5a637e190454602f8b5ab
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker exec -it tomcat84 bash
root@6a9deff836ca:/usr/local/tomcat# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
link/tunnel6 :: brd ::
root@6a9deff836ca:/usr/local/tomcat#
╭─yjq@yi-junquan /home/yjq ‹system›
╰─$ docker inspect tomcat84 | tail -n 20
"Networks": {
"none": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "679c5afa718a9295ecd2f8ba2f55a2a39f3f89bcbe572cfae7b44ca43123870b",
"EndpointID": "9240296ca8fb68770636b0c3820c62ac13e7ebbfa89c210ee4cc47fcc12f6353",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
}
}
]
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。
docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具
例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。
Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。
可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
安装过程请参考:Ubuntu安装docker-compose
github地址:https://github.com/docker/compose
docker-compose -h # 查看帮助
docker-compose up # 启动所有docker-compose服务
docker-compose up -d # 启动所有docker-compose服务并后台运行
docker-compose down # 停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps # 展示当前docker-compose编排过的运行的所有容器
docker-compose top # 展示当前docker-compose编排过的容器进程
docker-compose logs yml里面的服务id # 查看容器输出日志
docker-compose config # 检查配置
docker-compose config -q # 检查配置,有问题才有输出
docker-compose restart # 重启服务
docker-compose start # 启动服务
docker-compose stop # 停止服务
Docker Compose是一个基于Docker的单主机容器编排工具,功能并不像Docker Swarm和Kubernetes是基于Dcoker的跨主机的容器管理平台那么丰富。
因为k8s更常用,所以接下来我会进行k8s的学习
Docker
的口号是 Build,Ship,and Run Any App,Anywhere,在我们使用 Docker 的大部分时候,的确能感觉到其优越性,但是往往在我们 Build 一个应用的时候,是将我们的源代码也构建进去的,这对于类似于 golang 这样的编译型语言肯定是不行的,因为实际运行的时候我只需要把最终构建的二进制包给你就行,把源码也一起打包在镜像中,需要承担很多风险,即使是脚本语言,在构建的时候也可能需要使用到一些上线的工具,这样无疑也增大了我们的镜像体积。
比如我们现在有一个最简单的 golang 服务,需要构建一个最小的Docker
镜像,源码如下:
main.go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "PONG")
})
router.Run(":8080")
}
Docker 17.05版本以后,官方就提供了一个新的特性:Multi-stage builds
(多阶段构建)。 使用多阶段构建,你可以在一个 Dockerfile
中使用多个 FROM 语句。每个 FROM 指令都可以使用不同的基础镜像,并表示开始一个新的构建阶段。你可以很方便的将一个阶段的文件复制到另外一个阶段,在最终的镜像中保留下你需要的内容即可。
我们可以调整前面一节的 Dockerfile
来使用多阶段构建:(保存为Dockerfile)
# 基础镜像,基于golang的alpine版本镜像构建
# 基础镜像,基于golang的alpine镜像构建--编译阶段
FROM golang:alpine AS builder
# 全局工作目录
WORKDIR /go/server
# 把运行Dockerfile文件的当前目录所有文件复制到目标目录
COPY . /go/server
# 环境变量
# 用于代理下载go项目依赖的包
ENV GOPROXY https://goproxy.cn,direct
# 编译,关闭CGO,防止编译后的文件有动态链接,而alpine镜像里有些c库没有,直接没有文件的错误
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go
# 使用alpine这个轻量级镜像为基础镜像--运行阶段
FROM alpine AS runner
# 全局工作目录
WORKDIR /go/server
# 复制编译阶段编译出来的运行文件到目标目录
COPY --from=builder /go/server/main .
# 需暴露的端口
EXPOSE 8088
# docker run命令触发的真实命令(相当于直接运行编译后的可运行文件)
ENTRYPOINT ["./main"]
现在我们只需要一个Dockerfile
文件即可,也不需要拆分构建脚本了,只需要执行 build 命令即可:
╭─yjq@yi-junquan /home/yjq/gowork/demo ‹system›
╰─$ docker build -t yijunquan_demo:v1.0.0 .
默认情况下,构建阶段是没有命令的,我们可以通过它们的索引来引用它们,第一个 FROM 指令从0
开始,我们也可以用AS
指令为阶段命令,比如我们这里的将第一阶段命名为build-env
,然后在其他阶段需要引用的时候使用--from=build-env
参数即可。
最后我们简单的运行下该容器测试:
╭─yjq@yi-junquan /home/yjq/gowork/demo ‹system›
╰─$ docker run --rm -p:8080:8080 yijunquan_demo:v1.0.0
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /ping --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080