Docker简介及使用方法

导言

最近东东师兄毕业,接过了实验室服务器的维护工作。服务器的基本操作已经保存在Github的分组当中,但本人能力有限,难以帮每个人去配置环境,于是尝试使用Docker来提供服务器多环境。这样每个人首先去获取镜像然后创建自己容器就可以满足自己的需要了。废话不多说,以下内容记录对Docker的使用探索。

太长不看版

入门使用教程。以TensorFlow1.7的安装使用为例。

1、查看本地是否已经有需要用的镜像

docker image list

在这里插入图片描述
此处若已有需要的镜像,可直接跳转到 3 。
若没有,则需要去docker hub中寻找所需的镜像并拉取到本地。

2、Docker Hub寻找镜像并拉取

Docker Hub地址:https://registry.hub.docker.com/.
在这里插入图片描述
搜索所需库的名字,此处我们以搜索TensorFlow1.7为例
在这里插入图片描述
从搜索到的镜像中进行挑选,此处我们选择TensorFlow的官方镜像。
在这里插入图片描述
其中有对该docker镜像的描述,点击Tags选择需要安装的版本
在这里插入图片描述
此处选择了TensorFlow1.7.1的GPU版本,附带python3
在这里插入图片描述
使用docker pull命令拉取该镜像到本地。

docker pull tensorflow/tensorflow:1.7.1-gpu-py3

3、创建虚拟环境

有了需要的镜像后,需要将镜像创建为容器,从而得到一个虚拟环境。
使用 docker image ls 显示本地已有镜像:
在这里插入图片描述
使用docker run [选项] [镜像名:Tag]的方式创建容器。以创建一个TensorFlow1.7.1版本容器为例:

docker run -it --name lzx_tensorflow1.7.1 \
	--mount type=bind,source=/home/lizhaoxu/work_space,target=/home/target_work_space \
	--gpus all \
	tensorflow/tensorflow:1.7.1-gpu-py3 \
	/bin/bash

其中 -i 让容器的标准输入保持打开
-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上(一般这俩都带上就可以)
–name 为容器指定一个名字。
–mount 选项挂载本地目录到容器(虚拟环境)当中,source是要被挂载的本地目录,target是容器(虚拟环境)的挂载目标路径,这样就将work_space文件夹中的所有文件挂载到了容器中的/home/target_work_space文件夹下。
–gpus 如果容器需要使用GPU,则需要带上该选项,否则会报找不到cuda链接库的错误。
最后的/bin/bash代表打开一个shell,便于输入命令进行交互。

输入上述命令后,将进入虚拟环境:
在这里插入图片描述
在这里插入图片描述
切换到/home/target_work_space下,可以看到本地work_space中的文件,并进行读操作。(关于如何进行写操作,请看https://blog.csdn.net/BrHiker/article/details/107823385
(本地被挂载的文件夹与虚拟环境中的挂载的文件夹会同步变化)

在这里插入图片描述
像使用虚拟机一样使用该虚拟环境,尝试import tensorflow,成功。

4、虚拟环境的退出、重新进入

容器(虚拟环境)有两种退出方式:
1、退出并关闭容器exit
在这里插入图片描述
需要重新进入时,使用docker container list -a 可查看所有容器
在这里插入图片描述
可以看到之前被我们退出并关闭的容器。
使用docker start -i [容器名]打开。
在这里插入图片描述
2、退出但不关闭容器ctrl + p + q
此时退出容器但容器仍在后台运行,要进入则使用“docker attach [容器名]
在这里插入图片描述

5、镜像、虚拟环境的清理

为避免浪费磁盘空间,镜像、容器不再使用时,请注意及时清理掉。
镜像清理命令:

docker image rm [镜像名]

容器清理命令:

docker rm [容器名]

Docker简介

啥是Docker?

Docker是一个虚拟环境容器,可以将开发环境、代码、配置文件等一并打包到这个容器当中,并发布到任意平台中。支持Windows、MacOS以及Linux。
Docker内涵
Docker本身是一个开源项目,它使用Google的Go语言进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
Docker架构
( runc是一个Linux命令行工具,用于根据OCI容器运行时规范创建和运行容器。
containerd 是一个守护程序,它管理容器生命周期,提供了在一个节点上执行容器和管理镜像的最小功能集。)
== 它的架构其实我不懂…待我再修炼一下,日后填坑==

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。

这玩意跟VM虚拟机有啥区别?

使用虚拟机(如VMware Workstation)可以在当前系统虚拟化出另一台电脑,这一方面非常直观易用,但常常会出现网络连接、无法使用GPU等问题,且其资源占用贼大,一台普通电脑也就开几个。这是由于传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程。而Docker容器(什么是容器下文会讲)内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。此外,Docker容器也可以方便地读写本地文件。

传统虚拟机
Docker
Docker容器与虚拟机

Docker使用

Dcoker的基础概念

Docker使用起来需要清楚以下三个基本概念:

  1. 镜像(Image)
  2. 容器(Container)
  3. 仓库(Repository)
    首先对这三个概念进行抽象化介绍:可以将镜像理解为class,容器理解为根据class创建的实例,仓库理解为像GitHub那样的服务器仓库,用来远程存储构建好了的镜像。
    下面对其内涵进行介绍。

镜像

镜像是一个特殊的文件系统,它提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。如一个Ubuntu镜像就包含了Ubuntu系统的root文件系统。
镜像不包含任何动态数据,其内容在构建之后也不会被改变。

容器

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。

仓库

Docker提供用来几种存储、分发镜像的服务:Docker Registry 。一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

Docker安装

实验室服务器系统为Ubuntu 20.04(LTS)
首先卸载旧版本Docker(旧版本成为docker或docker-engine)

$ sudo apt-get remove docker \
               docker-engine \
               docker.io

使用APT安装

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

添加密钥并增加软件源

$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
    "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

安装Docker-ce

$ sudo apt-get update
$ sudo apt-get install docker-ce

启动Docker-ce

$ sudo systemctl enable docker
$ sudo systemctl start docker

建立docker用户组(docker命令需要root用户和docker组的用户才可以使用,因此需要将使用docker的用户加入用户组)

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

退出终端并重登录进行测试

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

能输出以上信息则说明安装成功。

下面进行镜像的获取

镜像获取及使用

在DockerHub上有大量的镜像可以获取
使用 docker pull从仓库中获取,其命令格式为:

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

(具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。)

尝试获取ubuntu:18.04的镜像:

docker pull ubuntu:18.04

随后开始对该镜像进行下载并给出它的sha256摘要以确保一致性。

镜像的常用命令

# 镜像的列出
$ docker image ls
# 列出某个
$ docker image ls ubuntu:18.04
# 查看镜像、容器、数据卷所占空间
docker system df
# 删除本地镜像
docker image rm [选项] <镜像1> [<镜像2> ...]

容器的使用

容器启动有两种方式:一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

1.新建并启动
命令:docker run

#启动一个bash终端,允许用户进行交互
docker run -it ubuntu:18.04 /bin/bash
root@af8bae53bdd3:/#

-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。此时如果使用exit退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用快捷键 ctrl+p退出,此时容器的状态为Up。
除了这两个参数之外,run命令还有很多其他参数。
如 -rm 退出容器后就销毁容器
-d 后台运行:

docker run -d ubuntu:18.04 /bin/bash -c "while true; do echo hello; sleep 1; done"

使用了-d参数,使这个容器处于后台运行的状态,不会对当前终端产生任何输出,所有的stdout都输出到log,可以使用docker logs container_name/container_id查看。

#查看当前运行的容器
docker container ls
#查看所有容器
docker container ls -a
#启动
docker start container_name/container_id
#带交互启动
docker start -i container_name/container_id
#停止
docker stop container_name/container_id
#重启
docker restart container_name/container_id
#进入后台运行的容器
docker attach container_name/container_id
#删除容器
docker rm container_name/container_id

容器运行时会开辟一个存储层,所有的运行文件会存储在该层当中,当容器销毁时,这个存储层也会一同被销毁。若要保存修改后的容器,可以使用docker cimmit命令,给容器依赖的镜像添加新的一层,形成一个新镜像,比如

docker commit \
	--author "lilei <lilei@xxx.com>" \
	--message "修改了给韩梅梅写的信" \
	love_letter(这里是<容器ID或容器名>) \
	letter_template:v2(这里是[<仓库名>[:<标签>]]

执行上述命令后,就可以将love_letter这个容器保存为letter_template:v2这个镜像啦,容器销毁也就无所谓了。
(当然docker commit其实会增添许多无关的内容,比如编译产生的文件啥的,这些内容会导致镜像非常臃肿,最好还是用Dockerfile来制作镜像,这部分后面补充吧)

数据卷与挂载主机目录

容器创建以后开辟的存储层是一个新的空间,里面贼干净。这个存储层只能供这个容器使用,而且容器一旦被删除,存储层也就没了。但是有时候我们希望能够让多个容器共用一个文件这时候就要用到数据卷这个东西。
此外我们经常需要使用宿主机当中的文件或者向宿主机中写一些文件,这种时候就需要用到挂载主机目录了。

下面介绍一下数据卷(想看用法可以直接跳过了)
数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
·数据卷 可以在容器之间共享和重用
·对 数据卷 的修改会立马生效
·对 数据卷 的更新,不会影响镜像
·数据卷 默认会一直存在,即使容器被删除
数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。

数据卷的常用命令:

# 创建一个数据卷
$ docker volume create my-vol

# 查看所有数据卷
$ docker volume ls
local		my-vol

# 查看指定数据卷信息
$ docker volume inspect my-vol
[
	{
		"Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

# 启动一个挂载数据卷的容器
# 下面以ubuntu18.04镜像创建一个名为test的容器,并加载一个数据卷到容器的 /home 目录
$ docker run -it \
	--name test \
	# -v my-vol:/home \
	--mount source=my-vol, target=/home \
	ubuntu:18.04 \
	/bin/bash

# 查看数据卷的具体信息
# 查看test容器的信息
$ docker inspect test
# 数据卷信息在“mounts”下
"Mounts": [
    {
        "Type": "volume",
        "Name": "my-vol",
        "Source": "/var/lib/docker/volumes/my-vol/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
]

# 删除数据卷
$ docker volume rm my-vol
# 数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除
# 后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。
# 如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个
# 命令。

# 清理无主数据卷
$ docker volume prune

挂载主机目录
与数据卷挂载方式类似,也可以挂载一个主机目录到容器去。
以主机当中/home/lilei/docker_ws/目录为例,其中有一个hello_world.txt文件。

# 挂载/home/lilei/docker_ws/到test容器当中
$ docker run -it --name test \
	# -v /home/lilei/docker_ws:/home \
	--mount type=bind, source=/home/lilei/docker_ws, target=/home \
	ubuntu:18.04 \
	/bin/bash
$ cd /home && cat hellow_world.txt
hello_world

注意 挂载目录的路径必须是绝对路径,使用**-v参数创建时如果本地目录不存在 Docker 会自动为你创建一个文件夹,使用–mount**参数时如果本地目录不存在,Docker 会报错。挂载的东西既可以是文件夹也可以是文件。
如记录在容器中输入过的命令:

$ docker run --rm -it \
   # -v $HOME/.bash_history:/root/.bash_history \
   --mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
   ubuntu:18.04 \
   bash

root@2affd44b4667:/# history
1  ls
2  diskutil list

后记

Docker确实是一个强大的虚拟环境容器,使用它可以免去依赖库找不到、版本冲突等麻烦的问题,极大地节省程序员本就不多的生命。
由于Docker原理上仍然容器作为一个隔离的进程,与主机共用一个内核,因此不进行设置的情况下在容器中默认具有root权限,虽然存储层接触不到主机文件,但是一些命令或者挂载的文件夹可能会影响到主机。因此后续还需要设置Docker的用户空间。关于这个问题以及使用Docker进行深度学习的操作实例、Dockerfile构建镜像后续会继续补充。

参考:
https://yeasy.gitbook.io/docker_practice/ link.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值