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),使得容器内外的用户名一致。
小Tips:
-p 8880-8890:8880
含义:
它告诉 Docker:“把容器里的 8880 端口,映射到宿主机 8880 到 8890 之间的某一个空闲端口上”
相关:新账户的创建
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
小Tips:
ENV DEBIAN_FRONTEND=noninteractive ———— 把 Debian/Ubuntu 系里的软件安装交互界面,设成“非交互模式”
这是在编写 Dockerfile(特别是基于 Ubuntu 或 Debian 系统时)非常常见且极为重要的一个环境变量。
简单来说,它的作用是:告诉操作系统,“现在是机器自动安装,没有任何人坐在屏幕前,请千万不要弹出任何交互式对话框或提示让我选择!”
由容器保存镜像
通过容器提交镜像(docker commit)以及推送镜像(docker push)笔记容器编排(docker compose)
Docker Compose 是建立在 Docker 之上的“多容器应用管理方式”
docker compose:Docker CLI 里的一个命令,用来根据一份 YAML 配置,一次性管理一组容器。
docker-compose.yml 是一个使用 YAML 格式编写的配置文件,用于定义和运行多容器(Multi-container)的 Docker 应用程序。
在实际开发和生产中,一个完整的项目通常不会只有一个容器。比如,一个 Web 项目可能需要:
一个 Nginx 容器做反向代理。
一个 Python/Java/Node.js 容器运行后端代码。
一个 MySQL 容器做数据库。
一个 Redis 容器做缓存。
如果仅使用纯 Docker,你需要手动输入好几条冗长复杂的 docker run 命令,并且还要手动配置它们之间的网络连接。
有了 docker-compose.yml,你可以把这些容器的配置(镜像、端口映射、环境变量、挂载目录、网络关系等)全部写在这一个文件里。 然后只需一条命令,就能一键启动或停止整个项目。
Docker Compose 基本工作流
使用 Docker Compose 通常分为三个步骤:
1. 准备应用的运行环境(Dockerfile,非必须)
如果你的应用需要自定义镜像,你需要写一个 Dockerfile。如果全都用官方现成的镜像(如 mysql, redis),这一步可以省略。
2. 编写 docker-compose.yml 文件
在你的项目根目录下创建一个 docker-compose.yml 文件,定义你的服务。例如,一个包含 Web 容器和 Redis 容器的简单配置如下:
version: '3.8' # compose 文件版本
services: # 定义服务
web:
image: nginx:latest # 使用 nginx 镜像
ports:
- "8080:80" # 将宿主机的 8080 端口映射到容器的 80 端口
depends_on:
- redis # 声明依赖关系,确保 redis 先启动
redis:
image: redis:alpine # 使用轻量级的 redis 镜像
ports:
- "6379:6379"
3. 运行 Docker Compose 命令
打开终端,进入 docker-compose.yml 所在的目录,执行命令来管理项目。
常用命令
一键启动所有服务:docker compose up
一键停止并删除所有服务(容器、网络等):docker compose down
查看当前项目正在运行的容器:docker compose ps
查看服务的运行日志:docker compose logs -f
停止服务(不删除容器):docker compose stop
重新启动服务:docker compose start
总结:docker-compose.yml 极大地简化了多容器部署的复杂度,它是将应用“基础设施代码化(Infrastructure as Code)”的一种极佳实践。