Docker 学习笔记

type
Post
status
Published
summary
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而Docker容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。
slug
docker
date
Jan 5, 2024
tags
docker
category
实践技巧
password
icon
URL
Property
Feb 28, 2024 02:16 PM

为什么用Docker

一种新兴的虚拟化方式。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而Docker容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。
Docker 对系统资源的利用率更高,不需要进行硬件虚拟以及运行完整操作系统等额外开销
Docker 容器应用直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间
Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性
Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署
Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的,可以更轻松的迁移

一些概念:

  1. 镜像(Image):可以将镜像视为软件打包的模板。
    1. 功能:镜像是一个只读的文件,其中包含了运行应用程序所需的一切,包括代码、运行时环境、依赖项和配置。
  1. 容器(Container):容器是基于镜像创建的一个运行实例。
    1. 工能:可以将容器视为镜像的运行时进程,它是一个隔离的环境,其中可以运行应用程序和服务。
    2. 特性:每个容器都具有自己的文件系统、网络和进程空间,可以独立运行应用程序。
    3. 📌
      镜像和容器关系的例子: 1. 莎士比亚心中的哈姆雷特就像是一个镜像,观众在看了这个戏剧之后,在自己心中就有了一个哈姆雷特,观众心中的哈姆雷特就是容器。 2. Python 中类和实例的关系,类是一个模板,实例是类的具体实现。
  1. 仓库(Repository):仓库是用于存储和组织镜像的地方。Docker Registry服务器专门用来存放仓库一个 Docker Registry 中可以包含多个 仓库;每个仓库可以包含多个 标签;每个标签对应一个镜像。
    1. 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

镜像操作

# 获取镜像 $ docker image pull [选项] [Docker Registry 地址[:端口号]/] 仓库名[:标签] # 旧版本的用法,也可以使用。 $ docker pull [选项] [Docker Registry 地址[:端口号]/] 仓库名[:标签] # [选项]:这是可选的参数,您可以在命令中添加一些选项来自定义拉取行为。例如,您可以指定镜像的版本、认证信息等。 # [Docker Registry 地址[:端口号]/]:这是可选的,用于指定仓库的地址和端口号。如果未提供,则默认使用 Docker 的公共仓库(Docker Hub)。 # 仓库名:这是要拉取的镜像所在的仓库的名称。仓库名可以是官方仓库中的镜像,也可以是您自己或其他人创建的仓库中的镜像。 # [:标签]:这是可选的,用于指定要拉取的镜像的标签或版本。如果未提供标签,则默认拉取最新版本的镜像。 $ docker pull hello-world # 获取 Docker Hub 的 hello-world 镜像的最新版本 # 查看镜像列表(顶层镜像) # 结果包含了仓库名、标签、镜像ID、创建时间以及所占用的空间(这里下载后解压的大小,DockerHub中显示的是压缩体积) $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest ee301c921b8a 7 months ago 9.14kB $ docker image ls -a # 查看中间镜像(相同的层只会存一遍,而这些镜像是别的镜像的依赖,没必要删除) $ docker image ls ubuntu # 根据仓库名列出不同标签镜像 $ docker image ls ubuntu:18.04 # 指定仓库名和标签查看镜像 $ docker image ls --digests # 增加列出镜像摘要 $ docker image ls -f since=mongo:3.2 # 根据条件筛选镜像,列出 mongo:3.2 以后版本的镜像 $ docker image ls -f before=mongo:3.2 # 根据条件筛选镜像,列出 mongo:3.2 之前版本的镜像 $ docker image ls -f dangling=true # 查看虚悬镜像(由于新建和新拉取的镜像和已有的镜像同名,旧镜像名称和标签就会变为<none>成为虚悬镜像) REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 00285df0df87 5 days ago 342 MB $ docker image prune # 删除虚悬镜像 # Docker系统的磁盘使用情况,查看镜像、容器、数据卷所占用的空间,返回类型、数量、大小、百分比等 $ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 24 0 1.992GB 1.992GB (100%) Containers 1 0 62.82MB 62.82MB (100%) Local Volumes 9 0 652.2MB 652.2MB (100%) Build Cache 0B 0B # 删除镜像 $ docker rmi [选项] <镜像1> [<镜像2> ...] $ docker image rm [选项] <镜像1> [<镜像2> ...] # 旧版 # <镜像> 可以是 镜像短ID、镜像长ID、镜像名或者镜像摘要(ls默认列出的就是短ID) # 有些删除行为可能有Untagged:提示,说明是在取消镜像的标签; # 因为除了当前想要删除的标签,可能还有别的标签指向了这个镜像;也可能可能某个其它镜像正依赖于当前镜像的某一层;还有可能有用这个镜像启动的容器存在(即使容器没有运行) # 所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已; $ docker image rm <镜像>@<镜像摘要> # 根据镜像摘要删除 $ docker image rm (docker image ls -q redis) # 根据条件查询删除 $ docker image rm (docker image ls -q -f before=mongo:3.2) # 根据条件查询删除 # 查看镜像历史 $ docker history nginx:v2 # 制作镜像 # 1.将已有容器的修改 commit 保存为镜像 $ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]] $ docker commit -m "Added python setuptool pip numpy" -a "wangxiaolei" 0c28c802c5e6 mycentos:mydev # -m 是修改信息;-a 是作者信息;-a 和 -m 都可以省略;这里的仓库名其实就是镜像名 # 2.Dockerfile方式从头开始定制镜像(以 Python 项目为例) # 2.1 在 Python 项目文件夹中生成项目依赖文件 requirements.txt # 2.2 在 Python 项目文件夹中新建文本文件 Dockerfile(无后缀),并添加相关内容 # 2.3 使用【docker build -t [镜像名:标签] .】命令构建镜像 # -t : --tag 设置镜像名和标签 # . 表示指定 Dockerfile 的路径,在当前目录查找Dockerfile文件
📌
Dockerfile文件配置参考
# 基于镜像基础 #FROM python:3.6 FROM python:3.6-slim-stretch #有更小的体积 # 维护者信息 MAINTAINER name name@163.com # 复制当前代码文件到容器中 /app ADD . /app # 设置app文件夹是工作目录 /app WORKDIR /app #解决Dockers中打印日志不及时的问题 ENV PYTHONUNBUFFERED=0 # 安装所需的包,默认为使用python官方镜像源,安装 比较慢 #RUN pip install -r requirements.txt #可以修改为清华源 RUN pip install -r requirements.txt - i https://pypi.tuna.tsinghua.edu.cn/simple # Run test.py when the container launches CMD ["python", "/app/test/test.py"]

容器操作

# 使用镜像创建并启动容器 $ docker container run [选项] <镜像名> [命令] # # 旧版本的用法,也可以使用 $ docker run [选项] <镜像名> [命令] $ docker run -it --rm ubuntu:18.04 bash # -it:这是两个选项的组合。-i 表示以交互模式运行容器,即允许用户与容器进行交互。-t 表示为容器分配一个伪终端,以便于在容器中运行命令和查看输出。 # --rm:这是一个选项,表示当容器退出后自动删除容器。这意味着在容器停止运行后,相关的容器文件将被自动清理。 # ubuntu:18.04:这是要使用的镜像名称。ubuntu:18.04 是一个具体的 Ubuntu 18.04 版本的镜像。如果本地没有此镜像,Docker 将尝试从默认仓库(如 Docker Hub)中拉取该镜像。 # bash:这是在容器内运行的命令。指定在容器启动后运行 Bash shell。这将使用户能够与容器交互,并在容器中执行 Bash 命令。 # 只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。 $ docker run -dp 127.0.0.1:3000:3000 getting-started # -dp 127.0.0.1:3000:3000这是用于设置容器的运行选项的部分。 # -d 表示以后台(detached)模式运行容器, # -p 127.0.0.1:3000:3000 表示将主机的 127.0.0.1 的 3000 端口映射到容器的 3000 端口。 # getting-started:这是指定要运行的 Docker 镜像的名称。
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
  • 检查本地是否存在指定的镜像,不存在就从 registry 下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止
📌
运行镜像生成容器后,通常有两种方式来操作容器内的内容:
  • 容器开放一个访问端口,从浏览器访问
  • 在容器内打开一个 bash shll,进行命令行操作
$ docker ps # 列出当前正在运行的 Docker 容器 $ docker container ls # 列出当前正在运行的 Docker 容器 $ docker container ls -a # 查看所有状态的容器 $ docker exec [选项] <容器ID> # 进入一个运行中的容器 $ docker exec -it 69d1 bash # -i保持标准输入流(stdin)打开,-t 分配一个伟终端,bash 在容器内启动一个交互式的 Bash Shell $ docker start <容器ID> # 启动一个终止状态的容器 $ docker stop <容器ID> # 终止一个运行中的容器 $ docker restart <容器ID> # 重启一个运行中的容器 $ docker rm <容器ID> [<容器ID>2] # 重启一个运行中的容器,可批量操作 $ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" # -d 表示静默运行,-d 参数启动后会返回一个唯一的 id(CONTAINER ID)用【$ docker container ls】命令可以看到 $ docker container logs [container ID or NAMES] # 获取容器的输出信息(用上面获取的 DI 或名称)

容器的导入导出

容器的导入(import)和导出(export)是用于在 Docker 中进行容器和镜像的迁移、备份和共享的操作。
导出容器(export)操作将容器的文件系统打包为一个单独的文件(通常是一个 tar 归档文件),其中包含容器的文件系统快照、元数据和配置信息。导出的文件可以通过文件共享、传输到其他环境或存档备份等方式进行传递和存储。导出的文件可以用于在不同的 Docker 主机上导入为一个新的镜像或容器。
导入容器(import)操作将导出的容器文件导入到 Docker 中,创建一个新的镜像。这是将容器从一个环境迁移到另一个环境的常见方法。导入的容器文件可以包含完整的文件系统、元数据和配置信息,从而可以还原出与导出时相同的容器。
# 导出容器 $ docker export 7691a814370e > ubuntu.tar # 会创建一个容器快照的归档文件(通常是一个 tar 文件)。该归档文件包含容器的文件系统内容和元数据,但不包含容器的运行状态或正在运行的进程。换句话说,导出的内容是容器当前状态的静态表示。 # 导入快照得到镜像 $ cat ubuntu.tar | docker import - test/ubuntu:v1.0 # cat ubuntu.tar:这个部分使用 cat 命令来读取名为 "ubuntu.tar" 的文件内容,并将其输出到标准输出(stdout)。 # |:这是一个管道操作符,它将 cat ubuntu.tar 的输出连接到下一个命令的输入。 # docker import - test/ubuntu:v1.0:通过管道传递的数据导入为一个 Docker 镜像。其中,"-" 表示从标准输入(stdin)读取数据,而 "test/ubuntu:v1.0" 是指定将导入的镜像的名称和标签。 $ docker image ls $ docker import http://example.com/exampleimage.tgz example/imagerepo

常用命令整理

notion imagenotion image

实操

官方前端案例:get started guide
  1. 克隆案例项目
    1. $ git clone https://github.com/docker/getting-started-app.git
  1. 创建项目镜像
    1. 创建Dockerfile文件
      1. $ cd /path/to/getting-started-app # 进入项目文件夹 $ touch Dockerfile # 创建Dockerfile文件
    2. 向文件内添加内容
      1. # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"] EXPOSE 3000
        内容解释
        • # syntax=docker/dockerfile:1:指定 docker 语法版本
        • FROM node:18-alpine:基于基础镜像node:18-alpine
        • WORKDIR /app :设置了容器内的工作目录为/app
        • COPY . .:将当前目录下的所有文件和文件夹复制到容器的工作目录 /app 中
        • RUN yarn install --production :在工作路径下运行yarn install --production安装依赖,根据package.json 文件安装
        • CMD ["node", "src/index.js"]:指定了容器启动时要执行的默认命令,将运行 Node.js,并执行 src/index.js 文件。
        • EXPOSE 3000:将容器内的 3000 端口暴露给外部网络
    3. 在终端创建镜像
      1. $ docker build -t getting-started .
        指令解读
        • 由于指定了基础镜像,可能会下载一些依赖镜像层
        • -t 表示指定镜像的名称和标签的组合,默认使用 "latest" 标签
        • . 表示告诉Docker在当前目录下查找Dockerfile文件
        📌
        创建时指定平台
        $ docker build --platform linux/amd64 -t YOUR-USER-NAME/getting-started .
  1. 运行镜像创建容器
    1. $ docker run -dp 127.0.0.1:3000:3000 getting-started
      指令解读
      • docker run:这是运行 Docker 容器的命令。
      • -dp 127.0.0.1:3000:3000:这是用于设置容器的运行选项的部分。
        • -d 表示以后台(detached)模式运行容器,
        • -p 127.0.0.1:3000:3000 表示将主机的 127.0.0.1 的 3000 端口映射到容器的 3000 端口。
      • getting-started:这是指定要运行的 Docker 镜像的名称。
      查看运行结果:http://localhost:3000/
      📌
      停止容器运行:
      $ docker ps # 列出当前正在运行的 Docker 容器,看到容器 ID $ docker stop <容器ID>
  1. 更新项目内容,并重新制作镜像
    1. 修改项目文件:src/static/js/app.js第 56 行
      1. - <p className="text-center">No items yet! Add one above!</p> + <p className="text-center">You have no todo items yet! Add one above!</p>
    2. 重新构建镜像并实例化容器
      1. $ docker build -t getting-started . $ docker run -dp 127.0.0.1:3000:3000 getting-started
        📌
        删除旧容器:
        $ docker container ls -a # 查看所有状态的容器,看到容器 ID $ docker rm <容器ID> [<容器ID>2] # 重启一个运行中的容器,可批量操作
  1. 在 DockerHub 上分享镜像
    1. 在DockerHub上创建一个和项目文件夹同名仓库getting-started,并记住仓库名称前面的 Namespace 内容;
    2. 为本地镜像添加一个标签,便签为Namespace/getting-started
    3. 将镜像推送到DockerHub仓库,推送用新添加的标签名
      1. $ docker push anjhon/getting-started
官方Python 案例:Containerize a Python application
  1. 克隆案例项目
    1. $ git clone https://github.com/docker/python-docker
  1. 初始化 Docker
    1. $ docker init
      会新增一些文件
      ├── python-docker/ │ ├── app.py │ ├── requirements.txt │ ├── .dockerignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md
  1. 构建并启动容器(在项目文件夹下运行)
    1. $ docker compose up --build $ docker compose up --build -d # 后台运行 $ docker compose down # 停止运行
      指令解读
      • Docker Compose 是一个用于定义和管理多个 Docker 容器的工具;通过执行 docker compose up,可以启动和管理定义在 docker-compose.yml 文件中的一组容器
      • 指令将会读取当前目录下的 docker-compose.yml 文件,并根据其中定义的服务配置来启动容器。如果镜像不存在或具有更新,它将会在启动之前构建所需的镜像。
      • up 表示启动容器,
      • --build 表示在启动之前构建镜像
Python 案例 1 —— 强化学习(井字棋)
  1. 井字棋代码——from 刘建平
  1. 将文件放入一个空文件夹,并用pipreqs生成项目依赖文件
    1. pip install pipreqs pipreqs ./ --encoding='utf-8' --force
  1. 新建Dockerfile文件,文件内容如下
    1. FROM python:3.11.3-slim-buster ADD . /app WORKDIR /app RUN pip install -r requirements.txt CMD ["python", "/app/reinforcement_learning_fromPinard.py"]
      如果直接用python:3.11.3,则体积 900+M;Python官方镜像地址
  1. 构建镜像
    1. docker build -t tictactoe:v1 . # tictactoe:v1 镜像名称tictactoe,标签v1 # 这里的-t 表示设置一个名称和标签
  1. 运行镜像生成容器
    1. docker run -it --rm tictactoe:v1 bash # 进入容器后,用 Python 运行程训 python reinforcement_learning_fromPinard.py # 退出容器(如果在启动时加了--rm,则会在退出时删除容器) exit
      📌
      程序会先训练模型 10 万次,然后会出现提示框,可以人机对弈;九宫格对应的按键位置为 qwe asd zxc
      notion imagenotion image
Python 数据挖掘环境案例
 

推荐连接

  1. 官方文档
  1. DockerHub
  1. Docker一从入门到实践
  1. 使用Docker安装配置Jupyter并配置R语言与Python环境
  1. docker desktop ubuntu镜像_基于docker的python数据挖掘环境搭建(Jupyter notebook))
If you have any questions, please contact me.