活动公告

系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

手把手教你Docker镜像构建从入门到精通结合仓库管理实战技巧解决容器化部署中的常见问题让DevOps工作流程更加顺畅高效

SunJu_FaceMall

3万

主题

2717

科技点

3万

积分

执行版主

碾压王

积分
32881

塔罗立华奏

执行版主 发表于 2025-8-31 20:30:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

Docker作为容器化技术的代表,已经彻底改变了软件开发、测试和部署的方式。它通过将应用程序及其依赖项打包到轻量级、可移植的容器中,解决了”在我机器上可以运行”的古老问题,使DevOps工作流程更加顺畅高效。本文将带你从Docker镜像构建的基础知识开始,逐步深入到高级技巧和实战应用,帮助你全面掌握Docker技术,并解决容器化部署中遇到的常见问题。

Docker基础概念

在开始构建Docker镜像之前,我们需要了解一些基本概念:

镜像(Image)

Docker镜像是一个只读的模板,用于创建容器。它包含了运行应用程序所需的所有内容——代码、运行时、库、环境变量和配置文件。镜像是分层构建的,每一层都是只读的,除了最上层。当镜像被修改时,实际上是创建了一个新的顶层可写层。

容器(Container)

容器是镜像的运行实例。你可以创建、启动、停止、移动或删除容器。容器之间是相互隔离的,每个容器都有自己独立的文件系统、网络和进程空间。

仓库(Repository)

Docker仓库是用来存储和分发镜像的地方。最著名的公共仓库是Docker Hub,此外你也可以搭建私有仓库,如Harbor、Nexus等。

Dockerfile

Dockerfile是一个文本文件,包含了一系列命令,用户可以通过调用这些命令来创建一个镜像。每个命令都会在镜像上创建一个新的层。

Docker镜像构建入门

基本Dockerfile指令

让我们从一个简单的Dockerfile开始,了解基本指令:
  1. # 使用官方Python运行时作为基础镜像
  2. FROM python:3.9-slim
  3. # 设置工作目录
  4. WORKDIR /app
  5. # 将当前目录内容复制到容器的/app目录
  6. COPY . /app
  7. # 安装依赖
  8. RUN pip install --no-cache-dir -r requirements.txt
  9. # 设置环境变量
  10. ENV NAME World
  11. # 容器启动时运行的命令
  12. CMD ["python", "app.py"]
复制代码

构建第一个镜像

假设我们有一个简单的Flask应用:

app.py
  1. from flask import Flask
  2. import os
  3. app = Flask(__name__)
  4. @app.route('/')
  5. def hello():
  6.     name = os.environ.get('NAME', 'World')
  7.     return f'Hello, {name}!'
  8. if __name__ == '__main__':
  9.     app.run(host='0.0.0.0', port=80)
复制代码

requirements.txt
  1. Flask==2.0.1
复制代码

要构建这个应用的镜像,我们需要在项目根目录下创建上面的Dockerfile,然后运行以下命令:
  1. docker build -t my-flask-app .
复制代码

• -t参数给镜像打标签,格式为name:tag,如果不指定tag,默认为latest
• .表示构建上下文是当前目录

构建完成后,我们可以运行容器:
  1. docker run -p 4000:80 my-flask-app
复制代码

现在访问http://localhost:4000,你将看到 “Hello, World!“。

Dockerfile常用指令详解

指定基础镜像,必须是第一条指令:
  1. FROM <image>[:<tag>] [AS <name>]
复制代码

例如:
  1. FROM ubuntu:20.04
  2. FROM python:3.9-slim AS builder
复制代码

设置工作目录,后续的指令(如RUN、CMD、ENTRYPOINT、COPY、ADD)都会在这个目录下执行:
  1. WORKDIR /path/to/workdir
复制代码

执行命令并创建新的镜像层:
  1. RUN <command> # shell格式
  2. RUN ["executable", "param1", "param2"] # exec格式
复制代码

例如:
  1. RUN apt-get update && apt-get install -y python3
  2. RUN ["pip", "install", "-r", "requirements.txt"]
复制代码

复制文件到镜像中:
  1. COPY <src>... <dest>
  2. ADD <src>... <dest>
复制代码

COPY和ADD的区别在于,ADD还支持URL和自动解压tar包:
  1. COPY requirements.txt /app/
  2. ADD https://example.com/file.tar.gz /app/
复制代码

容器启动时执行的命令,一个Dockerfile中只有最后一个CMD会生效:
  1. CMD ["executable", "param1", "param2"] # exec格式,推荐
  2. CMD command param1 param2 # shell格式
  3. CMD ["param1", "param2"] # 作为ENTRYPOINT的参数
复制代码

配置容器启动时运行的命令,与CMD类似,但不会被docker run的命令行参数覆盖:
  1. ENTRYPOINT ["executable", "param1", "param2"]
  2. ENTRYPOINT command param1 param2
复制代码

设置环境变量:
  1. ENV <key> <value>
  2. ENV <key>=<value>
复制代码

例如:
  1. ENV APP_VERSION=1.0
  2. ENV PATH /usr/local/nginx/bin:$PATH
复制代码

声明容器运行时监听的端口,但实际上不会发布端口,只是作为文档记录:
  1. EXPOSE <port> [<port>/<protocol>...]
复制代码

例如:
  1. EXPOSE 80 443
  2. EXPOSE 8080/tcp
复制代码

定义匿名数据卷,用于持久化数据或共享数据:
  1. VOLUME ["/path/to/dir"]
复制代码

例如:
  1. VOLUME ["/data"]
复制代码

指定运行容器时的用户名或UID:
  1. USER <user>[:<group>]
  2. USER <UID>[:<GID>]
复制代码

例如:
  1. USER nginx
复制代码

定义构建时变量,只在构建过程中有效:
  1. ARG <name>[=<default value>]
复制代码

例如:
  1. ARG VERSION=1.0
  2. RUN echo $VERSION
复制代码

添加元数据到镜像,以键值对的形式:
  1. LABEL <key>=<value> <key>=<value> ...
复制代码

例如:
  1. LABEL version="1.0" description="This is a sample application"
复制代码

Docker镜像构建进阶

多阶段构建

多阶段构建允许你在单个Dockerfile中使用多个FROM指令,每个FROM指令开始一个新的构建阶段。你可以选择性地将构件从一个阶段复制到另一个阶段,这可以创建更小、更安全的最终镜像。

下面是一个使用多阶段构建的Go应用示例:
  1. # 第一阶段:构建应用
  2. FROM golang:1.16 AS builder
  3. WORKDIR /app
  4. # 复制依赖文件
  5. COPY go.mod go.sum ./
  6. RUN go mod download
  7. # 复制源代码并构建
  8. COPY *.go .
  9. RUN CGO_ENABLED=0 GOOS=linux go build -o /server
  10. # 第二阶段:创建最终镜像
  11. FROM alpine:latest
  12. WORKDIR /root/
  13. # 从第一阶段复制构建的二进制文件
  14. COPY --from=builder /server .
  15. # 暴露端口
  16. EXPOSE 8080
  17. # 运行应用
  18. CMD ["./server"]
复制代码

在这个例子中,第一阶段使用Go SDK构建应用,第二阶段只复制构建好的二进制文件到干净的Alpine Linux镜像中。这样最终的镜像只包含运行应用所需的最小文件,大大减小了镜像大小。

镜像优化技巧

选择合适的基础镜像可以显著减小最终镜像的大小:
  1. # 大镜像
  2. FROM ubuntu:20.04
  3. # 小镜像
  4. FROM alpine:3.13
  5. # 更小的镜像
  6. FROM scratch
复制代码

每个RUN指令都会创建一个新的镜像层,合并RUN指令可以减少层数:
  1. # 不推荐
  2. RUN apt-get update
  3. RUN apt-get install -y python3
  4. RUN pip install flask
  5. # 推荐
  6. RUN apt-get update && \
  7.     apt-get install -y python3 && \
  8.     pip install flask && \
  9.     rm -rf /var/lib/apt/lists/*
复制代码

类似于.gitignore,.dockerignore可以排除不需要的文件和目录:
  1. .git
  2. .gitignore
  3. node_modules
  4. npm-debug.log
  5. Dockerfile
  6. .dockerignore
复制代码

将不常变化的文件放在前面,常变化的文件放在后面,这样可以利用Docker的层缓存机制:
  1. # 先复制依赖文件
  2. COPY package*.json ./
  3. RUN npm install
  4. # 再复制源代码
  5. COPY . .
复制代码

如前所述,多阶段构建可以帮助你创建更小的镜像。

在安装包后清理缓存和不必要的文件:
  1. RUN apt-get update && \
  2.     apt-get install -y python3 && \
  3.     pip install flask && \
  4.     rm -rf /var/lib/apt/lists/* && \
  5.     apt-get clean
复制代码

高级Dockerfile技巧

HEALTHCHECK指令告诉Docker如何测试容器是否仍在工作:
  1. HEALTHCHECK --interval=30s --timeout=3s \
  2.   CMD curl -f http://localhost:80/ || exit 1
复制代码

参数说明:

• --interval:检查间隔,默认30秒
• --timeout:超时时间,默认30秒
• --start-period:容器启动后等待多久开始健康检查,默认0秒
• --retries:重试次数,默认3次

ONBUILD指令在当前镜像构建时不会执行,而是在以当前镜像为基础镜像的下一个镜像构建时执行:
  1. FROM node:10
  2. WORKDIR /app
  3. ONBUILD COPY package.json /app
  4. ONBUILD RUN npm install
  5. ONBUILD COPY . /app
  6. CMD ["node", "app.js"]
复制代码

SHELL指令可以覆盖默认的shell格式:
  1. SHELL ["powershell", "-command"]
  2. RUN Write-Host hello
复制代码

STOPSIGNAL指令设置发送给容器以退出的系统调用信号:
  1. STOPSIGNAL SIGTERM
复制代码

Docker仓库管理

公共仓库:Docker Hub

Docker Hub是Docker官方提供的公共镜像仓库服务,拥有大量官方和社区维护的镜像。
  1. docker login
复制代码
  1. # 给镜像打标签
  2. docker tag my-flask-app username/my-flask-app:v1.0
  3. # 推送镜像
  4. docker push username/my-flask-app:v1.0
复制代码
  1. docker pull username/my-flask-app:v1.0
复制代码

私有仓库:Harbor

Harbor是一个开源的企业级Docker Registry项目,提供了安全、标识和管理等功能。

1. 下载Harbor安装包:
  1. wget https://github.com/goharbor/harbor/releases/download/v2.3.2/harbor-offline-installer-v2.3.2.tgz
  2. tar xvf harbor-offline-installer-v2.3.2.tgz
复制代码

1. 配置Harbor:
  1. cd harbor
  2. cp harbor.yml.tmpl harbor.yml
复制代码

编辑harbor.yml文件,设置hostname、端口、密码等参数。

1. 安装Harbor:
  1. ./install.sh
复制代码

1. 登录Harbor:
  1. docker login harbor.example.com
复制代码

1. 推送镜像到Harbor:
  1. # 给镜像打标签
  2. docker tag my-flask-app harbor.example.com/library/my-flask-app:v1.0
  3. # 推送镜像
  4. docker push harbor.example.com/library/my-flask-app:v1.0
复制代码

1. 从Harbor拉取镜像:
  1. docker pull harbor.example.com/library/my-flask-app:v1.0
复制代码

其他私有仓库选项

除了Harbor,还有其他一些流行的私有仓库选项:

Docker官方提供的开源Registry实现:
  1. # 运行Registry容器
  2. docker run -d -p 5000:5000 --restart=always --name registry registry:2
  3. # 推送镜像到私有Registry
  4. docker tag my-flask-app localhost:5000/my-flask-app
  5. docker push localhost:5000/my-flask-app
  6. # 从私有Registry拉取镜像
  7. docker pull localhost:5000/my-flask-app
复制代码

Sonatype提供的仓库管理器,支持多种包格式,包括Docker镜像:
  1. # 运行Nexus容器
  2. docker run -d -p 8081:8081 --name nexus sonatype/nexus3
  3. # 登录Nexus
  4. docker login localhost:8081
  5. # 推送镜像到Nexus
  6. docker tag my-flask-app localhost:8081/my-flask-app
  7. docker push localhost:8081/my-flask-app
复制代码

容器化部署常见问题及解决方案

1. 镜像过大

问题:构建的镜像体积过大,导致存储和传输效率低下。

解决方案:

• 使用更小的基础镜像,如alpine、scratch
• 使用多阶段构建
• 合并RUN指令,并在同一行中清理缓存
• 使用.dockerignore文件排除不必要的文件
• 删除不必要的包和文件

示例:
  1. # 优化前
  2. FROM ubuntu:20.04
  3. RUN apt-get update
  4. RUN apt-get install -y python3 python3-pip
  5. RUN pip install flask
  6. COPY . /app
  7. WORKDIR /app
  8. CMD ["python3", "app.py"]
  9. # 优化后
  10. FROM python:3.9-slim
  11. WORKDIR /app
  12. COPY requirements.txt .
  13. RUN pip install --no-cache-dir -r requirements.txt
  14. COPY . .
  15. CMD ["python", "app.py"]
复制代码

2. 构建速度慢

问题:镜像构建过程耗时太长。

解决方案:

• 利用Docker层缓存,将不常变化的文件放在前面
• 使用多阶段构建,避免重复构建
• 使用缓存服务,如APT缓存、PIP缓存
• 并行构建(使用BuildKit)

示例:
  1. # 利用Docker层缓存
  2. FROM node:14
  3. WORKDIR /app
  4. # 先复制依赖文件
  5. COPY package*.json ./
  6. RUN npm install
  7. # 再复制源代码
  8. COPY . .
  9. CMD ["node", "app.js"]
复制代码

启用BuildKit进行并行构建:
  1. DOCKER_BUILDKIT=1 docker build -t my-app .
复制代码

3. 容器启动失败

问题:容器启动后立即退出或无法正常启动。

解决方案:

• 使用正确的启动命令(CMD或ENTRYPOINT)
• 确保应用程序在前台运行
• 检查日志输出:docker logs <container_id>
• 使用docker exec进入容器调试
• 使用HEALTHCHECK监控容器状态

示例:
  1. # 确保应用在前台运行
  2. CMD ["node", "app.js"]  # 推荐,exec格式
  3. # 而不是
  4. CMD node app.js  # shell格式,可能会创建额外的shell进程
  5. # 添加健康检查
  6. HEALTHCHECK --interval=30s --timeout=3s \
  7.   CMD curl -f http://localhost:3000/ || exit 1
复制代码

4. 数据持久化问题

问题:容器删除后数据丢失。

解决方案:

• 使用数据卷(Volumes)或绑定挂载(Bind Mounts)
• 将数据存储在容器外的持久化存储中
• 考虑使用数据卷容器

示例:
  1. # 使用数据卷
  2. docker run -d -v mydata:/data my-app
  3. # 使用绑定挂载
  4. docker run -d -v /host/path:/container/path my-app
  5. # 使用数据卷容器
  6. docker create -v /data --name data-container busybox
  7. docker run --volumes-from data-container my-app
复制代码

5. 网络连接问题

问题:容器之间或容器与外部网络之间的通信问题。

解决方案:

• 正确配置端口映射
• 使用自定义网络
• 使用容器名称进行通信
• 配置DNS解析

示例:
  1. # 创建自定义网络
  2. docker network create my-network
  3. # 连接容器到网络
  4. docker run -d --name web --network my-network nginx
  5. docker run -d --name app --network my-network my-app
  6. # 使用容器名称通信
  7. # 在app容器中,可以使用http://web访问nginx服务
复制代码

6. 安全问题

问题:容器安全配置不当,可能导致安全风险。

解决方案:

• 使用非root用户运行容器
• 限制容器能力
• 使用安全扫描工具扫描镜像
• 定期更新基础镜像和依赖
• 使用内容信任(Docker Content Trust)

示例:
  1. # 使用非root用户
  2. FROM node:14
  3. WORKDIR /app
  4. COPY package*.json ./
  5. RUN npm install
  6. COPY . .
  7. RUN groupadd -r appuser && useradd -r -g appuser appuser
  8. USER appuser
  9. CMD ["node", "app.js"]
  10. # 限制容器能力
  11. docker run --cap-drop ALL --cap-add CHOWN my-app
复制代码

Docker在DevOps工作流程中的最佳实践

1. 持续集成/持续部署(CI/CD)

将Docker集成到CI/CD流程中,实现自动化构建、测试和部署。
  1. pipeline {
  2.     agent any
  3.    
  4.     stages {
  5.         stage('Build') {
  6.             steps {
  7.                 script {
  8.                     docker.build('my-app:${BUILD_ID}')
  9.                 }
  10.             }
  11.         }
  12.         
  13.         stage('Test') {
  14.             steps {
  15.                 script {
  16.                     docker.image('my-app:${BUILD_ID}').inside {
  17.                         sh 'npm test'
  18.                     }
  19.                 }
  20.             }
  21.         }
  22.         
  23.         stage('Deploy') {
  24.             steps {
  25.                 script {
  26.                     docker.withRegistry('https://registry.example.com', 'registry-credentials') {
  27.                         docker.image('my-app:${BUILD_ID}').push()
  28.                     }
  29.                 }
  30.             }
  31.         }
  32.     }
  33. }
复制代码
  1. stages:
  2.   - build
  3.   - test
  4.   - deploy
  5. build_image:
  6.   stage: build
  7.   script:
  8.     - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
  9.     - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  10. test_image:
  11.   stage: test
  12.   script:
  13.     - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  14.     - docker run --rm $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA npm test
  15. deploy_image:
  16.   stage: deploy
  17.   script:
  18.     - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  19.     - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
  20.     - docker push $CI_REGISTRY_IMAGE:latest
  21.   only:
  22.     - main
复制代码

2. 环境一致性

使用Docker确保开发、测试和生产环境的一致性。
  1. version: '3'
  2. services:
  3.   app:
  4.     build: .
  5.     ports:
  6.       - "3000:3000"
  7.     environment:
  8.       - NODE_ENV=development
  9.     volumes:
  10.       - .:/app
  11.       - /app/node_modules
  12.   db:
  13.     image: postgres:13
  14.     environment:
  15.       POSTGRES_PASSWORD: example
  16.     volumes:
  17.       - postgres-data:/var/lib/postgresql/data
  18. volumes:
  19.   postgres-data:
复制代码

3. 微服务架构

使用Docker容器化微服务,便于部署、扩展和管理。
  1. version: '3'
  2. services:
  3.   api-gateway:
  4.     build: ./api-gateway
  5.     ports:
  6.       - "80:80"
  7.     depends_on:
  8.       - user-service
  9.       - product-service
  10.   user-service:
  11.     build: ./user-service
  12.     environment:
  13.       - DB_HOST=user-db
  14.     depends_on:
  15.       - user-db
  16.   product-service:
  17.     build: ./product-service
  18.     environment:
  19.       - DB_HOST=product-db
  20.     depends_on:
  21.       - product-db
  22.   user-db:
  23.     image: postgres:13
  24.     environment:
  25.       POSTGRES_PASSWORD: user_password
  26.     volumes:
  27.       - user-db-data:/var/lib/postgresql/data
  28.   product-db:
  29.     image: postgres:13
  30.     environment:
  31.       POSTGRES_PASSWORD: product_password
  32.     volumes:
  33.       - product-db-data:/var/lib/postgresql/data
  34. volumes:
  35.   user-db-data:
  36.   product-db-data:
复制代码

4. 日志管理

集中管理容器日志,便于监控和故障排除。
  1. version: '3'
  2. services:
  3.   app:
  4.     build: .
  5.     logging:
  6.       driver: "gelf"
  7.       options:
  8.         gelf-address: "udp://localhost:12201"
  9.         tag: "my-app"
  10.   elasticsearch:
  11.     image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
  12.     environment:
  13.       - discovery.type=single-node
  14.     volumes:
  15.       - es-data:/usr/share/elasticsearch/data
  16.   logstash:
  17.     image: docker.elastic.co/logstash/logstash:7.10.1
  18.     ports:
  19.       - "12201:12201/udp"
  20.     volumes:
  21.       - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
  22.     depends_on:
  23.       - elasticsearch
  24.   kibana:
  25.     image: docker.elastic.co/kibana/kibana:7.10.1
  26.     ports:
  27.       - "5601:5601"
  28.     depends_on:
  29.       - elasticsearch
  30. volumes:
  31.   es-data:
复制代码

5. 监控和告警

监控容器性能和健康状态,设置告警机制。
  1. version: '3'
  2. services:
  3.   app:
  4.     build: .
  5.     labels:
  6.       - "prometheus.io/scrape=true"
  7.       - "prometheus.io/port=8080"
  8.   prometheus:
  9.     image: prom/prometheus
  10.     ports:
  11.       - "9090:9090"
  12.     volumes:
  13.       - ./prometheus.yml:/etc/prometheus/prometheus.yml
  14.     command:
  15.       - '--config.file=/etc/prometheus/prometheus.yml'
  16.   grafana:
  17.     image: grafana/grafana
  18.     ports:
  19.       - "3000:3000"
  20.     environment:
  21.       - GF_SECURITY_ADMIN_PASSWORD=admin
  22.     volumes:
  23.       - grafana-storage:/var/lib/grafana
  24.   alertmanager:
  25.     image: prom/alertmanager
  26.     ports:
  27.       - "9093:9093"
  28.     volumes:
  29.       - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
  30. volumes:
  31.   grafana-storage:
复制代码

实战案例:完整的项目容器化部署流程

让我们通过一个完整的示例,展示如何将一个Web应用容器化并部署到生产环境。

项目结构
  1. my-web-app/
  2. ├── src/
  3. │   ├── app.py
  4. │   ├── requirements.txt
  5. │   └── config.py
  6. ├── tests/
  7. │   └── test_app.py
  8. ├── docker/
  9. │   ├── Dockerfile
  10. │   ├── docker-compose.yml
  11. │   └── docker-compose.prod.yml
  12. ├── .dockerignore
  13. ├── .gitlab-ci.yml
  14. └── README.md
复制代码

应用代码

src/app.py
  1. from flask import Flask, jsonify
  2. import os
  3. from config import Config
  4. app = Flask(__name__)
  5. app.config.from_object(Config)
  6. @app.route('/')
  7. def index():
  8.     return jsonify({
  9.         'message': 'Welcome to My Web App',
  10.         'environment': app.config['ENV']
  11.     })
  12. @app.route('/health')
  13. def health():
  14.     return jsonify({'status': 'healthy'})
  15. if __name__ == '__main__':
  16.     app.run(host='0.0.0.0', port=5000)
复制代码

src/config.py
  1. import os
  2. class Config:
  3.     SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
  4.     ENV = os.environ.get('FLASK_ENV') or 'development'
复制代码

src/requirements.txt
  1. Flask==2.0.1
  2. gunicorn==20.1.0
复制代码

tests/test_app.py
  1. import pytest
  2. import sys
  3. import os
  4. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))
  5. from app import app
  6. @pytest.fixture
  7. def client():
  8.     app.config['TESTING'] = True
  9.     with app.test_client() as client:
  10.         yield client
  11. def test_index(client):
  12.     rv = client.get('/')
  13.     assert rv.status_code == 200
  14.     assert b'Welcome to My Web App' in rv.data
  15. def test_health(client):
  16.     rv = client.get('/health')
  17.     assert rv.status_code == 200
  18.     assert b'healthy' in rv.data
复制代码

Docker配置

.dockerignore
  1. .git
  2. .gitignore
  3. tests/
  4. README.md
复制代码

docker/Dockerfile
  1. # 多阶段构建
  2. # 第一阶段:构建
  3. FROM python:3.9-slim as builder
  4. WORKDIR /app
  5. # 安装依赖
  6. COPY src/requirements.txt .
  7. RUN pip install --no-cache-dir -r requirements.txt
  8. # 第二阶段:运行
  9. FROM python:3.9-slim
  10. WORKDIR /app
  11. # 从第一阶段复制安装的依赖
  12. COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
  13. # 复制应用代码
  14. COPY src/ .
  15. # 创建非root用户
  16. RUN useradd --create-home --shell /bin/bash app && \
  17.     chown -R app:app /app
  18. USER app
  19. # 暴露端口
  20. EXPOSE 5000
  21. # 健康检查
  22. HEALTHCHECK --interval=30s --timeout=3s \
  23.   CMD curl -f http://localhost:5000/health || exit 1
  24. # 启动命令
  25. CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
复制代码

docker/docker-compose.yml
  1. version: '3.8'
  2. services:
  3.   web:
  4.     build:
  5.       context: ..
  6.       dockerfile: docker/Dockerfile
  7.     ports:
  8.       - "5000:5000"
  9.     environment:
  10.       - FLASK_ENV=development
  11.     volumes:
  12.       - ../src:/app
  13.     depends_on:
  14.       - redis
  15.   redis:
  16.     image: redis:6-alpine
  17.     ports:
  18.       - "6379:6379"
  19.   tests:
  20.     build:
  21.       context: ..
  22.       dockerfile: docker/Dockerfile.test
  23.     volumes:
  24.       - ../tests:/app/tests
  25.     depends_on:
  26.       - web
复制代码

docker/docker-compose.prod.yml
  1. version: '3.8'
  2. services:
  3.   web:
  4.     image: registry.example.com/my-web-app:${VERSION:-latest}
  5.     ports:
  6.       - "80:5000"
  7.     environment:
  8.       - FLASK_ENV=production
  9.       - SECRET_KEY=${SECRET_KEY}
  10.     restart: unless-stopped
  11.     depends_on:
  12.       - redis
  13.     logging:
  14.       driver: "json-file"
  15.       options:
  16.         max-size: "10m"
  17.         max-file: "3"
  18.   redis:
  19.     image: redis:6-alpine
  20.     restart: unless-stopped
  21.     volumes:
  22.       - redis-data:/data
  23.   nginx:
  24.     image: nginx:1.20-alpine
  25.     ports:
  26.       - "443:443"
  27.     volumes:
  28.       - ./nginx.conf:/etc/nginx/nginx.conf
  29.       - ./ssl:/etc/nginx/ssl
  30.     depends_on:
  31.       - web
  32.     restart: unless-stopped
  33. volumes:
  34.   redis-data:
复制代码

CI/CD配置

.gitlab-ci.yml
  1. stages:
  2.   - test
  3.   - build
  4.   - deploy
  5. variables:
  6.   DOCKER_REGISTRY: registry.example.com
  7.   DOCKER_IMAGE: $DOCKER_REGISTRY/my-web-app
  8. test:
  9.   stage: test
  10.   image: python:3.9
  11.   script:
  12.     - pip install -r src/requirements.txt
  13.     - pip install pytest
  14.     - pytest tests/
  15. build:
  16.   stage: build
  17.   image: docker:20.10
  18.   services:
  19.     - docker:20.10-dind
  20.   script:
  21.     - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  22.     - docker build -f docker/Dockerfile -t $DOCKER_IMAGE:$CI_COMMIT_SHA .
  23.     - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
  24.     - docker tag $DOCKER_IMAGE:$CI_COMMIT_SHA $DOCKER_IMAGE:latest
  25.     - docker push $DOCKER_IMAGE:latest
  26.   only:
  27.     - main
  28.     - develop
  29. deploy_staging:
  30.   stage: deploy
  31.   script:
  32.     - ssh deploy@staging.example.com "cd /opt/my-web-app && VERSION=$CI_COMMIT_SHA docker-compose -f docker-compose.prod.yml pull && VERSION=$CI_COMMIT_SHA docker-compose -f docker-compose.prod.yml up -d"
  33.   environment:
  34.     name: staging
  35.     url: https://staging.example.com
  36.   only:
  37.     - develop
  38. deploy_production:
  39.   stage: deploy
  40.   script:
  41.     - ssh deploy@production.example.com "cd /opt/my-web-app && VERSION=$CI_COMMIT_SHA docker-compose -f docker-compose.prod.yml pull && VERSION=$CI_COMMIT_SHA docker-compose -f docker-compose.prod.yml up -d"
  42.   environment:
  43.     name: production
  44.     url: https://example.com
  45.   when: manual
  46.   only:
  47.     - main
复制代码

部署脚本

deploy.sh
  1. #!/bin/bash
  2. # 设置变量
  3. REGISTRY="registry.example.com"
  4. IMAGE_NAME="my-web-app"
  5. VERSION=${1:-latest}
  6. ENVIRONMENT=${2:-staging}
  7. # 根据环境设置服务器
  8. if [ "$ENVIRONMENT" = "production" ]; then
  9.     SERVER="deploy@production.example.com"
  10. else
  11.     SERVER="deploy@staging.example.com"
  12. fi
  13. # 构建镜像
  14. echo "Building Docker image..."
  15. docker build -f docker/Dockerfile -t $REGISTRY/$IMAGE_NAME:$VERSION .
  16. # 推送镜像
  17. echo "Pushing Docker image..."
  18. docker push $REGISTRY/$IMAGE_NAME:$VERSION
  19. # 部署到服务器
  20. echo "Deploying to $ENVIRONMENT..."
  21. ssh $SERVER "cd /opt/my-web-app && VERSION=$VERSION docker-compose -f docker-compose.prod.yml pull && VERSION=$VERSION docker-compose -f docker-compose.prod.yml up -d"
  22. echo "Deployment completed!"
复制代码

监控配置

monitoring/prometheus.yml
  1. global:
  2.   scrape_interval: 15s
  3. scrape_configs:
  4.   - job_name: 'my-web-app'
  5.     static_configs:
  6.       - targets: ['web:5000']
  7.     metrics_path: '/metrics'
  8.     scrape_interval: 5s
  9.   - job_name: 'redis'
  10.     static_configs:
  11.       - targets: ['redis:6379']
  12.   - job_name: 'node-exporter'
  13.     static_configs:
  14.       - targets: ['node-exporter:9100']
复制代码

monitoring/grafana/dashboards/dashboard.json
  1. {
  2.   "dashboard": {
  3.     "title": "My Web App Metrics",
  4.     "panels": [
  5.       {
  6.         "title": "HTTP Requests",
  7.         "type": "graph",
  8.         "targets": [
  9.           {
  10.             "expr": "rate(http_requests_total[5m])",
  11.             "legendFormat": "{{method}} {{status}}"
  12.           }
  13.         ]
  14.       },
  15.       {
  16.         "title": "Response Time",
  17.         "type": "graph",
  18.         "targets": [
  19.           {
  20.             "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
  21.             "legendFormat": "95th percentile"
  22.           }
  23.         ]
  24.       },
  25.       {
  26.         "title": "System Resources",
  27.         "type": "graph",
  28.         "targets": [
  29.           {
  30.             "expr": "100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)",
  31.             "legendFormat": "CPU Usage"
  32.           },
  33.           {
  34.             "expr": "(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100",
  35.             "legendFormat": "Memory Usage"
  36.           }
  37.         ]
  38.       }
  39.     ]
  40.   }
  41. }
复制代码

完整的部署流程

1. 开发阶段:开发人员使用docker-compose在本地运行应用运行测试确保代码质量提交代码到Git仓库
2. 开发人员使用docker-compose在本地运行应用
3. 运行测试确保代码质量
4. 提交代码到Git仓库
5. CI阶段:GitLab CI自动触发测试测试通过后构建Docker镜像将镜像推送到镜像仓库
6. GitLab CI自动触发测试
7. 测试通过后构建Docker镜像
8. 将镜像推送到镜像仓库
9. 部署到测试环境:自动部署到测试环境进行集成测试和用户验收测试
10. 自动部署到测试环境
11. 进行集成测试和用户验收测试
12. 部署到生产环境:手动触发生产环境部署使用蓝绿部署或滚动更新策略监控应用性能和健康状态
13. 手动触发生产环境部署
14. 使用蓝绿部署或滚动更新策略
15. 监控应用性能和健康状态
16. 监控和维护:使用Prometheus和Grafana监控应用设置告警规则定期更新镜像和依赖
17. 使用Prometheus和Grafana监控应用
18. 设置告警规则
19. 定期更新镜像和依赖

开发阶段:

• 开发人员使用docker-compose在本地运行应用
• 运行测试确保代码质量
• 提交代码到Git仓库

CI阶段:

• GitLab CI自动触发测试
• 测试通过后构建Docker镜像
• 将镜像推送到镜像仓库

部署到测试环境:

• 自动部署到测试环境
• 进行集成测试和用户验收测试

部署到生产环境:

• 手动触发生产环境部署
• 使用蓝绿部署或滚动更新策略
• 监控应用性能和健康状态

监控和维护:

• 使用Prometheus和Grafana监控应用
• 设置告警规则
• 定期更新镜像和依赖

总结与展望

通过本文的学习,我们从Docker的基础概念开始,逐步深入到镜像构建的高级技巧、仓库管理、常见问题解决以及DevOps最佳实践。Docker作为容器化技术的代表,已经深刻改变了软件开发和部署的方式,使DevOps工作流程更加顺畅高效。

关键要点回顾

1. Docker基础:理解镜像、容器、仓库等基本概念是掌握Docker的第一步。
2. 镜像构建:通过Dockerfile可以定义镜像的构建过程,使用多阶段构建和优化技巧可以创建更小、更安全的镜像。
3. 仓库管理:无论是使用公共仓库如Docker Hub,还是私有仓库如Harbor,都需要掌握镜像的推送和拉取操作。
4. 问题解决:容器化部署中常见的问题如镜像过大、构建速度慢、容器启动失败等,都有相应的解决方案。
5. DevOps集成:将Docker集成到CI/CD流程中,可以实现自动化构建、测试和部署,提高开发效率。
6. 实战应用:通过完整的示例,我们了解了如何将一个Web应用容器化并部署到生产环境。

Docker基础:理解镜像、容器、仓库等基本概念是掌握Docker的第一步。

镜像构建:通过Dockerfile可以定义镜像的构建过程,使用多阶段构建和优化技巧可以创建更小、更安全的镜像。

仓库管理:无论是使用公共仓库如Docker Hub,还是私有仓库如Harbor,都需要掌握镜像的推送和拉取操作。

问题解决:容器化部署中常见的问题如镜像过大、构建速度慢、容器启动失败等,都有相应的解决方案。

DevOps集成:将Docker集成到CI/CD流程中,可以实现自动化构建、测试和部署,提高开发效率。

实战应用:通过完整的示例,我们了解了如何将一个Web应用容器化并部署到生产环境。

未来展望

随着云原生技术的发展,Docker和容器技术也在不断演进:

1. Kubernetes:作为容器编排的事实标准,Kubernetes与Docker的结合将更加紧密,提供更强大的容器管理能力。
2. Serverless:无服务器架构与容器技术的结合,如Knative,将提供更灵活的应用部署方式。
3. 安全增强:容器安全将得到更多关注,包括镜像扫描、运行时安全、网络隔离等方面。
4. 边缘计算:Docker在边缘计算场景中的应用将更加广泛,支持更轻量级的容器运行时。
5. WebAssembly:WASM与容器的结合可能带来新的应用打包和运行方式。

Kubernetes:作为容器编排的事实标准,Kubernetes与Docker的结合将更加紧密,提供更强大的容器管理能力。

Serverless:无服务器架构与容器技术的结合,如Knative,将提供更灵活的应用部署方式。

安全增强:容器安全将得到更多关注,包括镜像扫描、运行时安全、网络隔离等方面。

边缘计算:Docker在边缘计算场景中的应用将更加广泛,支持更轻量级的容器运行时。

WebAssembly:WASM与容器的结合可能带来新的应用打包和运行方式。

作为DevOps工程师或开发者,掌握Docker及其生态系统将成为必备技能。通过不断学习和实践,我们可以更好地利用容器技术,构建更加高效、可靠的应用部署流程。

希望本文能够帮助你从入门到精通Docker镜像构建,并在实际工作中解决容器化部署中的常见问题,让你的DevOps工作流程更加顺畅高效。如果你有任何问题或建议,欢迎交流讨论。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则