Docker实践
Posted on Aug.1, 2020
简介
- 什么是Docker
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 OverlayFS 类的 Union FS 等技术,
对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。
・更高效的利用系统资源;
・更快速的启动时间;
・一致的运行环境;
・持续交付和部署;
---> 开发通过Dockerfile构建镜像,并结合持续集成系统进行集成测试,甚至结合持续部署系统进行自动部署。
・更轻松的迁移;
・更轻松的维护和扩展.
---> Docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。
此外,Docker团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个
基本概念
理解了这三个概念,就理解了 Docker 的整个生命周期。- 镜像 image
- 容器 container
'镜像与容器'类似'类与实例'的关系。 容器运行时 = 镜像(基础层) + 运行时的读写(容器存储层),但Docker最佳实践的要求是所有文件写入操作,都应该使用数据卷(Volume)或者绑定宿主目录。因为容器和存储层是有生存周期的。
查看镜像:docker image ls 或 nvidia-docker image ls
查看容器:docker ps -a
需要一个集中的存储、分发镜像的服务 ———— Docker Registry
服务
Registry
│
| 仓库
|—— Repository 1
|—— Repository 2
| . . .
└───Repository n
|
│ 标签
│—— Tag 1
|—— Tag 2
| . . .
└—— Tag n ———— Each tag corresponds to a image.
比如一个仓库包含同一个软件不同版本的镜像。
<仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
例:
ubuntu:16.04, ubuntu:18.04, ubuntu:latest
仓库名经常以 两段式路径 形式出现,如 devonnhou/pytorch1.5
最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。
Docker Hub
国内加速器下载Docker Hub的镜像更快,如阿里云加速器
实战篇
https://zhuanlan.zhihu.com/p/23599229安装和卸载
Docker可以安装在Windows、Linux、Mac等各个平台上。具体可以查看文档Install Docker。安装完成之后,可以查看Docker的版本信息:# docker version
查看Docker的帮助信息:# docker --help。各种命令的用法也不再赘述,后边用到哪些命令时会作出一定的解释。
关于镜像的基本操作
在dockerhub选择镜像时,比如为pytorch构建的不同tag的cuda镜像区别:
base:基于cuda,包含最精简的依赖,用于部署预编译的cuda应用,需要手动安装所需的其他依赖
runtime:基于base,添加了cuda toolkit共享的库,涵盖了运行环境的最小集合如动态库等,但没有cuda的编译工具nvcc
devel:基于runtime,添加了编译工具链、调试工具、头文件、静态库,用于从源码编译cuda应用,是有nvcc的
关于容器的基本操作
docker run -it --runtime=nvidia -p 9999:22 --restart=always --ipc=host -v /data/houdewang/containers-shared:/containers-shared --name=super-resolution hdw-pytorch:1.7.0 bash
通过宿主机的9999端口可以ssh登录到容器中 (和vscode的remote-SSH功能搭配使用,很方便)
-v 把宿主机上的目录映射到容器内
这样进去的用户名默认都是root,如果为了方便主环境管理员,可以把起容器的命令改进,加一组参数-v /etc/passwd:/etc/passwd -u $(id -u):$(id -g),使得容器内外的用户名一致。
相关:新账户的创建
sudo useradd -m eric -s /bin/bash
sudo passwd eric
sudo gpasswd -a eric docker # gpasswd的设计初衷是与组相关的密码管理。早期某些组可以设置密码以限制访问。但现代Linux系统中的组通常不使用密码。所以现在gpasswd仅用于添加和删除组成员。尽管它的名字中包含“passwd”,但它并不用于设置或修改用户的登录密码。这里我们组名就叫docker,sudo groupadd mygroup可以创建组。
newgrp docker # 启动一个新的shell会话,并将当前会话的有效组更改为指定的组。
cd /data
sudo mkdir eric
sudo chown -R eric eric
退出容器并结束容器运行 exit
返回容器 先start,再attach
退出容器但容器仍运行 Ctrl+P+Q
返回容器 docker attach container_id (或 docker exec -it container_id bashShell)
进不去使用 docker exec -it containerID /bin/bash
本地浏览器连容器内的tensorboard:
1.创建容器时 -p 6166:6166/tcp -p 6167:6167/tcp 多映射几对端口。(平时9999:22,是因为ssh一般默认22端口)
2.tensorboard --logdir dirname --bind_all --port 6167,可以tensorboard -h查看--bind_all的作用,可以知道它能将TensorBoard实例暴露给公网。
3.本地浏览器输入 http://服务器IP:6166
修改虚拟网卡ip: 配置文件添加 --bip,设置合适的地址。不然会报错。
step1: 删除原有配置
https://www.jianshu.com/p/c06d532ec8f8
step2: 修改docker配置
https://stackoverflow.com/questions/52225493/change-default-docker0-bridge-ip-address
注意:在docker内查看nvidia-smi是不显示进程号(pid), 回到服务器账户看就好啦
关于在远程容器中使用jupyter
对于一些教程类型的code,我们经常需要用到jupyter。那么如何在本地利用远程服务器的算力和docker容器启动jupyter呢?
step1: 首先创建容器时,就记得提前多留出一个特定端口
docker run -it --runtime=nvidia -p 9999:22 10000:8888 ...
step2: 安装jupyter并进行配置
安装:pip install notebook
生成配置文件:jupyter notebook --generate-config
修改配置:vim ~/.jupyter/jupyter_notebook_config.py
c.NotebookApp.ip='*' #表示同一网络的主机都可访问
c.NotebookApp.password = u'sha1密文'
c.NotebookApp.open_browser = False
c.NotebookApp.port =8888 #随便指定一个
第二行的密码是可选的,为了安全起见,我们可以生成一个。
In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:
Verify password:
# 粘贴到c.NotebookApp.password = u'sha1密文'这儿
Out[2]:'sha1:xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxx'
step3: 启动jupyter并在本地浏览器打开:
启动:jupyter notebook
url:服务器IP:10000 (对应创建容器时的-p设置)
step4: 输入密码
关于vscode进入远程容器
设置管理员密码
passwd root
enable管理员远程登录
sudo service ssh start
sudo vim /etc/ssh/sshd_config
PermitRootLogin 改为yes
容器内open-ssh开机自动启动
sudo service ssh restart
sudo apt update && apt install systemd && systemd enable ssh
Open SSH Configuration File... HostName和宿主机一致,端口用9999
Connect to Host...
Kubernetes(常简称为K8s)旨在提供跨主机集群的平台。它支持一系列容器工具,包括Docker等。
Kubectl基本命令
假设资源组namespace叫video
・ cd k8s folder
・ Run: sudo kubectl apply -f
・ Check status: sudo kubectl get pod -n video
・ Check real-time logs: sudo kubectl logs -n video
・ Stop: sudo kubectl delete –f
(Pod: Kubernetes的基本调度单元称为“pod”。)
同时可以在~/.bashrc定义快捷方式
# shortcut for kubectl commands
alias kapply="sudo kubectl apply -f"
alias kstat="sudo kubectl get pod -n video"
alias kdel="sudo kubectl delete -f"
alias klog="sudo kubectl logs -n video -f"
最后一个添加了-f,会stream log (持续更新log),不加-f是一次性显示那一时刻的log。
最后,最自由的使用方式是进入容器内。
sudo kubectl exec -it -n video -- /bin/bash 进 exit 出
也可以定义快捷方式
alias kexec='AttachDocker(){ sudo kubectl exec -it $1 -n video -- /bin/bash;};AttachDocker'
进容器后做什么都可以,但不要kill这个pod任务里的进程,不然当前pod会报Error,然后K8s又自动重新帮你开一个新的pod。
可以在启动脚本里加一句 date;sleep 2m;date 搭配使用,在训练开始的2分钟前,kexec进去安装或修改一些必要的库。
查看节点gpu数量:
kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"
查看现有任务所用的节点:
sudo kubectl get pod -n kube-system -o wide -n video
搭配NFS存储服务器
现在比较流行的是挂载NFS网络盘,适合深度学习批量传输小文件的需求。
sudo mount -t nfs -o rw xx.xx.xx.xx(IP):/export/xx(remote directory) /home/xx(local directory)
mount [-t vfstype] [-o options] device dir
其他参数见 https://www.cnblogs.com/linuxprobe/p/5473645.html
docker存储清理
docker system prune -a && docker volume prune
前一个命令安全,后一个不到不得已慎用。
BE CAREFUL docker volume prunewill remove all your data that was persisted from docker to host disk........ system prune is safe though
清理容器前,可以查看容器状态:
docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"
docker存储的维护:
docker system df 查看存储占用情况,
docker rmi IMAGE 指定删除镜像
docker rm CONTAINER 指定删除容器
docker system prune = docker image prune + docker container prune + docker network prune 自动清理未使用的镜像/容器/网络等。
docker存储迁移
docker安装时,存储位置默认在/var/lib/docker。一般这个是挂载在系统盘。而系统盘一般空间较小,无法满足docker用户增长的需求。并且出问题会带来一些风险。放数据盘更安全。
1. sudo systemctl stop docker 且 systemctl stop docker.socket
2. sudo cp -rp /var/lib/docker/* /新的存储位置/ 或 sudo rsync -aqxP /var/lib/docker/ /新的存储位置/
3. sudo rm -rf /var/lib/docker
4. sudo ln -s /新的存储位置/ /var/lib/docker
或
3. 更新docker配置文件 /etc/docker/daemon.json
{
"data-root": "/新的存储位置/"
}
4. 保存和关闭配置文件
5. sudo systemctl start docker
制作docker镜像
How to write dockerfile
除了使用docker镜像,很多时候我们需要制作镜像,来完成服务器部署。
常见流程:编写dockerfile→jenkins编译→发布到jfrog