Docker 配置记录

docker用linux来理解比较好,万物皆文件。
我们需要用docker来搭建一个environment,无非就是创建一些文件。
当我们使用(attach)这个env的时候,无非是修改各种symbol、pointer,指向这个env对应的文件。

每个env对应一个image file 镜像文件。
使用env时,会从image实例化一个runtime环境对象出来,称这个实例化对象为container。
程序员应该不模式吧。

第一次使用docker环境可以 ab initio (从零开始),或者用现成的image。

1. 使用现成镜像

一般会推荐新手用现成的image。
但新手会疑问去哪里找image,传送:dockerhub。

docker image采用类似git的方式管理。
常见格式{用户名}/{仓库名}:标签名
拉取语法也是git风格

docker pull {用户名}/{仓库名}:标签名
例如
docker pull bitnami/pytorch:latest

标签名(tag)的default value=latest
当标签名取缺省值时,可以省略不写。
或者说,当用户省略不写时,docker会自动填入缺省名,搜索latest这个tag。

有些教程里会把tag称为version,知道这两者等价就行。
前面的{用户名}/{仓库名}合称为repository name。也就是说,你可以不写所谓的用户名,docker官方并没有约定这一写法,但我们码农遵循社区礼仪,应该在自己的仓库name前加上自己的用户名标识,以示区分。
每个{repository name}:{version tag} 对应唯一一个image file。

所以上述代码等价于

docker pull bitnami/pytorch

拉取之后直接运行即可。

docker run -it --name pytorch bitnami/pytorch

run的参数

-it: i表示开STDIN,用于控制台交互;t表示分配tty设备,该可以支持终端登录。
--name: 实例化container的名字,会显示在docker ps中。
bitnami/pytorch: 从哪个image启动,后面的tag因为缺省所以自动变成latest。

2.从零开始build

走从零开始这条路呢,直觉上需要“新建env、安装一堆框架、保存到image”。
要麻烦很多。

想新建一个环境,需要自己写一个用于build的Dockerfile。
然后使用build命令

docker build [选项] <上下文路径URL>
这个上下文路径,默认不管的话,输入一个点.,表示cwd。

根据多年的码农经验,这个文件应该有着类似于cmake之类的构建命令。

docker build 构建过程会在 Docker 后台守护进程 docker daemon 中进行,并不是在客户端工具 CLI 中。
Docker 客户端会将构建命令后面指定的路径(.)下的所有文件打包发送给 Docker 服务端;
Docker 服务端收到客户端发送的包后进行解压,再根据 Dockerfile 里面的指令进行镜像的分层构建;

Dockerfile是默认约定的文件名。
如果像上面这样直接输入docker build,会自动搜索cwd下的Dockerfile文件。
如果想使用的Dockerfile不在cwd下,就用-f指定文件。

docker build -f /my/math/to/doooooooockerfile .
有个点 别忘了!

-t 指定输出的repository name

docker build -f /my/math/to/dooockerfile -t link/start .
有个点 别忘了!!!

就会创建一个名字叫做"link/start"的仓库名字,由于我们没有写tag,所以docker为我们补上了缺省值latest。

可以使用

docker images

查看本地的仓库,一般第一条就是刚刚build完成的。

Dockerfile语法

全局唯一一条From

FROM hh/bb

ENV 设置环境变量

ENV NUMBER_A=123

RUN 执行cli命令

RUN echo “hello world”

前面有环境变量了,所以可以bash脚本风格取值

RUN echo ${NUMBER_A}

可以用RUN这个装一堆软件

RUN apt-get update 
RUN apt-get install -y --no-install-recommends \
          包1 \
          包2

设置apt、conda、pip代理镜像

RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list && apt-get clean && \
     /opt/conda/bin/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ && \
     /opt/conda/bin/conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ && \
     /opt/conda/bin/conda config --set show_channel_urls yes && \
     /opt/conda/bin/pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

炼丹师最推荐的手法应该还是走conda路线吧。

RUN conda install -f requirements.txt

最后就是 dockerfile 中的 ENTRYPOINT 和 CMD了,在 docker 容器启动的时候,会自动运行 ENTRYPOINT 和 CMD 的命令,如果 docker run 的时候指定了命令,该命令会作为参数接在 ENTRYPOINT 后,并替换 dockerfile 里 CMD 的命令。

Run

最简单的带cuda的启动方式

docker run -it --gpus all image_name/image_ID /bin/bash

-p 端口映射。多个端口需要多个-p

docker run -it -p 8848:8848 -p 8888:8888 -p 9999:9999 -p 9998:9998

文件系统

不太理解。问自己几个问题。

Q:我在container里跑实验,保存数据。之后怎么获取,怎么拿出来?
A: 使用docker cp
docker cp CONTAINER:SRC_PATH DEST_PATH

Q:既然container是一个runtime的实例对象,我如果在container里面存了数据,后面突然断电了,没有经过正规的docker save操作,这些数据会丢失吗?
A: 不会。container里的文件分为runtime和storage两部分。实例化container之后,都存在docker的默认目录下。
一个container被关掉,只会让它的status变成’exited’。
只需要docker restart containerID即可,就能重新读取storage部分的数据。
当然runtime部分的文件在restart后会丢失。
container需要显式地用命令删除,才能彻底删掉storage部分的数据。

Q:有什么办法让docker读写外部文件?
A:
使用docker run创建 container 时,用-v挂载目录。
这样在container内可以读取宿主机上的文件,写入数据也在宿主机上。

挂载目录方法 -v (Data Volumes 数据卷)

docker run [其他指令…] -v /home/user1/:/dst/path/

-v指令的本质是一个软链接,所以不限于dir->dir,可以实现file->file。

如果想保持容器运行并退出终端,按 CTRL+P+Q;
重新进入容器的终端,执行 docker attach containerID;

内部存储

关键词叫storage driver

一般存储在device mapper中。

查看docker根目录

$ docker info | grep ‘Root’
Docker Root Dir: /home/admin/docker

/home/admin/docker/overlay2
/home/admin/docker/devicemapper

查看具体container中的数据存储路径。

docker inspect + 容器名/ID

3.其他操作

退出、删除

内部退出
exit退出 ,ctrl+d
不关闭地escape, ctrl+p+q

外部关闭

docker stop containerID
docker stop $(docker ps -a -q) 关闭所有容器

删除容器必须在关闭所有容器的前提下。因为不同容器是共享某些资源的(节约空间)。

docker rm containerID

保存修改,提交镜像

commit

docker commit containerID repo_name:tag

阿里云镜像服务器

. 将镜像推送到Registry
$ docker login --username=****@**.com registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/myrepo/myimage:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/myrepo/myimage:[镜像版本号]

4.docker + ssh 配置

docker pull continuumio/anaconda3

docker run --gpus all --name=doc_conda3 -idt -p8001:22 -p8888:8888 continuumio/anaconda3 /bin/bash

结尾/bin/bash表示进去就启动bash。

-p:指定端口映射,格式为:宿主机端口:容器端口。
-p 8001:22 表示用宿主机8001映射容器22

EXPOSE 22

apt-get update

apt-get install openssh-server
apt-get install openssh-client

修改sshd配置

vim /etc/ssh/sshd_config

禁用密码

PasswordAuthentication yes # 默认yes

PubkeyAuthentication yes #启用公钥私钥配对认证方式

#PermitRootLogin prohibit-password #默认展示密码
PermitRootLogin yes #改成yes, 允许root用户使用ssh登录

重启sshd

/etc/init.d/ssh restart
或者
service ssh start
不能用systemctl restart sshd.service
因为默认systemctl是不能在 docker容器中使用的,除非run的时候带上–priviledge=true

生成rsa密码

ssh-keygen -t rsa

Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory ‘/root/.ssh’.

Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:0KBt9nhyBEHUsCVT4KqhiSdEZI/SQ0r1OaPKEc0NDfg root@0e4402636444

这样生成的两个文件在

~/.ssh/id_rsa.pub, ~/.ssh/id_rsa.pub

其中公钥 ~/.ssh/id_rsa.pub, 需要放到服务端 ~/.ssh/authorized_keys ,用来解密。

怎么挪过去不重要,重要的是要挪过去。

cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys

然后就可以用私钥去登录有公钥的服务器。

docker cp doc_conda3:/root/.ssh/id_rsa ~/.ssh/id_rsa

ssh

-v 显示debug信息
-i 指定私钥文件。
不指定的话,只会找四个默认文件名。四个默认文件都不存在就用password。

debug1: Trying private key: /home/USER/.ssh/id_rsa
debug1: Trying private key: /home/USER/.ssh/id_dsa
debug1: Trying private key: /home/USER/.ssh/id_ecdsa
debug1: Trying private key: /home/USER/.ssh/id_ed25519

5.docker端口映射需求

很麻烦啊。
如果docker开启不特定的服务,就会有动态端口映射的需求。
除非你预先把可能遇到的所有端口都写上。

需求有两种解决路径

  1. 为运行的container 随时动态添加端口映射
  2. 为运行的container分配一个ip。这样container里面运行新的东西,就能用ip随时访问了。

//参考https://blog.csdn.net/zuoshenglo/article/details/78402772

docker inspect container_name | grep IPAddress

本机 8001 到 容器 8000

iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000

端口映射 最佳实践

提前规划好几个常用端口

ssh端口必留。 8001 -> 22。
jupyter lab端口一个。8888 -> 8888。
tensorboard端口一个。 8002->8002。

规划一个公共存储空间,所有代码+数据+中间结果都放这。

/data1/username/

即docker只承载运行环境。 代码数据是外部的,只是借docker使用。

于是可以配置文件里alias写好启动命令

alias dockerrun="docker run --gpus all --name=doc_conda3 -idt -p8001:22 -p8888:8888 -p8002:8002 -v ~/proj:/root/proj -v /data1:/data1 -v /data2:/data2  myrepo/myimage:latest /bin/bash"

还可以加上ssh命令

alias ssh-docker="ssh root@localhost -p 8001 -i ~/.ssh/id_rsa"

除了alias 另一种方法是
https://docs.docker.com/compose/

多次commit合并打包

docker commit的逻辑是, 删除也算增量操作。
所以commit多次,哪怕加一个文件再删一个,最终image大小还是会增加。

怎么把删除操作合并,使得最终镜像减小,就得手动打包。

参考
https://blog.csdn.net/u011195077/article/details/108148824

1.根目录下打包
记得使用--exclude排除不需要的目录。

# 根目录下:
tar --exclude=/proc --exclude=/sys --exclude=base_img.tar -cvf base_img.tar .

2.复制打包出来的文件

# 退出
exit
 
# copy
docker cp [容器id]:/base_img.tar .

3.导入新容器

# 导入
cat base_img.tar|docker import - base_img

最后得到的这个base_img 镜像文件,只有一个commit记录。

你可能感兴趣的:(docker,容器,运维)