参考文章:
docker-compose.yml 详解 http://www.imooc.com/article/278410
docker-compose 官网 https://docs.docker.com/compose/compose-file/#command

什么是 docker

我们在理解 docker 之前,我们首先要了解一下虚拟化技术.

百度百科定义:
在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资源的新虚拟部分是不受现有资源的架设方式,地域或物理组态所限制。一般所指的虚拟化资源包括计算能力和资料存储。
在实际的生产环境中,虚拟化技术主要用来解决高性能的物理硬件产能过剩和老的旧的硬件产能过低的重组重用,透明化底层物理硬件,从而最大化的利用物理硬件。

虚拟化分类:
分为: 硬件级虚拟化和操作系统级虚拟化.

1. 硬件级虚拟化:
我们用的传统虚拟机就是在硬件级别进行的虚拟化. 例如 VMware , VisualBox 之类的需要模拟整台机器包括硬件,每台虚拟机都需要有自己的操作系统,虚拟机一旦被开启,预分配给它的资源将全部被占用。每一台虚拟机包括应用,必要的二进制和库,以及一个完整的用户操作系统。

图解硬件级虚拟化:

2. 操作系统级虚拟化:
而容器技术是和我们的宿主机共享硬件资源及操作系统,可以实现资源的动态分配。容器包含应用和其所有的依赖包,但是与其他容器共享内核。容器在宿主机操作系统中,在用户空间以分离的进程运行。

容器技术是实现操作系统虚拟化的一种途径,可以让您在资源受到隔离的进程中运行应用程序及其依赖关系。通过使用容器,我们可以轻松打包应用程序的代码、配置和依赖关系,将其变成容易使用的构建块,从而实现环境一致性、运营效率、开发人员生产力和版本控制等诸多目标。容器可以帮助保证应用程序快速、可靠、一致地部署,其间不受部署环境的影响。容器还赋予我们对资源更多的精细化控制能力,让我们的基础设施效率更高。

而 docker 容器是 Linux 发展出了另一种虚拟化技术,简单来讲, docker 容器不是模拟一个完整的操作系统,而是对进程进行隔离,相当于是在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker ,就不用担心环境问题。

图解操作系统级虚拟化

为什么使用 docker

Docker 相比于传统虚拟化方式具有更多的优势:

  • docker 启动快速属于秒级别。虚拟机通常需要几分钟去启动
  • docker 需要的资源更少, docker 在操作系统级别进行虚拟化, docker 容器和内核交互,几乎没有性能损耗,性能优于通过 Hypervisor 层与内核层的虚拟化
  • docker 更轻量, docker 的架构可以共用一个内核与共享应用程序库,所占内存极小。同样的硬件环境, Docker 运行的镜像数远多于虚拟机数量,对系统的利用率非常高
  • 与虚拟机相比, docker 隔离性更弱, docker 属于进程之间的隔离,虚拟机可实现系统级别隔离
  • 安全性: docker 的安全性也更弱。 Docker 的租户 root 和宿主机 root 等同,一旦容器内的用户从普通用户权限提升为 root 权限,它就直接具备了宿主机的 root 权限,进而可进行无限制的操作。虚拟机租户 root 权限和宿主机的 root 虚拟机权限是分离的,并且虚拟机利用如 Intel 的 VT-d 和 VT-x 的 ring-1 硬件隔离技术,这种隔离技术可以防止虚拟机突破和彼此交互,而容器至今还没有任何形式的硬件隔离,这使得容器容易受到攻击
  • 高可用和可恢复性: docker 对业务的高可用支持是通过快速重新部署实现的。虚拟化具备负载均衡,高可用,容错,迁移和数据保护等经过生产实践检验的成熟保障机制, VMware 可承诺虚拟机 99.999% 高可用,保证业务连续性
  • 快速创建、删除:虚拟化创建是分钟级别的, Docker 容器创建是秒级别的, Docker 的快速迭代性,决定了无论是开发、测试、部署都可以节约大量时间
  • 交付、部署:虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化。 Docker 在 Dockerfile 中记录了容器构建过程,可在集群中实现快速分发和快速部署

docker 详解

docker 的运行机制:
启动一个 dockerClient 拉取 docker 镜像 (images) 启动镜像得到一个容器 (contain) 容器中就是我们的程序.

使用 docker

首先 docker 并不是一个容器, docker 是一个容器管理引擎.
使用 docker 要在 liunx 上使用, 当然 docker 也有 win 版本. 但是 win 版本依然是使用的 win10 系统的 hype-v 虚拟的 liunx 系统. 在其中运行 docker.

安装 docker
使用 centos 7 1810 版本

yum  install  docker  -y            # 使用yum直接安装
docker  -version                      #  验证安装,查看版本

启动 docker

service docker start                # 启动docker
service  docker status            #  查看docker运行状态
service  docker  stop             #  停止docker

常用的命令

docker                                   #  显示帮助信息
docker  info                           #   查看docker信息
docker  search   tomcat        # 搜索所有的镜像 
docker  pull   redis                #  拉去镜像           docker   pull  java:8   可以冒号指定标签(版本)
docker   images                    # 查看所有的镜像

生成容器

docker  run  tomcat                 # 运行容器
docker  run  -d  -p  8080:8080  tomcat    #  -d 后台运行   -p  指定端口映射

docker  exec  -it   容器id   bash        # 进入容器的内部查看

docker ps   查看所有的正在运行容器  -a  所有的  -q只显示容器id

docker stop   容器id

配置 Tomcat 外部文件挂载
将 war 包或是 HTML 页面放在宿主机固定的位置就可以在 Tomcat 中发布. 而不用将其复制进入容器的内部.

tomcat默认位置在  :  /usr/local/tomcat/webapps 
tomcat外部文件挂载:

1.不要在home下创建共享(挂载目录),权限问题,会无法访问.

2.创建/usr/local/webapps/web1目录(web1是Tomcat的上下文根)

3.将main.html放在目录下.

4.启动docker:   将webapps下的内容挂载到容器中.      (是文件夹下的内容,不包含当前文件夹!!!!!!!!)
--name  指定容器的名称 
docker run -d -p 8090:8080 -v /usr/webapps:/usr/local/tomcat/webapps --name tomcat44 tomcat

命令解释   -d 后台运行    -p 指点端口   -v 进行文件夹挂载  [宿主机路径:容器内路径]    

--name 为容器创建一个名字     

最后一个tomcat是:启动的镜像

5.启动之后访问   .../web1/main.html即可

启动 mysql 并进行数据持久化

mysql配置文件路径:   /etc/mysql/my.conf
mysql的数据存储位置:   /var/lib/mysql 
mysql的命令bin位置:  /usr/bin

1.创建 /usr/local/mysql/conf/my.conf文件,将容器内的my.conf复制出来.

2.创建/usr/local/mysql/data ,用于没有mysql的数据存放

3.运行镜像
docker run -p 3306:3306 
-v /usr/local/mysql/conf/my.conf:/etc/mysql/my.conf       # 文件的映射
-v /usr/local/mysql/data:/var/lib/mysql                           # ,目录的映射
-e MYSQL_ROOT_PASSWORD=112233                    # 配置环境
--name mysql22 
--privileged=true                                                        #提升容器内root用户权限为真正的root权限
-d mysql

4.在mysql中创建一个数据库,  停止容器,并删除.   运行一个新的镜像(数据目录挂载相同文件夹).
可以看到数据库存在.

docker 数据卷
数据卷就是 docker 目录的挂载的固定路径.

查看数据卷:docker volume ls

清理无用的数据卷: docker volume prune

查看数据卷的具体指向:docker volume inspect  数据卷名

docker cp 命令
将宿主机的内容复制到容器中

docker cp  /usr/local/main.txt    96f7f14e99ab:/usr/tmp

docker 查看容器日志

docker logs -f -t --tail 200 dda7df088656
-f动态的查看   -t --tail 使用tail方式查看

Dockerfile

Dockerfile 中包括 FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME、USER、WORKDIR、ONBUILD 等 13 个指令。下面一一讲解。

1.FROM
格式为 FROM image 或 FROM image:tag,并且 Dockerfile 中第一条指令必须是 FROM 指令,且在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令。

2.MAINTAINER
格式为 MAINTAINER user_name user_email,指定维护者信息
这个已经过期, 推荐使用 LABLE k=v 来使用.

3.RUN
格式为 RUN command 或 RUN [“EXECUTABLE”,“PARAM1”,“PARAM2”…],前者在 shell 终端中运行命令,/bin/sh -c command,例如:/bin/sh -c “echo hello”;后者使用 exec 执行,指定其他运行终端使用 RUN[“/bin/bash”,“-c”,“echo hello”]

每条 RUN 指令将当前的镜像基础上执行指令,并提交为新的镜像,命令较长的时候可以使用 \ 来换行, 同时可以使用 && 并列执行, 减少镜像的层数。

4.CMD
支持三种格式:
CMD [“executable”,“param1”,“param2”],使用 exec 执行,这是推荐的方式。
CMD command param1 param2 在 / bin/sh 中执行。
CMD [“param1”,“param2”] 提供给 ENTERYPOINT 的默认参数。
CMD 用于指定容器启动时执行的命令,每个 Dockerfile 只能有一个 CMD 命令,多个 CMD 命令只执行最后一个。若容器启动时指定了运行的命令,则会覆盖掉 CMD 中指定的命令。

5.EXPOSE
格式为 EXPOSE port [port2,port3,…],例如 EXPOSE 80 这条指令告诉 Docker 服务器暴露 80 端口,供容器外部连接使用。
在启动容器的使用使用 - P,Docker 会自动分配一个端口和转发指定的端口,使用 - p 可以具体指定使用哪个本地的端口来映射对外开放的端口。

6.ENV
格式为:EVN key value 。用于指定环境变量,这些环境变量,后续可以被 RUN 指令使用,容器运行起来之后,也可以在容器中获取这些环境变量。
例如
ENV word hello
RUN echo $word

7.ADD
格式:ADD src dest
该命令将复制指定本地目录中的文件到容器中的 dest 中,src 可以是是一个绝对路径,也可以是一个 URL 或一个 tar 文件,tar 文件会自动解压为目录。

8.COPY
格式为:COPY src desc
复制本地主机 src 目录或文件到容器的 desc 目录,desc 不存在时会自动创建。

9.ENTRYPOINT
格式有两种:
ENTRYPOINT [“executable”,“param1”,“param2”]
ENTRYPOINT command param1,param2 会在 shell 中执行。
用于配置容器启动后执行的命令,这些命令不能被 docker run 提供的参数覆盖。和 CMD 一样,每个 Dockerfile 中只能有一个 ENTRYPOINT,当有多个时最后一个生效。

10.VOLUME
格式为 VOLUME [“/data”]
作用是创建在本地主机或其他容器可以挂载的数据卷,用来存放数据。

11.USER
格式为:USER username
指定容器运行时的用户名或 UID,后续的 RUN 也会使用指定的用户。要临时使用管理员权限可以使用 sudo。在 USER 命令之前可以使用 RUN 命令创建需要的用户。
例如:RUN groupadd -r docker && useradd -r -g docker docker

12.WORKDIR
格式: WORKDIR /path
为后续的 RUN CMD ENTRYPOINT 指定配置工作目录,可以使用多个 WORKDIR 指令,若后续指令用得是相对路径,则会基于之前的命令指定路径。

13.ONBUILD
格式 ONBUILD [INSTRUCTION]
该配置指定当所创建的镜像作为其他新建镜像的基础镜像时所执行的指令。
例如下面的 Dockerfile 创建了镜像 A:
ONBUILD ADD . /app
ONBUILD RUN python app.py

则基于镜像 A 创建新的镜像时,新的 Dockerfile 中使用 from A 指定基镜像时,会自动执行 ONBBUILD 指令内容,等价于在新的要构建镜像的 Dockerfile 中增加了两条指令:
FROM A
ADD ./app
RUN python app.py

14.docker build
创建好 Dockerfile 之后,通过 docker build 命令来创建镜像,该命令首先会上传 Dockerfile 文件给 Docker 服务器端,服务器端将逐行执行 Dockerfile 中定义的指令。
通常建议放置 Dockerfile 的目录为空目录。另外可以在目录下创建. dockerignore 文件,让 Docker 忽略路径下的文件和目录,这一点与 Git 中的配置很相似。

通过 -t 指定镜像的标签信息,例如:docker build -t regenzm/first_image .”.” 指定的是 Dockerfile 所在的路径

实例 dockerfile

  1. 构建一个 jdk 镜像, 并输出 java -version
FROM centos
MAINTAINER gty
ADD ["jdk-8u221-linux-x64.tar.gz","/usr/local/apps/"]
ENV JAVA_HOME /usr/local/apps/jdk1.8.0_221/
ENV PATH $PATH:$JAVA_HOME/bin/
CMD ["java","-version"]

执行命令
末尾的点不能少. 是指的寻找 .dockerignore

docker build -t one11:1.0  .

也可以显示的指定 Dockerfile 的路径

docker build  -t two22:1.0   -f  Dockerfile.two  .
  1. 构建一个 jdk 镜像, 并输出 java -version, 练习使用一些复杂的过程控制
FROM centos
MAINTAINER gty
WORKDIR /usr/local/
COPY ["jdk-8u221-linux-x64.tar.gz","soft/"]
RUN mkdir apps &&\
    tar -xf soft/jdk-8u221-linux-x64.tar.gz -C apps &&\
    ln -s /usr/local/apps/jdk1.8.0_221/ apps/jdk
ENV JAVA_HOME apps/jdk/
ENV PATH $PATH:$JAVA_HOME/bin/
CMD java -version
  1. 自定一个项目, 将其打包为镜像
FROM java:8
MAINTAINER gty
COPY 18-docker-1.0.0.jar /usr/local
ARG one11=0            # 定义一个变量,进行赋值测试
RUN echo $one11
EXPOSE 8080
CMD java -jar /usr/local/18-docker-1.0.0.jar

docker-compose

docker-compose 是编排容器的。例如,你有一个 php 镜像,一个 mysql 镜像,一个 nginx 镜像。如果没有 docker-compose,那么每次启动的时候,你需要敲各个容器的启动参数,环境变量,容器命名,指定不同容器的链接参数等等一系列的操作,相当繁琐。而用了 docker-composer 之后,你就可以把这些命令一次性写在 docker-composer.yml 文件中,以后每次启动这一整个环境(含 3 个容器)的时候,你只要敲一个 docker-composer up 命令就 ok 了。就好像是 docker 容器的构建过程记录一样.

dockerfile 的作用是从无到有的构建镜像。它包含安装运行所需的环境、程序代码等。这个创建过程就是使用 dockerfile 来完成的。Dockerfile - 为 docker build 命令准备的,用于建立一个独立的 image ,在 docker-compose 里也可以用来实时 build docker-compose.yml - 为 docker-compose 准备的脚本,可以同时管理多个 container ,包括他们之间的关系、用官方 image 还是自己 build 、各种网络端口定义、储存空间定义等.

安装 docker-compose

这是 docker compose 的 GitHub 主页发行版

https://github.com/docker/compose/releases/

  1. 下载从 GitHub
    -L 指定的是链接
    -o (这是路径, 指定到文件, docker-componse 是文件名)
curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  1. 给 docker-componse 权限
chmod +x /usr/local/bin/docker-compose
  1. 注册环境变量
    或者向 java_home 似的那样注册也行
ln -s  /usr/local/bin/docker-compose  /usr/bin/docker-compose
  1. 验证
docker-compose  -version

docker-compose.yml

在 docker-compose.yml 中可以包含一个 Dockerfile, 进行直接镜像构建和启动.
先构建两个 web 服务的镜像. 然后编排进入 redis 和 mysql
1. 第一个 Dockerfile

# 第一个Dockerfile.web11

FROM java:8
COPY 18-docker-1.0.0.jar /usr/local
ARG oneparam=default
ARG twoparam=default
RUN echo "oneparam是:$oneparam" &&  echo "twoparam是:$twoparam"
EXPOSE 8080
CMD java -jar /usr/local/18-docker-1.0.0.jar

第二个 Dockerfile

# 第二个Dockerfile.web22

FROM java:8
COPY 18-docker-1.0.0-web22.jar /usr/local
EXPOSE 8080
CMD java -jar /usr/local/18-docker-1.0.0-web22.jar

3. 编写 docker-compose.yml
注释都有用法, 注意缩进

version: '3'

#在这里声明服务s
services:

  # 第一个容器服务,名字是web11
 web11:   # 这是服务名
 container_name: web11   # 指定容器名

 image: "web11:1.0"      # 指定dockerfile构建的镜像名

 build: 
 context: ./           # dockerfile上下文, ./ 表示当前docker-compose.yml的文件位置

 dockerfile: ./Dockerfile.web11    # 相对于上下文的位置

 args:
 - oneparam="false"    # 参数如果是Boolean,必须用"Boolean"括起来才能识别,否则就是字符串
 - twoparam=19         # 可以给Dockerfile中的ARG参数传值传值

 links:                # 将Redis11容器的IP配置进web11容器的hosts文件, 包括redis11中的环境变量
       # 例如 192.168.25.25  redis11   在web11中可以ping redis11,可以使用redis11作为redis的IP
 - "redis11"

       # 同时可以指定别名,会同时添加web22以及otherwebservice 
 - "web22:otherwebservice"   

 ports:
 - "8080:8080"            # - 指定一个列表,进行端口映射

 depends_on:                # 指定web11的启动需要在redis11之后在启动.
 - "redis11"              # 其实就是指定启动的顺序.使用depends_on后web11一定在redis11之后启动
 - "mysql44"

 extra_hosts:                 # 在/etc/hosts中添加对应的hosts解析
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"     # 启动之后就会有 50.31.209.229  otherhost

  # 这是第二个容器
 web22:
 container_name: web22
 image: "web22:1.0"
 build: 
 context: .
 dockerfile: ./Dockerfile.web22
 links:
 - "redis11"
 - "mysql44"
 ports:
 - "8081:8080"

  # 这是第三个容器
 redis11:
 image: "redis"
 container_name: redis11
 ports: #端口映射
 - "6379:6379"

  # 这是第四个容器
 mysql44:
 image: mysql
 container_name: mysql44
 ports:
 - 3306:3306
 volumes:
      # - /usr/local/mysql/data:/var/lib/mysql        # 使用固定的目录挂载mysql的容器的目录
 - /usr/local/mysql/conf/my.conf:/etc/mysql/my.conf 
      # 使用docker的路径挂载容器目录docker volume ls和docker volume inspect 目录名查看具体位置
 - mysql-data:/var/lib/mysql                    
      
 privileged: true     # 提升容器内用户权限为真正的root权限

 environment:         # 配置环境变量
 - MYSQL_ROOT_PASSWORD=112233

# 配置数据卷,与service平级
volumes:
 mysql-data:

4. 启动 docker-compose

docker-compose up  -d      # -d是后台启动

使用 idea 连接 docker 并发布镜像

1. 开启 docker 远程访问

vim   /etc/sysconfig/docker

添加   -H unix://var/run/docker.sock -H tcp://0.0.0.0:2375

重启 docker

systemctl daemon-reload

systemctl start docker

查看端口 2375 的占用, 并关闭防火墙

netstat -tulp

2.idea 安装 docker 插件

3. 配置 docker 访问

4. 在 maven 中引入 docker 插件

<!--使用docker-maven-plugin插件-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.0.0</version>
                <!--将插件绑定在某个phase执行-->
                <executions>
                    <execution>
                        <id>build-image</id>
                        <!--将插件绑定在package这个phase上。也就是说,用户只需执行mvn package ,就会自动执行mvn docker:build-->
                        <!--这里我测试docker-compose的时候注释掉了.需要使用的时候记得打开-->
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--指定生成的镜像名,镜像名中不能有下划线特殊符号,可以:指定标签-->
                    <imageName>gty/${project.artifactId}:1.0</imageName>
                    <!--指定标签可以用<imageTags>标签,但是也可以name:tag指定-->
                    <!--指定维护者-->
                    <maintainer>gty</maintainer>
                    <!-- 指定 Dockerfile 路径-->
                    <dockerDirectory>${project.basedir}/docker</dockerDirectory>
                    <!--指定远程 docker api地址-->
                    <dockerHost>http://192.168.25.25:2375</dockerHost>
                    <!-- 这里是复制 jar 包到 docker 容器指定目录配置 -->
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <!--jar 包所在的路径  此处配置的 即对应 target 目录-->
                            <directory>${project.build.directory}</directory>
                            <!-- 需要包含的 jar包 ,这里对应的是 Dockerfile中添加的文件名 -->
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>

5. 在项目根目录下创建 / docker/Dockerfile

  1. 项目打包, 同时开始了 Dockerfile 的构建

  1. 在启动项中添加

然后配置一下