Docker - 2
Docker
Docker 本质:
Docker = 创建隔离进程 + 资源管理 + 网络/文件映射
容器就是进程
- 容器内的应用进程直接运行在宿主机内核上
- 容器 ID 本质上就是一组经过特殊配置的进程
- 这些进程通过 Linux 的 namespaces 和 cgroups 实现隔离
宿主机视角:
PID 1001: nginx 主进程 ← 这就是一个"容器"
PID 1002: nginx worker
...
容器视角:
PID 1: nginx 主进程
PID 2: nginx worker
...
基本概念
Docker 包括三个基本概念
- 镜像(Image)
- 容器(Container)
- 仓库(Repository)
镜像
操作系统分为 内核 和 用户空间
对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持
而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统
Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变
容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间
因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间
容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机
仓库
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签
以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest
完整 Ubuntu 操作系统 = Linux 内核 + Ubuntu 用户空间
Docker 中的 ubuntu 镜像 = Ubuntu 用户空间(只有根文件系统)
因为 Linux 内核是向下兼容的:
Ubuntu 18.04 的用户空间程序可以在任何较新的 Linux 内核上运行 容器内的程序通过系统调用与宿主机内核通信 只要内核 API 兼容,就能正常运行
仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务
基本使用
容器是操作系统级别的虚拟化,不需要运行完整的操作系统,启动和运行更为高效
镜像操作
# 拉取镜像
docker pull ubuntu:20.04
docker pull nginx:latest
# 查看本地镜像
docker images
# 删除镜像
docker rmi <image_id>
# 搜索镜像
docker search nginx
容器操作
# 启动容器
docker run ubuntu:20.04
docker run -it ubuntu:20.04 /bin/bash # 交互式终端
# 常用参数
docker run -d nginx # 后台运行
docker run -p 8080:80 nginx # 端口映射(宿主机 8080 -> 容器 80)
docker run -v /data:/app/data # 挂载卷
docker run --name my-container # 指定容器名
docker run --rm # 退出后自动删除
# 查看容器
docker ps # 运行中的容器
docker ps -a # 所有容器(包括已停止)
# 控制容器
docker start <container_id>
docker stop <container_id>
docker restart <container_id>
docker kill <container_id>
# 删除容器
docker rm <container_id>
docker rm -f <container_id> # 强制删除运行中的容器
进入容器
# 进入运行中的容器
docker exec -it <container_id> /bin/bash
# 查看容器日志
docker logs <container_id>
docker logs -f <container_id> # 实时跟踪
构建镜像
# 从 Dockerfile 构建
docker build -t my-image:1.0 .
# 从容器提交
docker commit <container_id> my-image:1.0
示例
# 运行 Nginx
docker run -d -p 80:80 --name web nginx
# 运行 MySQL
docker run -d -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql mysql:8.0
# 运行 Redis
docker run -d -p 6379:6379 --name redis redis
# 查看资源使用
docker stats
清理
# 删除所有停止的容器
docker container prune
# 删除所有悬空镜像
docker image prune
# 一键清理所有未使用的资源
docker system prune
Docker Dockerfile
Dockerfile 是一个文本文件,包含了构建 Docker 镜像的所有指令。
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
通过定义一系列命令和参数,Dockerfile 指导 Docker 构建一个自定义的镜像
基本结构
# 1. 指定基础镜像
FROM ubuntu:20.04
# 2. 设置维护者信息
LABEL maintainer="your@email.com"
# 3. 设置工作目录
WORKDIR /app
# 4. 复制文件到镜像
COPY . /app
# 5. 安装依赖
RUN apt-get update && apt-get install -y python3
# 6. 设置环境变量
ENV NODE_ENV=production
# 7. 暴露端口
EXPOSE 3000
# 8. 启动命令
CMD ["node", "app.js"]
| Docker | 面向对象 |
|---|---|
docker build | 类(Class) 定义 |
docker run | 根据类创建实例(Object) |
构建镜像
在 Dockerfile 文件的存放目录下,执行构建动作
docker build -t my-app:1.0 .
最后的 . 代表本次执行的上下文路径
上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包
运行容器
# 步骤 1:构建镜像
docker build -t my-app:1.0 .
# ↓ 镜像存储在本地
# 步骤 2:查看镜像
docker images
# REPOSITORY TAG
# my-app 1.0
# 步骤 3:运行容器
docker run -d -p 3000:3000 my-app:1.0
# ↓ 现在应用才真正跑起来
# 步骤 4:查看运行中的容器
docker ps
Docker Compose
Docker Compose 是用来管理多个容器的工具,用 YAML 文件定义一组相关的容器,一键启动/停止
通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务
Compose 使用的三个步骤:
- 使用 Dockerfile 定义应用程序的环境。
- 使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
- 最后,执行 docker-compose up 命令来启动并运行整个应用程序
基本结构
# docker-compose.yml
version: '3.8'
services:
# Web 应用
app:
build: . # 从当前目录 Dockerfile 构建
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on: # 依赖,先启动
- db
- redis
volumes:
- ./src:/app/src # 代码挂载,开发时自动重载
# 数据库
db:
image: postgres:14
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
# 缓存
redis:
image: redis:7
ports:
- "6379:6379"
volumes:
pgdata: # 命名卷,数据持久化
常用命令
# 启动所有服务(后台运行)
docker-compose up -d
# 停止所有服务
docker-compose down
# 查看运行状态
docker-compose ps
# 查看日志
docker-compose logs
docker-compose logs -f web # 跟踪某个服务
# 重启服务
docker-compose restart web
# 进入某个服务容器
docker-compose exec web bash
# 重新构建并启动
docker-compose up -d --build
# 一键清理(删除容器、网络、镜像)
docker-compose down -v
