【工具】docker

1 理解docker

1.1 什么是docker

docker是一个用来装应用的容器,可以把任何想得到的程序放在docker中。

1.2 docker思想

1.2.1 集装箱

目前我们要把程序部署到一台新的机器上,可能会启动不起来,比如少一些配置文件什么的或者少了什么数据,有了docker的集装箱可以保证我们的程序不管运行在哪不会缺东西。

1.2.2 标准化

  • 运输方式
    • Docker的标准化确实可以让应用在不同的环境中运行,而且可以方便地在不同的机器之间传输。
    • 类似于Git,Docker也采用了类似的版本控制机制,可以对应用进行版本管理和追踪。
      Docker和Git的主要区别在于:
      1. Docker是一个应用容器化的工具
        Git是一个版本控制工具。
      2. Docker的主要目的是为了方便应用的部署和运行
        Git的主要目的是为了方便代码的管理和协作。
  • 存储方式
    • 未使用docker:程序存储到笔记本后,需要记住所在目录,下次还需要打开
    • 使用docker:不再需要记住程序所在目录,只需要一条命令。
  • API接口
    • docker提供了一系列REST风格的API接口,包括对应用的启动、停止、查看、删除等操作。
    • 未使用docker:启动tomcat时要执行start up命令,停止需要执行shut down命令等等
    • 使用docker:只需要记住docker命令对其的操作即可

1.2.3 隔离

说到隔离,这里就要提一下和docker有着类似效果的虚拟机了。我们在使用虚拟机的时候 ,虚拟机有自己的CPU、硬盘、内存等等,感受不到外界主机的存在。那么docker也是类似的,但是docker更加轻量级。

Docker容器技术原理(了解)

  • chroot
    • 针对正在运作的软件行程和它的子进程,改变它外显的根目录
    • 一个运行在这个环境下,经由 chroot 设置根目录的程序,它不能够对这个指定根目录之外的文件进行访问动作,不能读取,也不能更改它的内容
  • Namespace
    • 对内核资源进行隔离
      • pid namespace:用于隔离进程 ID。
      • net namespace:隔离网络接口,在虚拟的 net namespace 内用户可以拥有自己独立的 IP、路由、端口等。
      • mnt namespace:文件系统挂载点隔离。
      • ipc namespace:信号量,消息队列和共享内存的隔离。
      • uts namespace:主机名和域名的隔离。
  • Cgroups
    • 通常用来限制容器的 CPU 和内存等资源的使用
  • 联合文件系统
    • 通过创建文件层进程操作的文件系统
    • Docker 使用联合文件系统为容器提供构建层,使得容器可以实现写时复制以及镜像的分层构建和存储。
    • 常用的联合文件系统有 AUFS、Overlay 和 Devicemapper等。

1.3 docker解决的问题

  • 系统环境不一致(集装箱解决)
    docker会把程序需要的环境需要都放入集装箱,例如:操作系统、jdk、tomcat、代码、配置文件等等
  • 系统卡顿(隔离性解决)
    Docker可以限制容器的资源使用,如CPU和内存等,以避免容器过度占用系统资源导致系统崩溃。但是,如果其他程序出现问题,例如占用了过多的系统资源,可能会影响整个系统的性能,包括Docker容器。所以,Docker并不能完全保证容器的运行不受其他程序的影响。
  • 服务器崩溃(标准化解决)
    可以快速部署无上限台服务器,因为标准化的原因,程序部署变的只需要一两条docker命令即可解决问题。

1.4 docker核心概念

1.4.1 镜像

  • 每一层都是可以控制的
  • docker镜像系统的每一层都是只读的
  • 把每一层加载完成后,这些文件就被看成同一个目录,就相当于只有一个文件的系统,这种系统就被称为镜像

1.4.2 容器

  • docker和虚拟机系统的区别在于,这里的文件系统是一层一层的,下面的n层都是只读,最上面一层却是可写的。因为程序的运行,势必要带有日志的写、文件的写和修改操作。
  • 在程序的运行过程中,如果要写镜像文件时,因为镜像的每一层都是只读的,它会把文件的每一层拷到文件的最上层,然后再对它进行修改,修改之后,当我们的应用读一个文件时会从顶层进行查找,如果没有才会找下一层。
  • 容器的最上一层是可以修改的,镜像是不能修改的,这样就能保证镜像可以生成多个容器独立运行,没有任何干扰。

1.4.1 仓库

  • 我们要先把我们的镜像传到docker仓库中,再由目的地把docker仓库拉过去,这就完成了这样的一次传输过程。这和git项目的推送和拉取十分相似。

2 docker基本命令

参考文章
https://blog.csdn.net/leilei1366615/article/details/106267225?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168481266516800180630145%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168481266516800180630145&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-106267225-null-null.142^v87^koosearch_v1,239^v2^insert_chatgpt&utm_term=docker%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4&spm=1018.2226.3001.4187

3 dockerfile & docker-compose的使用

区分dockerfile & docker-compose

  • Dockerfile:用来定义Docker镜像的构建规则的文件,文本文件以.dockerfile结尾
  • Docker-compose:用于定义和运行多个 Docker 容器,过一个.yml文件来配置应用程序的服务,并使用一个命令来启动、停止和重启整个应用程序。

3.1 Dockerfile

  • Dockerfile用于管理容器
  • 下面提供一个基本的Dockerfile模板:
# 设置基础镜像
FROM base_image

# 维护者信息 可不写
MAINTAINER your_name <your_email>

# 设置工作目录
WORKDIR /app

# 复制本地文件到容器中
COPY . /app

# 安装依赖
RUN apt-get update && \
    apt-get install -y dependency1 dependency2

# 设置环境变量
ENV ENV_VARIABLE value

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["command"]
  • 可变参数

    • base_image:你所选择的基础镜像
    • your_nameyour_email:你的个人信息
    • dependency1dependency2:需要安装的依赖
    • value:需要设置的环境变量
    • 8080:需要暴露的端口
    • command:启动命令
  • 举例(SpringBoot后端项目):

# 基础镜像
FROM openjdk:8-jdk-alpine

# 作者信息
MAINTAINER haomin <2219880752@qq.com>

# 添加应用
ADD target/myapp.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

3.1.1 部署SpringBoot项目详细流程

★ 部署SpringBoot项目的流程分为以下几步:

  1. 编写Dockerfile文件
  2. 构建Docker镜像
  3. 运行Docker容器

★ 具体步骤

① 在项目根目录下创建Dockerfile文件

IDEA中右键根目录,上图的Demo,new中可以直接创建Dockerifle文件
image

# 使用官方的Java 8作为基础镜像
FROM openjdk:8-jdk-alpine

# 添加镜像信息 LABEL可以多次使用
LABEL name="my-springboot-image"
LABEL version="1.0"

# 将当前目录下的jar包复制到容器的根目录下
COPY target/*.jar /app.jar

# 暴露容器的8080端口 (端口自定义,别和其他端口冲突即可)
EXPOSE 8080


# 启动容器时运行的命令
ENTRYPOINT ["java","-jar","/app.jar"]
  • 解释:
    • COPY与ADD的区别
      • COPY:只能复制本地文件或目录到容器中
        源文件可以是一个通配符表达式,如 *.jar,而 ADD 不支持通配符表达式
      • ADD:支持复制本地文件或目录到容器中,同时还支持解压缩本地压缩文件到容器中
        源文件可以是一个通配符表达式,除COPY支持的外,还支持从 URL 或 tar 文件中复制文件到容器中
    • ENTRYPOINT三个参数
      • "java":要执行的命令
      • "-jar": 是第一个参数(java)命令的一个选项
      • "/app.jar":要执行的 JAR 文件的路径
② SpringBoot打jar包

A SpringBoot打包前要确保添加以下依赖和插件
依赖

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

插件

<build>
    <plugins>
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>docker-maven-plugin</artifactId>
            <version>1.2.0</version>
            <configuration>
                <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
                <dockerDirectory>src/main/docker</dockerDirectory>
                <resources>
                    <resource>
                        <targetPath>/</targetPath>
                        <directory>${project.build.directory}</directory>
                        <include>${project.build.finalName}.jar</include>
                    </resource>
                </resources>
            </configuration>
        </plugin>
    </plugins>
</build>

B 确认好后 打包
image
C 打包成功
image
image

③ 构建Docker镜像

image
点击Build Image for 'Dockerfile'
image

④ 运行Docker容器

image
同样是它,点击Run Dockerfile
image

⑤测试

  • 当前我们的界面如下
    image
  • 当我们使用postman进行测试(仅简单测试,不涉及数据库连接)时
    image
    该测试,判断参数为空,直接返回null值,不进行数据库查询,因此可以作为简单测试,但是发现目前无法使用本地的8080端口。这是因为在 Docker 中,容器内的端口默认是隔离的,不能直接从主机访问。因此,当你在 Dockerfile 中使用 EXPOSE 命令时,它只是声明了容器内部应该监听的端口,但并没有将该端口暴露给主机。要将容器内部的端口映射到主机上,需要使用 -p 参数来指定端口映射规则。
  • 因此我们要将容器内部的 8080(暴露) 端口映射到主机的 8000(自定义) 端口上,这样就可以通过访问主机的 8000 端口来访问容器内部的应用程序了
    image
    • IDEA内打开终端,这个在Windows下的PowerShell也是一样的
    • 使用docker ps命令查询镜像信息,找到我们创建镜像对应的镜像名IMAGE
    • 使用docker run -p 本地端口号:暴露端口号 镜像名,将容器内部端口映射到主机端口
  • 这时就可以访问应用程序了
    image
  • 但是涉及mysql环境还需要配置,之后与Docker-compose搭配使用

3.2 Docker-compose

3.2.1 基础模板(包含MySQL Redis Nignx Node.js)

version: '3'

services:
  # MySQL数据库服务
  mysql:
    # 使用MySQL官方镜像latest版本
    image: mysql:latest
    # 总是重启
    restart: always
    # sql环境
    environment:
      # root用户密码
      MYSQL_ROOT_PASSWORD: your_mysql_root_password
      # 数据库名称
      MYSQL_DATABASE: your_mysql_database_name
      # 用户名
      MYSQL_USER: your_mysql_user
      # 密码
      MYSQL_PASSWORD: your_mysql_password
    ports:
      # 将容器的3306端口映射到主机的3306端口
      - "3306:3306"
    volumes:
      # MySQL数据存储在一个名为mysql_data的卷中
      - mysql_data:/var/lib/mysql

  # Redis缓存服务
  redis:
    image: redis:latest
    restart: always
    ports:
      - "6379:6379"

  # Nginx反向代理服务
  nginx:
    image: nginx:latest
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/certs:/etc/nginx/certs
      - ./nginx/html:/usr/share/nginx/html
      - ./nginx/logs:/var/log/nginx

  # Node.js应用服务
  app:
    build: .
    restart: always
    environment:
      NODE_ENV: production
      PORT: 3000
      MYSQL_HOST: mysql
      MYSQL_USER: your_mysql_user
      MYSQL_PASSWORD: your_mysql_password
      MYSQL_DATABASE: your_mysql_database_name
      REDIS_HOST: redis
      REDIS_PORT: 6379
    ports:
      - "3000:3000"
    depends_on:
      - mysql
      - redis

volumes:
  mysql_data:

3.2.2 运行docker-compose

  1. 编写 Docker Compose 文件:在项目根目录下创建一个名为 docker-compose.yml 的文件,并在其中定义服务和容器的配置。(参考3.2.1)

  2. 启动 Docker Compose:在终端中进入项目根目录,然后运行 docker-compose up 命令,Docker Compose 将会启动并运行所有定义的服务和容器。
    Docker Desktop的容器Containers显示
    image

  3. 停止 Docker Compose:在终端中进入项目根目录,然后运行 docker-compose down 命令,Docker Compose 将会停止并删除所有运行的服务和容器。

4 多阶段构建的方式编写dockerfile

  • 使用多阶段构建的方式可以有效地减小 Docker 镜像的体积,提高构建效率。下面是一个使用多阶段构建的 Dockerfile,以部署mysql为例:
# 第一阶段:构建 MySQL
FROM ubuntu:18.04 AS builder

# 安装 MySQL
RUN apt-get update && \
    apt-get install -y mysql-server && \
    rm -rf /var/lib/apt/lists/*

# 第二阶段:运行 MySQL
FROM ubuntu:18.04

# 复制 MySQL 安装包
COPY --from=builder /var/lib/mysql /var/lib/mysql

# 安装 MySQL
RUN apt-get update && \
    apt-get install -y mysql-server && \
    rm -rf /var/lib/apt/lists/*

# 启动 MySQL
CMD ["mysqld"]
  • 上述 Dockerfile 中:
    • 第一阶段使用 ubuntu:18.04 作为基础镜像,安装 MySQL 并创建数据目录
      • RUN 表示在构建镜像时执行命令。
      • apt-get update 用于更新apt软件包列表。
      • apt-get install -y mysql-server 用于安装MySQL服务器,-y 表示自动回答yes。
      • rm -rf /var/lib/apt/lists/* 用于清理apt软件包缓存,减小镜像大小。
    • 第二阶段也使用 ubuntu:18.04 作为基础镜像,从第一阶段复制 MySQL 数据目录,并安装 MySQL。最后启动 MySQL 服务。
      • --from=builder 之前构建的镜像的名称或 ID,这个指令通常用于将之前构建的镜像中的某些文件或目录复制到当前镜像中,以便在当前镜像中使用。
  • 为什么第一、二阶段进行了两次mysql的安装
    • 这是因为 Docker 的多阶段构建特性,第一阶段是用来构建 MySQL 的,第二阶段是用来运行 MySQL 的。
    • 在第二阶段中,需要将第一阶段构建好的 MySQL 安装包复制到当前阶段中,然后再次安装 MySQL。
    • 这样做的好处是可以减小 Docker 镜像的大小,避免将构建 MySQL 的过程打包到最终的镜像中。
      因此使用多阶段构建的方式可以避免将编译工具和中间文件等不必要的内容打包进最终的镜像中,从而减小镜像的体积。
posted @ 2023-05-19 09:30  沫漓fighting  阅读(87)  评论(0)    收藏  举报