news 2026/4/3 4:41:11

Docker从入门到放弃?不存在的!这份实战指南让你秒变容器老司机

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker从入门到放弃?不存在的!这份实战指南让你秒变容器老司机

最近总有朋友问我Docker到底是个啥,怎么用,感觉很高大上但又不知道从哪里下手。说实话,我刚开始接触Docker的时候也是一脸懵逼,各种概念搞得头大。不过用了几年下来,现在回头看,Docker真的是个好东西,能解决很多实际问题。

今天就来聊聊Docker的实际使用,不讲那些虚头巴脑的理论,直接上干货。我会把自己这几年踩过的坑、用过的技巧都分享出来,希望能帮到大家。

什么是Docker,为什么要用它

Docker说白了就是一个容器技术,可以把应用程序和它的运行环境打包在一起。你可以理解为一个轻量级的虚拟机,但比虚拟机要快很多。

我记得以前部署应用的时候,经常遇到"在我电脑上能跑啊"这种问题。开发环境Python 3.8,测试环境Python 3.7,生产环境又是3.9,各种版本冲突搞得人头疼。有了Docker之后,这些问题基本就不存在了,因为环境都打包好了,到哪里都一样。

还有一个好处就是资源利用率高。以前一台服务器可能只跑一个应用,现在可以跑十几个容器,每个容器都是独立的,互不影响。

安装Docker

安装Docker其实很简单,官网都有详细的文档。不过我还是说几个注意点。

在Ubuntu上安装:

# 更新包索引sudoaptupdate# 安装必要的包sudoaptinstallapt-transport-https ca-certificatescurlgnupg lsb-release# 添加Docker官方GPG密钥curl-fsSL https://download.docker.com/linux/ubuntu/gpg|sudogpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg# 设置稳定版仓库echo"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu$(lsb_release -cs)stable"|sudotee/etc/apt/sources.list.d/docker.list>/dev/null# 安装Docker Enginesudoaptupdatesudoaptinstalldocker-ce docker-ce-cli containerd.io

安装完成后记得把当前用户加入docker组,这样就不用每次都sudo了:

sudousermod-aG docker$USER

然后重新登录一下,或者执行newgrp docker

CentOS的安装过程类似,就是包管理器换成yum。Windows和Mac的话直接下载Docker Desktop就行了,图形界面操作很方便。

Docker的基本概念

在开始实际操作之前,先理解几个核心概念。

镜像(Image):可以理解为一个模板,包含了运行应用所需的所有东西。比如一个Ubuntu镜像就包含了Ubuntu系统的基本文件。

容器(Container):镜像运行起来就是容器。一个镜像可以创建多个容器,就像一个类可以实例化多个对象一样。

仓库(Repository):存放镜像的地方,Docker Hub是最大的公共仓库。

这三个概念搞清楚了,后面的操作就好理解了。

常用Docker命令

镜像相关命令

查看本地镜像:

docker images

拉取镜像:

docker pull nginx:latest docker pull ubuntu:20.04

删除镜像:

docker rmi nginx:latest

构建镜像:

docker build -t myapp:v1.0.

容器相关命令

运行容器:

# 最基本的运行docker run hello-world# 交互式运行docker run -it ubuntu:20.04 /bin/bash# 后台运行docker run -d nginx:latest# 端口映射docker run -d -p8080:80 nginx:latest

查看容器:

# 查看正在运行的容器dockerps# 查看所有容器(包括停止的)dockerps-a

停止和启动容器:

docker stop container_id docker start container_id docker restart container_id

进入容器:

dockerexec-it container_id /bin/bash

删除容器:

dockerrmcontainer_id

查看容器日志:

docker logs container_id docker logs -f container_id# 实时查看

实战案例:部署一个Web应用

说了这么多理论,来个实际例子。我们部署一个简单的Flask应用。

首先创建一个Flask应用,文件名app.py:

fromflaskimportFlask app=Flask(__name__)@app.route('/')defhello():return'<h1>Hello from Docker!</h1>'@app.route('/health')defhealth():return{'status':'ok'}if__name__=='__main__':app.run(host='0.0.0.0',port=5000)

创建requirements.txt:

Flask==2.3.3

然后写Dockerfile:

# 使用Python 3.9作为基础镜像 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制requirements文件 COPY requirements.txt . # 安装依赖 RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY app.py . # 暴露端口 EXPOSE 5000 # 运行应用 CMD ["python", "app.py"]

构建镜像:

docker build -t flask-app:v1.0.

运行容器:

docker run -d -p5000:5000 --name my-flask-app flask-app:v1.0

现在访问http://localhost:5000就能看到应用了。

这个例子看起来简单,但包含了Docker的核心用法。在实际项目中,可能还需要考虑数据持久化、环境变量配置等问题。

数据持久化和卷(Volume)

容器默认是无状态的,容器删除后数据就没了。但很多时候我们需要持久化数据,比如数据库文件、日志文件等。

Docker提供了几种数据持久化的方式:

绑定挂载(Bind Mount)

直接把主机的目录挂载到容器里:

docker run -d -v /host/path:/container/path nginx:latest

比如运行一个MySQL容器,把数据目录挂载到主机:

docker run -d\--name mysql-server\-eMYSQL_ROOT_PASSWORD=mypassword\-v /opt/mysql-data:/var/lib/mysql\-p3306:3306\mysql:8.0

这样即使容器删除了,数据库文件还在主机的/opt/mysql-data目录里。

Docker卷(Volume)

这是Docker推荐的方式:

# 创建卷docker volume create mydata# 使用卷docker run -d -v mydata:/data nginx:latest# 查看卷docker volumels# 查看卷详情docker volume inspect mydata

卷的好处是由Docker管理,不用担心路径问题,而且可以在容器间共享。

临时文件系统(tmpfs)

把数据存储在内存中,容器停止后数据就没了:

docker run -d --tmpfs /tmp nginx:latest

这种方式适合存储临时数据,比如缓存文件。

Docker网络详解

Docker的网络功能很强大,也是很多人觉得复杂的地方。我刚开始学的时候也被搞得晕头转向,不过理解了原理之后就清晰多了。

Docker网络架构

Docker使用Linux的网络命名空间来实现容器网络隔离。每个容器都有自己的网络栈,包括网卡、路由表、防火墙规则等。

Docker在宿主机上创建了一个虚拟网桥docker0,默认情况下所有容器都连接到这个网桥上。容器之间可以通过这个网桥进行通信。

默认网络模式

Docker提供了几种网络模式:

bridge模式(默认)
容器连接到docker0网桥,有自己的IP地址,可以和其他容器通信。

docker run -d --name web nginx:latest

host模式
容器直接使用宿主机的网络,没有网络隔离。

docker run -d --networkhostnginx:latest

这种模式下容器的网络性能最好,但失去了隔离性。

none模式
容器没有网络接口,完全隔离。

docker run -d --network none nginx:latest

container模式
容器共享另一个容器的网络。

docker run -d --name web nginx:latest docker run -d --network container:web busybox:latest

查看默认网络

Docker安装后会创建几个默认网络:

docker networkls

通常会看到这些网络:

  • bridge:默认网桥网络
  • host:主机网络
  • none:无网络

查看网络详情:

docker network inspect bridge

这个命令会显示网络的配置信息,包括子网、网关、连接的容器等。

自定义网络

自定义网络是Docker网络的精髓,可以实现更灵活的网络配置。

创建bridge网络
# 创建自定义网络docker network create mynetwork# 指定子网和网关docker network create --driver bridge\--subnet=172.20.0.0/16\--ip-range=172.20.240.0/20\--gateway=172.20.0.1\mynetwork2
使用自定义网络
# 运行容器时指定网络docker run -d --network mynetwork --name web nginx:latest docker run -d --network mynetwork --name db mysql:8.0# 给运行中的容器连接网络docker network connect mynetwork existing_container
自定义网络的优势

在自定义网络中,容器可以通过容器名相互访问,这是默认bridge网络没有的功能:

# 创建网络和容器docker network create webapp docker run -d --network webapp --name database mysql:8.0 docker run -d --network webapp --name backend node-app:latest docker run -d --network webapp --name frontend nginx:latest# backend容器可以通过"database"这个主机名访问数据库# frontend容器可以通过"backend"访问后端API

这个功能叫做自动服务发现,非常实用。

网络连接和断开

容器可以同时连接多个网络:

# 创建两个网络docker network create frontend docker network create backend# 容器连接到多个网络docker run -d --network frontend --name web nginx:latest docker network connect backend web# 断开网络连接docker network disconnect frontend web

这样可以实现更复杂的网络拓扑,比如三层架构中的网络隔离。

端口映射详解

端口映射让外部可以访问容器内的服务,有几种方式:

基本端口映射
# 映射单个端口:宿主机8080端口映射到容器80端口docker run -d -p8080:80 nginx:latest# 映射多个端口docker run -d -p8080:80 -p8443:443 nginx:latest# 映射到指定IPdocker run -d -p127.0.0.1:8080:80 nginx:latest# 随机映射端口docker run -d -P nginx:latest
UDP端口映射
# TCP端口(默认)docker run -d -p53:53 dns-server:latest# UDP端口docker run -d -p53:53/udp dns-server:latest# 同时映射TCP和UDPdocker run -d -p53:53/tcp -p53:53/udp dns-server:latest
查看端口映射
# 查看容器端口映射docker port container_name# 查看所有容器的端口dockerps--format"table {{.Names}}\t{{.Ports}}"

容器间通信实例

来个实际例子,部署一个Web应用,包含前端、后端、数据库:

# 创建自定义网络docker network create webapp --subnet=172.18.0.0/16# 启动数据库容器docker run -d\--name database\--network webapp\--ip172.18.0.10\-eMYSQL_ROOT_PASSWORD=rootpass\-eMYSQL_DATABASE=myapp\mysql:8.0# 启动后端API容器docker run -d\--name api\--network webapp\--ip172.18.0.20\-eDATABASE_HOST=database\-eDATABASE_PORT=3306\my-api:latest# 启动前端容器docker run -d\--name frontend\--network webapp\--ip172.18.0.30\-p80:80\-eAPI_HOST=api\-eAPI_PORT=3000\my-frontend:latest

在这个例子中:

  • 数据库容器不需要暴露端口到宿主机,只在内部网络中通信
  • API容器可以通过"database"主机名访问数据库
  • 前端容器可以通过"api"主机名访问后端
  • 只有前端容器暴露端口给外部访问

网络故障排查

当容器网络出问题时,可以用这些方法排查:

检查容器网络配置
# 查看容器的网络配置docker inspect container_name|grep-A20"NetworkSettings"# 查看容器IP地址docker inspect container_name|grepIPAddress
进入容器测试网络
# 进入容器dockerexec-it container_name /bin/bash# 测试网络连通性pinganother_container telnet database3306curlhttp://api:3000/health# 查看网络接口ipaddr show route -n
在容器内安装网络工具
# Debian/Ubuntu容器apt-getupdate&&apt-getinstall-y iputils-pingcurltelnet net-tools# Alpine容器apkadd--no-cache iputilscurl# CentOS容器yuminstall-y iputilscurltelnet net-tools
宿主机网络检查
# 查看docker网桥ipaddr show docker0 brctl show docker0# 查看iptables规则iptables -L -n iptables -t nat -L -n# 查看网络命名空间ipnetns list

跨主机网络

当应用需要在多台服务器上运行时,就需要跨主机网络了。Docker提供了几种方案:

Docker Swarm网络
# 初始化Swarm集群docker swarm init --advertise-addr192.168.1.100# 在其他节点加入集群docker swarmjoin--token<token>192.168.1.100:2377# 创建overlay网络docker network create -d overlay myoverlay# 部署服务到overlay网络dockerservicecreate --network myoverlay --name web nginx:latest

Overlay网络让不同主机上的容器可以直接通信,就像在同一台机器上一样。

第三方网络插件

比如Calico、Flannel、Weave等,提供更高级的网络功能。

网络安全

容器网络安全也很重要,几个建议:

网络隔离
# 创建独立的网络,避免容器间不必要的通信docker network create --internal backend-only docker run -d --network backend-only database:latest
防火墙规则
# Docker会自动添加iptables规则,但可以手动调整iptables -I DOCKER-USER-s172.17.0.0/16 -d192.168.1.0/24 -j DROP
使用非特权端口
# 避免使用特权端口(1-1024)docker run -d -p8080:80 nginx:latest# 好docker run -d -p80:80 nginx:latest# 不推荐

网络性能优化

选择合适的网络模式
  • 对性能要求极高的应用可以使用host模式
  • 一般应用使用bridge模式就够了
  • 需要网络隔离的用自定义网络
调整网络参数
# 修改docker daemon配置cat>/etc/docker/daemon.json<<EOF { "bip": "172.17.0.1/16", "mtu": 1500, "default-address-pools": [ { "base": "172.80.0.0/12", "size": 24 } ] } EOF
监控网络流量
# 查看容器网络统计docker stats --format"table {{.Container}}\t{{.NetIO}}"# 使用iftop监控网络流量dockerexec-it container_name iftop

环境变量和配置

很多应用需要通过环境变量进行配置,Docker提供了几种方式:

运行时指定

docker run -d -eMYSQL_ROOT_PASSWORD=mypassword mysql:8.0 docker run -d -eNODE_ENV=production -ePORT=3000node-app:latest

使用env文件

创建.env文件:

MYSQL_ROOT_PASSWORD=mypassword MYSQL_DATABASE=myapp MYSQL_USER=appuser MYSQL_PASSWORD=apppassword

然后运行:

docker run -d --env-file .env mysql:8.0

这种方式比较安全,敏感信息不会出现在命令行里。

在Dockerfile中设置默认值

FROM node:16 ENV NODE_ENV=production ENV PORT=3000 # ...

Docker Compose:多容器应用的救星

当应用变复杂,需要多个容器协同工作时,一个个手动启动容器就很麻烦了。Docker Compose就是来解决这个问题的。

安装Docker Compose

现在的Docker Desktop都自带了Compose,Linux上可能需要单独安装:

sudocurl-L"https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname-s)-$(uname-m)"-o /usr/local/bin/docker-composesudochmod+x /usr/local/bin/docker-compose

编写docker-compose.yml

比如一个典型的Web应用,需要Web服务器、数据库、Redis缓存:

version:'3.8'services:web:build:.ports:-"5000:5000"environment:-DATABASE_URL=mysql://user:password@db:3306/myapp-REDIS_URL=redis://redis:6379depends_on:-db-redisvolumes:-./logs:/app/logsnetworks:-frontend-backenddb:image:mysql:8.0environment:MYSQL_ROOT_PASSWORD:rootpasswordMYSQL_DATABASE:myappMYSQL_USER:userMYSQL_PASSWORD:passwordvolumes:-mysql_data:/var/lib/mysqlnetworks:-backendredis:image:redis:7-alpinenetworks:-backendnginx:image:nginx:alpineports:-"80:80"volumes:-./nginx.conf:/etc/nginx/nginx.confdepends_on:-webnetworks:-frontendnetworks:frontend:driver:bridgebackend:driver:bridgeinternal:truevolumes:mysql_data:

这个例子展示了网络分层:

  • frontend网络连接nginx和web服务
  • backend网络连接web、db和redis
  • backend网络设置为internal,外部无法直接访问

使用Compose命令

# 启动所有服务docker-compose up -d# 查看服务状态docker-composeps# 查看日志docker-compose logs web docker-compose logs -f# 实时查看所有服务日志# 停止服务docker-compose stop# 停止并删除容器docker-compose down# 重新构建并启动docker-compose up -d --build

用了Compose之后,管理多容器应用就轻松多了。一个命令就能启动整个应用栈,而且服务之间的依赖关系也处理得很好。

实际生产环境的最佳实践

镜像优化

镜像大小直接影响部署速度,所以要尽量优化:

使用多阶段构建

# 构建阶段 FROM node:16 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # 运行阶段 FROM node:16-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . EXPOSE 3000 CMD ["node", "server.js"]

选择合适的基础镜像

  • alpine版本通常比较小
  • slim版本是精简版,比完整版小很多
  • 如果不需要包管理器,可以用distroless镜像

清理不必要的文件

RUN apt-get update && apt-get install -y \ package1 \ package2 \ && rm -rf /var/lib/apt/lists/*

安全考虑

不要用root用户运行应用

RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 USER nextjs

扫描镜像漏洞

docker scan myapp:latest

使用.dockerignore

node_modules npm-debug.log .git .gitignore README.md .env .nyc_output coverage .nyc_output

健康检查

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1

在Compose中:

services:web:build:.healthcheck:test:["CMD","curl","-f","http://localhost:3000/health"]interval:30stimeout:10sretries:3start_period:40s

日志管理

Docker默认的json-file日志驱动会让日志文件越来越大,生产环境建议配置日志轮转:

docker run -d\--log-driver json-file\--log-opt max-size=10m\--log-opt max-file=3\nginx:latest

或者使用外部日志系统,比如ELK Stack。

监控和调试

查看容器资源使用情况

# 实时查看docker stats# 查看特定容器docker stats container_name

进入容器调试

# 进入运行中的容器dockerexec-it container_name /bin/bash# 如果没有bash,试试shdockerexec-it container_name /bin/sh

查看容器详细信息

docker inspect container_name

这个命令会输出容器的所有配置信息,包括网络、挂载点、环境变量等,调试时很有用。

容器内安装调试工具

有时候需要在容器内安装一些调试工具:

# 在Debian/Ubuntu容器内apt-getupdate&&apt-getinstall-ycurlvimnet-tools# 在Alpine容器内apkadd--no-cachecurlvim

不过这种方式只是临时的,容器重启后就没了。

常见问题和解决方案

容器启动失败

首先查看日志:

docker logs container_name

常见原因:

  • 端口被占用
  • 环境变量配置错误
  • 挂载路径不存在
  • 权限问题

容器内时间不对

容器默认使用UTC时间,如果需要本地时间:

docker run -d -v /etc/localtime:/etc/localtime:ro myapp:latest

或者设置时区环境变量:

docker run -d -eTZ=Asia/Shanghai myapp:latest

镜像拉取慢

国内网络环境下,从Docker Hub拉取镜像可能很慢,可以配置镜像加速器。

编辑/etc/docker/daemon.json:

{"registry-mirrors":["https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com"]}

然后重启Docker服务:

sudosystemctl restart docker

容器间通信问题

确保容器在同一个网络中,或者使用link(虽然已经deprecated):

docker run -d --name db mysql:8.0 docker run -d --link db:database web-app:latest

不过建议还是用自定义网络,更灵活。

数据丢失问题

记住容器是无状态的,重要数据一定要持久化。数据库、配置文件、日志文件都要挂载到主机或者Docker卷。

Docker在CI/CD中的应用

Docker在持续集成和持续部署中发挥了重要作用。

GitLab CI示例

stages:-build-test-deployvariables:DOCKER_IMAGE:$CI_REGISTRY_IMAGE:$CI_COMMIT_SHAbuild:stage:buildscript:-docker build-t $DOCKER_IMAGE .-docker push $DOCKER_IMAGEtest:stage:testscript:-docker run--rm $DOCKER_IMAGE npm testdeploy:stage:deployscript:-docker pull $DOCKER_IMAGE-docker stop myapp||true-docker rm myapp||true-docker run-d--name myapp-p 80:3000 $DOCKER_IMAGEonly:-main

GitHub Actions示例

name:Build and Deployon:push:branches:[main]jobs:build-and-deploy:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v2-name:Build Docker imagerun:docker build-t myapp:${{github.sha}}.-name:Run testsrun:docker run--rm myapp:${{github.sha}}npm test-name:Deployrun:|docker stop myapp || true docker rm myapp || true docker run -d --name myapp -p 80:3000 myapp:${{ github.sha }}

这样每次代码提交后,就会自动构建镜像、运行测试、部署应用。

性能优化技巧

减少镜像层数

每个RUN、COPY、ADD指令都会创建一个新的镜像层,尽量合并:

# 不好的做法 RUN apt-get update RUN apt-get install -y package1 RUN apt-get install -y package2 # 好的做法 RUN apt-get update && apt-get install -y \ package1 \ package2 \ && rm -rf /var/lib/apt/lists/*

利用构建缓存

Docker会缓存镜像层,把变化频繁的指令放在后面:

# 先复制依赖文件 COPY package.json package-lock.json ./ RUN npm ci # 再复制源代码 COPY . .

这样源代码变化时,依赖安装的缓存还能用。

使用.dockerignore

类似.gitignore,避免把不必要的文件复制到镜像中:

node_modules .git .gitignore README.md Dockerfile .dockerignore

选择合适的基础镜像

  • 开发环境可以用完整版镜像,方便调试
  • 生产环境用精简版,减少攻击面和镜像大小
  • 如果追求极致性能,可以用scratch或distroless

Docker网络高级配置

刚才讲了基础的网络知识,现在来看看一些高级配置。

MacVLAN网络

MacVLAN可以让容器直接获得物理网络的IP地址,就像虚拟机一样:

# 创建MacVLAN网络docker network create -d macvlan\--subnet=192.168.1.0/24\--gateway=192.168.1.1\-oparent=eth0\macvlan-net# 运行容器docker run -d --network macvlan-net --ip=192.168.1.100 nginx:latest

这种方式的好处是容器可以直接和物理网络中的设备通信,不需要端口映射。但缺点是需要网络管理员分配IP地址,而且不是所有的网络环境都支持。

IPvlan网络

IPvlan类似MacVLAN,但使用同一个MAC地址:

docker network create -d ipvlan\--subnet=192.168.1.0/24\--gateway=192.168.1.1\-oparent=eth0\-oipvlan_mode=l2\ipvlan-net

IPvlan有两种模式:

  • L2模式:类似MacVLAN,但共享MAC地址
  • L3模式:路由模式,需要配置路由规则

网络别名

在自定义网络中,可以给容器设置别名:

docker network create mynet docker run -d --network mynet --network-alias db --network-alias database mysql:8.0

这样其他容器可以通过"db"或"database"来访问这个MySQL容器。

网络插件

Docker支持第三方网络插件,比如:

Weave

# 安装Weavesudocurl-L git.io/weave -o /usr/local/bin/weavesudochmoda+x /usr/local/bin/weave# 启动Weaveweave launcheval$(weaveenv)# 运行容器docker run -d --name web nginx:latest

Calico

# 下载Calicocurl-O -L https://github.com/projectcalico/calicoctl/releases/download/v3.24.0/calicoctlchmod+x calicoctl# 配置网络策略calicoctl apply -f network-policy.yaml

这些插件提供了更高级的网络功能,比如网络策略、跨数据中心网络等。

Docker存储驱动

Docker支持多种存储驱动,不同的驱动有不同的特点:

查看当前存储驱动

docker info|grep"Storage Driver"

常见存储驱动

overlay2(推荐):

  • 性能好,功能完整
  • 支持所有Docker功能
  • 大部分Linux发行版的默认选择

aufs

  • 老的存储驱动
  • 在一些老系统上还在用
  • 性能不如overlay2

devicemapper

  • 使用块设备
  • 适合企业级存储
  • 配置复杂

btrfs

  • 支持快照和子卷
  • 适合需要高级存储功能的场景

配置存储驱动

编辑/etc/docker/daemon.json:

{"storage-driver":"overlay2","storage-opts":["overlay2.override_kernel_check=true"]}

然后重启Docker服务。

Docker安全最佳实践

安全是生产环境必须考虑的问题,Docker提供了很多安全功能。

用户命名空间

默认情况下,容器内的root用户就是宿主机的root用户,这很危险。用户命名空间可以把容器内的root映射到宿主机的普通用户:

# 配置用户命名空间echo'dockremap:165536:65536'>>/etc/subuidecho'dockremap:165536:65536'>>/etc/subgid# 修改Docker配置echo'{"userns-remap": "default"}'>/etc/docker/daemon.json systemctl restart docker

限制容器资源

防止容器消耗过多资源:

# 限制内存和CPUdocker run -d --memory=512m --cpus=1.0nginx:latest# 限制磁盘IOdocker run -d --device-read-bps /dev/sda:1mb --device-write-bps /dev/sda:1mb nginx:latest

使用非特权容器

# 以非特权用户运行docker run -d --user1000:1000 nginx:latest# 禁用特权模式docker run -d --security-opt no-new-privileges nginx:latest

AppArmor和SELinux

如果系统支持,可以使用AppArmor或SELinux加强安全:

# 使用AppArmor配置文件docker run -d --security-opt apparmor:docker-default nginx:latest# 使用SELinux标签docker run -d --security-opt label:type:container_t nginx:latest

扫描镜像漏洞

定期扫描镜像,发现安全漏洞:

# 使用Docker官方扫描工具docker scan nginx:latest# 使用Clair扫描器docker run -d --name clair-db postgres:latest docker run -d --name clair --link clair-db:postgres quay.io/coreos/clair:latest

签名和验证镜像

使用Docker Content Trust验证镜像:

# 启用内容信任exportDOCKER_CONTENT_TRUST=1# 推送签名镜像docker push myregistry.com/myimage:latest# 拉取时自动验证docker pull myregistry.com/myimage:latest

Docker Registry私有仓库

在企业环境中,通常需要搭建私有镜像仓库。

搭建简单的Registry

# 运行Registry容器docker run -d\-p5000:5000\--restart=always\--name registry\-v registry-data:/var/lib/registry\registry:2# 推送镜像到私有仓库docker tag myapp:latest localhost:5000/myapp:latest docker push localhost:5000/myapp:latest# 从私有仓库拉取docker pull localhost:5000/myapp:latest

配置HTTPS和认证

生产环境的Registry需要HTTPS和认证:

# 生成证书mkdircerts openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days365-out certs/domain.crt# 生成认证文件mkdirauth docker run --entrypoint htpasswd registry:2 -Bbn admin password>auth/htpasswd# 运行带认证的Registrydocker run -d\-p443:5000\--restart=always\--name secure-registry\-v$(pwd)/certs:/certs\-v$(pwd)/auth:/auth\-eREGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt\-eREGISTRY_HTTP_TLS_PRIVATE_KEY=/certs/domain.key\-eREGISTRY_AUTH=htpasswd\-eREGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd\-eREGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"\registry:2# 登录私有仓库docker login myregistry.com

Harbor企业级仓库

Harbor是VMware开源的企业级Docker仓库:

# 下载Harborwgethttps://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-offline-installer-v2.8.0.tgztarxvf harbor-offline-installer-v2.8.0.tgz# 配置Harborcdharborcpharbor.yml.tmpl harbor.ymlvimharbor.yml# 修改配置# 安装Harborsudo./install.sh

Harbor提供了Web界面、用户管理、项目管理、漏洞扫描等企业级功能。

Docker多架构镜像

现在有越来越多的ARM服务器,多架构镜像变得重要了。

构建多架构镜像

# 创建builder实例docker buildx create --name mybuilder --use docker buildx inspect --bootstrap# 构建多架构镜像docker buildx build\--platform linux/amd64,linux/arm64,linux/arm/v7\-t myapp:latest\--push.

使用manifest

# 创建manifestdocker manifest create myapp:latest\myapp:amd64\myapp:arm64\myapp:armv7# 推送manifestdocker manifest push myapp:latest

在Dockerfile中处理架构差异

FROM --platform=$BUILDPLATFORM golang:1.19 AS builder ARG TARGETPLATFORM ARG BUILDPLATFORM ARG TARGETOS ARG TARGETARCH WORKDIR /app COPY . . RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o myapp FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app/myapp /usr/local/bin/ CMD ["myapp"]

Docker和Kubernetes集成

虽然Kubernetes现在支持多种容器运行时,但Docker仍然是最常用的。

准备镜像给Kubernetes使用

# 使用非root用户 FROM node:16-alpine RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN chown -R nextjs:nodejs /app USER nextjs EXPOSE 3000 CMD ["node", "server.js"]

Kubernetes部署文件

apiVersion:apps/v1kind:Deploymentmetadata:name:myappspec:replicas:3selector:matchLabels:app:myapptemplate:metadata:labels:app:myappspec:containers:-name:myappimage:myregistry.com/myapp:v1.0ports:-containerPort:3000env:-name:NODE_ENVvalue:productionresources:limits:memory:512Micpu:500mrequests:memory:256Micpu:250mlivenessProbe:httpGet:path:/healthport:3000initialDelaySeconds:30periodSeconds:10readinessProbe:httpGet:path:/readyport:3000initialDelaySeconds:5periodSeconds:5

镜像拉取策略

spec:containers:-name:myappimage:myapp:latestimagePullPolicy:Always# 总是拉取最新镜像# imagePullPolicy: IfNotPresent # 本地没有才拉取# imagePullPolicy: Never # 从不拉取,只用本地镜像

容器监控和日志

生产环境中,监控和日志收集很重要。

Prometheus监控

version:'3.8'services:app:build:.ports:-"3000:3000"labels:-"prometheus.io/scrape=true"-"prometheus.io/port=3000"-"prometheus.io/path=/metrics"prometheus:image:prom/prometheus:latestports:-"9090:9090"volumes:-./prometheus.yml:/etc/prometheus/prometheus.yml-/var/run/docker.sock:/var/run/docker.sock:rocommand:-'--config.file=/etc/prometheus/prometheus.yml'-'--storage.tsdb.path=/prometheus'-'--web.console.libraries=/etc/prometheus/console_libraries'-'--web.console.templates=/etc/prometheus/consoles'grafana:image:grafana/grafana:latestports:-"3001:3000"environment:-GF_SECURITY_ADMIN_PASSWORD=adminvolumes:-grafana-data:/var/lib/grafanavolumes:grafana-data:

ELK日志收集

version:'3.8'services:elasticsearch:image:docker.elastic.co/elasticsearch/elasticsearch:8.5.0environment:-discovery.type=single-node-xpack.security.enabled=falseports:-"9200:9200"volumes:-es-data:/usr/share/elasticsearch/datalogstash:image:docker.elastic.co/logstash/logstash:8.5.0volumes:-./logstash.conf:/usr/share/logstash/pipeline/logstash.confdepends_on:-elasticsearchkibana:image:docker.elastic.co/kibana/kibana:8.5.0ports:-"5601:5601"environment:-ELASTICSEARCH_HOSTS=http://elasticsearch:9200depends_on:-elasticsearchapp:build:.logging:driver:"gelf"options:gelf-address:"udp://localhost:12201"tag:"myapp"volumes:es-data:

使用Fluentd收集日志

version:'3.8'services:fluentd:image:fluent/fluentd:v1.14-1ports:-"24224:24224"-"24224:24224/udp"volumes:-./fluentd.conf:/fluentd/etc/fluent.confenvironment:-FLUENTD_CONF=fluent.confapp:build:.logging:driver:"fluentd"options:fluentd-address:localhost:24224tag:docker.myapp

Docker性能调优

容器资源限制

合理设置资源限制,避免容器消耗过多资源:

# 内存限制docker run -d --memory=1g --memory-swap=2g nginx:latest# CPU限制docker run -d --cpus=1.5nginx:latest docker run -d --cpu-shares=512nginx:latest# 磁盘IO限制docker run -d --device-read-bps /dev/sda:1mb nginx:latest docker run -d --device-write-bps /dev/sda:1mb nginx:latest

优化Docker daemon

编辑/etc/docker/daemon.json:

{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"},"storage-driver":"overlay2","storage-opts":["overlay2.override_kernel_check=true"],"default-ulimits":{"nofile":{"name":"nofile","hard":65536,"soft":65536}},"max-concurrent-downloads":10,"max-concurrent-uploads":5}

容器启动优化

# 使用多阶段构建减少镜像大小 FROM node:16-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force FROM node:16-alpine RUN apk --no-cache add dumb-init WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node ENTRYPOINT ["dumb-init", "--"] CMD ["node", "server.js"]

使用dumb-init作为PID 1进程,处理僵尸进程和信号转发。

故障排查和调试技巧

容器无法启动

# 查看容器日志docker logs container_name docker logs --details container_name# 查看容器事件docker events --filtercontainer=container_name# 进入容器调试(如果容器还在运行)dockerexec-it container_name /bin/bash# 如果容器已经退出,用同样的镜像启动一个临时容器docker run -it --rm --entrypoint /bin/bash image_name

网络问题排查

# 查看容器网络配置docker inspect container_name|jq'.[0].NetworkSettings'# 测试网络连通性dockerexeccontainer_namepinggoogle.com dockerexeccontainer_namenslookupgoogle.com dockerexeccontainer_name telnet another_container3306# 查看端口监听情况dockerexeccontainer_namenetstat-tulpn dockerexeccontainer_name ss -tulpn

性能问题排查

# 查看容器资源使用docker stats container_name# 查看容器进程dockerexeccontainer_namepsaux dockerexeccontainer_nametop# 查看系统调用dockerexeccontainer_namestrace-p1# 查看文件系统使用dockerexeccontainer_namedf-h dockerexeccontainer_namedu-sh /*

使用dive分析镜像

dive是一个很好用的镜像分析工具:

# 安装divewgethttps://github.com/wagoodman/dive/releases/download/v0.10.0/dive_0.10.0_linux_amd64.debsudoaptinstall./dive_0.10.0_linux_amd64.deb# 分析镜像dive myapp:latest

dive可以显示镜像的每一层,帮助优化镜像大小。

总结

Docker确实是个好东西,能解决很多实际问题。从开发环境的统一,到生产部署的简化,再到微服务架构的实现,都离不开容器技术。

这篇文章涵盖了Docker的方方面面,从基础概念到高级应用,从网络配置到安全实践。特别是网络部分,我花了很多篇幅来讲解,因为这确实是很多人觉得困难的地方。

网络是Docker中比较复杂的部分,但一旦理解了原理,用起来就得心应手了。记住几个要点:

  • 默认bridge网络适合简单场景
  • 自定义网络支持服务发现,更适合多容器应用
  • 生产环境要考虑网络安全和隔离
  • 跨主机网络需要用overlay或第三方插件

不过也要理性看待,Docker不是银弹,不能解决所有问题。有些场景下,传统的部署方式可能更简单直接。关键是要根据实际需求选择合适的技术。

我自己用Docker这几年,最大的感受就是它让部署变得可预测了。以前部署应用总是提心吊胆,生怕环境不一致出问题。现在有了Docker,本地测试通过的应用,到生产环境基本不会有环境问题。

当然,Docker的学习曲线还是有的,特别是网络和存储这块。但一旦掌握了,效率提升还是很明显的。建议大家从简单的应用开始,慢慢积累经验。

最后,技术是为业务服务的,不要为了用Docker而用Docker。如果现有的部署方式已经很成熟,没必要强行迁移。但如果遇到了环境不一致、部署复杂等问题,Docker确实是个不错的选择。

希望这篇文章对大家有帮助,如果觉得有用的话,别忘了点赞转发。有问题也欢迎留言讨论,大家一起学习进步。

关注@运维躬行录,我会持续分享更多实用的运维技术和经验,让我们一起在运维的路上走得更远!

公众号:运维躬行录

个人博客:躬行笔记

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 22:55:38

Syncthing-Android终极指南:打造专属私有云同步网络

在数据隐私日益受到重视的今天&#xff0c;您是否还在为云盘限速、隐私泄露而烦恼&#xff1f;Syncthing-Android为您提供了一种革命性的解决方案——通过去中心化架构构建完全私有的同步网络&#xff0c;让您的数据真正掌握在自己手中。 【免费下载链接】syncthing-android Wr…

作者头像 李华
网站建设 2026/3/30 20:33:38

pgsql

索引 B-tree索引 B-tree适合处理那些能够按顺序存储的数据,比如对于一些字段涉及到使用“<”、“<=”、“=”、“>=”或“>”操作符进行比较的时候,可以建立一个索引。 Hash索引 Hsh索引只能处理简单的等于比较。当一个索引了的列涉及到使用“=”操作符进行比较…

作者头像 李华
网站建设 2026/3/22 22:50:21

HuggingFace AutoClass自动加载Qwen-Image-Edit-2509模型结构

HuggingFace AutoClass自动加载Qwen-Image-Edit-2509模型结构 在图像编辑逐渐从“专业工具操作”迈向“自然语言驱动”的今天&#xff0c;一个普通用户只需说一句“把这张照片里的狗删掉&#xff0c;换成猫”&#xff0c;系统就能精准理解并完成修改——这不再是科幻场景&#…

作者头像 李华
网站建设 2026/3/28 4:58:50

基于Wan2.2-T2V-A14B的高分辨率720P视频生成技术全解析

基于Wan2.2-T2V-A14B的高分辨率720P视频生成技术全解析 在短视频日活破十亿、内容创作竞争白热化的今天&#xff0c;一条广告片从策划到上线动辄需要数天甚至数周——而用户等待的时间&#xff0c;往往只有几秒钟。如何在保证画质与创意的前提下&#xff0c;将视频生产周期压缩…

作者头像 李华
网站建设 2026/3/14 14:46:39

Windows虚拟显示器完全指南:轻松扩展你的桌面空间

Windows虚拟显示器完全指南&#xff1a;轻松扩展你的桌面空间 【免费下载链接】virtual-display-rs A Windows virtual display driver to add multiple virtual monitors to your PC! For Win10. Works with VR, obs, streaming software, etc 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/3/27 11:41:34

Beyond Compare 5完全激活指南:告别试用期限制

Beyond Compare 5完全激活指南&#xff1a;告别试用期限制 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen 还在为文件对比工具Beyond Compare 5的试用期结束而烦恼吗&#xff1f;这款专业级文件…

作者头像 李华