言简意赅的讲解Docker解决的痛点

Docker的背景与意义

Docker 是一个开源的容器化平台,旨在帮助开发者简化应用程序的构建、打包、部署和运行。通过将应用程序及其所有依赖打包到一个容器中,Docker 使得应用能够在任何环境中以相同的方式运行。最初,Docker 是 Kubernetes 默认的容器运行时(container runtime),但为了适配更多的容器运行时,Kubernetes 将 Docker 更底层的容器运行时管理工具——containerd(Kubernetes 的 CRI 工具)作为核心容器运行时,这样就实现了容器运行时管理的灵活切换。除了 containerd,还可以将容器运行时管理切换为其他工具,比如 CRI-OgVisor,它们都能与 Kubernetes 集成,帮助 Kubernetes 实现容器的隔离和管理。

Docker 解决的问题

  1. 一致性
    Docker 的最大优势之一是容器的一致性。通过 Docker 镜像生成的容器在任何环境中都能保持相同的运行状态,不论是在开发、测试、生产等环境中,容器中的应用始终是相同的。例如,你可以在本地开发机器中使用:

    docker start {容器名}
    

    这个命令就能在任何环境中启动一个一致的容器,确保应用的一致性。

  2. 高效性
    Docker 容器通过共享宿主机的操作系统内核,而不是像虚拟机那样每个实例都需要一个完整的操作系统内核。这种方式显著降低了资源开销,解决了传统虚拟机在启动速度、占用空间方面的瓶颈。容器在宿主机内核之上直接运行,避免了冗余的资源消耗,因此启动速度更快,资源占用更低。

    用例
    假设你需要部署一个 Web 应用(例如 Node.js),如果使用虚拟机,每次启动都需要启动一个完整的操作系统,而 Docker 容器只需启动应用所需的部分资源,启动时间可能从几分钟缩短到几秒钟。比如:

    docker run -d -p 8080:80 nginx
    

    上述命令可以在几秒钟内启动一个 Nginx 容器,而不需要虚拟化的完整操作系统。

  3. 隔离性
    Docker 的容器化特性实现了强隔离性,这是很多人选择 Docker 的核心原因之一。容器内的应用及其运行环境相互隔离,不会互相干扰。例如,如果你在同一台机器上运行两个需要使用 8080 端口的应用,Docker 可以通过端口映射来解决端口冲突问题。你可以为两个容器分别映射不同的端口:

    docker run -d -p 8080:8080 container1
    docker run -d -p 9090:8080 container2
    

    这样,即使容器内部使用的是相同的端口,宿主机上不同的端口(如 8080 和 9090)会指向不同的容器,解决了端口占用冲突的问题。

    用例
    假设你有两个不同版本的数据库应用,分别运行在不同的容器中,你可以通过端口映射将它们的数据库端口暴露到宿主机上:

    docker run -d -p 3306:3306 mysql:5.7   # MySQL 5.7 容器
    docker run -d -p 3307:3306 mysql:8.0   # MySQL 8.0 容器
    

    宿主机的 3306 端口映射到 MySQL 5.7 容器,3307 端口映射到 MySQL 8.0 容器,这样就解决了端口冲突的问题。

  4. 可移植性
    由于 Docker 容器将应用程序与其依赖打包在一起,因此容器可以在任何支持 Docker 的平台上运行,无论是在开发者的本地环境、云服务器,还是其他任何支持 Docker 的系统。这种高度的可移植性使得开发、测试和生产环境中的一致性变得更加容易保证。

    用例
    假设你开发了一个 Python 应用,并且在本地使用 Docker 容器进行调试。当你准备部署到生产环境时,可以直接将这个容器镜像推送到 Docker Hub 或私有镜像仓库,然后在生产环境的服务器上拉取并运行这个容器:

    docker pull myusername/my-python-app
    docker run -d myusername/my-python-app
    

    这使得开发、测试、生产环境之间的过渡变得极其顺畅。

  5. 可扩展性
    Docker 支持容器的水平扩展,能够通过增加更多的容器实例来扩展应用的容量。此外,Docker 容器也与容器编排工具(如 Kubernetes)紧密集成,后者可以自动化管理容器的部署、扩展和负载均衡等。

    用例
    假设你有一个负载均衡的 Web 应用,需要处理大量并发请求。你可以通过 Kubernetes 或 Docker Swarm 启动多个容器实例来实现水平扩展:

    docker service scale my-web-app=5  # 使用 Docker Swarm 启动 5 个容器实例
    

    Kubernetes 也可以根据负载自动扩展容器实例:

    kubectl scale deployment my-web-app --replicas=5
    

Docker 的核心用法

Docker 的主要功能是容器的构建、打包、部署和运行。与容器编排工具 Kubernetes 相结合,Docker 解决了容器的生命周期管理问题。

  1. Dockerfile:构建 Docker 镜像的核心文件,定义了如何从基础镜像创建新镜像并运行应用程序。Dockerfile 使得容器镜像的创建过程自动化。

    用例1
    你可以创建一个简单的 Dockerfile 来构建一个 Node.js 应用的镜像:

    FROM node:14
    WORKDIR /app
    COPY . /app
    RUN npm install
    CMD ["npm", "start"]
    

    说明

    • 这个 Dockerfile 从官方的 Node.js 14 镜像开始,拷贝当前目录下的应用代码并安装依赖,最后指定运行命令为 npm start

    用例2
    你还可以进阶的使用dockerfile完成更多更复杂的操作,比如下面这个项目中同时启动多个端口,即build了前端静态又同时启动了一个后端服务。这样的话这个镜像构建出来直接完成了前后端的部署,在同一个容器中。可玩性非常高:

    # 使用官方 Node.js 镜像
    FROM node:18-alpine AS builder
    
    ARG REACT_APP_HOMEPAGE_KEYCLOAK_REALMS
    ARG REACT_APP_HOMEPAGE_KEYCLOAK_CLIENTID
    
    # 设置工作目录
    WORKDIR /app
    
    # 复制项目文件
    COPY . .
    
    # 安装 pnpm
    RUN npm install -g pnpm
    
    # 安装依赖
    RUN pnpm install
    
    ENV REACT_APP_HOMEPAGE_KEYCLOAK_REALMS=${REACT_APP_HOMEPAGE_KEYCLOAK_REALMS}
    ENV REACT_APP_HOMEPAGE_KEYCLOAK_CLIENTID=${REACT_APP_HOMEPAGE_KEYCLOAK_CLIENTID}
    
    # 生成生产构建
    RUN pnpm run build
    
    # 使用轻量级的 Node.js 镜像来运行构建后的文件
    FROM node:18-alpine
    
    # 设置工作目录
    WORKDIR /app
    
    # 只复制构建后的文件
    COPY --from=builder /app/build ./build
    COPY . .
    
    # 安装 serve 和 concurrently
    RUN npm install -g serve concurrently
    
    # 设置后端工作目录
    WORKDIR /app/.docker-node
    
    RUN npm install -g pnpm
    
    RUN pnpm install
    
    # 暴露端口
    EXPOSE 3000 4000
    
    # 启动命令
    CMD ["concurrently", "serve -s ../build -l 3000", "node index.js"]
    

    说明

    • Dockerfile 分为两阶段:第一个阶段(builder)用于构建前端应用,第二个阶段用于创建运行时容器,确保最终镜像的大小更小。
    • 使用了 pnpm 来安装依赖,避免了 npm 带来的一些性能问题。
    • serve 用于提供构建后的前端静态文件,concurrently 用于同时启动前端和后端服务。
    • 最终通过 CMD 命令运行前端和后端,前端服务监听 3000 端口,后端服务监听 4000 端口。
  2. Docker镜像与容器
    镜像是容器的模板,每次创建容器时,都会从镜像中复制出一个新的实例。镜像是只读的,而容器是镜像的可执行实例,容器的变更不会影响到原始镜像。

    用例
    假设你已经有了一个基础镜像 python:3.8,你可以通过 docker build 命令从 Dockerfile 构建出自己的镜像:

    # 使用 Python 3.8 镜像作为基础镜像
    FROM python:3.8
    
    # 设置工作目录
    WORKDIR /app
    
    # 复制项目文件
    COPY . .
    
    # 安装项目依赖
    RUN pip install -r requirements.txt
    
    # 设置容器启动时的命令
    CMD ["python", "app.py"]
    
    docker build -t my-python-app .
    

    构建完成后,你就可以基于这个镜像启动容器:

    docker run -d my-python-app
    
  3. Docker命令:

    Docker 提供了许多常用的命令来管理容器和镜像:

    • docker pull:从 Docker Hub 或其他镜像仓库拉取镜像。
      docker pull ubuntu
      
    • docker build:从 Dockerfile 构建镜像。
      docker build -t myimage .
      
    • docker run:启动一个新的容器。
      docker run -d -p 8080:80 nginx
      
    • docker ps:列出正在运行的容器。
      docker ps
      
    • docker stop:停止一个正在运行的容器。
      docker stop container_name
      
    • docker exec:在容器中执行命令。
      docker exec -it container_name /bin/bash
      

通过上述内容,你就已经基本理解了这个产品,基础用法我也都有展示。如果你能融会贯通,我相信你会很强

Best
Wenhao (楠博万)

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐