Introduction to Docker

本文最后更新于:1 年前

Docker 学习

简介

Docker 的吉祥物是 Moby Dock,最初是 dotCloud 公司的内部项目,在 2013 年 3 月以 Apache 2.0 协议开源,开放容器联盟 OCl (Open Container Initiative)。

其使用 Go 语言实现,基于 Linux 内核的 cgroup、namespace 以 及 UnionFS 等技术实现对进程的封装隔离,最初基于 LXC (Linux Containers),后来转向自行开发的 libcontainer

环境

检查 Linux 系统版本

1
$ uname -a

列出本地 Docker 环境中所有的 Docker 镜像

1
$ docker images

列出当前正在运行的 Docker 容器,ps = process status,加上 -a 则列出所有 Docker 容器

1
$ docker ps

Docker 入门

交互式容器

1
2
3
4
# 运行容器
$ docker run -it ubuntu bash
# 进入类似 Ubuntu 的”新环境“
root@f7cbdac22a02:/#

-i 保证 STDIN 是开启的,持久的标准输入是 shell 的半边天,-t 则是另外一个半边天,让 docker 将容器“打”到一个伪 tty 终端(该例子为 bash。这里没有给定名字,docker 会创建一个随机名,例如 gray_cat,加上名字的方法为 --name [NAME]

创建之后如同在新的 bash 环境下交互,输入 exit 或者 Ctrl+D 退出。输入 docker start 加上名字或者容器 ID 启动已经停止的容器,如要重新启动为 docker restart,但这都只是“启动”容器,要重新回到交互界面需要输入 docker attach

守护式容器

交互式容器适合运行应用程序和服务,例如

1
2
# -d 代表放置在后台运行
$ docker run --name hello_world -d ubuntu bash -c "while true: do echo hello world; sleep 1; done"

该容器则没有 shell 会话可供交互。以下命令查看日志,其中 -ftail -f 中的 -f 相似,用来监控日志的变化,而 -t 表示显示时间戳,可以看到如下输出

1
2
3
4
$ docker logs -ft hello_world
2023-07-29T15:03:44.879386240Z hello world
2023-07-29T15:03:45.880515741Z hello world
2023-07-29T15:03:46.882173546Z hello world

时间间隔不是严格 1s 因素:系统调度、写入日志、获取时间耗时等。

容器信息

查看容器进程、显示一个或者多个容器状态信息(整理为 markdown 格式)

1
$ docker top hello_world
UID PID PPID C STIME TTY TIME CMD
root 2282 1922281 7520 23:09 ? 00:00:00 bash -c "while true; do echo hello world; sleep 1; done"
root 2282 3542282 1920 23:09 ? 00:00:00 sleep 1
1
2
# 显示所有正在运行容器,-q 代表显示容器 ID
$ docker stats 'docker ps -a -q'
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
5639808ea117 hello_world 0.16% 3.277MiB / 1007GiB 0.00% 1.16kB / 0B 0B / 0B 2
4127b9416379 affectionate_cartwright 2.39% 16.29GiB / 1007GiB 1.62% 0B / 0B 24.6kB / 9.21MB 191
79bbf82e8628 pedantic_williams 0.42% 360.6MiB / 1007GiB 0.03% 0B / 0B 0B / 106kB 83
cfb008147738 musing_newton 0.00% 2.407GiB / 1007GiB 0.24% 0B / 0B 27.6MB / 38.7MB 1

输入如下命令则可以停止容器,但容器仍在 docker ps -a 里,要想彻底删除,输入 docker rm [NAME_OR_ID]

1
2
# 使用名称或者容器 ID
$ docker stop hello_world

自动重启容器加入 --restart=always 参数,深入查看容器信息命令 docker inspect hello_world,加入 --format 得到想要的指令

1
2
# 获取容器的 IP 地址
$ docker inspect --format '{{ .NetworkSettings.IPAddress}}' hello_world

删除所有名为 <none> 的镜像

1
docker rmi $(docker images -f "dangling=true" -q)

镜像构建

列出存在宿主机某个 /docker 目录下的镜像 docker images;拉取镜像 docker pull 用户名:仓库名,Docker Hub 中有两类仓库,用户仓库和顶层仓库,后者是 Docker 公司选定的优质基础镜像;查找镜像 docker search

可以在镜像与 bash 交互的时候对镜像更改,退出之后 docker commit 容器ID,但这样做会导致镜像到最后臃肿,不推荐这样做,采用 DockerFile 编写构建脚本,例如在 docker 中换源且安装

注意 COPY 指令只能在当前 DockerFile 目录以及子目录下进行,因为其描述的是一个可重复构建的、独立于任何特定主机的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 使用 NVIDIA PyTorch镜像作为基础镜像,这是一个基于PyTorch(一个流行的深度学习库)的镜像
FROM nvcr.io/nvidia/pytorch:23.06-py3

# 更改 apt 的源为清华大学的镜像源,可以加快下载速度
RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list

# 更新 apt 的软件源列表
RUN apt-get update

# 设置 SHELL 为 bash
SHELL [ "/bin/bash", "-c" ]

# 安装一些实用工具和开发工具
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \
gcc \ # GCC编译器
g++ \ # G++编译器
zlib1g-dev \ # zlib库和头文件,用于文件压缩
# ...(省略部分注释)
software-properties-common # 一个用于管理软件源的工具

# 更改 pip 的源为中国科技大学的镜像源,可以加快下载速度
RUN pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple
RUN pip config set global.extra-index-url https://mirrors.ustc.edu.cn/pypi/web/simple
RUN pip config set global.trusted-host mirrors.ustc.edu.cn

# 更新 pip 至最新版本
RUN pip install --upgrade pip

# 将当前目录(即包含这个Dockerfile的目录)下的所有文件和文件夹复制到容器的 /workspace 目录下
COPY . /workspace

# 设置容器中的当前工作目录为 /workspace
WORKDIR /workspace

# 安装 /workspace 目录下的 Python 包,"-e" 选项表示以"可编辑"模式安装,即在开发环境下安装
RUN pip install -e .

Docker compose

Docker Compose 可以在单个主机上定义和运行多个 docker 的工具,使用 YAML 文件来配置应用程序的服务

基本结构

创建 docker compose.yml 如下所示

1
2
3
4
5
6
7
8
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"

这个文件定义了两个服务:web 和 redis。web 服务将当前目录构建为 Docker 镜像,并绑定宿主机和容器的 5000 端口。redis 服务使用 Docker Hub 上的公共 Redis 镜像。

常见命令

  • docker compose up:根据 compose.yml 文件启动服务。

  • docker compose down:停止并删除由 up 命令启动的所有容器。

  • docker compose build:构建服务镜像。

  • docker compose ps:列出由 up 命令启动的所有容器。

  • docker compose logs:显示服务的日志。

Docker Compose 的使用实例

下面是一个使用 Flask 和 Redis 的简单应用的示例。

首先,创建一个 app.py 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from redis import Redis

app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
count = redis.incr('hits')
return f"""Hello World! This page is checked by {count} times"""

if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)

创建一个 requirements.txt 文件,内容如下:

1
2
flask
redis

创建一个 Dockerfile 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用官方 Python 运行时作为父镜像
FROM python:3.7-slim

# 将工作目录设置为 /app
WORKDIR /app

# 将当前目录内容复制到容器的 /app 中
ADD . /app

# 安装 requirements.txt 中指定的所有必需软件
RUN pip install --no-cache-dir -r requirements.txt

# 让 80 端口可以让外界访问
EXPOSE 5000

# 定义环境变量
ENV NAME World

# 在容器启动时运行 app.py
CMD ["python", "app.py"]

docker-compose.yml 文件的 web 服务中,使用了 build: . 来指定使用当前目录下的 Dockerfile 构建镜像,依次执行以下指令

1
2
3
4
5
$ docker compose build
$ docker compose up
✔ Network docker_instruction_default Created 0.1s
✔ Container docker_instruction-redis-1 Created 0.0s
✔ Container docker_instruction-web-1 Created 0.0s

在浏览器中看到应用程序,每次刷新页面,页面上的数字都会增加。

注意如果想修改代码,必须先 build 后再起(up)。

实战要点

添加环境变量,用于设置 OpenAI API 相关参数

1
$ docker run -e MY_VARIABLE=my_value -e ANOTHER_VARIABLE=another_value

Introduction to Docker
https://lr-tsinghua11.github.io/2023/08/01/%E7%BC%96%E7%A8%8B/Docker%20%E5%AD%A6%E4%B9%A0/
作者
Learning_rate
发布于
2023年8月1日
许可协议