端午节, 先祝愿大家端午安康,阖家幸福, 哈哈!这篇讲下Docker这一现代软件开发中不可或缺的技术。软件工程涉及软件开发的整个生命周期,包括需求分析、设计、构建、测试、部署和维护。Docker作为一种容器化技术,直接关联到软件部署、环境一致性保证、持续集成/持续部署(CI/CD)等软件工程领域的关键实践。在这个部分,将会探讨Docker如何改进开发流程、简化部署步骤、保证环境一致性,并支持微服务架构等现代软件工程实践。


在这里插入图片描述

1 Docker基本概念(容器、镜像、仓库)

1.1 容器

  • 定义:容器是一种轻量级、可执行的软件包,包含运行某个应用程序所需的所有内容——代码、运行时环境、库、环境变量和配置文件。
  • 特点:与虚拟机相比,容器直接运行在操作系统的内核上,没有自己的操作系统,启动快,资源占用少。
  • 生命周期:可通过docker run创建和启动,docker stop停止,docker start重新启动,docker rm删除。

1.2 镜像

  • 定义:镜像是构建Docker容器的蓝本,包含运行容器所需的代码、库、环境变量、配置文件等。
  • 特点:镜像是只读的,当容器启动时,Docker会在镜像的顶部添加一个可写层。
  • 操作:可通过docker build创建,通过docker pull从仓库拉取,通过docker push推送到仓库。

1.3 仓库

  • 定义:仓库是集中存储和分发镜像的地方。
  • 类型:有公共仓库(如Docker Hub)和私有仓库。
  • 用途:允许用户上传和下载镜像,便于版本控制和共享。

1.4 Docker架构图

Docker Engine
CLI/REST API
pull/push
Image Layers
Custom Images
Docker Daemon
Client
Containerd
runc
Buildkit
Registry
Docker Hub
Private Registry
Network Management
Volume Management
Image Management
Base Images
Application Images

通过这个架构图,我们可以清晰地看到Docker是如何通过各个组件协同工作来实现容器化技术的。Docker Client发送命令到Docker Daemon,Daemon则通过Containerd和runc管理容器的生命周期,同时通过Buildkit构建镜像,并与Registry交互存储和分发镜像。网络管理、卷管理和镜像管理等功能确保了容器的正常运行和资源管理。

2 使用Docker构建、运行和管理容器

2.1 构建容器

  • Dockerfile:使用Dockerfile定义创建镜像的步骤,包括设置环境变量、安装软件、复制代码等。
  • 构建命令:使用docker build -t <tag> .命令根据Dockerfile构建镜像,<tag>是自定义的镜像标签。
  • 日志: 在运行docker build命令时,你可以添加--progress=plain来获得更详细的输出,这有助于发现问题。

2.2 运行容器

  • 启动容器:使用docker run命令从镜像启动新容器。常用参数包括-d(后台运行)、-p(端口映射)、-v(卷挂载)、--name(命名容器)等。
  • 交互模式:使用docker run -it进入容器的交互模式,通常用于调试。
参数含义使用示例
-p <host-port>:<container-port>将容器内的端口映射到宿主机的端口。-p 2222:22 映射容器的22端口到宿主机的2222端口
-v <host-path>:<container-path>将宿主机的目录挂载到容器内的指定路径。-v /path/on/host:/workspace 挂载宿主机的/path/on/host目录到容器的/workspace
--name <name>为容器指定一个名称。--name mycontainer 给容器指定名称mycontainer
-d以后台模式运行容器。-d 启动容器并使其在后台运行
--rm容器退出时自动清理容器文件系统。--rm 容器退出后自动删除
-e <key>=<value>设置环境变量。-e MY_VAR=myvalue 在容器内设置环境变量MY_VAR
--env-file <file>从文件中读取环境变量。--env-file ./env.listenv.list文件读取环境变量
--network <network>连接容器到指定的网络。--network my-net 将容器连接到my-net网络
--link <container>将容器链接到另一个容器。--link db:db 将当前容器与名为db的容器链接
--expose <port>暴露容器的端口或一系列端口。--expose 80 暴露容器的80端口
-it启动一个交互式的容器,通常与/bin/bash一起使用。-it ubuntu /bin/bash 启动一个Ubuntu容器并进入bash交互模式
--entrypoint <cmd>覆盖容器启动后默认执行的命令。--entrypoint /bin/bash 覆盖默认的entrypoint为/bin/bash

2.3 管理容器

  • 查看容器:使用docker ps(正在运行的容器)和docker ps -a(所有容器)查看容器。
  • 停止容器:使用docker stop <container-id/name>停止运行中的容器。
  • 启动容器:使用docker start <container-id/name>启动已停止的容器。
  • 删除容器:使用docker rm <container-id/name>删除容器。如果容器正在运行,需要先停止容器。

2.4 Docker操作流程图

在使用Docker进行开发和部署时,理解其操作流程至关重要。以下是Docker操作的主要步骤,通过一个示意图详细描述了从编写Dockerfile到运行容器的全过程。

Developer Dockerfile Docker CLI Docker Daemon Docker Registry Docker Container Write Dockerfile docker build -t myapp . Send build request Parse Dockerfile Build Image Layer by Layer Image Built Successfully docker run -d -p 80:80 myapp Send run request Pull Image if not locally available Image layers Create and Start Container Running Container Running Container Running and accessible Developer Dockerfile Docker CLI Docker Daemon Docker Registry Docker Container

通过上面的流程图,我们可以看到:

  1. 开发者编写Dockerfile:开发者首先编写Dockerfile,定义镜像构建所需的步骤和依赖。
  2. 构建镜像:开发者使用docker build -t myapp .命令来构建镜像。Docker CLI将构建请求发送给Docker Daemon。
  3. 解析Dockerfile:Docker Daemon解析Dockerfile,逐层构建镜像。
  4. 镜像构建完成:镜像构建成功后,Docker Daemon通知Docker CLI。
  5. 运行容器:开发者使用docker run -d -p 80:80 myapp命令运行容器。Docker CLI将运行请求发送给Docker Daemon。
  6. 拉取镜像:如果镜像在本地不存在,Docker Daemon会从Registry中拉取镜像。
  7. 创建和启动容器:Docker Daemon创建并启动容器。
  8. 容器运行:容器开始运行,并将状态反馈给Docker CLI。
  9. 开发者访问容器:开发者可以通过映射的端口访问正在运行的容器。

3 Docker与开发流程集成(DevOps实践)

3.1 容器化的开发环境

  • 一致性:Docker确保开发、测试、生产环境的一致性,减少了“在我的机器上能运行”的问题。
  • 可重复性:通过使用Docker镜像,开发环境可以快速、可重复地被创建和销毁。

3.2 持续集成/持续部署(CI/CD)

  • 自动化构建:在代码提交后,使用Docker自动化构建应用和运行测试。
  • 自动化部署:在构建成功后,自动化将应用部署到测试或生产环境。使用Docker标签管理不同的版本,确保部署的可追溯性。

3.2 微服务架构

  • 独立部署:在微服务架构中,每个服务可以被封装在独立的容器中,使得各服务独立部署和扩展。
  • 服务隔离:Docker提供的网络和存储隔离,确保了服务之间的安全和数据隔离。

3.3 配置管理

  • 环境变量:使用Docker,可以通过环境变量将配置传递给应用,无需改动代码。
  • 配置文件:可以通过挂载卷的方式将配置文件放入容器,便于管理和更新。

3.4 日志和监控

  • 日志收集:容器的标准输出(stdout)和标准错误(stderr)可以被Docker自动捕获,便于集中日志管理。
  • 性能监控:Docker提供的API和内置命令可以监控容器的性能指标,集成到现有的监控系统中。

3.5 开发流程的优化

  • 快速迭代:Docker容器的快速启动和停止,使得开发和测试的迭代周期大大缩短。
  • 依赖管理:使用Dockerfile明确项目的依赖,确保开发环境和生产环境的一致性。

4 Docker在微服务架构中的应用

4.1 微服务架构概述

微服务架构是一种设计方法,它将一个大型应用程序分解为一组小的、相互独立的服务,每个服务实现特定业务功能,运行在自己的进程中,并通过轻量级通信机制(通常是HTTP RESTful API或消息队列)进行交互。

4.2 Docker与微服务架构的契合

  1. 服务隔离:Docker容器为每个微服务提供了隔离的运行环境,确保服务间相互独立,互不影响。
  2. 环境一致性:Docker保证了从开发到生产环境的一致性,减少了“在我机器上能跑”的问题。
  3. 快速部署和扩展:Docker容器的轻量性质使得启动速度非常快,易于快速部署新服务或扩展现有服务。

4.3 Docker在微服务架构中的应用实践

  1. 服务容器化
    • 每个微服务打包成一个Docker镜像,包含服务的代码、运行时、依赖等。
    • 使用Dockerfile定义服务镜像的构建过程。
  2. 服务编排
    • 使用Docker Compose或Kubernetes等编排工具管理容器。
    • 定义服务间的依赖、网络、存储等配置。
  3. 持续集成/持续部署(CI/CD)
    • 利用Docker容器的一致性和隔离性,实现自动化的测试、构建、部署流程。
    • 每个服务的更新可以独立构建和部署,加快迭代速度。
  4. 负载均衡与服务发现
    • 使用Docker Swarm模式或Kubernetes集群来管理多个服务实例。
    • 利用这些平台提供的负载均衡器和服务发现机制自动分配请求并维护服务健康。
  5. 日志管理和监控
    • 利用Docker的日志驱动收集容器日志。
    • 集成Prometheus、Grafana等监控工具,实现服务的性能监控和告警。
  6. 安全性
    • 利用Docker提供的安全机制,如用户命名空间、只读文件系统等,增强服务安全性。
    • 定期扫描镜像中的安全漏洞。

5 Docker与云计算(AWS、Azure、GCP)

6 案例研究和最佳实践

6.1 Github项目用docker来体验

6.1.1 创建Dockerfile
# Use an official Ubuntu runtime as a parent image
FROM ubuntu:20.04

# Set environment variables to non-interactive (this prevents some prompts)
ENV DEBIAN_FRONTEND=noninteractive

# Replace the source list with USA mirrorb
#RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://us.archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list

# Replace the source list with China's USTC University mirror
#RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.ustc.edu.cn/ubuntu|g' /etc/apt/sources.list
# Replace the source list with China's Tsinghua University mirror
RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.tuna.tsinghua.edu.cn/ubuntu|g' /etc/apt/sources.list
# Replace the source list with Alibaba Cloud mirror
#RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list

# Update and install build and utility dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    git \
    curl \
    wget \
    make \
    cmake \
    unzip \
    pkg-config \
    software-properties-common \
    openssh-server && \
    apt-get clean && \
	rm -rf /var/lib/apt/lists/*

# Install GCC, G++ for C++ projects
RUN apt-get install -y gcc g++

# Install Go
RUN add-apt-repository ppa:longsleep/golang-backports
RUN apt-get update
RUN apt-get install -y golang-go

# (Optional) Install vcpkg for managing C++ libraries
RUN git clone https://github.com/Microsoft/vcpkg.git
RUN ./vcpkg/bootstrap-vcpkg.sh
RUN ./vcpkg/vcpkg integrate install

# Install OpenSSH Server
RUN mkdir /var/run/sshd
RUN echo 'root:your_password' | chpasswd

# SSH login fix. Otherwise user is kicked off after login
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config

# Expose the SSH port
EXPOSE 22

# Set the working directory in the container
WORKDIR /workspace

# Copy the content of the local src directory to the working directory
COPY . /workspace

# Start SSH server
CMD ["/usr/sbin/sshd", "-D"]

为了减少镜像的大小和层数,一个常见的做法是将多个命令合并到一个RUN指令中,通常使用逻辑运算符(如&&)来连接这些命令。这样做不仅能减少镜像的层数,还可以减少中间层的大小,从而减少整体镜像的大小。

通常来说,如果后续的RUN指令并不会显著增加镜像的大小,或者它们之间有逻辑上的分隔(比如一个RUN指令用于安装依赖,另一个用于配置软件),那么将它们保持分开可能会更有利于Dockerfile的可读性和维护性。

合并RUN指令主要用于以下情况:

  1. 减少镜像层数:Docker镜像由多层构成,每个RUN指令都会创建一个新层。过多的层可能会导致镜像变大。
  2. 减少镜像大小:临时文件和中间数据可能会在镜像的层中保留下来。合并命令可以在一个层内完成创建和清理临时文件的操作,避免临时文件占用额外空间。
  3. 提高构建效率:合并多个命令可以减少Docker构建的步骤,可能会略微提高构建的速度。

比如: 你可能想在一个RUN指令中编译你的应用,在另一个RUN指令中配置你的应用环境。在这种情况下,保持命令的分离有助于使Dockerfile更加清晰和易于理解。

# Use an official Ubuntu runtime as a parent image
FROM ubuntu:20.04

# Set environment variables to non-interactive (this prevents some prompts)
ENV DEBIAN_FRONTEND=noninteractive

# Replace the source list with USTC mirror and install dependencies
# - `--fix-missing`选项尝试修复因为包丢失、损坏或无法下载导致的问题。
# - `apt-get clean`清理了本地仓库的下载的`.deb`包,这些包在安装后不再需要。
# - `rm -rf /var/lib/apt/lists/*`删除了`apt`获取的包列表信息,这些在构建镜像之后不再需要。
RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.ustc.edu.cn/ubuntu|g' /etc/apt/sources.list && \
    apt-get update && \
    apt-get install -y --fix-missing\
    build-essential \
    git \
    curl \
    wget \
    make \
    cmake \
    unzip \
    pkg-config \
    software-properties-common \
    openssh-server && \
    apt-get clean && \
	rm -rf /var/lib/apt/lists/*
# ... (rest of your Dockerfile)
6.1.2 构建镜像

docker build -t lyupeichen/dev:0.0.1 .
output:

# docker build -t lyupeichen/dev:0.0.1 .
[+] Building 76.2s (5/19)                                        docker:default
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 1.54kB                                     0.0s 
 => [internal] load .dockerignore                                          0.0s 
 => => transferring context: 2B                                            0.0s 
 => [internal] load metadata for docker.io/library/ubuntu:22.04            3.7s 
 => [internal] load build context                                         12.1s
 => => transferring context: 96.90kB                                      12.1s 
 => CACHED [ 1/15] FROM docker.io/library/ubuntu:22.04@sha256:e6173d4dc55  0.0s 
 => [ 2/15] RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirror  72.4s 
 => => # ages [1344 kB]
 => => # Get:4 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd6 
 => => # 4 Packages [44.6 kB]
 => => # Get:5 http://security.ubuntu.com/ubuntu jammy-security/restricted amd6 
 => => # 4 Packages [1600 kB]
 => => # Ign:6 http://mirrors.ustc.edu.cn/ubuntu jammy InRelease          ...
 ...
 ...
6.1.3 启动容器

使用以下命令启动容器,其中:

  • -p 2222:22 将容器的22端口(SSH默认端口)映射到宿主机的2222端口。
  • -v /path/on/host:/workspace 将宿主机上的某个目录映射到容器中的/workspace目录。
docker run -d -p 2222:22 -v /path/on/host:/workspace --name your-container-name your-image-name

使用docker pull confluentinc/cp-kafka 的全面指南

7 总结

Docker作为一种现代化的容器化技术,为软件开发提供了高效的解决方案。通过容器化,我们可以实现开发环境、测试环境和生产环境的一致性,简化部署步骤,并提高软件的可靠性和可维护性。Docker在持续集成、持续部署和微服务架构中发挥了重要作用,极大地促进了DevOps实践的发展。希望通过本文的介绍,能够帮助读者更好地理解和应用Docker,提升软件开发的效率和质量。

References

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部