Docker实战教程-02Docker高级篇

865 阅读19分钟

1、docker高级篇

本篇文章主要介绍docker的高级内容,包括Dockerfile、Dockerfile实战、Docker网络、Docker-compose容器编排等

2、DockerFile解析

2.1 是什么

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

image-20221103163939278

官网:docs.docker.com/engine/refe…

image-20221103165827146

构建三步骤

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

2.2 DockerFile文件解析过程

基础知识

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会窗机哪一个新的镜像层并对镜像进行提交(就是分层的镜像,想想之前的花卷)

Docker执行DockerFile的大致流程

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

最终形成多个分层构成的新镜像供你使用

小总结

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

  • Dockerfile是软件的原材料
  • Docker镜像是软件的交付品
  • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例 Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。

image-20221103165105187

1 Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;

2 Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;

3 Docker容器,容器是直接提供服务的。

2.3 Dockerfile常用保留字指令

网站

可以看这个,说明的挺详细的

Docker Dockerfile指令 | 奇客谷教程 💯 (qikegu.com)

保留字指令

FROM

基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from

MAINTAINER

镜像维护者的姓名和邮箱地址

RUN

容器构建时需要运行的命令

比如需要在ubuntu中安装vim,就需要执行yum -y install vim

两种格式:

shell格式:

image-20221103170050264

RUN yum -y install vim

exec格式:

image-20221103170113625

RUN是在 docker build时运行

EXPOSE

当前容器对外暴露出的端口

WORKDIR

指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

比如,交互模式进入ubuntu容器,默认进入根目录,进入Tomcat容器,默认进入/usr/local/tomcat

image-20221103170424773

USER

指定该镜像以什么样的用户去执行,如果都不指定,默认是root

一般我们不指定,都是默认为root

ENV

用来在构建镜像过程中设置环境变量

ENV MY_PATH /usr/mytest 这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样; 也可以在其它指令中直接使用这些环境变量,

比如:WORKDIR $MY_PATH

VOLUME

容器数据卷,用于数据保存和持久化工作

通过dockerfile的 VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。

还有一个区别是,通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。

比如:

#test
FROM ubuntu
MAINTAINER hello1
VOLUME ["/data1","/data2"]

上面的dockfile文件通过VOLUME指令指定了两个挂载点 /data1 和 /data2.

我们通过docker inspect 查看通过该dockerfile创建的镜像生成的容器,可以看到如下信息

"Mounts": [
        {
            "Name": "d411f6b8f17f4418629d4e5a1ab69679dee369b39e13bb68bed77aa4a0d12d21",
            "Source": "/var/lib/docker/volumes/d411f6b8f17f4418629d4e5a1ab69679dee369b39e13bb68bed77aa4a0d12d21/_data",
            "Destination": "/data1",
            "Driver": "local",
            "Mode": "",
            "RW": true
        },
        {
            "Name": "6d3badcf47c4ac5955deda6f6ae56f4aaf1037a871275f46220c14ebd762fc36",
            "Source": "/var/lib/docker/volumes/6d3badcf47c4ac5955deda6f6ae56f4aaf1037a871275f46220c14ebd762fc36/_data",
            "Destination": "/data2",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],

ADD

将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包

COPY

类似ADD,拷贝文件和目录到镜像中。 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置

COPY src dest

COPY ["src", "dest"]

<src源路径>:源文件或者源目录

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

CMD

指定启动容器后要做的事情

image-20221103184438723

比如Tomcat的镜像文件,在文件最后指定CMD["catalina.sh","run"]

注意:

  • DockerFile中可以有多个CMD指令,但是只有最后一个生效(注意是在run的时候,在build的时候,CMD里面比如打印语句之类的会执行),CMD会docker run 之后的参数替换
  • 参考Tomcat的dockerFile
    • 最后一行命令:image-20221103184849184
    • 如果自己在执行docker run的时候指定了命令参数/bin/bash:image-20221103184928254
    • 那么就无法访问Tomcat,启动失败

和RUN命令的区别

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

ENTRYPOINT

  • 也是用来指定一个容器启动时要运行的命令

  • 类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序

案例

命令格式:image-20221103201818833

ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。 当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变

image-20221103201830234

案例如下:假设已通过 Dockerfile 构建了 nginx:test 镜像:

image-20221103201843712

上面这个ENTRYPOINT 和 CMD一起使用,CMD里面的内容将作为参数传给ENTRYPOINT,如果在docker run中传递了参数,那么CMD的内容将会被覆盖,传的参数将会给ENTRYPOINT。因此,可以看出,如果这两个保留字一起使用,CMD里面可以设置默认的内容传递给ENTRYPOINT,然后用户可以在docker run中自定义参数覆盖掉CMD里面的内容传给ENTRYPOINT

优点:在执行docker run的时候可以指定 ENTRYPOINT 运行所需的参数。

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

小总结

image-20221103202218038

2.4 案例

要求

Centos7镜像具备vim+ifconfig+jdk8

JDK的下载镜像地址:mirrors.yangxingzhen.com/jdk/

编写

注意:命名一定是Dockerfile,第一个字母大写

FROM centos:7
#基于centos7,注意带上标签
MAINTAINER zylai<zylai0712@163.com>
 
ENV MYPATH /usr/local
WORKDIR $MYPATH

#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
 
EXPOSE 80

#构建过程中,打印
#实际中run容器的时候,只会执行最后一个CMD
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash

构建

docker build -t ImageName:TagName dir

  • -t − 给镜像加一个Tag
  • ImageName − 给镜像起的名称
  • TagName − 给镜像的Tag名
  • Dir − Dockerfile所在目录

比如我们这里执行

docker build -t contosjava8:1.5 .

  • centosjava8是镜像名
  • 0.1是tag
  • . 表示当前目录,即Dockerfile所在的目录

本例中一共有17条命令,所以共有17个步骤

image-20221103211452539

运行

docker run -it 镜像id /bin/bash

发现,该有的环境全部都有了

image-20221103210350651

所以说,我们通过Dockerfile构建的镜像天生就NB,自带我们指定的软件

2.5 虚悬镜像

是什么

仓库名、标签都是的镜像,俗称dangling image

自己用Dockerfile写一个:

from ubuntu CMD echo 'action is success'

查看

docker image ls -f dangling=true

image-20221103213652976

删除

docker image prune

虚悬镜像已经失去存在价值,可以删除

image-20221103213709877

3、Docker微服务实战

3.1 新建一个微服务模块

改POM,就是一个非常简单的web应用

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zylai</groupId>
    <artifactId>docker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>docker</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.zylai.docker.DockerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

写YML

# 应用名称
spring.application.name=docker
# 应用服务 WEB 访问端口
server.port=8101

业务类

@RestController
public class DockerController {

    @Value("${server.port}")
    private String port;

    @RequestMapping("/order/docker")
    public String helloDocker()
    {
        return "hello docker"+"\t"+port+"\t"+ UUID.randomUUID().toString();
    }

    @RequestMapping(value ="/order/index",method = RequestMethod.GET)
    public String index()
    {
        return "服务端口号: "+"\t"+port+"\t"+UUID.randomUUID().toString();
    }

}

到这里就没有了,然后使用maven打成jar包即可

3.2发布微服务到指定容器

编写Dockerfile

就是教程给的那个版本,只是做一个很简单的演示,实际中的部署可能要更复杂

# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
#暴露6001端口作为微服务
EXPOSE 6001
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar /zzyy_docker.jar
#修改这个文件的访问时间和修改时间为当前时间,而不会修改文件的内容。
RUN bash -c 'touch /zzyy_docker.jar'
# 运行jar包
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]

构建镜像

docker build -t sgdt:1.1 .

4、Docker 网络

4.1 是什么

docker不启动,虚拟机的默认网络情况:

image-20221104225641716

docker启动后,网络情况:

会产生一个名为docker0的虚拟网桥

image-20221104225704003

docker默认网络模式

docker会默认创建三大网络模式

image-20221104225735497

4.2 常用命令

所有命令

image-20221104225846272

  • 查看网络:docker network ls
  • 查看网络源数据:docker network inspect XX网络名字
  • 删除网络:docker network rm XX网络名字

such as:

image-20221104230119070

4.3 能干嘛

  1. 容器间的互联和通信以及端口映射

  2. 容器IP变动时候可以通过服务名直接网络通信而不受到影响

4.4 网络模式

1、总体介绍

image-20221104231347285

  • bridge模式:使用--network bridge指定,默认使用docker0

    • 几乎都是用的这个模式
  • host模式:使用--network host指定

    • 使用宿主机的IP和端口
  • none模式:使用--network none指定;了解即可,几乎不会用到

  • container模式:使用--network container:NAME或者容器ID指定

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

1、先启动两个ubuntu容器实例

image-20221104232930105

2、 docker inspect 容器ID or 容器名字

image-20221104232944395

3、关闭u2实例,新建u3,查看ip变化

image-20221104232959662

结论:docker容器内部的ip是有可能会发生改变的

3、bridge

是什么

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信

image-20221105225711783

image-20221105225846322

详细说明:star2:

很重要哇

1、Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

2、docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址

3、 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。

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

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

也就是说,容器就是通过docker0这个网桥和宿主机进行通信,还有容器与容器之间的通信

image-20221105230840498

img

启动两个容器

docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8

docker run -d -p 8082:8080 --name tomcat81 billygoo/tomcat8-jdk8

两两匹配验证

可以看到在宿主机中,有veth开头的两个配置,这两个配置其实和容器中是一一对应的,容器中各有一个eth0的配置,他们的编号和宿主机对应。这也就说明了上图的结构。

image-20221105231020701

4、host

是什么

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。

详细说明

容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。

image-20221105231559665

启动一个容器

docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8

image-20221105233857568

问题: docke启动时总是遇见标题中的警告 原因: docker启动时指定--network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告, 并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。 解决: 解决的办法就是使用docker的其他网络模式,例如--network=bridge,这样就可以解决问题,或者直接无视。。。。O(∩_∩)O哈哈~

没有警告得命令:

docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8

查看容器内部

查看详情,并没有网关和ip地址的分配,因为他是和宿主机共用ip的

image-20221105234312134

容器内部的网络配置情况几乎和宿主机上一模一样

image-20221105234213064

无端口映射问题

没有设置-p的端口映射了,如何访问启动的tomcat83??

http://宿主机IP:8080/

在CentOS里面用默认的火狐浏览器访问容器内的tomcat83看到访问成功,因为此时容器的IP借用主机的, 所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。

总之就是相当于直接在机器上直接安装了Tomcat

5、none

只需要了解一下即可

是什么

在none模式下,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo 需要我们自己为Docker容器添加网卡、配置IP等。

禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)

启动一个容器

容器内部查看

image-20221105234655945

在容器外部查看

image-20221105234711752

6、container

是什么

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

image-20221105235815789

案例-失败

分别执行:

docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8

docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8

image-20221105235922379

就是说,两个Tomcat容器在容器内部都是使用8080端口,而这两个容器又是共用ip和端口的,所以会发生端口冲突

案例-成功

Alpine操作系统

Alpine操作系统是一个面向安全的轻型 Linux发行版

Alpine Linux 是一款独立的、非商业的通用 Linux 发行版,专为追求安全性、简单性和资源效率的用户而设计。 可能很多人没听说过这个 Linux 发行版本,但是经常用 Docker 的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到 6M的大小,所以特别适合容器打包。

启动两个容器

docker run -it --name alpine1 alpine /bin/sh

docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh

运行结果

image-20221106000233331

假如关闭alpine1,看看alpine2

image-20221106000329952

15: eth0@if16: 消失了。。。。。。关闭alpine1,再看看alpine2

image-20221106000322602

7、自定义网络

可能听说过docker link,但是它已经过时了,不必在意这个

是什么

docker默认有三种网络,我们可以创建自定义的网络模式(默认是bridge类型)

image-20221106173357352

能干嘛

在第2小节中已经说明了:docker容器内部的ip是有可能会发生改变的。因此我们需要在容器IP变动时候可以通过服务名直接网络通信而不受到影响,这时,就用到了d自定义网络

使用自定义网络之前

如果都是默认使用bridge网络模式,容器间是可以通过ip ping通的,但是无法通过服务名ping通

image-20221106173727334

image-20221106173731685

image-20221106173743975

使用自定义网络

自定义桥接网络,就是默认使用bridge网络类型

image-20221106173855967

新建容器,并将容器加入自定义的网络

docker run -d -p 8081:8080 --network zzyy_network --name tomcat81 billygoo/tomcat8-jdk8

docker run -d -p 8082:8080 --network zzyy_network --name tomcat82 billygoo/tomcat8-jdk8

通过服务名ping测试

image-20221106174011436

惊不惊喜,意不意外,就是直接新建一个自定义的桥接网络即可实现容器间通过服务名通信

如果有多台物理机,可以通过交换机将其连接配置之类的,这里不再展开说,也用不到可能

想简单看一下的话,看这个教程

Docker基础 - Docker网络使用和配置 | Java 全栈知识体系 (pdai.tech)

结论

自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)

5、Docker-compose容器编排

5.1 是什么

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器

Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。

和Spring的IOC里面对比

image-20221106175356235

5.2 能干嘛

docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?

如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具

例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。。。。。。

Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)

可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。

5.3 安装

官网

文档:

Compose file version 3 reference | Docker Documentation

安装教程:

docs.docker.com/compose/ins…

安装步骤

三个命令:

curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose

docker-compose --version

image-20221106203337290

如果需要卸载:

image-20221106203409630

5.4 Compose的核心概念

一个文件

docker-compose.yml

详细的配置:

docker compose 配置文件 .yml 全面指南 - 知乎 (zhihu.com)

(205条消息) docker-compose.yml 配置文件编写详解_种子选手的博客-CSDN博客_docker-compose.yml

两个要素

服务(service)

一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器

工程(project)

由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

5.5 Compose使用的三个步骤

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

image-20221106204942524

5.6 compose常用命令

image-20221106205149927

5.7 编排微服务

改造升级微服务工程docker_root

略,就是一个很简单的springboot项目,依赖于mysql和redis

不使用compose

略,反正就是手动一个个run容器

单独的mysql容器实例

docker run -p 8136:3306 --name mysql57 --privileged=true -v /zzyyuse/mysql/conf:/etc/mysql/conf.d -v /zzyyuse/mysql/logs:/logs -v /zzyyuse/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

进入mysql容器实例并新建库db2021+新建表t_user

单独的redis实例

docker run -p 6380:6379 --name redis608 --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf

微服务工程

docker run -d -p 6001:6001 zzyy_docker:1.6

不使用compose的问题

先后顺序要求固定,先mysql+redis才能微服务访问成功

多个run命令......

容器间的启停或宕机,有可能导致IP地址对应的容器实例变化,映射出错, 要么生产IP写死(可以但是不推荐),要么通过服务调用

怎么说呢,课件上这么写的,但是我觉得这个ip一般不都是宿主机的ip吗?不都是宿主机的ip加上容器的映射端口就可以访问容器了?,嘶,他到底是个啥意思。他的意思可是就是直接使用bridge网络下的局域网ip,而不使用宿主机的ip?就是说只让镜像暴露端口,而运行容器的时候不进行端口映射,那么访问的时候直接通过他的局域网ip加暴露的端口访问。。可能是这个意思吧。

但是这样确实好用,就是一个宿主机上多个容器,多个容器间如果只用端口映射区分,很容易记错,而我们使用服务名称代替了局域网中的ip

使用compose(简单版)

修改docker_boot

更改yaml文件,通过服务名访问,而不通过ip访问

image-20221107154450734

打包微服务,并上传

编写Dockerfile

# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 运行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作为微服务
EXPOSE 6001

构建镜像

执行命令docker build -t zzyy_docker:1.6 .

可以自己手动通过命令docker build -t zzyy_docker:1.6 .构建出来镜像,但是如果有多个jar包岂不是又得手动一堆?又回去了?所以docker compose不仅可以编排容器,还可以根据Dockerfile构建镜像,具体构建方法见编排微服务实战

编写docker-compose.yml

version: "3"
 
services:
  # microService为服务名称,在同一个网络中可以通过服务名称来访问容器,而不通过ip
  microService:
    image: zzyy_docker:1.6 #镜像名称
    container_name: ms01 #容器名称,如果不指定
    ports: #指定端口映射
      - "6001:6001"
    volumes: #挂载容器卷
      - /app/microService:/data
    networks: #指定使用的网络
      - atguigu_net
    depends_on: #依赖哪些服务,这些服务先启动之后才会启动当前服务
      - redis
      - mysql
 
  redis:
    image: redis:6.0.8
    ports:
      - "6380:6379"
    volumes:
      - /app/redis/redis.conf:/etc/redis/redis.conf
      - /app/redis/data:/data
    networks: 
      - atguigu_net
    command: redis-server /etc/redis/redis.conf
 
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: '123456'
      MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
      MYSQL_DATABASE: 'db2021'
      MYSQL_USER: 'zzyy'
      MYSQL_PASSWORD: 'zzyy123'
    ports:
       - "3306:3306"
    volumes:
       - /app/mysql/db:/var/lib/mysql
       - /app/mysql/conf/my.cnf:/etc/my.cnf
       - /app/mysql/init:/docker-entrypoint-initdb.d
    networks:
      - atguigu_net
    command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
 
networks: 
   atguigu_net: 

执行docker-compose

执行命令docker-compose up(前台启动)

执行命令docker-compose up -d(后台启动,一般使用这个)

一套带走

image-20221107162631043

查看启动的容器,发现如果不指定容器名称,那么会将容器名称指定为当前路径_服务名称_第几个容器

image-20221107162745022

最后访问swagger地址进行测试即可

image-20221107162830442

数据库:

image-20221107162903023

redis:

image-20221107163004492

删除所有正在运行的容器

image-20221107163417697

6、轻量级可视化工具Portainer

直接执行就完事了

docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

image-20221110202558884

进入网址:ip:9000,第一次进入要你创建admin账户的密码,创建完成之后就可以了