目录
docker 是一种领先的容器化平台,可以简化应用程序的开发、部署和管理。通过使用 docker,您可以创建一致、可移植且可扩展的环境,从而实现高效的开发和运营。本指南将介绍 docker 的最佳实践,帮助您充分利用 docker 的潜力并确保应用程序的安全性和性能。
一、安全性最佳实践
1、最小权限原则
-
容器运行权限
-
避免容器以
root
用户身份运行:- 默认情况下,docker 容器是以
root
用户身份运行的。 - 如果容器内的应用程序被攻破,攻击者将拥有容器内的
root
权限,可能对主机造成威胁。 - 在 dockerfile 中使用
user
指令,将容器默认用户设置为非特权用户,例如:from ubuntu:latest run groupadd -r appgroup && useradd -r -g appgroup appuser user appuser
- 默认情况下,docker 容器是以
-
以非特权用户身份运行:
- 可以在运行容器时使用
--user
标志指定非特权用户:docker run --user 1000:1000 myimage
- 或者在 docker compose 文件中指定用户:
version: '3' services: app: image: myimage user: "1000:1000"
- 可以在运行容器时使用
-
-
docker 守护进程权限
-
确保只有可信用户能访问 docker 守护进程:
- docker 守护进程默认以
root
用户身份运行,具有较高的系统权限。 - 默认情况下,docker 守护进程监听 unix 套接字
/var/run/docker.sock
,只有docker
组的成员可以访问。 - 使用
chmod
或chown
确保/var/run/docker.sock
只允许可信用户访问:sudo chown root:docker /var/run/docker.sock sudo chmod 660 /var/run/docker.sock
- 将可信用户添加到
docker
组:sudo usermod -ag docker trusted_user
- docker 守护进程默认以
-
限制 docker 守护进程的网络访问权限:
- 默认情况下,docker 守护进程不监听 tcp 套接字。
- 如果需要通过网络远程访问 docker 守护进程,应开启 tls 并确保只接受来自可信客户端的连接。
-
2、网络安全
-
防火墙
-
限制 docker 守护进程的网络访问:
- 使用防火墙(如
iptables
或firewalld
)确保 docker 守护进程仅接受来自可信主机的连接:iptables -a input -p tcp --dport 2375 -s trusted_ip -j accept iptables -a input -p tcp --dport 2375 -j drop
- 使用防火墙(如
-
隔离容器网络:
- 使用防火墙规则隔离不同容器之间的网络通信,例如:
iptables -a forward -s 172.17.0.0/16 -d 172.18.0.0/16 -j drop
- 使用防火墙规则隔离不同容器之间的网络通信,例如:
-
-
docker 网络模型
-
合理选择网络模型:
- bridge 网络:适用于单主机上的容器通信,默认启用 nat 转发。
- host 网络:容器与主机共享网络命名空间,通信性能最佳,但存在安全风险。
- overlay 网络:适用于多主机集群,提供跨主机的容器通信。
- macvlan 网络:直接为容器分配主机网络中的 mac 地址和 ip 地址,适用于需要直接访问局域网的容器。
-
配置网络策略:
- 使用网络策略(如 kubernetes networkpolicy)控制不同容器之间的网络访问。
- 使用防火墙规则限制容器网络的入站和出站流量。
-
3、镜像安全
-
可信镜像
-
使用官方或可信来源的镜像:
- 官方镜像通常经过严格审核,优先选择官方镜像或来自可信组织的镜像。
- docker hub 提供了官方镜像的认证标志(如官方或受信任的发布者)。
-
避免使用
latest
标签:latest
标签指向最新版本的镜像,可能引入未知风险。- 明确指定镜像版本,例如:
docker run myimage:1.0.0
-
-
定期扫描
-
使用漏洞扫描工具:
- 使用工具(如 clair、trivy、anchore)定期扫描镜像中的漏洞。
- 在 ci/cd 流程中集成漏洞扫描工具,确保每次构建都进行安全检查。
-
持续监控新漏洞:
- 持续关注镜像所依赖的软件包的漏洞公告,及时更新镜像。
-
4、主机安全
-
docker 守护进程隔离
- 将 docker 守护进程与主节点隔离:
- 在生产环境中,将 docker 守护进程与主节点隔离,减少潜在攻击面。
- 使用专用虚拟机或裸金属服务器作为 docker 守护进程的运行环境。
- 将 docker 守护进程与主节点隔离:
-
安全配置审计
- 定期进行配置审计:
- 使用工具(如 docker bench for security)定期审计 docker 守护进程的安全配置。
- 审查 docker 守护进程和容器的配置文件,确保关键设置符合最佳实践。
- 定期进行配置审计:
5、资源限制
-
内存限制
- 使用
--memory
限制容器内存使用:- 限制容器的最大内存使用量,例如:
docker run --memory 500m myimage
- 在 docker compose 文件中指定内存限制:
version: '3' services: app: image: myimage deploy: resources: limits: memory: 500m
- 限制容器的最大内存使用量,例如:
- 使用
-
cpu 限制
- 使用
--cpus
或--cpu-shares
限制容器 cpu 使用:- 限制容器的 cpu 使用量,例如:
docker run --cpus 1.5 myimage
- 或者使用
--cpu-shares
指定相对权重:docker run --cpu-shares 512 myimage
- 在 docker compose 文件中指定 cpu 限制:
version: '3' services: app: image: myimage deploy: resources: limits: cpus: '1.5'
- 限制容器的 cpu 使用量,例如:
- 使用
-
磁盘 io 限制
- 使用
--device-read-bps
和--device-write-bps
限制读取和写入速率:- 限制容器对块设备的读取和写入速率,例如:
docker run --device-read-bps /dev/sda:1mb --device-write-bps /dev/sda:1mb myimage
- 限制容器对块设备的读取和写入速率,例如:
- 使用
-
文件句柄数量限制
- 使用
--ulimit
设置文件句柄数量限制:- 限制容器中的文件句柄数量,例如:
docker run --ulimit nofile=1024:2048 myimage
- 限制容器中的文件句柄数量,例如:
- 使用
二、docker 性能最佳实践
1、分层结构优化
-
减少 dockerfile 中的图层数量,合并不必要的
run
命令-
在 docker 中,每个指令(如
run
、copy
、add
)都会创建一个新的镜像层。过多的层次会导致镜像体积过大、构建时间变长。 -
合并相关的
run
命令,可以减少不必要的层次。例如,将多个run
命令合并为一个:
优化前:from ubuntu:latest run apt-get update run apt-get install -y python python-pip
优化后:
from ubuntu:latest run apt-get update && \ apt-get install -y python python-pip
-
在确保构建过程中不会引入额外问题的情况下,尽量合并相关的指令来减少层数。
-
-
使用 docker 缓存(
--cache-from
)构建镜像-
docker 会使用缓存加速镜像构建,但在某些情况下,缓存可能失效,导致完全重新构建镜像。
-
使用
--cache-from
参数可以从现有镜像中恢复缓存层,加速构建:docker build --cache-from=myimage:previous -t myimage:latest .
-
在多阶段构建中,
--cache-from
可以帮助不同阶段共享缓存,提高构建效率。
-
2、多阶段构建
-
多阶段构建减少最终镜像大小
-
多阶段构建可以将构建和生产环境分离,确保生产镜像中只包含必要的文件。
-
通过引入多个
from
指令,实现构建环境和生产环境的分离:# 构建阶段 from golang:1.19 as builder workdir /app copy . . run go build -o myapp # 生产阶段 from alpine:latest workdir /app copy --from=builder /app/myapp . cmd ["./myapp"]
-
通过这种方法,最终的生产镜像仅包含编译后的二进制文件和必要的依赖项,减少了镜像大小。
-
3、卷的使用
-
使用卷保持数据持久化
-
docker 容器的文件系统是临时性的,容器停止或删除后,内部数据将丢失。
-
使用 docker 卷,可以将数据存储在主机上,从而实现数据持久化:
docker run -v /host/data:/container/data myimage
-
在 docker compose 中使用卷:
version: '3' services: app: image: myimage volumes: - /host/data:/container/data
-
-
避免频繁修改容器内部文件系统
-
docker 容器的文件系统性能相对较低,频繁修改文件系统会影响容器性能。
-
使用卷将数据存储到主机上可以提高性能,并减少容器内部文件系统的修改:
docker run -v /host/data:/container/data myimage
-
使用 tmpfs 卷在内存中存储临时数据,可以进一步提高性能:
docker run --tmpfs /container/tmpfs myimage
-
4、镜像大小优化
-
选择合适的基础镜像
- 使用精简的基础镜像可以显著减少最终镜像的大小。
- 常见的精简镜像包括:
- alpine:约 5mb 的精简 linux 发行版。
- busybox:一个小型的 unix 工具集。
- 例如,将基础镜像从
ubuntu:latest
更改为alpine:latest
:
优化前:
优化后:from ubuntu:latest run apt-get update && apt-get install -y python
from alpine:latest run apk --no-cache add python
-
清理构建依赖
- 构建过程中的依赖可能会占用大量空间,在最终镜像中应尽可能清理。
- 在 dockerfile 中使用多阶段构建,或在单阶段构建中清理不必要的文件:
单阶段构建清理方式:from ubuntu:latest run apt-get update && \ apt-get install -y build-essential && \ make && \ apt-get remove -y build-essential && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/*
-
多阶段构建
- 多阶段构建不仅可以减少最终镜像大小,还能加速构建过程。
- 通过在构建阶段安装构建依赖并编译程序,最终镜像只包含编译后的二进制文件。
-
压缩镜像
- 使用
docker-slim
等工具可以进一步压缩镜像大小:docker-slim build myimage
- 使用
5、docker 编排性能优化
-
资源限制
在编排工具中设置资源限制,确保容器不会滥用集群资源:
docker compose:
version: '3'
services:
app:
image: myimage
deploy:
resources:
limits:
cpus: '1.0'
memory: '500m'
kubernetes:
apiversion: v1
kind: pod
metadata:
name: app
spec:
containers:
- name: app
image: myimage
resources:
limits:
memory: "500mi"
cpu: "1"
-
副本数
- 在编排工具中合理设置副本数,确保应用具有足够的可用性和性能。
-
水平扩展
- 使用负载均衡和水平扩展技术提高应用程序的可用性和性能,例如:
- docker swarm 中的服务扩展。
- kubernetes 中的副本集和自动扩展器。
- 使用负载均衡和水平扩展技术提高应用程序的可用性和性能,例如:
docker 的性能优化涉及多个方面,包括分层结构、镜像大小、卷的使用、多阶段构建以及编排性能优化。在实际开发和生产中,合理应用这些技术和策略可以显著提高 docker 容器的性能和效率。
三、docker 开发流程
1、dockerfile 最佳实践
-
保持 dockerfile 简洁,注释清晰,并遵循官方最佳实践指南
-
使用官方基础镜像:官方基础镜像通常经过严格的测试和优化,选择合适的官方镜像可以提高质量并减少潜在问题。
from python:3.9-slim
-
明确标签和版本:避免使用
latest
标签,尽量指定明确的镜像版本。from node:16.14.0
-
合并相关命令,减少层数:合并
run
指令,减少镜像层数。
优化前:from ubuntu:latest run apt-get update run apt-get install -y python3 python3-pip
优化后:
from ubuntu:latest run apt-get update && \ apt-get install -y python3 python3-pip
-
最小化构建依赖:尽量只安装构建过程中需要的依赖,并在构建完成后进行清理。
run apt-get update && \ apt-get install -y --no-install-recommends build-essential && \ make && \ apt-get purge -y --auto-remove build-essential && \ rm -rf /var/lib/apt/lists/*
-
多阶段构建:使用多阶段构建确保最终镜像只包含必要文件。
# 构建阶段 from golang:1.19 as builder workdir /app copy . . run go build -o main # 生产阶段 from alpine:latest workdir /app copy --from=builder /app/main . cmd ["./main"]
-
注释清晰:为关键步骤添加注释,方便他人理解和维护。
# 使用 python 官方精简版镜像 from python:3.9-slim # 设置工作目录 workdir /app # 复制应用程序代码 copy . /app # 安装依赖 run pip install --no-cache-dir -r requirements.txt # 启动应用 cmd ["python", "app.py"]
-
-
避免使用
latest
标签,尽量指定明确的版本- 避免使用
latest
标签,因为它可能指向不同的版本,导致不一致的行为。 - 为基础镜像和依赖都指定明确的版本:
from node:16.14.0
- 避免使用
2、持续集成
-
将 docker 镜像构建纳入持续集成流程,确保镜像构建自动化
-
集成工具:使用 jenkins、gitlab ci/cd、github actions 或其他 ci/cd 工具自动化 docker 镜像的构建和测试。
-
示例:github actions
name: build and push docker image on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - name: checkout code uses: actions/checkout@v2 - name: set up docker buildx uses: docker/setup-buildx-action@v1 - name: cache docker layers uses: actions/cache@v2 with: path: ~/.docker key: ${{ runner.os }}-docker-${{ hashfiles('**/dockerfile') }} restore-keys: | ${{ runner.os }}-docker - name: log in to docker hub uses: docker/login-action@v1 with: username: ${{ secrets.docker_hub_username }} password: ${{ secrets.docker_hub_token }} - name: build and push docker image uses: docker/build-push-action@v2 with: push: true tags: myusername/myimage:latest
- 创建
.github/workflows/docker-build.yml
文件,并添加以下内容:
- 创建
-
示例:gitlab ci/cd
image: docker:latest services: - docker:dind stages: - build - test - deploy variables: docker_driver: overlay2 build: stage: build script: - docker build -t myusername/myimage:latest . test: stage: test script: - docker run --rm myusername/myimage:latest pytest deploy: stage: deploy script: - docker login -u "$ci_registry_user" -p "$ci_registry_password" $ci_registry - docker push myusername/myimage:latest
- 在项目根目录中创建
.gitlab-ci.yml
文件,并添加以下内容:
- 在项目根目录中创建
-
3、版本管理
-
定期更新 docker 版本
docker --version sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io
- 确保使用最新的稳定版本,可以获得最新的功能和性能改进,并减少安全漏洞。
- 使用以下命令检查和更新 docker 版本:
-
关注官方的安全公告
- 关注 docker 官方发布的安全公告,及时修复漏洞和更新补丁。
- 官方安全公告地址:
docker security advisories
四、社区资源
1、docker hub
- docker hub 是 docker 官方的镜像仓库,提供了大量的官方镜像和社区镜像。
- 使用 docker hub 可以方便地获取和共享镜像,并从中学习最佳实践。
- 官方地址:docker hub
2、docker 官方文档
- docker 官方文档详细介绍了 docker 的安装、配置和使用方法,以及最佳实践和安全指南。
- 官方地址:docker documentation
3、docker 论坛
- docker 官方论坛是一个活跃的社区,用户可以在其中提问、分享经验和获取支持。
- 官方地址:docker forums
4、docker github 仓库
- docker 在 github 上拥有多个开源项目仓库,用户可以通过提交问题、贡献代码和阅读源代码来获取最佳实践和支持。
- 官方地址:docker github
通过遵循 dockerfile 最佳实践、将 docker 镜像构建纳入持续集成流程、定期更新 docker 版本、利用社区资源等手段,可以确保 docker 开发流程的高效和安全。持续学习和改进这些实践,有助于在生产环境中成功运行容器化应用。
五、总结
遵循这些 docker 最佳实践,您可以最大限度地提高应用程序的安全性、性能和开发效率。docker 提供了一套强大的工具和功能来优化应用程序的交付和运行时环境。通过实施这些最佳实践,您可以充分利用 docker 的潜力,确保应用程序的可靠性和可扩展性。
祝您的 docker 之旅一切顺利!
发表评论