跳至主要內容

Docker 基础笔记

Kevin 吴嘉文大约 15 分钟知识笔记Linux

看越来越多人使用 docker 来进行服务的交付了,在深度学习环境的配置上有了 docker 也节省了很多时间。于是记了一些笔记,接下来通过在 docker 上搭建 NVIDIA 深度学习环境来熟悉吧!

docker 相对与虚拟机,占用空间更小,启动更快。它米有自己的内核且不会对操作系统和硬件进行模拟。

安装卸载指南 https://www.docker.com/

docker 类似一个 linux 虚拟机,占有一个端口

常用命令

docker version   
docker info       #显示 docker 的系统信息,包括镜像和容器的数量
docker 命令 --help 

镜像命令

docker images 
docker search 镜像名称
docker pull tomcat:8
#如果不写 tag,默认就是 latest
docker rmi -f 镜像 id #删除指定的镜像
docker rmi -f $(docker images -aq) #删除全部的镜像

容器命令

创建容器需要创建镜像

sudo docker pull ubuntu

新建容器并启动

docker run [可选参数] image | docker container run [可选参数] image

--name="Name"		容器名字 tomcat01 tomcat02 用来区分容器
-d					后台方式运行
-it 				使用交互方式运行,进入容器查看内容
-p					指定容器的端口 -p 8080(宿主机):8080(容器)
		-p ip:主机端口:容器端口
		-p 主机端口:容器端口(常用)
		-p 容器端口
		容器端口
-P(大写) 				随机指定端口


exit #容器直接退出
ctrl +P +Q #容器不停止退出
docker ps #列出当前正在运行的容器
  -a, --all             Show all containers (default shows just running)
  -n, --last int        Show n last created containers (includes all states) (default -1)
  -q, --quiet           Only display numeric IDs

删除容器

docker rm 容器 id   #删除指定的容器,不能删除正在运行的容器,如果要强制删除 rm -rf
docker rm -f $(docker ps -aq)  #删除指定的容器
docker ps -a -q|xargs docker rm  #删除所有的容器

启动和停止容器

docker start 容器 id	
docker restart 容器 id	
docker stop 容器 id	
docker kill 容器 id	# 强制停止当前容器

常用其他命令

后台运行容器的时候,如果容器没有前台进程,docker 就会自动停止他

docker run -d centos
# 如果让容器运行一个 while 循环那么就可以发现它了

显示日志

docker logs -t --tail n 容器 id #查看 n 行日志
docker logs -ft 容器 id #查看并实时输出全部日志

容器状态查看

docker top 容器 id 查看容器进程信息

docker inspect 容器 id 查看镜像的元数据

  • Mounts 查看挂载卷
  • "PortBindings" 查看端口

docker stats 容器 id 查看 docker 容器使用内存情况

进入当前正在运行的容器

docker exec -it 容器 id /bin/bash
docker attach 容器 id # 进入容器正在执行的进程

从容器内拷贝文件到主机

docker cp 容器 id:容器内路径 主机目的路径
img
img
  attach      #当前 shell 下 attach 连接指定运行的镜像
  build       Build an image from a Dockerfile 
  commit      Create a new image from a containers changes 
  cp          Copy files/folders between a container and the local filesystem 
  create      Create a new container 
  diff        #查看 docker 容器的变化
  events      Get real time events from the server 
  exec        Run a command in a running container 
  export      #导出容器文件系统作为一个 tar 归档文件[对应 import]
  history     Show the history of an image 
  images      List images 
  import      # 从 tar 包中导入内容创建一个文件系统镜像
  info        Display system-wide information 
  inspect     Return low-level information on Docker objects 
  kill        Kill one or more running containers 
  load        Load an image from a tar archive or STDIN 
  login       Log in to a Docker registry 
  logout      Log out from a Docker registry
  logs        Fetch the logs of a container
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  ps          List containers
  pull        Pull an image or a repository from a registry
  push        Push an image or a repository to a registry
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  rmi         Remove one or more images
  run         Run a command in a new container
  save        Save one or more images to a tar archive (streamed to STDOUT by default)
  search      Search the Docker Hub for images
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop one or more running containers
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  version     Show the Docker version information
  wait        Block until one or more containers stop, then print their exit codes

容器开启时的配置

先参考一个深度学习容器开启的命令

docker run -i -d -v /export/username/:/export/username/ --name=tf_img_cls --net=host --runtime=nvidia -e NVIDIA_VISIBLE_DEVICE=0 ai-image.jdcloud.com

不同镜像开启需要的配置不同,一般通过 -e 传递。

-p 建立容器与宿主机的端口映射,可以同时配置多个端口。如下,我们在宿主机上通过访问 666 端口来访问容器上部署的 nginx。

# -p 配置容器的端口,可以同时配置多个端口
docker run -d --name nginx01 -p 666:80 nginx
curl localhost:666

-e 容器环境配置,不同容器环境配置不同。如下 es 的环境配置为内存限制。NVIDIA 容器 -e 可以配置指定显卡。 具体的 -e 传参应该参考容器发布者提供的信息。

docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2

-v 挂载数据卷,具体参考[下方](# 容器数据卷)

--net 容器网络,具体参考[下方](# docker 网络)

portainer 可视化面板可以玩玩

镜像讲解

镜像获得途径:

  • docker pull
  • 通过朋友给你的 dockerfile 构建

镜像加载原理

UnionFs(联合文件系统) :Union 文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

docker 的镜像 实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。 boots(boot file system)主要包含 bootloader 和 Kernel, bootloader 主要是引导加 kernel, Linux 刚启动时会加 bootfs 文件系统,在 Docker 镜像的最底层是 boots。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加載器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。 rootfs(root file system),在 bootfs 之上。包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件。 rootfs 就是各种不同的操作系统发行版,比如 Ubuntu, Centos 等等。

Docker 镜像都是只读的,当容器启动(run)时,一个新的可写层加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层!因此我们所做的操作都是基于容器层的。

commit 镜像

docker commit 来保存当前容器的状态。下词可以直接通过加载对应的镜像来开启保存的容器

docker commit -m="描述信息" -a="作者" 容器 id 目标镜像名:[TAG]

容器数据卷

docker 将应用和环境打包成环境,但是并不用于保存数据。将数据存储与本地,也可以实现容器间的数据共享。

使用数据卷

将主机上的文件挂在到容器上,两个文件的内容会自动同步。在创建容器时候制定 -v ,后续开启容器就不需要 -v 操作了。

docker run -it -v 主机目录:容器内目录

Mysql 案例

# 参考官网 hub 
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

挂载卷的不同方式

-v 容器内路径			 # 方式一:没有提供容器外路径
-v 卷名:容器内路径		   # 方式二:挂载时候提供了名称,没有提供路径
-v 宿主机路径:容器内路径   # 方式三

方式一:

docker run -d -P --name nginx01 -v /etc/nginx nginx

# 查看所有的 volume 的情况,可以查看到容器外路径为随即分配的 16 进制数
docker volume ls    
DRIVER              VOLUME NAME
local               33ae588fae6d34f511a769948f0d3d123c9d45c442ac7728cb85599c2657e50d

方式二:

docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
docker volume ls                  
DRIVER              VOLUME NAME
local               juming-nginx

# 通过 -v 卷名:容器内路径
# 查看一下这个卷
docker volume inspect 卷名

大多数情况都使用方式二, 所有的 docker 容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxx/_data

方式三:

docker run -d -P --name nginx03 -v /etc/nginx:/etc/nginx nginx
# 通过 docker volume ls 查看不到卷信息

设置卷的读写权限

docker run -d -P --name nginx05 -v juming:/etc/nginx:ro nginx
# ro 只能通过宿主机改变卷数据
docker run -d -P --name nginx05 -v juming:/etc/nginx:rw nginx

数据卷容器

容器二使用与容器一相同的数据卷

docker run -it --name docker02 --volumes-from docker-1 image_name

端口

公开端口,根据 docker inspect 中的 config.ExportPort 可以查看到暴露端口情况。一般可以通过 dockerfile 中配置 EXPOSE 80 (更像是一个文档注释,说明 80 端口被监听)。

公开不会立即产生任何影响。该语句仅表示容器内的应用程序侦听端口80。它不会向世界开放该端口或明确提供对任何其他容器的访问。

作为镜像作者,列出工作负载使用的EXPOSE端口有助于用户在启动容器时配置适当的端口转发规则。这在使用非标准端口时尤为重要:虽然可以预期 Web 服务器侦听端口 80,但用户将无法猜测自定义套接字服务器使用的端口。

再创建容器的时候使用 -p 操作,将本机端口与容器绑定。

构建 Dockerfile

Dockerfile 是用来构建 docker 镜像的构建文件。

构建步骤:

1、 编写一个 dockerfile 文件

2、 docker build 构建称为一个镜像

3、 docker run 运行镜像

4、 docker push 发布镜像(DockerHub 、阿里云仓库)

编写 dockerfile

dockerfile 命令

FROM # 基础镜像,一切从这里开始构建 MAINTAINER # 镜像是谁写的, 姓名+邮箱 RUN # 镜像构建的时候需要运行的命令 ADD # 步骤,tomcat 镜像,这个 tomcat 压缩包!添加内容 添加同目录 WORKDIR # 镜像的工作目录 VOLUME # 挂载的目录 EXPOSE # 保留端口配置 CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。 ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令 ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令。 COPY # 类似 ADD,将我们文件拷贝到镜像中 ENV # 构建的时候设置环境变量!

编写 dockerfile 示例

FROM centos
MAINTAINER myname<myemail>

ENV MYPATH /usr/local
WORKDIR $MYPATH

VOLUME ["volume01","volume02"]
# 生成镜像时候自动挂载的数据卷,匿名挂载
# 如果创建的时候没有挂载,那么后续需要手动挂载

RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH
CMD echo "-----end----"
CMD /bin/bash

CMD 与 ENTRYPOINT 区别

# dockerfile
FROM centos
CMD ["ls","-a"]

构建镜像

# 如果传入了 tag 参数,那么打开时也需要使用 tag 参数
docker build -f dockerfile1 -t image_name:tag path

docker build -f dockerfile1 -t cmd-test:1.0 . 

构建成功后运行:

docker run cmd-test:1.0  # 输出 ls -a
docker run cmd-test:1.0 -l  # 报错 -l 不是命令
docker run cmd-test:1.0 ls -l  # 原先的 ls -a 会被替换为 ls -l

以上为 CMD 指令的特点,相对的 ENTRYPOINT 就不会被覆盖

# dockerfile 中编写以下内容
FROM centos
ENTRYPOINT ["ls","-a"]

建立后测试 ENTRYPOINT

docker build -f dockerfile1 -t ep-test:1.0 .

docker run ep-test:1.0  # 输出 ls -a
docker run ep-test:1.0 -l  # 输出 ls -al

编写 dockerfile 案例二

FROM centos #
MAINTAINER name<mail@qq.com>
COPY README /usr/local/README #复制文件
ADD jdk-8u231-linux-x64.tar.gz /usr/local/ #复制后自动解压
ADD apache-tomcat-9.0.35.tar.gz /usr/local/ #复制解压
RUN yum -y install vim
ENV MYPATH /usr/local #设置环境变量
WORKDIR $MYPATH #设置工作目录
ENV JAVA_HOME /usr/local/jdk1.8.0_231 #设置环境变量
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.35 #设置环境变量
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib #设置环境变量 分隔符是:
EXPOSE 8080 #设置暴露的端口
CMD /usr/local/apache-tomcat-9.0.35/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.35/logs/catalina.out # 设置默认命令

从容器创建镜像

docker commit -m "comment" -a "author" container_id new_image_name:tag

容器 run 时候的配置信息都将被移除,改在路径下的文件不会保留。

发布自己的镜像

首先登陆

docker login -u kevinng77

制作镜像

docker build -t kevin/mytomcat:0.1

提交 push 镜像

docker push kevin/mytomcat:0.1

docker 网络

Docker 启动会默认创建 docker0 虚拟网桥,是 Linux 的一个 bridge,可以理解成一个软件交换机。它会在挂载到它的网口之间进行转发。之后所有容器都是在 172.17.0.x 的网段。当创建一个 Docker 容器的时候,同时会创建一对 veth pair。Linux 虚拟网络设备 veth-pair 详解,看这一篇就够了open in new window

因此容器都通过接口链接到网关 docker0,实现了相互通信。

img
img

通过 --link 可以在 hosts 配置中添加映射,实现使用容器名称代替 ip:

docker run -d -P --name ubuntu02 --link ubuntu03 ubuntu

在 ubuntu02 中可以使用 ping ubuntu03 。不建议使用 link

自定义网络

docker network 相关命令: connect Connect a container to a network create Create a network disconnect Disconnect a container from a network inspect Display detailed information on one or more networks ls List networks prune Remove all unused networks rm Remove one or more networks

docekr network ls 查看网络模式,包括 bridge 桥接 docker(默认),none 不配置网络,host 和宿主机共享网络。自定义网络:

docker network create --driver bridge --subnet 192.168.0.0/16 --getway 192.168.0.1 mynet

自定义的网络不需要 --link 也可以使用容器名称 ping 。不同集群使用不同的网络。docekr network connect mynet container 将容器加入到某个网络中

Docker Compose

Docker Compose 文档open in new window。准备好 dockerfile, docker-compose.yml(如下),使用 docker-compose up 运行。

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

version 对应 docker 版本,services 中配置各个容器,如上有 webredis 两个项目。 build 即通过 docker build 构建镜像,或者使用 image 使用已有镜像。
其他参数open in new window 比较常用的有 depends_on, deploys, expose 等。

后台运行 docker-compose up -d 包括网络配置,自动下载依赖镜像, 建立 dockerfile 镜像,启动 全部服务 。使用 docker-compose down 结束所有服务(容器)。docker-compose ps 查看所有 compose 进程。 docker-compose run web env

docker Swarm

文档open in new window 小规模服务可以使用 swarm 实现,规模大时考虑 k8s

docker swarm Commands: ca Display and rotate the root CA init Initialize a swarm join Join a swarm as a node and/or manager join-token Manage join tokens leave Leave the swarm unlock Unlock swarm unlock-key Manage the unlock key update Update the swarm

Swarm 分为主节点、副节点 初始化 Swarm 主节点:docker swarm init --advertise-addr 私网 ip 其他机器加入节点:docker swarm join --token xxxxx 在主节点上通过 docker swarm join-token managerdocker swarm join-token worker 生成 token。

保证至少 2 个管理节点存活才可用。集群需要 3 个以上主节点。

docker service

启动服务:docker service create -p 8888:80 --name new_service images ,服务会在随机的集群机器上运行。

服务扩缩容:docker service update --replicas 3 new_service new_service 在 3 个机器上运行 new_service 服务。等于 docker service scale new_service=3

其他相关命令:docker stack 部署集群,docker secret 证书相关,docker config

其他

jupyter notebook 再使用的时候需要配置:

jupyter notebook --generate-config

#docker 中使用 ipython 生成密码
In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:  # 这个密码再后来宿主机登陆时候需要
Verify password:   # 宿主机登陆 localhost:8888
Out[2]: 'sha1:38a5ecdf288b:c82dace8d3c7a212ec0bd49bbb99c9af3bae076e'

#去配置文件.jupyter/jupyter_notebook_config.py 中修改以下参数
c.NotebookApp.ip='*'                          #绑定所有地址
c.NotebookApp.password = u'刚才生成的密码'
c.NotebookApp.open_browser = False            #启动后是否在浏览器中自动打开
c.NotebookApp.port =8888  

jupyterlab

pip install jupyterlab

jupyter lab --generate-config 
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ jupyterlab-language-pack-zh-CN jupyterlab-lsp==3.2.0 jupyter-lsp==1.1.1 jupyterlab_code_formatter python-language-server[all] black isort

# 修改默认终端
vim /root/.jupyter/
c.ServerApp.terminado_settings = {'shell_command' : ['/bin/bash']}
# 设置登录密码
jupyter server password  

jupyter lab --notebook-dir=/ --ip='*' --port=8888 --allow-root --no-browser

其他插件open in new window

jupyterhub

安装:

sudo apt-get -y install nodejs npm
conda install -c conda-forge jupyterhub
conda install jupyterlab notebook

jupyterhub --generate-config 生成配置文件

修改配置文件信息:

c.JupyterHub.ip="0.0.0.0"
c.JupyterHub.port=443

c.JupyterHub.admin_access = True
c.JupyterHub.admin_user= {"kevin"}

c.Authenticator.admin_users = {"kevin"}

c.Spawner.notebook_dir = "~"  # 用户登录时,显示的 root dir

jupyterhub 使用的用户和 linux 是对应的,添加用户:

sudo useradd stu01
passwd stu01

使用 root 启动服务:

sudo su
jupyterhub

ubuntu 下移动默认储存位置

sudo service docker stop
sudo vim /etc/systemd/system/multi-user.target.wants/docker.service
# 将 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock 修改以下内容:
ExecStart=/usr/bin/dockerd --graph=/home/data/docker --storage-driver=overlay2

重启 docker 服务

sudo systemctl daemon-reload
sudo systemctl restart docker

其他参考

Docker 部署深度学习环境open in new windownvidia dockeropen in new windownvidia docker 及训练环境配置文档open in new window

上次编辑于:
贡献者: kevinng77