Docker 是一种容器技术,可以让开发者在一个隔离的环境中运行和部署应用程序,从而提高应用程序的可移植性、安全性和效率。但是仅仅使用 Docker 并不能保证应用程序的可靠性、可扩展性和可维护性,为了实现这些目标,Docker 的使用也需要进行一些工程化改造。因此也就有了本文,本文中博主将给大家介绍 Docker 工程化的发展以及实践方式。
Docker 工程化发展
Docker 工程化的发展历程可以追溯到 2013 年,当时 Docker 公司成立并推出了第一个版本以及 Dockerfile,Dockerfile 是一种文本文件,它包含了一系列的指令,用于定义一个 Docker 镜像。这些指令可以指定基础镜像、安装软件包、设置环境变量等等。通过 Dockerfile 开发者可以快速地构建自己的 Docker 镜像,从而实现快速部署和可移植性。
2015 年 Docker 推出 Docker Compose。Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它使用一个 YAML 文件来配置应用程序的服务、网络和卷,然后使用一个命令来创建和启动所有服务。通过 Docker Compose 使得开发者可以更方便地管理和部署多容器应用程序
2017 年 Docker 推出 Docker Swarm 技术。Docker Swarm 是 Docker 自己的 Docker 容器本地集群解决方案,具有与 Docker 生态系统紧密集成并使用自己的 API 的优势。它监视跨服务器群集的容器数量,是在没有其他硬件的情况下创建群集 docker 应用程序的最便捷方式。
2023 年 Docker 发展至今 Kubernetes 已经成为了容器编排引擎的事实标准。Kubernetes 技术是在 2014 年正式发布的,是 Google 开源的一个容器编排引擎,用来对容器进行自动部署、扩缩和管理。本文会给大家简单介绍下 Kubernetes。
推荐博主开源的 H5 商城项目waynboot-mall,这是一套全部开源的微商城项目,包含三个项目:运营后台、H5 商城前台和服务端接口。实现了商城所需的首页展示、商品分类、商品详情、商品 sku、分词搜索、购物车、结算下单、支付宝/微信支付、收单评论以及完善的后台管理等一系列功能。 技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。分模块设计、简洁易维护,欢迎大家点个 star、关注博主。
github 地址:github.com/wayn111/way...
工程化实践
为了实现工程化目标,开发者需要遵循一些工程化的最佳实践,包括以下五个:
- 使用 Dockerfile 来定义应用程序的镜像
- 使用 docker-compose 来管理多个容器之间的依赖和协作
- 使用 docker swarm 或 kubernetes 来实现容器的集群管理和服务发现
- 使用 CI/CD 工具来自动化应用程序的构建、测试和部署
- 使用监控和日志工具来收集和分析应用程序的运行状况和性能指标
使用 Dockerfile 来定义应用程序的镜像
Dockerfile 是一个文本文件,用来描述如何从一个基础镜像(例如 ubuntu 或 alpine)构建出一个新的镜像,包括安装依赖、复制文件、设置环境变量、暴露端口等操作。使用 Dockerfile 可以让开发者清晰地记录应用程序的配置和依赖,以及保证应用程序在不同的环境中运行的一致性。
一个简单的 Dockerfile 示例如下:
dockerfile
# 基于 Java 镜像构建
FROM openjdk:8u212-jre
# 配置参数
ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom -Duser.timezone=GMT+8 -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"
# 设置时区
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 设置工作目录
WORKDIR /app
# 复制 jar 包到镜像里
COPY target/my-app.jar /app/my-app.jar
# 暴露端口
EXPOSE 8080
# 配置启动命令
CMD java $JAVA_OPTS -jar /app/my-app.jar --server.port=8080
简单介绍下上述 Dockerfile 示例的语法:
- FROM - 指定基础镜像,这里是基于开源的 Java 8 JRE 镜像。
- ENV - 设置环境变量,这里配置了时区、JVM 参数等。
- RUN - 执行命令,这里用来设置时区。
- WORKDIR - 设置工作目录,相当于 cd 命令,之后的命令都在这个目录执行。
- COPY - 复制文件到镜像,这里复制了编译好的 Java jar 包。
- EXPOSE - 声明暴露的端口,这里是 8080。
- CMD - 启动命令,这里配置了启动 Java 程序的命令。
我们要使用 Dockerfile 构建镜像的话,可以使用以下命令:
bash
docker build -t my-app .
其中 -t
参数指定了镜像的名称和标签(默认为 latest),.
表示当前目录。
要运行构建好的镜像,可以使用以下命令:
bash
docker run -p 8080:8080 my-app
其中 -p
参数指定了容器内外部的端口映射关系,my-app
是镜像的名称。
这样我们的 Java 服务就可以在容器里运行,并能够通过宿主机的 8080 端口访问了。
使用 docker-compose 来管理多个容器之间的依赖和协作
在实际开发中,一个应用程序通常不是单独运行的,而是需要与其他服务(例如数据库、缓存、消息队列等)进行交互。这些服务也可以使用 Docker 来运行,但是如果每个服务都需要单独使用docker run
命令来启动,那么就会非常繁琐和容易出错。为了解决这个问题,可以使用 docker-compose 来定义和运行多个容器之间的依赖和协作关系。
docker-compose 是一个工具,可以让开发者使用一个 YAML 文件(通常命名为docker-compose.yml
)来描述多个容器之间的配置,包括镜像、端口、环境变量、挂载卷、网络等。使用 docker-compose 可以让开发者一次性地启动或停止所有相关的容器,以及方便地管理容器之间的通信。
一个简单的 docker-compose.yml 示例如下:
yaml
version: '3'
services:
waynboot-mobile-api:
image: ibm-semeru-runtimes:open-17-jdk
container_name: mobile
volumes:
- /etc/localtime:/etc/localtime
- /home/logs:/home/logs
- /opt/waynboot-mall/upload:/opt/waynboot-mall/upload
- ./jars/waynboot-mobile-api.jar:/home/app/waynboot-mobile-api.jar
restart: always
command: java -Xms512m -Xmx512m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-mobile-api.jar
ports:
- "82:82"
environment:
- TZ=Asia/Shanghai
- LOG_PATH_PREFIX=/home/logs
- UPLOAD_DIR=/opt/waynboot-mall/upload
network_mode: "host"
# 依赖于redis和mysql,在启动本服务之前会先启动依赖的服务
depends_on:
- redis
- mysql
- rabbitmq
- elasticsearch
waynboot-admin-api:
image: ibm-semeru-runtimes:open-17-jdk
container_name: admin
volumes:
- /etc/localtime:/etc/localtime
- /home/logs:/home/logs
- /opt/waynboot-mall/upload:/opt/waynboot-mall/upload
- ./jars/waynboot-admin-api.jar:/home/app/waynboot-admin-api.jar
restart: always
command: java -Xms512m -Xmx512m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-admin-api.jar
ports:
- "81:81"
environment:
- TZ=Asia/Shanghai
- LOG_PATH_PREFIX=/home/logs
- UPLOAD_DIR=/opt/waynboot-mall/upload
network_mode: "host"
# 依赖于redis和mysql,在启动本服务之前会先启动依赖的服务
depends_on:
- redis
- mysql
- rabbitmq
- elasticsearch
# 依赖于redis和mysql,在启动本服务之前会先启动依赖的服务
depends_on:
- redis
- mysql
- rabbitmq
- elasticsearch
# Redis服务
redis:
container_name: redis
image: redis:latest
ports:
- "6379:6379"
volumes:
- ./redis/redis.conf:/etc/redis/redis.conf
- ./redis/data:/data
command: redis-server /etc/redis/redis.conf
network_mode: "host"
# RabbitMQ
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
container_name: rabbitmq
environment:
#rabbitmq的初始用户名
RABBITMQ_DEFAULT_USER: guest
#rabbitmq的初始密码
RABBITMQ_DEFAULT_PASS: guest
network_mode: "host"
# MySQL服务
mysql:
container_name: mysql
image: mysql:8.0.33
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'wayn_shop'
volumes:
- ./mysql/data:/var/lib/mysql
- ./db-init:/docker-entrypoint-initdb.d
command: mysqld --lower_case_table_names=1 --default-authentication-plugin=mysql_native_password
network_mode: "host"
# Elasticsearch服务
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.10.0
container_name: elasticsearch
volumes:
- ./es/data:/usr/share/elasticsearch/data
- ./es/plugins:/usr/share/elasticsearch/plugins
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
network_mode: "host"
简单介绍下上述 docker-compose 示例语法:
- version - 指定 docker-compose 版本,这里是 3。
- services - 定义 compose 项目中的服务名称。
- image - 指定服务使用的镜像。
- container_name - 容器名称。
- volumes - 数据卷挂载配置。
- restart - 重启策略,这里是 always 表示总是重启。
- command - 容器启动执行的命令。
- ports - 映射宿主机和容器的端口。
- environment - 设置环境变量。
- network_mode - 网络模式,这里是 host 表示与宿主机共享网络命名空间。
- depends_on - 声明服务依赖,确保在该服务启动前先启动依赖服务。
我们要使用 docker-compose 启动所有容器的话,可以使用以下命令:
bash
docker-compose up -d
启动单个容器的话,可以使用以下命令:
bash
docker-compose up waynboot-mobile
要停止所有的容器,可以使用以下命令:
bash
docker-compose down
通过 docker-compose 我们就能够一键启动整个应用栈。
使用 docker swarm 或 kubernetes 来实现容器的集群管理和服务发现
docker swarm 和 kubernetes 都是用于管理容器化应用程序的集群化平台。它们可以实现以下功能:
- 集群管理:将多个节点(物理机或虚拟机)组织成一个集群,提供统一的管理接口和调度策略。
- 服务编排:将应用程序分解为多个微服务,定义服务之间的依赖关系和通信方式,实现服务的自动部署和更新。
- 负载均衡:根据服务的访问量和性能,动态地分配请求到不同的节点或容器,实现服务的高可用性和弹性伸缩。
- 服务发现:为服务分配唯一的标识和地址,实现服务之间的自动注册和发现,解决服务位置变化的问题。
- 网络管理:为服务提供隔离和安全的网络环境,实现跨主机和跨区域的网络通信。
- 存储管理:为服务提供持久化和共享的存储空间,实现数据的备份和恢复。
docker swarm 是 Docker 自带的集群管理工具,它使用 Docker 引擎作为节点间通信和状态同步的基础。docker swarm 可以直接使用 Docker 命令行工具和 API 进行操作,无需额外安装。docker swarm 支持使用 docker-compose.yml 文件来定义应用程序的服务,并使用docker stack deploy
命令来部署应用程序到集群中。
kubernetes 是 Google 开源的集群管理平台,它使用 etcd 作为分布式键值存储来保存集群状态。kubernetes 需要在每个节点上安装 kubeadm、kubelet 和 kubectl 等组件,并使用 kubectl 命令行工具和 API 进行操作。kubernetes 支持使用 YAML 或 JSON 文件来定义应用程序的资源对象(如 Pod、Service、Deployment 等),并使用kubectl apply
命令来部署应用程序到集群中。
docker swarm 和 kubernetes 的比较
docker swarm 和 kubernetes 有许多相同的功能,但每个工具在不同方面有自己的优势和劣势。下表提供了 docker swarm 和 kubernetes 在主要功能上的比较:
功能 | docker swarm | kubernetes |
---|---|---|
安装和配置 | 简单快速,无需额外安装 | 复杂繁琐,需要安装多个组件 |
命令行工具 | 使用 Docker CLI,与 Docker 容器操作一致 | 使用 kubectl,需要学习新的语法和概念 |
文件格式 | 使用 docker-compose.yml,与 Docker Compose 一致 | 使用 YAML 或 JSON,需要定义多种资源对象 |
集群状态 | 弱一致性,存在数据延迟 | 强一致性,实时同步 |
集群规模 | 适合小型或中型集群(最多几百个节点) | 适合大型或超大型集群(最多数千个节点) |
负载均衡 | 内置负载均衡器,自动将请求分发到容器 | 需要配置 Service 或 Ingress 对象,手动指定负载均衡策略 |
服务发现 | 使用内置 DNS 服务器,根据服务名称解析 IP 地址 | 使用内置 DNS 服务器,根据服务名称解析 IP 地址或端口号 |
网络管理 | 使用 overlay 网络,实现跨主机通信 | 支持多种网络插件,实现跨主机通信 |
存储管理 | 支持使用 volume 或 bind mount,实现数据持久化或共享 | 支持多种存储插件,实现数据持久化或共享 |
服务更新 | 支持滚动更新,可以指定更新批次和延迟 | 支持滚动更新,可以指定更新速率和策略 |
服务扩缩 | 支持手动或基于 CPU 利用率的自动扩缩 | 支持手动或基于多种指标的自动扩缩 |
服务监控 | 需要使用第三方工具,如 Prometheus 或 Grafana | 需要使用第三方工具,如 Prometheus 或 Grafana |
服务日志 | 需要使用第三方工具,如 ELK Stack 或 Fluentd | 需要使用第三方工具,如 ELK Stack 或 Fluentd |
社区支持 | 社区较小,活跃度较低 | 社区庞大,活跃度较高 |
本文关于 docker swarm 和 kubernetes 的介绍可能不够细致,欢迎各位评论留言补充。
使用 CI/CD 工具来自动化应用程序的构建、测试和部署
CI/CD 工具是指用于实现持续集成和持续部署的工具。持续集成是指将开发人员的代码频繁地合并到主分支,并进行自动化的构建和测试。持续部署是指将通过测试的代码自动化地部署到生产环境。
使用 CI/CD 工具可以带来以下好处:
- 提高开发效率和质量,减少人为错误和冲突。
- 加快交付速度和反馈周期,满足用户需求和市场变化。
- 降低运维成本和风险,提高系统稳定性和安全性。
市面上有许多 CI/CD 工具可供选择,例如 Jenkins、GitLab CI、Travis CI、CircleCI、GitHub Actions 等。这些工具各有特点和优劣,需要根据项目的规模、技术栈、需求和预算等因素进行选择。
以 Docker 为例,使用 CI/CD 工具可以实现以下流程:
- 开发人员在本地编写代码,并使用 Dockerfile 或 docker-compose.yml 来定义应用程序的镜像和服务。
- 开发人员将代码推送到代码仓库(如 GitHub 或 GitLab),触发 CI/CD 工具的构建和测试流程。
- CI/CD 工具使用 Docker 命令或 Docker GitHub Actions 等插件来构建镜像,并运行容器进行单元测试、集成测试、端到端测试等。
- CI/CD 工具将通过测试的镜像推送到镜像仓库(如 Docker Hub 或私有仓库),并触发部署流程。
- CI/CD 工具使用 SSH 或其他方式连接到目标服务器(如 Digital Ocean 或其他云服务商),并使用 Docker 命令或 docker-compose 命令来拉取镜像并运行容器。
- CI/CD 工具返回部署结果,并发送通知或报告给开发人员或运维人员。
使用监控和日志工具来收集和分析应用程序的运行状况和性能指标
监控和日志工具是指用于收集、存储、展示和分析应用程序的运行状况和性能指标的工具。这些指标包括:
- 容器的状态、资源利用率、事件等
- 应用程序的响应时间、吞吐量、错误率等
- 网络的延迟、流量、连接数等
- 存储的容量、读写速度、IO 等
使用监控和日志工具可以带来以下好处:
- 提高系统可见性和透明度,及时发现和定位问题。
- 优化系统性能和资源分配,提升用户体验和满意度。
- 支持系统决策和规划,预测系统需求和趋势。
市面上有许多监控和日志工具可供选择,例如 Prometheus、Grafana、ELK Stack、Fluentd 等。这些工具各有特点和优劣,需要根据项目的规模、技术栈、需求和预算等因素进行选择。
以 Docker 为例,使用监控和日志工具可以实现以下流程:
- 在每个节点上安装并运行监控和日志代理(如 Prometheus Node Exporter 或 Fluentd),用于收集容器的指标和日志。
- 在集群中部署并运行监控和日志服务器(如 Prometheus Server 或 Elasticsearch),用于存储和查询容器的指标和日志。
- 在集群中部署并运行监控和日志可视化工具(如 Grafana 或 Kibana),用于展示和分析容器的指标和日志。
- 在监控和日志可视化工具中配置仪表盘和图表,用于查看容器的状态、资源利用率、事件等。
- 在监控和日志可视化工具中配置告警规则和通知方式,用于在容器出现异常或故障时发送通知或报告给开发人员或运维人员。
总结
工程化的 Docker 实践是构建可靠、安全、高效 Docker 应用的基石。遵循这些工程化思想可以大幅提升 Docker 应用部署运维的稳定性、效率和质量。
关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!