Docker: 码头工人 (搬砖高手)
Life is short. We use Docker.
版本:Ubuntu 16.04 LTS,Docker 19.03.1, build 74b1e89e8a
安装 docker
# 本节参考 Docker 官方文档:
# https://docs.docker.com/install/linux/docker-ce/ubuntu/
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates gnupg-agent software-properties-common
# 添加 Docker 的官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 设置稳定存储库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 测试
sudo docker run hello-world
执行步骤
Docker 的 5 个基本部件:Docker Client,Docker Daemon,Docker Image,Docker Container,Docker Hub (Registry)。
- Docker Client 联系 Docker Daemon。
- Docker Daemon 从 Docker Hub 中提取名为 hello-world 的 Docker Image。
- Docker Daemon 从该 Docker Image 创建了一个 Docker Container,用于生成当前输出。
- Docker Daemon 将该输出流式传输到 Docker Client。
卸载 docker
sudo apt-get purge docker-ce
# 删除相关文件
sudo rm -rf /var/lib/docker
基本命令
docker --version
sudo docker info
USER 加入 docker 组
# 用户访问 Docker daemon,需要管理员权限 sudo,或加入 docker 组
# Docker 安装完成会建立 docker 组
cat /etc/group | grep docker
cat /etc/group | sed -n '/docker/p' | awk -F ":" '{print $1}'
# 加入 docker 组
sudo usermod -aG docker $USER
# 重启
echo -e "USER_PASSWDn" | sudo -S reboot
# 检查是否已加入 docker 组
groups | grep docker
常用命令
# 详细版本信息
docker version
# 列出 image
docker image ls --all
docker images
docker images -a
# 列出 container
docker container ls
docker container ls --all
docker container ls -aq
# 搜索
docker search python
docker search anaconda
# 显示运行的容器
docker ps
docker ps -a
上图介绍了基本的 docker 命令流,我们从一个小例子开始。
docker pull busybox
docker images
docker run busybox pwd
docker run busybox echo 'I love Python'
docker pull + docker run 能够让我们迅速、便捷地运行一个 docker image。
dock pull
格式:docker pull [OPTIONS] NAME[:TAG|@DIGEST]
常用参数
-a, --all-tags # 下载 repository 中所有 tagged images
--disable-content-trust # 跳过镜像验证 (默认跳过)
-q, --quiet # 取消进度显示
docker run
格式:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
常用参数:
-it # -i 交互模式,-t 分配伪 tty
-P # 随机端口映射
-p # 指定端口映射,主机端口:容器端口
--name # 指定容器名称
-e, --env list # 设置环境变量
-v, --volume list # 本地文件夹与 container 文件夹绑定
-w # 容器内的工作目录
--dns # 设置 DNS 服务器
-h, --hostname string # 设置容器的 hostname;
--rm # 自动移除容器
--env-file list # 从文件读入环境变量
-d # 后台运行容器
-m # 设置容器内存上限
--link list # 链接到其他容器
拉取并执行镜像
# 通过示例展示 docker run 的用法
# 例一:busybox
docker pull busybox
# -it 表示可以交互式地输入命令
docker run -it busybox sh
# 例二:tensorflow
docker pull tensorflow/tensorflow
# --rm 在容器退出时自动清理容器并删除文件系统
# 默认 python=2.7,tensorflow=1.14.0
docker run -it --rm tensorflow/tensorflow
# 退出后运行
docker container ls --all #不显示 tensorflow,说明 --rm 功能
# 定制 python=3.5,tensorflow=1.11.0
docker pull tensorflow/tensorflow:1.11.0-rc2-py3
# --name 指定 container 名称 [a-zA-Z0-9][a-zA-Z0-9_.-]
# 命令最后指定 python 解释器,后面可以接 .py 文件
docker run -it --rm --name tf_11-py_35 tensorflow/tensorflow:1.11.0-rc2-py3 python
# -v 将本地目录映射 container 目录
# $PWD:/dl 将本地当前目录 $PWD,挂载到 container 的 /dl 目录下
# -v 命令可以实现数据的完全共享、读写同步
# 如果想要禁止 container 的写权利,可以 -v $PWD:/dl:ro
docker run -it --rm --name tf_11-py_35 -v $PWD:/dl tensorflow/tensorflow:1.11.0-rc2-py3 sh # bash 窗口操作
# -p 指定端口映射,可以在本地 http://127.0.0.1:8888 访问 container
# 本地的 $HOME 目录将被挂载到 /tf 目录下
docker run -it --rm -v $HOME:/tf -p 8888:8888 tensorflow/tensorflow:nightly-py3-jupyter
# 例三:jupyter
# 将 container 的 8888 端口映射到本机 127.0.0.1 的 666 端口,
# http://localhost:666/
docker run -p 127.0.0.1:666:8888 jupyter/scipy-notebook:17aba6048f44
删除 Container,Image
# 删除 container
# 用 CONTAINER ID 或名字
docker rm c1b9ce7f84d5
docker rm tf_11-py_35
docker rm -f $(docker ps -aq)
# 删除所有状态为 exited 的容器
docker rm $(docker ps -a -q -f status=exited)
# 删除镜像 [需要删除镜像的容器]
# 用 IMAGE ID 或 REPOSITORY:TAG
docker rmi [OPTIONS] IMAGE [IMAGE...]
容器操作
# 启动 container
docker start d3c0ea01414d
# 停止 container
docker stop d3c0ea01414d
# kill container
docker kill -s kill d3c0ea01414d
# 重启 container
docker restart d3c0ea01414d
# commit
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
创建镜像
首先是 docker 的核心之一,Dockerfile —— 制作 Docker Image 的说明书。
Dockerfile
先看三个小例子
例一:ubuntu 16.04 LTS (xenial) 官方 Dockerfile
FROM ubuntu:xenial
RUN apt-get update && apt-get install -y --no-install-recommends
ca-certificates
curl
netbase
wget
&& rm -rf /var/lib/apt/lists/*
RUN set -ex;
if ! command -v gpg > /dev/null; then
apt-get update;
apt-get install -y --no-install-recommends
gnupg
dirmngr
;
rm -rf /var/lib/apt/lists/*;
fi
例二:jupyter minimal-notebook 官方 Dockerfile
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
ARG BASE_CONTAINER=jupyter/base-notebook
FROM $BASE_CONTAINER
LABEL maintainer="Jupyter Project <[email protected]>"
USER root
# Install all OS dependencies for fully functional notebook server
RUN apt-get update && apt-get install -yq --no-install-recommends
build-essential
emacs
git
inkscape
jed
libsm6
libxext-dev
libxrender1
lmodern
netcat
pandoc
python-dev
texlive-fonts-extra
texlive-fonts-recommended
texlive-generic-recommended
texlive-latex-base
texlive-latex-extra
texlive-xetex
tzdata
unzip
nano
&& rm -rf /var/lib/apt/lists/*
# Switch back to jovyan to avoid accidental container runs as root
USER $NB_UID
例三:nvidia/cuda 的 cuda9.0+cudnn7.6 官方 Dockerfile
ARG IMAGE_NAME
FROM ${IMAGE_NAME}:9.0-devel-ubuntu16.04
LABEL maintainer "NVIDIA CORPORATION <[email protected]>"
ENV CUDNN_VERSION 7.6.3.30
LABEL com.nvidia.cudnn.version="${CUDNN_VERSION}"
RUN apt-get update && apt-get install -y --no-install-recommends
libcudnn7=$CUDNN_VERSION-1+cuda9.0
libcudnn7-dev=$CUDNN_VERSION-1+cuda9.0
&&
apt-mark hold libcudnn7 &&
rm -rf /var/lib/apt/lists/*
Dockerfile 指令解析
# FROM 指定基础的 Docker Image,可以来自官方远程仓库,也可位于本地仓库
# 没有指定镜像标签,则默认使用 latest 标签
FROM ImageName
# 创建镜像的用户
MAINTAINER UserName
MAINTAINER "Jupyter Project <[email protected]>"
LABEL maintainer="Jupyter Project <[email protected]>"
# 每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像,
# 后续的 RUN 都以之前 RUN 提交的镜像为基础,
# 镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建。
RUN command1 && command2 && ...
# ENV 设置环境变量,后续 RUN 指令使用,并在容器运行时保留
ENV KEY value # 只能设置一个变量
ENV KEY=value # 允许一次设置多个变量
# ARG 定义了一个变量,能让用户可以在构建期间使用 docker build 命令和其参数 --build-arg 对这个变量赋值
# 例三中,可以用如下命令传入参数:
# docker build --build-arg IMAGE_NAME=nvidia/cuda Dockerfile
ARG <name>[=<default value>]
# COPY,ADD 将宿主目录下 [或者远程文件 URLS] 的文件拷贝进镜像
# ADD 可自动处理 URL 和解压 tar 包,COPY 不能指定远程文件 URLS
COPY src dest
ADD src dest
# WORKDIR 指定在创建 container 后,终端默认登录的 container 的工作目录,未指定则在根目录
WORKDIR
# CMD 指定一个容器启动时要运行的命令
# DockerFile 中可以有多个 CMD 指令,但是只有最后一个生效
# 如果用户启动容器时指定了运行的命令,则会覆盖掉 CMD 指定的命令。
# 如:docker run -it --rm --name tf_11-py_35 -v $PWD:/dl tensorflow/tensorflow:1.11.0-rc2-py3 sh
CMD
CMD echo $HOME
CMD [ "sh", "-c", "echo $HOME" ]
# ENTRYPOINT 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
# 每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效。
ENTRYPOINT
# 指定对外的端口号
# 默认为 8888,对于 docker run 指令中的 -p 参数产生影响
EXPOSE
# 容器数据卷,用于数据保存和持久化工作
VOLUME
VOLUME /docker_data
docker build
格式:
docker build [OPTIONS] PATH | URL | -
docker image build [OPTIONS] PATH | URL | -
常用参数:
-f, --file string # Name of the Dockerfile (Default is 'PATH/Dockerfile')
--build-arg list # Set build-time variables
--compress # Compress the build context using gzip
-t, --tag list # Name and optionally a tag in the 'name:tag' format
--rm # Remove intermediate containers after a successful build (default true)
下面通过两个例子展示编写 Dockerfile 及创建镜像的过程。
例一:定制一个简单的 tensorflow 工作环境
python==3.6,numpy==1.14.2,tensorflow==1.12.0
Dockerfile
FROM python:3.6
WORKDIR /tensorflow
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD [ "python", "./tf.py" ] # tf.py 是用户自己的 .py 文件
requirements.txt
numpy==1.14.2
tensorflow==1.12.0
创建镜像
# build image
docker image build -t py36tf:0.0.1 .
创建过程如下,注意 docker 镜像是分层创建的,每次执行命令都会产生新的镜像
Step 1/6 : FROM python:3.6 #下载 python:3.6 的 image
---> 1c515a624542
Step 2/6 : WORKDIR /home/lizhongding/codeLinux/dp
---> Running in 4c77d0b86f09 # 设置工作目录,在容器 4c77d0b86f09 中运行
Removing intermediate container 4c77d0b86f09 # 移除中间 container 4c77d0b86f09
---> 708c4ca1c9dd
Step 3/6 : COPY requirements.txt ./ # 拷贝 requirements.txt,到镜像的根目录下
---> ee1bde24d6b1
Step 4/6 : RUN pip install --no-cache-dir -r requirements.txt # 安装 numpy==1.14.2,tensorflow==1.12.0 及相关依赖
---> Running in ed988a6febfe
Removing intermediate container ed988a6febfe
---> 22348b6a3d9f
Step 5/6 : COPY . . # 将当前目录的文件拷贝进镜像的当前目录
---> 0117e5a5ae26
Step 6/6 : CMD python tf.py # 执行命令 python tf.py
---> Running in 4edf63c674f8
Removing intermediate container 4edf63c674f8
---> 1fde2f3a630f
Successfully built 1fde2f3a630f # 建立镜像 1fde2f3a630f
Successfully tagged py36tf:0.0.1 # REPOSITORY:TAG = py36tf:0.0.1
上传镜像
# 在 hub.docker.com 上注册帐号
docker login
# 建立本地映像与存储库的关联
docker image tag ImageName UserName/repository[:tag] # 默认 tag 为 latest
docker image tag py36tf:0.0.1 lizhongding/py36tf:0.0.1
docker image push lizhongding/py36tf:0.0.1
# 成功后,可在 hub.docker.com 上查看 image
# 相似依赖的镜像,再上传时会尽量利用已有镜像
docker image tag py36tf:0.0.2 lizhongding/py36tf:0.0.2
docker image push lizhongding/py36tf:0.0.2
例二:基于 cuda_9.0,cudnn_7.0 定制可训练对抗生成网络(GAN)的环境
下载最新版的 ananconda3,利用 conda 安装以下 package:
- jupyter,Pillow,matplotlib,pyyaml,python-lmdb,scikit-learn,tqdm,
- tensorflow-gpu=1.11.0,cudatoolkit=9.0,python=3.6
Dockfile 构建如下。[参考项目:okwrtdsh/anaconda3]
FROM nvidia/cuda:9.0-cudnn7-devel
USER root
ENV DEBIAN_FRONTEND=noninteractive
LANG=C.UTF-8
LC_ALL=C.UTF-8
PATH=/opt/conda/bin:$PATH
NOTEBOOK_DIR=/src/notebooks
NOTEBOOK_IP=0.0.0.0
NOTEBOOK_PORT=8888
RUN apt-get update -qq
&& apt-get upgrade -y
&& apt-get install --no-install-recommends -y
curl grep sed dpkg wget bzip2 ca-certificates
libglib2.0-0 libxext6 libsm6 libxrender1
git mercurial subversion
libgtk2.0-0
# 最新版 annaconda3 安装
&& echo 'export PATH=/opt/conda/bin:$PATH' > /etc/profile.d/conda.sh
&& ANACONDA_INSTALL_SCRIPT='Anaconda3-2019.03-Linux-x86_64.sh'
&& wget --quiet https://repo.continuum.io/archive/$ANACONDA_INSTALL_SCRIPT -O ~/anaconda.sh
&& /bin/bash ~/anaconda.sh -b -p /opt/conda
&& rm ~/anaconda.sh
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*
RUN conda config --add channels conda-forge
&& conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
&& conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
&& conda install -y --quiet
jupyter
Pillow
matplotlib
pyyaml
python-lmdb
scikit-learn
tqdm
tensorflow-gpu=1.11.0
cudatoolkit=9.0
python=3.6
&& conda install -c menpo opencv3
&& conda clean -tipsy
# 安装 tini
ENV TINI_VERSION=v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini
RUN chmod +x /usr/bin/tini
ENTRYPOINT [ "/usr/bin/tini", "--" ]
CMD jupyter notebook
--notebook-dir=${NOTEBOOK_DIR}
--ip=${NOTEBOOK_IP}
--port=${NOTEBOOK_PORT}
--NotebookApp.token=''
--no-browser
--allow-root
上传镜像
docker image build -t :0.0.1 .
docker login
docker image tag gan:0.0.1 lizhongding/gan:0.0.1
docker image push lizhongding/gan:0.0.1
docker save and docker load
docker 镜像可以不必在 docker hub 存取,也可本地存取。
格式:docker save [OPTIONS] IMAGE [IMAGE...]
参数:--output , -o
示例:
docker save busybox > busybox.tar
ls -sh busybox.tar
# 2.7M busybox.tar
docker save --output busybox.tar busybox
ls -sh busybox.tar
# 2.7M busybox.tar
# 用 gzip 压缩
docker save gan:0.0.1 | gzip > gan:0.0.1.tar.gz
格式:docker load [OPTIONS]
参数:--input , -i
docker load < busybox.tar.gz
docker load --input fedora.tar
docker images
nvidia-docker
当镜像中包含 gpu 的操作时,需要 nvidia-docker 来 run 镜像。
# 卸载 nvidia-docker 1.0
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
sudo apt-get purge nvidia-docker
# 安装依赖
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
# 用 nvidia-smi 测试镜像
docker run --gpus all nvidia/cuda:9.0-base nvidia-smi
# 安装 nvidia-docker2
sudo apt-get install nvidia-docker2
sudo pkill -SIGHUP dockerd
# 用 tensorflow-gpu 进行测试
# 注意 tensorflow/tensorflow 镜像中支持的 cuda 版本要与本机 cuda 版本匹配
# 最新版要求 cuda>=10.0
nvidia-docker run -it --rm --name tf_gpu tensorflow/tensorflow:1.11.0-devel-gpu-py3
python -c "import tensorflow as tf; tf.enable_eager_execution(); print(tf.reduce_sum(tf.random_normal([1000, 1000])))"
docker exec
在正在运行的容器中运行命令
格式:docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
参数:
--detach, -d # 分离模式:在后台运行命令
--detach-keys # 覆盖用于分离容器的键序列
--env, -e # 设置环境变量
--interactive, -i # 即使没有连接,也要保持STDIN打开
--privileged # 为命令提供扩展权限
--tty, -t # 分配伪TTY
--user, -u # 用户名或UID(格式:<name | uid> [:<group | gid>])
--workdir, -w # 容器内的工作目录
示例:
docker restart tf_gpu
# 访问 container
docker exec -it tf_gpu bash
docker exec -it tf_gpu python
docker inspect
格式:docker inspect [OPTIONS] NAME|ID [NAME|ID...]
docker inspect tf_gpu
docker inspect 157efda75cac
# 打印 json 字符串,所有相关信息都可以查看
docker attach
将本地标准输入,输出和错误流附加到正在运行的容器。
简单说,就是可以在命令行进入并操作容器。
格式:docker attach [OPTIONS] CONTAINER
docker attach tf_gpu
docker attach f8268dbfd125
Namespace
namespace 是实现隔离的机制。
- 每个 namespace 中的进程只能影响同一个 namespace 或子 namespace 中的进程。
- /proc 包含正在运行的进程,在 container 中的 /proc 目录只能看到自己 namespace 中的进程。
- namespace 允许嵌套,父 namespace 可以影响子 namespace 的进程,子 namespace 可在父 namespace 中看到,但是具有不同的 pid。
- 网络隔离是通过网络 namespace 实现的, 每个网络 namespace 有独立的 network devices,IP addresses,IP routing tables,/proc/net 目录。这样每个 container 的网络就能隔离开来。 docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge 连接在一起。
- 每个 container 有不同的 user 和 group id, container 用户在 container 内部执行程序而非 Host 上的用户。
参考链接
https://blog.csdn.net/zmx729618/article/details/72930474blog.csdn.net | Docker Documentationdocs.docker.com
Docker Hubhub.docker.com A Docker Tutorial for Beginnersdocker-curriculum.com
okwrtdsh/anaconda3github.com
DockerFile解析www.jianshu.com
Docker容器技术之Docker filemp.weixin.qq.com
https://www.centos.bz/2016/12/dockerfile-arg-instruction/www.centos.bz CMD 容器启动命令 · Docker -- 从入门到实践yeasy.gitbooks.io docker loaddocs.docker.com
NVIDIA/nvidia-dockergithub.com