Docker User Guide

Docker 简明用户指引

简介

要使用 Docker 容器,除了安装 Docker 运行环境外,还需要准备相应的镜像文件。镜像文件由不同的只读文件系统层构成。同一镜像可以创建运行不同的容器,相对镜像而言,每个容器均有各自的读写文件层,这些文件层会随着容器一起删除。

需要读写的文件可以放在宿主机,通过卷(Volumn)挂载入容器,容器删除后,文件并不会被删除。写入卷的内容默认在宿主机的 /mnt 目录下。

常用场景

  • 解决环境版本冲突。

安装

阿里云和中国版 Azure 的虚机上安装 Docker 有专属加速镜像,详细参考相关章节。

Ubuntu

Ubuntu 上有多种渠道安装 Docker,比如:Docker 官方安装包、Snap 版本、发行版自带版本。

# 通过 Docker 官方安装包安装
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

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 update && sudo apt install docker-ce

# 相关路径
/var/lib/docker  # 镜像存放处
# 通过 Snap 安装
snap install docker
snap start docker --enable   # 启动 docker,并添加随机启动

snap services docker                # 确认服务状态
service snap.docker.dockerd status  # 确认服务详情 

snap refresh docker          # 更新 Snap 安装的 docker 服务

# Snap 安装的 Docker 服务相关路径
/snap/docker/current         # 安装目录
# 服务启动配置文件
/etc/systemd/system/snap.docker.dockerd.service
# 配套 AppArmor 配置文件
/var/lib/snapd/apparmor/profiles/snap.docker.docker
/var/lib/snapd/apparmor/profiles/snap.docker.dockerd
# 默认 Unix Socket 文件路径
unix:///var/snap/docker/current/run/docker/libcontainerd/docker-containerd.sock

阿里云虚机

在阿里云虚机上可以使用以下命令安装 Docker 环境:

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

Azure 中国版虚机

curl -fsSL https://get.docker.com | bash -s docker --mirror AzureChinaCloud

常用设置

配置 docker 仓库镜像

国内用户使用 docker 之前需要配置国内的 Docker Hub 加速镜像服务,以下为国内常用镜像地址:

Docker CE 桌面用户在 Preference 的 Daemon 标签下设置:

Linux 服务器用户参考以下方法:

# Ubuntu 16.04+
sudo mkdir -p /etc/docker
sudo vim /etc/docker/daemon.json # 打开 Docker 配置文件

# 添加镜像相关配置 
"registry-mirrors": ["https://<阿里云分配前缀>.mirror.aliyuncs.com", "https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com"]

# 重新加载所有 Service 的配置文件后,重启 Docker 服务
sudo systemctl daemon-reload
sudo systemctl restart docker

启用远程管理

docker 默认跑在 Unix Socket 上,只有本机 docker 命令行可以与之交互,需要打开端口侦听启用远程管理。编辑 /etc/docker/daemon.json 文件,加入 hosts 配置项目。

{
    "hosts": ["fd://", "tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"],
    "ip": "0.0.0.0",
    "registry-mirrors": ["https://<阿里云分配前缀>.mirror.aliyuncs.com", "https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com"]
}

Snap 方式安装的 Docker 服务,socket 路径稍有不同,参考以下例子设置 hosts 参数:

{
  "hosts": ["unix:///var/snap/docker/current/run/docker/libcontainerd/docker-containerd.sock", "tcp://0.0.0.0:2375"],
}

重启 docker 服务后,用 netstat -ltpn 可以观察到 dockerd 进程开始侦听 2375 端口:

# netstat -ltpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      1353/nginx -g daemo
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1353/nginx -g daemo
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1912/sshd       
tcp6       0      0 :::443                  :::*                    LISTEN      1353/nginx -g daemo
tcp6       0      0 :::2375                 :::*                    LISTEN      27493/dockerd   
tcp6       0      0 :::80                   :::*                    LISTEN      1353/nginx -g daemo

管理远程 docker 服务

通过 SSH 隧道将 Linux 的 docker 运行端口映射到本地,设置如下环境变量后,即可在本机管理远程 docker 服务。

# Linux / Mac
export DOCKER_HOST="tcp://localhost:2375"

# Windows CMD
set DOCKER_HOST=tcp://localhost:2375

# Windows PowerShell
$env:DOCKER_HOST = "tcp://localhost:2375"

Windows 平台可以独立安装客户端,下载、解压后,将 docker.exe 复制到 C:\Windows\system32 下即可使用 docker 命令。

常用命令

环境确认

docker info    # 确认 Docker 及相关系统层面状态。
docker version # 确认客户端及服务器版本。
docker --version   # 检查 Docker 客户端版本。
docker-compose --version
docker-machine --version

Docker Machine

docker-machine ls # 所有可以发送 docker 命令的虚机。
docker-machine [start|stop|status] <Machine Name>
docker-machine env <Machine Name> # 获取指定虚机环境设置命令用于配置 docker 客户端命令行环境变量。
docker-machine ip <Machine Name>

docker-machine ssh <Machine Name> # 连接到指定虚机的 Console。

docker

用户主要使用 docker 命令行和 docker 服务进行交互。

# 镜像管理
docker pull <镜像名称>           # 从 Docker 仓库获取镜像。
docker pull <镜像名称>:<tag>     # 从 Docker 仓库获取指定版本镜像。

docker images                       # 列出所有已安装镜像名称。
docker rmi <Image Name>|<Image ID>  # 删除指定镜像。
docker build . -t <Image Name>      # 在当前目录编译 Dockerfile 生成镜像。

# 更新所有本地镜像已安装版本对应的最新版。
docker images | grep -v REPOSITORY | awk '{print $1":"$2}' | xargs -L1 docker pull # Linux / Mac
# 清理升级后无用的历史镜像
docker rmi $(docker images -q -f dangling=true)

# 指定镜像,配置端口、挂载读写卷,启动一个新容器。
docker run -p <Host 端口>:<容器内部端口> -v <宿主机路径>:<容器内部路径> --name <容器别名> <镜像名称>
# 容器管理
docker ps    # 列出运行中的容器。
docker ps -a # 列出所有容器。

docker rm <容器名称>|<容器 ID>      # 删除容器。
docker rm -v <容器名称>|<容器 ID>   # 删除容器及相关卷。
docker start <容器名称>|<容器 ID>   # 启动容器。
docker restart <容器名称>|<容器 ID> # 重启容器。
docker stop <容器名称>|<容器 ID>    # 停止容器。

docker top <容器名称>|<容器 ID> [<ps 开关项>]    # 查看容器内的进程。
docker inspect <容器名称>|<容器 ID>             # 巡查容器配置,比如:卷 Volume 映射到宿主机的路径。


docker exec -it <容器名称>|<容器 ID> bash   # 进入容器巡查,完毕后执行 exit 退出 bash 及容器。
docker attach <容器名称>|<容器 ID>          # 进入容器,查看主进程输出,用 Ctrl + p, Ctrl + q 快捷键序列退出

# 容器宿主机传递文件
docker cp <容器名称>|<容器 ID>:<容器内 Path> <宿主机 Path>
docker cp <宿主机 Path> <容器名称>|<容器 ID>:<容器内 Path>
# 卷管理
docker volume prune # 清理所有无用的卷。

镜像构建

docker 命令除了管理镜像、容器和卷,还能通过 Dockerfile 文件编译自定义镜像:

命令 功能
docker build -t <Vender Name>/<Tag Name> -f <Docker File> . 根据当前目录的 dockerfile 文件编译镜像
   
# 网络管理
docker network create --driver bridge <子网名称,比如:docker_network>
docker network create -d bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 <子网名称,比如:default_net>

常见用例

Apache

# 在 8088 端口,以 Daemon 方式运行 Apache 2.4 容器,挂载宿主机源码目录到容器内 DocRoot: /usr/local/apache2/hcdocs。
docker run -d -p 8088:80 --name apache24_shib --restart=always -v <宿主机源码目录>:/usr/local/apache2/htdocs httpd:2.4

# 进入 Docker 命令行交互模式。
docker exec -it apache24_shib bash   # 退出交互模式:快捷键 Ctrl + p 然后紧跟 Ctrl + q

# 检查容器详情,如:挂载的卷
docker inspect apache24_shib         
// docker inspect apache24_shib 返回值。
{
...
"Mounts": [
    {
        "Type": "bind",
        "Source": "/content/d_static_mG3klP9rd",
        "Destination": "/usr/local/apache2/htdocs",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],
...
}

NodeJS

# 挂载目录,启动 docker 后,在容器的 /var/www 目录下执行 npm start。
docker run -d -p 3000:3000 --name node_express --restart=always -v <宿主机源码存放目录>:/var/www -w /var/www node:9.0 npm start

 

 

nginx + php:5-fpm

nginx 默认配置文件没有开启 FastCGI 支持及 PHP 集成,需要挂载自定义配置文件或自定义 nginx 镜像实现 LNMP 环境:

## nginx 配置文件: default.conf,覆盖镜像中的 /etc/nginx/conf.d/default.conf 文件
 # 想定代码放在 /var/www/html 目录中
 #
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /var/www/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /var/www/html;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header REMOTE_ADDR $remote_addr;

        root           /var/www/html;
        fastcgi_pass   php_5_fpm:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}

启动 php:5-fpm 容器:

# 启动基于最新 php:5-fpm 镜像的容器

# 停止并删除已有容器
docker stop php_5_fpm
docker rm -v php_5_fpm

# 挂载代码目录,启动容器,PHP 应用存放的路径,FastCGI 服务需要读取权限
docker run -d -v /var/www/html:/var/www/html --name php_5_fpm --restart=always php:5-fpm

# 进入容器巡查,用 exit 命令可以退出容器
docker exec -it php_5_fpm bash

启动 nginx 容器(基于自定义镜像): 

# 镜像定义文件 Dockerfile
FROM nginx:1.15
COPY conf/default.conf /etc/nginx/conf.d/default.conf
# 停止并删除既存容器、删除既存镜像
docker stop nginx_php_5_fpm
docker rm -v nginx_php_5_fpm
docker rmi nginx_php_5_fpm

# 生成自定义镜像
docker build . -t nginx_php_5_fpm

# 启动容器
docker run -d -it -p 60080:80 -v /var/www/html:/var/www/html --name nginx_php_5_fpm --link php_5_fpm:php_5_fpm --restart=always nginx_php_5_fpm

# 查看容器内进程
docker top nginx_php_5_fpm -ef

# 进入容器巡查,用 exit 命令可以退出容器
docker exec -it nginx_php_5_fpm bash

启动 nginx 容器(基于官方镜像),需要部署自定义 nginx 配置文件,以下想定部署位置为 /var/www/conf/default.conf。

# 停止并删除既存容器、删除既存镜像
docker stop nginx_php_5_fpm
docker rm -v nginx_php_5_fpm


# 启动容器
docker run -d -it -p 60080:80 -v /var/www/conf/default.conf:/etc/nginx/conf.d/default.conf -v /var/www/html:/var/www/html --name nginx_php_5_fpm --link php_5_fpm:php_5_fpm --restart=always nginx:1.15

# 查看容器内进程
docker top nginx_php_5_fpm -ef

# 进入容器巡查,用 exit 命令可以退出容器
docker exec -it nginx_php_5_fpm bash

 

自定义 nginx 镜像

# 镜像定义文件 Dockerfile
FROM nginx:1.14.0
COPY conf/<site-domain>.conf /etc/nginx/conf.d/
COPY ~/nginx/headers-more-nginx-module/nginx-module-headersmore_1.14.0-1~xenial_amd64.deb /tmp
COPY ~/nginx/nginx-http-shibboleth/nginx-module-shibboleth_1.14.0-1~xenial_amd64.deb /tmp
RUN dpkg -i /tmp/nginx-module-headersmore_1.14.0-1~xenial_amd64.deb /tmp/nginx-module-shibboleth-dbg_1.14.0-1~xenial_amd64.deb

 

官方 nginx 镜像 + 宿主机 PHP

利用 nginx 官方镜像,可以让不同版本的 nginx 服务同时运行,并访问宿主机上的其他服务,具体方法如下:

按需准备 nginx.conf,Ubuntu 需要把 nginx 运行在 www-data 用户下:

# Ubuntu 版 nginx.conf,user 改为 www-data,否则访问 PHP Unix socket 有权限问题。
user  www-data;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  300;

    gzip  on;

    client_max_body_size 64m;

    include /etc/nginx/conf.d/*.conf;
}

准备应用配置文件,不同的应用,所需配置稍有不同:

# Drupal 应用配置(无 HTTPS)
server {
    listen 80;
    listen [::]:80;

    root        <Application Root Path>;
    server_name <domain name>;

    index index.php index.html;

    # 屏蔽容易泄漏系统信息的文件
    location ~* ^/(readme\.txt|CHANGELOG\.txt|web\.config|robots\.txt|LICENSE\.txt|README.mk) {
        return 404;
        #deny all;
    }

    location ~* \.(eot|ttf|woff|otf)$ {
        add_header Access-Control-Allow-Origin *;
    }

    location / {
        try_files $uri /index.php?$query_string;
        # 屏蔽沙俄黑客 IP
        deny 180.97.0.0/16; 
    }

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;
    }

    location ~ \.php$ {
        fastcgi_pass   unix:/run/php/php7.0-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    error_log   /var/log/nginx/<domain name>-error.log warn;
    access_log  /var/log/nginx/<domain name>-access.log  main;

    fastcgi_read_timeout 300s;
}

容器更新、启动命令: 

# 停止并删除既存容器
docker stop nginx_local_php_fpm
docker rm nginx_local_php_fpm

# 挂载宿主机 Socket 文件,nginx 配置文件
export PHP_APPLICATION=$(pwd)
export NGINX_MODULE_PATH=$(pwd)
docker run -d -p 60601:80 -v /run/php/php7.0-fpm.sock:/run/php/php7.0-fpm.sock -v $PHP_APPLICATION/docs:$PHP_APPLICATION/docs -v $NGINX_MODULE_PATH/nginx-http-shibboleth/usr/lib/nginx/modules/ngx_http_shibboleth_module.so:/usr/lib/nginx/modules/ngx_http_shibboleth_module.so -v $NGINX_MODULE_PATH/headers-more-nginx-module/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so:/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so -v $PHP_APPLICATION/logs/:/var/log/nginx/ -v $PHP_APPLICATION/conf/nginx.conf:/etc/nginx/nginx.conf -v $PHP_APPLICATION/conf/site.conf:/etc/nginx/conf.d/site.conf --name nginx_local_php_fpm --restart=always nginx:1.14.0

自定义 PHP 镜像

 

常见错误

Ubuntu 上的 docker 服务默认把日志输出到 /var/log/syslog 中,发生错误的时候可以检查日志内容修复。

File not found

nginx + php:fpm 构建的系统,访问 PHP 资源,返回 File not found. 错误。Nginx Docker 镜像中的 default.conf 默认配置的脚本路径并不存在:

fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;

需要修复为

# 如果相关 server 或 location 的配置里定义了 root,则使用 $document_root 变量
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

# 否则使用绝对路径,需要确保 FastCGI 服务可以访问到相关路径下的脚本文件
# 如果 FastCGI 服务也是运行在 Docker 中,需要把相关路径挂入 Docker
fastcgi_param  SCRIPT_FILENAME  /var/www/html$fastcgi_script_name;

自定义 hosts 后无法启动

在 /etc/docker/daemon.json 中配置了 hosts,就无法启动 docker,/var/log/syslog 中出现 unable to configure the Docker daemon with file /etc/docker/daemon.json: the following directives are specified both as a flag and in the configuration file: hosts 之类的错误。

Ubuntu 16.04 上安装 Docker CE,会在服务启动脚本中默认添加 -H 开关项,这和 daemon.json 中的 hosts 配置冲突,导致此类错误。

# 打开服务启动脚本
sudo vim /lib/systemd/system/docker.service
# 找到
ExecStart=/usr/bin/dockerd -H fd://
# 修改为
ExecStart=/usr/bin/dockerd

# 存盘后执行以下命令,刷新 Service 缓存
systemctl daemon-reload

 

Snap 安装版无法修改配置

Snap 安装的 dokcer 自定义后无法启动,/var/log/syslog 中出现 apparmor="DENIED" operation="open" profile="snap.docker.dockerd" name="/etc/docker/daemon.json" 之类的错误。

Snap 安装的软件都有对应的 AppArmor 白名单,配置哪些目录可以读写。

vim /var/lib/snapd/apparmor/profiles/snap.docker.dockerd
# 找到添加 #include <abstractions/openssl>,在下面添加:
/etc/docker/** r,

# 重启 AppArmor 服务后即可启动 docker 服务。
service apparmor restart

自定义镜像 apt 报错

执行 apt 命令的时候无法初始化对话框会报如下错误:

debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline

设置禁用交互的环境变量即可解决:

# 加在 Dockerfile 文件中
ENV DEBIAN_FRONTEND noninteractive

 

Author: njun
njun's picture
Updated: 2018/09/27