服务器与Docker技术指南 -大杂烩篇

188 阅读15分钟

服务器与Docker技术指南

服务器、Docker容器和物理地址之间的关系可以这样理解: 物理地址:指的是服务器的实际硬件位置,包含真实的CPU、内存、存储设备等物理资源。每台物理服务器都有唯一的MAC地址用于网络识别。 服务器:可以是物理机器(物理服务器),也可以是虚拟机。它提供计算资源和运行环境,有自己的操作系统、IP地址等。 Docker容器:是在服务器上运行的轻量级虚拟化环境,共享宿主服务器的内核,但拥有独立的文件系统、进程空间和网络配置。

关系: Docker容器运行在服务器之上,利用服务器的物理资源 多个容器可以在同一台服务器上并行运行,共享物理资源但彼此隔离 容器可以有自己的虚拟IP地址,通过端口映射与外部网络通信 容器内的应用不直接访问物理地址,而是通过Docker的网络层进行通信 简言之,物理地址提供实际硬件资源,服务器提供运行环境,Docker容器则在服务器上提供隔离的应用运行空间。

Docker 基础概念

Docker 架构

  • Docker引擎:Docker的核心组件,包括Docker守护进程(Docker daemon)、REST API和CLI客户端
  • Docker镜像:容器的只读模板,包含运行应用所需的文件系统、代码、运行时和依赖
  • Docker容器:镜像的运行实例,拥有独立的文件系统、网络和进程空间
  • Docker仓库:存储和分发Docker镜像的地方,如Docker Hub、私有仓库等

Docker 基本命令

# 拉取镜像
docker pull [镜像名]:[标签]

# 构建镜像
docker build -t [镜像名]:[标签] [Dockerfile目录]

# 运行容器
docker run [选项] [镜像名]

# 查看镜像列表
docker images

# 删除镜像
docker rmi [镜像ID]

# 删除容器
docker rm [容器ID]

容器生命周期管理

容器状态转换

  1. 创建docker create - 创建但不启动容器
  2. 启动docker start - 启动已创建的容器
  3. 运行docker run - 创建并启动容器
  4. 暂停docker pause - 暂停容器中的所有进程
  5. 恢复docker unpause - 恢复容器中被暂停的进程
  6. 停止docker stop - 发送SIGTERM信号,优雅停止容器
  7. 强制停止docker kill - 发送SIGKILL信号,立即停止容器
  8. 重启docker restart - 重启容器
  9. 删除docker rm - 删除已停止的容器

SSH 和 jupter SSH连接的是物理机(宿主机)- 您通过SSH协议直接连接到运行Docker的物理服务器上,获得物理机的shell访问权限。 Jupyter打开的是Docker容器的端口 - Jupyter服务运行在Docker容器内部,容器将Jupyter的端口(通常是8888)映射到物理机的某个端口上,使您可以通过浏览器访问。 Docker的文件和物理机之间有一个映射 - 这通过Docker的卷(volume)或绑定挂载(bind mount)实现,允许容器内的进程访问物理机上的文件系统。当您在Jupyter中编辑文件时,实际上是在操作这个映射的文件系统。 这种架构使您可以在隔离的容器环境中运行Jupyter,同时仍能通过SSH访问底层的物理机进行管理。

Docker 数据管理详解

卷(Volumes)与绑定挂载(Bind Mounts)的区别

  • 卷(Volumes):由Docker管理,存储在Docker区域内,更安全,跨平台支持更好
  • 绑定挂载(Bind Mounts):直接映射主机文件系统的路径到容器内,依赖主机文件系统

数据持久化最佳实践

# 创建命名卷
docker volume create my_volume

# 使用卷运行容器
docker run -v my_volume:/container/path image_name

# 使用绑定挂载
docker run -v /host/path:/container/path image_name

# 查看卷的详细信息
docker volume inspect my_volume

# 移除未使用的卷
docker volume prune

Docker 网络详解

网络类型

  • bridge:默认网络,容器通过虚拟网桥连接
  • host:容器使用主机网络栈,无网络隔离
  • none:容器没有网络连接
  • overlay:用于Docker Swarm服务间通信的多主机网络
  • macvlan:允许容器拥有自己的MAC地址
  • ipvlan:类似macvlan,但共享MAC地址

自定义网络

# 创建自定义网络
docker network create --driver bridge my_network

# 使用自定义网络运行容器
docker run --network=my_network image_name

# 将容器连接到网络
docker network connect my_network container_name

# 断开容器与网络的连接
docker network disconnect my_network container_name

Q 获得物理机的shell访问权限是什么 A 获得物理机的shell访问权限是指通过SSH协议连接到运行Docker的物理服务器上,直接获得物理机的shell环境。这通常用于管理物理机上的文件、目录、进程等。

Q shell环境是什么 A shell环境是指用户与操作系统交互的界面,它提供命令行界面,允许用户输入命令并执行。在Linux和Unix系统中,常见的shell有bash、sh、csh等。通过shell环境,用户可以执行各种系统命令、脚本程序,以及运行应用程序。

Docker 安全性

安全最佳实践

  • 使用非root用户运行容器:docker run --user 1000:1000 image_name
  • 限制容器资源:使用--memory--cpu选项
  • 使用只读文件系统:docker run --read-only image_name
  • 定期更新镜像以修复漏洞
  • 扫描镜像中的安全漏洞:docker scan image_name
  • 使用Docker Content Trust验证镜像:export DOCKER_CONTENT_TRUST=1

特权容器和安全隐患

特权容器(--privileged)可以访问主机的所有设备,具有与主机相同的权限,应谨慎使用,仅在必要时启用。

Docker容器、SSH和物理机交互的详细操作指南 下面是详细的执行细节和查看方式,帮助您理解和操作Docker容器、SSH连接和物理机之间的关系:

# 连接到物理机
ssh username@server_ip

# 连接成功后,您会获得物理机的shell
username@server_ip:~$ 

# 查看Docker容器列表
docker ps

# 示例输出
CONTAINER ID   IMAGE           COMMAND                  CREATED        STATUS        PORTS                    NAMES
abc123def456   jupyter/scipy   "jupyter notebook --…"   2 hours ago    Up 2 hours    0.0.0.0:8888->8888/tcp   jupyter_container

# 查看Docker容器详细信息
docker inspect container_id_or_name

# 查看Docker容器的网络配置
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_id_or_name
# 查看Docker容器的端口映射
docker port container_id_or_name

# 连接到Docker容器
docker exec -it container_id_or_name /bin/bash

# 示例输出
root@abc123def456:/# 

# 在Docker容器中运行Jupyter Notebook
jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root

# 在物理机上访问Jupyter Notebook
http://server_ip:8888

# 在Docker容器中查看Jupyter Notebook的日志
jupyter notebook list

# 示例输出
Currently running servers:
http://0.0.0.0:8888/?token=abcdef1234567890abcdef1234567890 :: /home/jovyan/work

# 在物理机上访问Jupyter Notebook的日志
http://server_ip:8888/?token=abcdef1234567890abcdef1234567890

# 在Docker容器中停止Jupyter Notebook
jupyter notebook stop

# 在Docker容器中重启Jupyter Notebook
jupyter notebook restart

# 在Docker容器中查看Jupyter Notebook的配置文件
jupyter notebook --generate-config

# 在Docker容器中查看Jupyter Notebook的配置文件
jupyter notebook --config

# 在Docker容器中查看Jupyter Notebook的配置文件
jupyter notebook --config-dir

Docker Compose与多容器应用

Docker Compose基础

Docker Compose是用于定义和运行多容器Docker应用的工具,使用YAML文件配置应用的服务。

# docker-compose.yml示例
version: '3'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

常用Docker Compose命令

# 启动所有服务
docker-compose up

# 在后台启动所有服务
docker-compose up -d

# 停止所有服务
docker-compose down

# 查看服务状态
docker-compose ps

# 查看服务日志
docker-compose logs
  1. 查看端口映射
docker port container_id_or_name
# 查看特定容器的详细信息,包括端口映射
docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}}{{$p}} -> {{(index $conf 0).HostPort}}{{end}}' jupyter_container

# 示例输出
8888/tcp -> 8888
  1. 查看文件映射(卷和绑定挂载)
# 查看容器的卷映射
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' jupyter_container

# 示例输出
/home/username/jupyter_data -> /home/jovyan/work
  1. 进入Docker容器内部
# 进入运行中的容器
docker exec -it jupyter_container bash

# 现在您在容器内部
jovyan@abc123def456:~$ 
  1. 在容器内外验证文件映射
# 在物理机上创建测试文件
echo "Hello from host" > /home/username/jupyter_data/test_file.txt

# 进入容器查看文件
docker exec -it jupyter_container bash -c "cat /home/jovyan/work/test_file.txt"
# 输出: Hello from host

# 从容器内部修改文件
docker exec -it jupyter_container bash -c "echo 'Hello from container' >> /home/jovyan/work/test_file.txt"

# 在物理机上查看变化
cat /home/username/jupyter_data/test_file.txt
# 输出:
# Hello from host
# Hello from container
  1. 访问Jupyter服务 在浏览器中访问:http://server_ip:8888 如果您是通过SSH隧道连接的:
# 在本地机器上创建SSH隧道
ssh -L 8888:localhost:8888 username@server_ip

# 然后在本地浏览器访问
http://localhost:8888
  1. 查看网络配置
# 查看物理机网络接口
ip addr show

# 查看Docker网络
docker network ls

# 查看特定Docker网络详情
docker network inspect bridge

# 查看容器内部网络配置
docker exec jupyter_container ip addr show
  1. 查看Docker容器的资源使用情况
# 查看容器资源使用情况
docker stats jupyter_container

# 查看容器日志
docker logs jupyter_container
  1. 检查Docker卷
# 列出所有Docker卷
docker volume ls

# 查看特定卷的详细信息
docker volume inspect volume_name

退出交互式容器会话

# 方法1:输入exit命令
exit

# 方法2:使用键盘组合键
# 按Ctrl+D

Docker资源管理与限制

限制容器资源

# 限制CPU使用
docker run --cpus=0.5 image_name  # 限制使用0.5个CPU核心

# 限制内存使用
docker run --memory=1g image_name  # 限制使用1GB内存

# 限制交换空间
docker run --memory-swap=2g image_name  # 限制内存+交换空间总共2GB

# 设置重启策略
docker run --restart=always image_name  # 总是重启容器

优雅停止容器(发送SIGTERM信号,等待容器清理后退出)

docker stop 容器ID或容器名称

示例

docker stop jupyter_container

强制立即停止容器(发送SIGKILL信号)

docker kill 容器ID或容器名称

这是docker stats命令的输出结果,显示了名为"commonService"的容器的实时资源使用情况。这个命令会持续更新容器的性能数据,所以您看到了三次几乎相同的输出。我来解释每一列的含义: CONTAINER ID: 6860a06aae63 - 容器的唯一标识符 NAME: commonService - 容器的名称 CPU %: 3.95%/3.52% - 容器使用的CPU百分比,可以看到使用率略有波动 MEM USAGE / LIMIT: 18.62GiB / 251.3GiB - 内存使用情况和限制 容器当前使用了18.62GB内存 容器被允许使用的最大内存是251.3GB MEM %: 7.41% - 内存使用占限制的百分比 NET I/O: 149GB / 38.4GB - 网络输入/输出总量 容器已接收149GB的数据 容器已发送38.4GB的数据 BLOCK I/O: 18.2GB / 236GB - 块设备(磁盘)读/写总量 容器已读取18.2GB的数据 容器已写入236GB的数据 PIDS: 373 - 容器内运行的进程数量 这些数据表明这是一个相当大型的服务容器,使用了大量内存(18.62GB),有大量的网络传输(接收149GB,发送38.4GB)和磁盘写入(236GB),并运行了多个进程(373个)。CPU使用率相对适中,在3.5%-4%左右。

Dockerfile与镜像构建最佳实践

Dockerfile基本指令

FROM node:14  # 基础镜像
WORKDIR /app  # 工作目录
COPY package*.json ./  # 复制文件
RUN npm install  # 运行命令
COPY . .  # 复制所有文件
EXPOSE 3000  # 暴露端口
CMD ["npm", "start"]  # 容器启动命令

镜像构建最佳实践

  • 使用多阶段构建减小镜像大小
  • 合并RUN指令减少镜像层数
  • 使用.dockerignore排除不必要的文件
  • 安装后清理缓存(如apt-get clean)
  • 使用特定标签而非latest
  • 使用非root用户运行应用

查看Docker容器的GPU显存占用 要查看Docker容器的GPU显存占用,您需要使用NVIDIA的工具,因为Docker stats命令本身不显示GPU相关信息。以下是几种查看方法:

  1. 使用nvidia-smi命令 如果您的容器使用了NVIDIA GPU,最直接的方法是:
# 在物理机上查看所有GPU状态和显存占用
nvidia-smi

# 查看实时更新的GPU状态(每1秒刷新)
nvidia-smi --loop=1

示例输出:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 515.65.01    Driver Version: 515.65.01    CUDA Version: 11.7     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  On   | 00000000:00:1E.0 Off |                    0 |
| N/A   37C    P0    42W / 300W |  15360MiB / 16384MiB |     18%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

Q 这个命令里GPU怎么看 A 这个命令里GPU的编号是0,表示第一块GPU。

  1. 在容器内部查看GPU显存 如果容器有权限访问GPU,可以在容器内部执行:
# 在容器内部查看GPU状态
nvidia-smi

# 在容器内部查看GPU显存
nvidia-smi --query-gpu=memory.used --format=csv

示例输出:

memory.used
15360MiB
  1. 查看特定容器的GPU使用情况 使用NVIDIA的Docker集成工具:
# 安装nvidia-container-toolkit(如果尚未安装)
sudo apt-get install -y nvidia-container-toolkit

# 使用nvidia-docker命令查看特定容器的GPU使用
nvidia-docker stats 容器名称或ID
  1. 使用nvidia-smi查询特定进程 找出容器中的进程ID,然后查询该进程的GPU使用情况:
# 查找容器中的进程ID
docker top 容器名称或ID
# 查询进程的GPU使用情况
nvidia-smi --query-compute-apps=pid,used_memory --format=csv,noheader

示例输出:

PID     Used GPU Memory
12345   15360MiB
  1. 使用Docker命令行工具查询GPU使用情况 Docker命令行工具本身不直接提供查询GPU使用情况的功能,但你可以结合使用Docker和NVIDIA的Docker插件来查询GPU使用情况。
# 查询容器的GPU使用情况
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}\t{{.GPUMemory}}"

示例输出:

CONTAINER NAME    CPU %    MEM USAGE / LIMIT    NET I/O    BLOCK I/O    PIDS    GPU MEM USAGE
my_container      0.00%    0B / 0B               0B / 0B    0B / 0B      0       15360MiB

注意:以上命令可能需要根据你的具体环境和需求进行调整。

查询特定进程的GPU显存使用 步骤1: 找出容器中的进程ID 在容器内部,使用ps命令或top命令查找进程ID。例如,假设进程ID为12345。

# 在容器内部查找进程ID
ps aux | grep my_process

步骤2: 使用nvidia-smi查询进程的GPU显存使用 使用nvidia-smi命令查询进程的GPU显存使用情况。例如,查询进程ID为12345的进程的GPU显存使用情况。

# 查询进程的GPU显存使用
nvidia-smi --query-compute-apps=pid,used_memory --format=csv,noheader

示例输出:

PID     Used GPU Memory
12345   15360MiB

注意:以上命令可能需要根据你的具体环境和需求进行调整。

# 查看容器中的进程
docker top 容器名称或ID

示例输出:

UID     PID      PPID     C    STIME    TTY      TIME        CMD
root    48213    48200    0    10:23    ?        00:02:14    python train.py
root    48567    48200    0    10:25    ?        00:01:45    python inference.py

步骤2: 使用grep过滤特定进程ID的GPU使用情况 使用grep命令过滤特定进程ID的GPU使用情况。例如,假设进程ID为12345。

# 查询特定进程的GPU显存使用
nvidia-smi --query-compute-apps=pid,used_memory --format=csv,noheader | grep 12345

示例输出:

PID     Used GPU Memory
12345   15360MiB

注意:以上命令可能需要根据你的具体环境和需求进行调整。

步骤3: 更复杂的查询和过滤 查询特定进程名称: 示例输出:

PID     Used GPU Memory
12345   15360MiB
# 获取容器内所有进程ID
container_pids=$(docker top 容器名称或ID | awk 'NR>1 {print $2}')

# 对每个进程ID查询GPU使用
for pid in $container_pids; do
  nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv | grep $pid
done

步骤4: 查询容器内所有进程的GPU使用

# 获取容器内所有进程ID
container_pids=$(docker top 容器名称或ID | awk 'NR>1 {print $2}')

# 对每个进程ID查询GPU使用
for pid in $container_pids; do
  nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv | grep $pid
done

容器中进程关系与PID详解 在Docker容器中,每个进程都有一个唯一的进程ID(PID)。这些进程在容器内部运行,并且它们之间的父子关系与主机系统中的进程关系类似。理解这些关系有助于更好地管理和调试容器内的进程。

以下是一些关于容器中进程关系和PID的要点:

  1. 容器内的进程ID
  2. 容器内的进程ID与主机系统中的进程ID是隔离的,这意味着容器内的进程ID在容器内部是唯一的,但在主机系统中可能不是唯一的。
  3. 容器内的进程ID与主机系统中的进程ID之间的映射关系由Docker维护,并且可以通过Docker命令行工具查询。
  4. 容器内的进程ID与主机系统中的进程ID之间的映射关系可以通过Docker的网络命名空间来实现。

PID基本概念 PID (Process ID) 是操作系统分配给每个运行进程的唯一标识符。在容器中,有两种PID视角: 容器内部PID:容器内进程看到的PID,通常从1开始 宿主机PID:宿主机操作系统看到的实际PID Docker容器使用Linux的PID命名空间(namespace)实现进程隔离,使容器内的进程拥有自己的PID体系。 进程关系 容器内的进程关系遵循标准的Linux进程层次结构: 父进程(PPID): 创建当前进程的进程 子进程: 由当前进程创建的进程 PID 1: 容器内的初始进程,负责处理孤儿进程 常用进程管理命令 在容器内部使用的命令

# 查看容器内所有进程
ps aux

# 查看进程树
ps -ef --forest

# 查看特定进程信息
ps -p <PID> -o pid,ppid,cmd

# 查看进程详细信息
cat /proc/<PID>/status

# 查看进程打开的文件
ls -l /proc/<PID>/fd/

# 查看进程的环境变量
cat /proc/<PID>/environ | tr '\0' '\n'

# 查看进程的内存映射
cat /proc/<PID>/maps

# 杀死进程
kill <PID>
kill -9 <PID>  # 强制杀死

# 查看进程使用的资源
top

从宿主机管理容器进程的命令

# 查看容器内的进程列表
docker top <容器名或ID>

# 查看容器内进程的宿主机PID
docker inspect --format '{{.State.Pid}}' <容器名或ID>

# 查看容器的资源使用情况
docker stats <容器名或ID>

# 进入容器执行命令
docker exec -it <容器名或ID> bash

# 向容器内进程发送信号
docker kill --signal=<信号> <容器名或ID>

# 查看宿主机上所有进程(包括容器进程)
ps aux | grep docker

# 查看特定容器的所有进程(宿主机视角)
for pid in $(docker top <容器名或ID> -aq | awk '{print $2}'); do
  ps -p $pid -o pid,ppid,cmd
done