Docker学习2——Docker高级

Docker学习2——Docker高级

2.1 Dockerfile解析

2.1.1 Dockfile是什么

Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。

Docker学习2——Docker高级_第1张图片

构建步骤:

  1. 编写Dockerfile文件
  2. docker build命令构建镜像
  3. docker run 根据镜像运行容器实例

2.1.2 Dockerfile构建过程解析

1、基础知识

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

2、执行过程

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器作出修改
  3. 执行类似docker commit的操作提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个新容器
  5. 执行dockerfile中的下一条指令直到所有指令都执行完成

3、小结

从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段

  • Dockerfile是软件的原材料
    • Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
  • Docker镜像是软件的交付品
    • 在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务。
  • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
    • 容器是直接提供服务的。

Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。

2.1.3 使用Dockerfile定制镜像

1、引入

下面以定制一个 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 层镜像。

2、构建

在 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 引擎,如果文件过多会造成过程缓慢。

2.1.4 Dockerfile指令详解

1、COPY

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式:

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

2、ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

3、CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

格式:

CMD <shell 命令> 
CMD ["<可执行文件或命令>","","",...] 
CMD ["","",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

4、ENTRYPOINT

类似于 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

5、ENV

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:

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"

6、ARG

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

格式:

ARG <参数名>[=<默认值>]

7、VOLUME

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

格式:

VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

8、WORKDIR

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

格式:

WORKDIR <工作目录路径>

9、USER

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

格式:

USER <用户名>[:<用户组>]

10、HEALTHCHECK

用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

格式:

HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

11、ONBUILD

用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式:

ONBUILD <其它指令>

12、LABEL

LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

比如我们可以添加镜像的作者:

LABEL org.opencontainers.image.authors="runoob"

2.2 Docker微服务实例

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️⃣ 浏览器测试结果如下

image-20221031162826265

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCelFiMK-1667363431972)(./assets/image-20221031162752425.png)]

4️⃣ 打包成jar包

Docker学习2——Docker高级_第2张图片

在target目录下得到jar包

image-20221031171759243

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️⃣ 访问测试

image-20221031174140041

2.3 Docker网络

2.3.1 引入

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

2.3.2 常用命令

  • 全部命令
╭─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

2.3.3 用处

  • 容器间的互联和通信以及端口映射
  • 容器IP发生变动时可以通过服务名直接网络通信而不受到影响

2.3.4 网络模式

1、总体介绍

网络模式 参数 说明
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 指定端口映射。

2、容器实例内默认网络IP生产规则

  1. 启动两个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
    
    
  2. 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
                    }
                }
            }
        }
    ]
    
  3. 关闭u2实例,新建u3,查看ip变化

    ╭─yjq@yi-junquan /home/yjq  ‹system› 
    ╰─$ docker inspect u3 | tail -n 20127
                "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
                    }
                }
            }
        }
    ]
    
    
  4. 结论:docker容器内部的ip会发生变化,并不是固定的

3、bridge

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,成对匹配。

  • 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
  • 每个容器实例内部也有一块网卡,每个接口叫eth0;
  • docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。

将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。

Docker学习2——Docker高级_第3张图片

4、host

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。

Docker学习2——Docker高级_第4张图片

╭─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,这样的好处是外部主机与容器可以直接通信。

5、none

在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
                }
            }
        }
    }
]

6、container

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

Docker学习2——Docker高级_第5张图片

2.4 Docker-compose 容器编排

2.4.1 概念与用途

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 解决了容器与容器之间如何管理编排的问题。

2.4.2 安装

安装过程请参考:Ubuntu安装docker-compose

github地址:https://github.com/docker/compose

2.4.3 Compose核心概念

  • 文件
    • docker-compose.yml
  • 两要素
    • 服务(service):一个个应用容器实例。
    • 工程(project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

2.4.4 使用步骤

  1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
  2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
  3. 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线

2.4.5 常用命令

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的学习

2.5 Docker的多阶段构建

Docker的口号是 Build,Ship,and Run Any App,Anywhere,在我们使用 Docker 的大部分时候,的确能感觉到其优越性,但是往往在我们 Build 一个应用的时候,是将我们的源代码也构建进去的,这对于类似于 golang 这样的编译型语言肯定是不行的,因为实际运行的时候我只需要把最终构建的二进制包给你就行,把源码也一起打包在镜像中,需要承担很多风险,即使是脚本语言,在构建的时候也可能需要使用到一些上线的工具,这样无疑也增大了我们的镜像体积。

2.5.1 示例

比如我们现在有一个最简单的 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")
}

2.5.2 多阶段构建

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

image-20221102122121997

参考资料

  1. 菜鸟教程 Docker Dockerfile
  2. 尚硅谷2022版Docker实战教程(docker教程天花板)
  3. Docker的四种网络模式
  4. docker-compose教程(安装,使用, 快速入门)
  5. kubernetes-learning

你可能感兴趣的:(#,java基础,云原生学习,docker,容器,云原生)