《Docker 实战》阅读笔记 (Part1:保持一台干净的机器)

** 1. Docker 的安装和使用
*** 1. 在 Ubuntu 中安装 Docker
1. [ ] 更新 ubuntu 的 apt 索引
   sudo apt-get update

2. [ ] 安装包允许 apt 通过 HTTPS 使用仓库(将下面的命令完全复制,然后粘贴到命令窗
   口)
   sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

3. [ ] 添加 Docker 官方 GPG key 
   curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

4. [ ] 设置 Docker 稳定版仓库
   sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

5. [ ] 添加仓库后,更新 apt 源索引
   sudo apt-get update

6. [ ] 安装最新版 Docker CE(社区版)
   sudo apt-get install docker-ce

7. [ ] 检查  Docker CE 是否安装正确
   sudo  docker  run hello-world

8. [ ] 为了避免每次命令 都输入 sudo,可以设置用户权限 ,注意执行后须注销重新登录
   sudo usermode -a -G docker #USER

9. [ ] 安装完成 Docker  后 ,默认已经 启动了 docker 服务
   ps aux | grep docker

10. [ ] 如需手动控制 docker  服务的启停,可以执行如下命令
    1. 启动 docker 
       sudo service  docker start

    2. 停止 docker 
       sudo service  docker  stop

    3. 重启 docker 
       sudo service docker  restart

11. [ ] Docker 镜像操作
    
    | docker 列出镜像                | docker image ls                                       |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | docker 拉取镜像                | docker image pull library/hello-world                 |                                        |
    |                                |                                                       |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | docker 删除镜像                | docker image rm 镜像名或镜像id                        |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | docker 创建容器                | docker run [option] 镜像名 [向启动 容器中传入的命令]  | docker run -dit -name=myubuntu2.ubuntu |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | docker 进入已运行的容器        | docker exec -it 容器名或容器id 进入后执行的第一个命令 | docker exec -it myubuntu2 /bin/bash    |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 查看容器--列出 所有的容器      | docker container ls --all                             |                                        |
    |                                |                                                       |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 停止一个已经运行在运行 的 容器 | docker container stop 容器名或容器id                  |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 启动一个已经停止的容器         | docker container start 容器名或容器id                 |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | kill 掉一个已经在运行的容器    | docker container kill 容器名或容器id                  |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 删除容器                       | docker container rm 容器名或容器id                    |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 将容器保存为镜像               | docker commit 容器名 镜像名                           |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 将容器打包成文件               | docker save -o 保存的文件名 镜像名                    | docker save -o ./ubuntu.tar ubuntu     |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    | 加载本地镜像                   | docker load -i ./ubuntu.tar                           |                                        |
    |--------------------------------+-------------------------------------------------------+----------------------------------------|
    
*** 2. 在 CenterOS 安装 Docker
1. [ ] 确保本机上无 Docker 环境
   yum remove docker \

2. [ ] 安装docker
   1. 安装存储库
      
      yum install -y yum-utils \
      device-mapper-persistent-data \
      lvm2

   2. 设置稳定的存储库
      yum-config-manager \
      --add-repo \
      https://download.docker.com/linux/centos/docker-ce.repo

   3. 安装当前最新 Docker-CE(社区版)
      yum install docker-ce

   4. 启动 docker 
      service docker start

   5. 停用 docker 
      service docker stop

   6. 重启 docker 
      service docker restart

   7. 测试是否安装成功
      docker run hello-world
      
** 2. 在容器中运行软件
*** 1. 从 Docker 命令中获取帮助 
    1. [ ] docker help
       docker help cp  查看 docker cp 命令的使用
*** 2. 创建和启动一个新的容器
    1. [ ] 
       docker run --detach \
          --name web nginx:latest

    2. [ ] 
       docker run -d(--detach) \
          --name mailer dockerinaction/ch2_mailer

    3. [ ] 运行交互式容器
       docker run --interactive --tty \
         --link web:web \
         --name web_test \
         busybox:latest /bin/sh 
         
         访问上面创建的 nginx 服务器
         wget -O - http://web:80 /

        输入 exit 退出程序,并停止该容器

       1. 也可以先创建一个交互式的容器,然后手动启动该容器中的一个进程,最后再分
          离终端。你可以通过 按住【Ctrl】(或【Control】)键,然后按【P】键,接着
          按【Q】键。只有使用了 --tty 选项,以上操作才会生效(退出可交换式界面,
          进程在后台运行)

       2. 启动监控器 
          docker run -it \
          --name agent \
          --link web:insideweb \
          --link mailer:insideemailer \
          dockerinaction/ch2_agent

          测试 Web 服务器,每秒会打印  System up
          
          退出操作 【Ctrl】+ 【P】+【Q】

          再次进入agent 容器 
          docker exec -it agent /watcher/watcher.sh

       3. 查看日志
          docker  logs web --follow 一直刷新日志
          【Ctrl】+ 【C】 退出日志显示

    4. [ ] 已解决的问题和 PID 命名空间
       docker run --pid  host busybox:latest ps 

       docker run -d --name webConflict nginx:latest 
       docker logs webConflict
       docker exec webConflict nginx -g 'daemon off;'

    5. [ ] 灵活的容器标识
       docker rename webid  webid-old
       docker run -d --name webid nginx

    6. [ ] docker create nginx 该容器是被停止状态创建的

    7. [ ] 将创建的容器 id 写入到文件中
       docker run 和 docker create 都可以

       docker create --cidfile /tmp/web.cid nginx

       cat /tmp/web.cid

    8. [ ] 容器的状态和依赖
       
       MAILER_CID=$(docker run -d dockerinaction/ch2_mailer)
       WEB_ID=$(docker create nginx)

       AGETN_CID=$(docker create --link $WEB_ID:insideweb \
       --link $MAILER_CID:insidemailer \
       dockerinaction/ch2_agent)

       MAILER_CID=$(docker run -d dockerinaction/ch2_mailer)
       WEB_CID=$(docker run -d nginx)
       AGENT_CID=$(docker run -d \
         --link $WEB_CID:insideweb \ 
         --link $MAILER_CID:insideemailer \
         dockerinaction/ch2_agent)

    9. [ ] 删掉所有容器
       1. 列出所有的容器 ID 
          docker ps -aq

       2. 停止所有的容器
          docker stop $(docker ps -aq)

       3. 删除所有的容器
          docker rm $(docker ps -aq)

       4. 删除所有的镜像
          docker rmi $(docker ps -aq)

       5. 删除所有不使用的镜像
          docker image prune --force --all 或者 docker image prune -f -a

       6. 删除所有停止的容器
          docker cotainer prune
    
    10. [ ] 只读文件系统
        1. 从 WordPress 镜像 创建和启动 一个容器
           docker run -d --name wp --read-only wordpress:4

        2. 查看指定的容器是否在运行
           docker inspect --format "{{.State.Running}}" wp

        3. 安装mysql数据库
           docker run -d --name wpdb \
           -e MYSQL_ROOT_PASSWORD=ch2demo \
           mysql:5

        4. 将 wordpress 链接到  mysql 中 
           docker run -d --name wp2 \
           --link  wpdb:mysql \
           -p 80 --read-only \
           wordpress:4

        5. 为可写空间创建指定的存储卷
           docker run -d --name wp3 --link wpdb:mysql -p 80 \
           -v /run/lock/apache2/ \                // 为可写空间创建
           -v /run/apache2/ \                     // 指定的存储卷
           --read-only wordpress:4 


        6. 将上面命令总结成脚本执行 
           SQL_CID=$(docker create -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5)
           
           docker start SQL_CID
           
           MAILER_CID=$(docker create dockerinaction/ch2_mailer)
           docker start $MAILER_CID
           
           WP_CID=$(docker create --link $SQL_CID:mysql -p 80 \
              -v /run/lock/apache2/ -v /run/apache2/ \
              --read-only wordpress:4)

           docker start $WP_CID 
          
           AGENT_CID=$(docker create --link  $WP_CID:insideweb \
             --link $MAILER_CID:insidemailer \
             dockerinaction/ch2_agent)

           docker start $AGENT_CID           


    11. [ ] 环境变量的注入
        docker run --env MY_ENVIRONMENT_VAR="this is a test" \
                    busybox:latest \
                    env
                    
             --env 标志或 -e 缩写,可用于注入任何环境变量。如果变量已经由镜像或
        Docker 设置,则该值被覆盖。容器 内部运行程序的方式,可以依赖于这种变量。
     
        docker create \
        --env WORDPRESS_DB_HOST=<my database hostname>
        --env WORDPRESS_DB_USER=site_admin \
        --env WORDPRESS_DB_PASSWORD=MeowMix42 \
        wordpress:4 
        
        
        设置数据库名称
        docker create --link wpdb:mysql \
        -e WORDPRESS_DB_NAME=client_a_wp wordpress:4    //针对 client A 

        docker create --link wpdb:mysql \
        -e WORDPRESS_DB_NAME=client_b_wp wordpress:4    //针对 client B
        
        设置机器只能运行 一个 MYSQL 容器
        DB_CID=$(docker run -d -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5)
        MAILER_CID=$(docker run -d dockerinaction/ch2_mailer)

        网站配置脚本

        if [! -n "$CLIENT_ID"]; then // 假设 $CLIENT_ID 变量已设置为脚本的输入
           echo "CLIENT ID not set"
           exit 1 
        fi 

        WP_CID=$(docker create \
        --link $DB_CID:mysql \
        --name wp_$CLIENT_ID \
        -p 80 \
        -v /run/lock/apache2/ -v /run/apache2/ \
        -e WORDPRESS_DB_NAME=$CLIENT_ID  \
        --read-only wordpress:4)

        docker start $WP_CID
        
        AGENT_ID=$(docker create \
        --name agent_$CLIENT_ID \
        --link $WP_CID:insideweb \
        --link $MAILER_CID:insidemailer \
        dockerinaction/ch2_agent)

        docker start $AGENT_CID

    12. [ ] 建立持久化的容器
        构建一个总是重新启动并简单打印时间的容器
        docker run -d --name backoff-detector --restart always busybox date 
        
        docker logs -f backoff-detector

        在补偿期间,容器并没有运行。容器等待重新启动的时候,仍是在重新启动的状态。
        在回退探测容器时,尝试运行另一个进程
        
        docker exec backoff-detector echo Just a Test 运行该命令会报错,因为
        backoff-detector 并不是运行状态
        
        更好的重启策略是容器中运行 init 或 supervisor 进程

        
    13. [ ] 使用 init 和 supervisor 进程维持容器的运行状态
        
        docker run -d -p 80:80 --name lamp-test tutum/lamp

        使用 docker top lamp-test 命令可以看到哪些进程正运行着

        docker exec lamp-test ps 查看进程 pid 

        docker exec lamp-test kill <PID>

        docker run wordpress:4 cat /entrypoint.sh 查看启动脚本内容


        docker run --entrypoint="cat" \ //使用 cat 命令作为容器执行的入口
        wordpress:4  /entrypoint.sh     // 将 /entrypoint.sh 作为 cat 命令的参数

    14. [ ] 清理
        docker ps -a  
        
        docker run --rm --name --auto-exit-test busybox:latest echo Hello World 
        docker ps -a 

        快速清除命令  
        docker rm -vf $(docker ps -a -q)

** 3. 软件安装的简化
*** 1. 选择所需的软件
    1. 一个镜像是一个文件,包含了创建容器所需的文件和镜像元数据。该元数据包含 关
       于镜像之间的关联信息,命令历史,暴露的端口,卷的定义等

    2. 什么是仓库?
       主机 + 用户账户 + 简短的名称
       
       quay.io/dockerinaction/ch3_hello_registry

    3. 使用标签 
       标签是唯一指定 镜像的重要路径,也是一种创建有用别名的便利方法。尽管一个标
       签只能在一个仓库中被应用到单个镜像,但一个镜像可以有多个标签。
       7,7-jdk,7u71,7u71-jdk,openjdk-7 和 openjdk-7u71 这些标签被施加到相同的镜
       像。

*** 2. 查找和安装软件
    1. [ ] docker run -it --rm dockerinaction/ch3_ex2_hunt

    2. [ ] docker rmi  dockerinaction/ch3_ex2_hunt

    3. [ ] 注册服务器地址
       [REGISTRYHOST/][USERNAME/]NAME:[:TAG]
       docker pull quay.io/dockerinaction/ch3_hello_registry:latest
       docker rmi quay.io/dockerinaction/ch3_hello_registry

    4. [ ] 镜像文件
       docker load 加载本地镜像 

       从已经加载的镜像中保存文件到本地
       docker pull busybox:latest
       docker  save -o myfile.tar busybox:latest

       docker rmi busybox
       docker load -i  myfile.tar

    5. [ ] 从 Dockerfile  安装
       Dockerfile 是 Docker 用来描述新镜像构建步骤的脚本。这个文件 会 和作者 想
       要放入镜像的软件一起发布。
      
       git clone  https://github.com/dockerinaction/ch3_dockerfile.git
       docker build -t  dia_ch3/dockerfile:latest ch3_dockerfile

    6. [ ] 安装文件和隔离
       docker pull dockerinaction/ch3_myapp
       docker pull dockerinaction/ch3_myotherapp

       docker rmi \
       dockerinaction/ch3_myapp \
       dockerinaction/ch3_myotherapp \
       java:6

    7. [ ] 分层关系

    8. [ ] 指定 Docker 使用某个文件系统
       在启动 Docker 守护进程时,就加上   --storage-driver 或 -s 选项
       
** 4. 持久化存储和卷间状态共享
*** 1. NoSQL 数据库使用存储卷
    1. [ ] 
       docker run -d \
       --volume  /var/lib/cassandra/data \     //在容器中指定 存储卷的挂载点
       --name  cass-shared \
       alpine  echo Data Container

       docker run -d \
       --volumes-from cass-shared \        //继承了存储卷的定义
       --name cass1 \
       cassandra:2.2 

       容器的存储卷挂载在 /var/lib/cassandra/data 
       docker run -it --rm \
       --link cass1:cass \
       cassandra:2.2 cqlsh cass 

       CQLSH 命令行检查或修改 Cassandra 数据库。首先,查找一个名为
       docker_hello_world 的键空间
       
       select *  
       from system.schema_keyspaces 
       where keyspace_name = 'docker_hello_world';

       用命令创建键空间
       create keyspace docker_hello_world 
       with replication = {
       'class' : 'SimpleStrategy',
       'replication_factor' : 1
       };
       
       退出CQLSH 程序并停止客户端容器
       #leave and stop the current container 
       quit 
       
       客户端容器在创建时,使用 --rm 标志,在命令停止会被自动 删除。然后通过停止
       和去除所创建的 Cassandra 节点,清理该示例的第一部分
       docker stop cass1 
       docker rm -vf cass1
       
       接下来测试数据的恢复
       
       docker run -d \
       --volumes-from cass-shared \
       --name cass2 \
       cassandra:2.2 

       docker run -it --rm \
       --link cass2:cass \
       cassandra:2.2 \
       cqlsh cass 

       退出 cqlsh ,并清理工作区,确保删除该存储卷容器
       quit 
       docker  rm -vf cass2 cass-shared 

       

       
       
*** 2. 存储卷的类型
    1. [ ] 存储卷有两种类型。每一个存储卷就是容器目录树的挂载点在主机目录树中的
       位置,但不同的存储卷类型在主机的位置是不同的。第一种类型是绑定挂载存储卷。
       绑定挂载存储卷 使用用户提供的 主机目录或文件。第二类型是管理存储卷。管理
       存储卷使用由 Docker 守护进程控制的位置,被称为 Docker 管理空间。

    2. [ ] 绑定挂载卷
       1. 绑定挂载卷是一种存储卷,指向主机文件系统上用户指定的位置。绑定挂载卷在
          主机提供的文件或目录需要挂载到容器目录的特定位置时,非常有用

          在 Docker 中创建一个 web 服务器,将服务器的主页地址指向主机目录中的位
          置
          
          docker run -d --name bmweb \
          -v ~/example-docs:/usr/local/apache2/htdocs \
          -p  80:80 \
          httpd:latest
          
          在这个示例中,使用了 -v 选项和位置映射来创建绑定挂载卷。该映射以冒号分
          割(这是 Linux 命令行工具的常见风格 )。映射键(冒号之前的路径)是主机
          系统上的一个绝对路径,该键值(冒号后面路径)是容器中挂载的目标存储 位
          置。必须使用绝对路径指定该位置
          
          将挂载卷和存储卷设为只读权限
          docker rm -vf bmweb 
          
          docker run --name bmweb_ro \
          --volume ~/example-docs:/usr/local/apache2/htdocs/:ro \
          -p 80:80 \
          httpd:latest
           
          通过挂载只读卷,可以避免容器内的任何进程修改该卷的内容
          
          测试命令
          docker run -rm \
          -v ~/example-docs:/testspace:ro \
          alpine \
          /bin/sh -c 'echo test > /testspace/test'
          
          此命令启动一个 web 服务器的容器,绑定挂载卷为只读。运行时,试图将单词
          “test” 添加到卷上一个名为 test 的文件中。由于卷被挂载成只读,该命令失
          败。

         如果你指定了一个不存在的主机目录,Docker 会为你创建相应的目录
         ls ~/example-docs/absent  //验证 absent 目录不存在
         
         docker run --rm -v ~/example-docs/absent:/absent alpine:latest \
         /bin/sh -c 'mount |grep absent'      //检查挂载卷的定义

         ls ~/example-docs/absent

         绑定挂载卷并不限于目录,但目录是它们常用的方式。          

    3. [ ] 管理存储卷
       1. [ ] 
          docker run -d \
          -v /var/lib/cassandra/data \        //容器中指定存储卷的挂载点
          --name  cass-shared \
          alpine echo Data Container

       2. [ ] docker inspect 过滤卷键
          docker inspect -f "{{.Mounts}}" cass-shared
          [{volume
          a15966c248f9eafeaaca0dcced5b8ecc335c16ba1212ebb522148ec12717e6bd
          /var/lib/docker/volumes/a15966c248f9eafeaaca0dcced5b8ecc335c16ba1212ebb522148ec12717e6bd/_data
          /var/lib/cassandra/data local  true }] 

          主机中的地址 /var/lib/docker/volumes/容器ID/_data
     
    4. [ ] 共享存储卷

       1. [ ] 主机依赖的共享
          mkdir ~/web-logs-example //创建已知目录
          
          绑定挂载该目录为日志可写容器
          docker run --name plath -d \
          -v ~/web-logs-example:/data \
          dockerinaction/ch4_writer_a     

          绑定挂载该目录为日志只读容器
          docker run --rm \
          -v ~/web-logs-example:/reader-data \
          alpine:latest \
          head /reader-data/logA
          
          查看主机上的日志
          cat ~/web-logs-example/logA 
          
          docker stop plath
        
          创建四个容器
          docker run --name woolf -d \
          --volume ~/web-logs-example:/data \
          dockerinaction/ch4_writer_a 

          docker run --name  alcott -d \
          -v ~/web-logs-example:/data \
          dockerinaction/ch4_writer_b 
          
          docker run --rm --entrypoint head \
          -v ~/web-logs-example:/towatch:ro \
          alpine:latest \
          /towatch/logA
          
          docker run --rm \
          -v ~/web-logs-example:/toread:ro \
          alpine:latest \
          head /toread/logB

       2. [ ] 共享和 volumes-from 标志
          docker run --name fowler \
          -v ~/example-books:/library/PoEAA \
          -v library/DSL \
          alpine:latest \
          echo "Flower collection created"

          docker run --name knuth \
          -v /library/TAoCP.vol1 \
          -v /library/TAoCP.vol2 \
          -v /library/TAoCP.vol3 \
          -v /library/TAoCP.vol4.a \
          alpine:latest \
          echo "Knuth collection created"

          docker run --name reader \
          --volumes-from fowler \
          --volumes-from knuth \
          alpine:latest ls -l /library/
          
          docker inspect --format "{{.Mounts}}" reader  检查新容器的卷列表

          输出就结果
          [{volume
          8aafd10942594a49635c0719fd74ada036ff5be50a49dc60b3ada84238120e04
          /var/lib/docker/volumes/8aafd10942594a49635c0719fd74ada036ff5be50a49dc60b3ada84238120e04/_data
          library/DSL local  true } {volume
          a32bea2947866fa4298ab2209f517c73428fc566817eacc86ba5564f3d3966a5
          /var/lib/docker/volumes/a32bea2947866fa4298ab2209f517c73428fc566817eacc86ba5564f3d3966a5/_data
          /library/TAoCP.vol3 local  true } {volume
          b1d2480a7f48a233e2850e516dfef3f046368b5e02f48a93043e953b17c8fa25
          /var/lib/docker/volumes/b1d2480a7f48a233e2850e516dfef3f046368b5e02f48a93043e953b17c8fa25/_data
          /library/TAoCP.vol4.a local  true } {volume
          7def0b5ce25c8143a7a2012c50a5f442feef2455abe2c787eafe108afbe29410
          /var/lib/docker/volumes/7def0b5ce25c8143a7a2012c50a5f442feef2455abe2c787eafe108afbe29410/_data
          /library/TAoCP.vol1 local  true } {volume
          183d7b6110d07c2689a61e08889bfb320c6f66815d8daa1df90d775a2a4999d0
          /var/lib/docker/volumes/183d7b6110d07c2689a61e08889bfb320c6f66815d8daa1df90d775a2a4999d0/_data
          /library/TAoCP.vol2 local  true } {bind  /home/yuandd/example-books
          /library/PoEAA   true rprivate}] 

          docker run --name aggregator \
          --volumes-from fowler \
          --volumes-from knuth \
          alpine:latest \
          echo "Collection Created."
          
          复制了其他两个容器的存储卷,使用该容器的存储卷,查看指定目录
          
          docker run --rm \
          --volumes-from aggregator \
          alpine:latest \
          ls -l /library

       3. 一下三种情况不能用 --volumes-from 
          1. 如果你构建的容器需要共享卷挂载到不同的位置,没有工具也无法提供重新映
             射该挂载点。它只会通过指定的容器中指定的挂载点进行复制

          2. 卷源之间彼此冲突,或者有新的卷规格。如果一个或多个源创建的管理卷具
             有相同的挂载点,将只能接收其中之一
             
             docker run --name chomsky --volume /library/ss \
             alpine:latest echo "Chomsky collection created."
             
             docker run --name lamport --volume /library/ss \
             alpine:latest echo "Lamport collection created."
          
             docker run --name student \
             --volumes-from chomsky --volumes-from lamport \
             alpine:latest ls -l /library/
             
             docker inspect -f "{{.Mounts}}" student

             结果只有一个挂载点 
             [{volume
             2d372a032555e8087d9bc825499ff84ed68d471ad858e8b693bf56cc373ccdff
             /var/lib/docker/volumes/2d372a032555e8087d9bc825499ff84ed68d471ad858e8b693bf56cc373ccdff/_data
             /library/ss local  true }] 
             因为两个源卷使用相同的挂载点
             
             docker inspect 会显示最后一个容器只能有一个存储卷挂载在 /library/ss。
             每个源容器定义了相同的挂载点,复制到新容器的两个卷会产生一个竞争状
             态。两个复制操作中,只有一个能够成功

          3. 如果你需要更改卷的写权限,就不能使用 --volumes-from 。这是因为
             --volumes-from 复制了卷的定义。

    5. [ ] 管理卷的生命周期
       1. 管理卷的权限
          管理卷是二等实体。你没有办法分享或删除特定的管理卷,因为你没有办法指定
          一个管理卷

       2. 储存卷的清理
          docker rm -v $(docker ps -aq)

    6. [ ] 存储卷的高级容器模式
       1. 卷容器模式:它是 Docker 处理数据的基础工具,并且可以以多样有趣的方式来
          扩展

       2. 数据打包的存储卷容器
          复制镜像内容到一个存储卷
          docker run --name dpvc \
          -v /config \
          dockerinaction/ch4_packed /bin/sh -c 'cp /packed/* /config/'
          
          列出共享资料 
          docker run --rm --volumes-from dpvc \
          alpine:latest ls /config 
          
          查看共享资料 
          docker run --rm --volumes-from dpvc \
          alpine:latest cat /config/packedData 
          
          清理容器 
          docker rm -v dpvc

       3. 多态容器模式
          创建一个存储容器,包含相应的工具
          docker run  --name tools dockerinaction/ch4_tools 
          
          列出这些工具
          docker run --rm \
          --volumes-from tools \
          alpine:latest \
          /bin/sh -c 'ls /operations/*'

          利用这些新工具,启动一个新的容器 
          docker run -d --name important_application \
          --volumes-from tools \
          dockerinaction/ch4_ia 
          
          在该容器中使用这些共享工具
          docker exec important_application /operations/tools/diagnostics

          关闭该工具 
          docker rm -vf important_application 
          
          清理这些工具
          docker rm -v tools 

          使用多态容器来注入应用程序配置。考虑一个多态部署管道,其中应用程序的配
          置将根据部署它的位置而改变。你可以使用数据压缩卷缩容器,在每个阶段提供
          特定于环境的配置,然后应用程序将在某个已知位置查找其配置

          docker run  --name devConfig \
          -v /config \
          dockerinaction/ch4_packed_config:latest \
          /bin/sh -c 'cp /development/* /config/'
          
          docker run  --name prodConfig \
          -v /config \
          dockerinaction/ch4_packed_config:latest \
          /bin/sh -c 'cp /production/* /config/'

          docker run --name devApp \
          --volumes-from  devConfig \
          dockerinaction/ch4_polyapp 
          
          docker run --name prodApp \
          --volumes-from prodConfig \
          dockerinaction/ch4_polyapp 
                    
    7. [ ] 本章小结
       1. 存储卷允许容器与主机或其他容器共享文件

       2. 存储卷是主机 文件系统的一部分,Docker 将主机文件系统挂载到容器中指定位
          置

       3. 两种类型的存储卷:Docker 管理挂载主机文件系统的 Docker 目录,绑定挂载
          卷可挂载主机文件系统的任何位置

       4. 存储卷有生命周期,其独立于任何特定的容器,但是一个用户只能通过容器句柄
          引用 Docker 管理卷

       5. 孤立卷问题会导致磁盘空间难以恢复。可在 docker rm 命令中使用 -v 选项来
          避免此问题

       6. 卷容器模式对保证存储卷有序组织,并避免孤立卷问题非常有用(?)

       7. 数据打包的卷容器模式对给其他容器分发静态内容非常有用

       8. 多态容器模式是一种组成最小功能组件并最大化重用的方法

** 5. 网络访问
*** 1. 网络容器原型
    1. [ ] 单主机虚拟网络和多主机虚拟网络
       本地虚拟网络用来提供容器的隔离。多主机虚拟网络构建了一个抽象的覆盖网络,
       在这个网络中,任何容器相对于网络上的其他容器都有独立,可路由的 IP 地址

    2. [ ] 四种网络容器原型
       1. Closed 容器
          不允许任何网络流量,运行在这一种容器中的进程只能够访问本地回环接口
          
          创建一个 Closed 容器 
          docker run --rm \
          --net none \
          alpine:latest \
          ip addr   //列出所有的接口

          尝试访问谷歌 DNS 服务器 
          docker run --rm \
          --net none \
          alpine:latest \
          ping -w 2 8.8.8.8
          
       2. Bridged 容器
          Docker 的默认选项,可定制性最高。Bridged 容器拥有两个接口,一个是私有
          的本地回环接口,另外一个私有接口通过网桥连接到主机的其他容器

          加入网桥网络
          docker run --rm \
          --net bridge \
          alpine:latest \
          ip addr
          
          去掉 --net bridge 参数,默认是 Bridged 容器
          docker run --rm \
          alpine:latest \
          ping -w 2 8.8.8.8 

          设置容器的主机名
          
          docker run --rm \
          --hostname barker \
          alpine:latest \
          nslookup barker   //将主机名解析为 ip 地址

          设置主 DNS 服务器 
          
          docker run --rm \
          --dns 8.8.8.8 \
          alpine:latest \
          nslookup docker.com  //解析 docker.com 的 IP 地址

          关于设置 DNS 的重要笔记 
          1. 值必须是 IP 地址。原因很明显,容器需要一个 DNS 服务器来查找某一个名
             字的 IP 地址

          2. --dns=[] 选项可以被使用多次来设置多个 DNS 服务器 (防止一个或多个服
             务器不可用)

          3. --dns=[] 选项可以在你启动后台进程 Docker daemon 时进行设置。这么做
             之后,这些 DNS 服务器会默认配置到每一个容器上。如果跟该后台程序相关
             的容器依旧运行,这时候 你停止该后台程序并修改默认的 DNS 服务器设置,
             当你重新启动该后台程序时,运行中的容器依旧会保留老的 DNS 服务器设置。
             你需要重新启动这些容器让 DNS 服务器改动生效。

          4. --dns=[], 允许你指定一个 DNS 查找域,这个查找域就像 host 名的一个默
             认后缀。当该选项被设置,在查询时,任何不包括已知顶级域名(比如 .com
             或者 .net)的主机名都会自动加上该后缀名
             
             registry.hub.docker.com 解析的快捷方法
             
             docker run --rm \
             --dns-search dev.mycompany \
             busybox:latest \
             nslookup mysearvice  //解析 myservice.dev.mycompany

             docker run --rm \
             --dns-search test.mycompany \
             busybox:latest \
             nslookup myservice  //解析 myservice.test.mycompany 

             你可以同一个容器提供多个自定义的查找域,只需要简单的设置多次
             --dns-search 选项就行了。举个例子
             docker run --rm \
             --dns-search mycompany \
             --dns-search myothercompany ... 

             这个选项也可以在启动 Docker 后台进程时进行设置,为每个新创建的容器
             提供默认的查找域。注意,只有创建容器时,这些选项才会生效。如果一个
             容器正在运行,你改边了默认值,那么这个容器会保留就的值。
             
             最后一个 DNS 相关的选项提供了覆盖 DNS 系统的能力。这个系统和
             --hostname 选项中提到的系统是一样的。--add-host=[] 选项能自定义主机
             名到 IP 地址的映射关系 
             docker run --rm \
             --add-host test:10.10.10.255 \
             alpine:latest \
             nslookup test  //解析到 10.10.10.255 
             
             类似于 --dns 和 --dns-search 选项,这个选项能够被设置多次。但是不同
             意其他的选项,这个选项不能够在启动 Docker 后台进程时设置默认值

             所有的自定义转换关系都保存在容器中的 /etc/hosts 文件中

             docker run --rm \
             --hostname mycontainer \
             --add-host docker.com:127.0.0.1 \
             --add-host test:10.10.10.2 \
             alpine:latest \
             cat /etc/hosts
             
          开放对容器的访问
          1. docker run 命令提供了一个 -p --publish=[] 选项,它能够在主机网络栈
             上的端口和容器端口之间创建映射关系。映射的格式有四种
             1. 这种格式会将容器端口绑定到所有主机接口的一个动态端口上
                docker run  -p 3333 ...

             2. 这种格式会将一个具体的容器端口绑定到每一个主机接口的某一个具体接
                口上。
                docker run -p 3333:3333 ...

             3. 这种格式会将容器端口绑定到拥有指定 IP 地址的主机接口 的动态端口
                上。
                docker run -p 192.168.0.32::2222 ...

             4. 这种格式会将容器端口绑定到拥有指定 IP 地址的主机接口的一个具体的
                端口上。
                docker run -p 192.168.0.32:1111:1111 ...
                
          2. -P  --publish-all 选项 
             这个选项会告诉 Docker daemon 去创建端口映射关系,效果类似于 -p 命 
             的第一种格式作用于容器所有的端口,将容器的端口都暴露出去。
             dockerinaction/ch5_expose 开发了5000,6000 和 7000 端口,下面的命令
             都能起到同样的效果 
             
             docker run -d --name dawson \
             -p 5000 \
             -p 6000 \
             -p 7000 \
             dockerinaction/ch5_expose   //暴露所有的端口 

             docker run -d --name woolery \
             -P \
             dockerinaction/ch5_expose   //暴露相关的端口

          3. --expose 选项可以设置多次 ,它能设置容器想要开放的端口
             docker run -d --name philbin \
             --expose 8000 \ //暴露另个端口
             -P \           //暴露所有的端口
             dockerinaction/ch5_expose 

             以上命令中的 --expose 选项会将 8000 端口添加到 -P 选项的端口列表中

          4. docker port  查看端口映射情况
             docker port 容器名
             docker inspect 容器名

             
          跨容器通信 
          1. 
             docker run  -it --rm dockerinaction/ch5_nmap -sS -p 3333
             172.17.0.0/24 
             
             启动 Docker 后台进程 时,选择关闭容器之间的 网络连接 
             docker -d -icc=false ..
             

          修改网桥接口的配置 
          1. Docker 提供了三个选项来自定义网桥接口,这个接口在 Docker daemon 首
             次启动时候创建。这些选项 能够让使用者做以下事情 
             1. 定义网桥的地址和子网
             2. 自定义容器所能够获取的 IP 地址的范围
             3. 定义最大传输单元(MTU)
             
          2. 为了定义网桥的 IP 地址和子网范围,可以在启动 Docker 后台进程时使用
             --bip 选项(bridge IP 的缩写,表示网桥 IP)。如果你想要将你的网桥
             IP 地址设置为 192.168.0.128,并且只想分配这个子网最后的128个地址。
             在这种情况下,你需要将 --bip 选项的值设置为 192.168.0.128/25。更详
             细的说,这个值会创建一个 IP 地址为 192.168.0.128 的 docker0 接口,
             并且子网 IP 地址的范围为 192.168.0.128 到 192.168.0.255。命令类似
             于: 
             docker -d -bip "192.168.0.128" ...
             
          3. --fixed-cidr 选项提供了一个类似无类域内路由概念的配置 
             如果你想保留最后的64个地址,那么你可以使用192.168.0.192/26。当这个
             值被设置,Docker daemon 启动后,新的容器的  IP 地址限制于
             192.168.0.192 到 192.168.0.255。
             docker -d -fixed-cidr "192.168.0.192/26"

          4. 设置数据包传输单元 
             docker -d -mtu 1200 
                       
          5. 自定义网桥接口
             docker -d -b mybridge ...
             docker -d -bridage mybridge ... 
                              
          6. Joined 容器
             1. 启动一个本地回环接口监听的服务器 
                docker run -d --name brady \
                --net none alpine:latest \
                nc -l 127.0.0.1:3333 

             2. 列出当前容器所有的开放端口 
                docker run -it \
                --net container:brady \
                alpine:latest netstat -al

             3. 第一个命令创建了一个 Closed 容器,第二个容器只共享了那个本地回环
                接口。--net 选项中容器的值决定了新容器要和哪一个容器进行连接。
             

       4. Open 容器
          1. 指定 host 作为 docker run 命令的 --net 选项的值 
             创建一个 Open 容器
             docker run --rm \
             --net host \
             alpine:latest ip addr 

*** 2. Docker 如何与计算机的网络一同工作
*** 3. Docker 如何构建网络容器
*** 4. 如何自定义容器网络
*** 5. 如何使容器对网络可见
    1. [ ] 链接-本地服务发现
       为新容器添加一条链接会发生以下三件事 
       1. 描述目标容器的环境变量会被创建
       2. 链接的别名和对应的目标容器的 IP 地址会被添加到 DNS 覆盖列表中
       3. 最有趣的是,如果跨容器通信被禁止了,Docker 会被添加特定 的防火墙规则来
          允许被链接的容器间通信
          
    2. [ ] 
       创建被链接的目标容器
       
       docker run -d --name importantData \
       --expose 3306 \
       dockerinaction/mysql_noauth \
       service mysql_noauth start 

       创建一条链接并且将别名设置为 db 

       docker run -d --name importantWebapp \
       --link importantData:db \
       dockerinaction/ch5_web startapp.sh -db tcp://db:3306 

       创建一个容器,这个容器没有到 importantData 容器的路由 
       docker run -d --name buggyProgram \
       dockerinaction/ch5_buggy 

       如果 buggyProgram 容器里面的程序被攻击了,即便它运行在非特权账号下,攻击
       者也能探测该网桥网络
       如果跨容器通信被允许了,那么攻击者很容易就能够从数据库中偷取信息。他们可
       以通过简单的网络扫描来锁定具体的开放端口,然后 通过发起数据库链接就能够获
       得访问权了。即使网络管理员偶然看到了这条数据库链接,他也会认为这条链接是
       正确的,毕竟没有任何容器依赖被强制性构建

       如果跨容器通信被禁止,攻击者就不能够从被攻击的容器访问到其他容器了。
       
       不要认为简单的禁止跨容器就能够包含 那些不能自保的资源。只有正确恰当的配置,强
       大的网络规则设置,加上服务依赖的声明才能构造一个深度安全防御的系统

    3. [ ] 链接别名
       a,b,c 容器已经在运行 
       docker run --link a:alias-a --link b:alias-b --link c:alias-c
       
       如果犯错将全部的人容器豆都赋予了同一个别名,那么这个别名只能包含其中一个
       容器的链接信息。在这种情况下,防火墙规则依旧会被创建,但是没有了链接信息,
       这些规则几乎是无用的。

       依赖检测代码 ,在容器启动时检测是否存在一个叫做 database 的链接别名 
       !/bin/sh 
       if [ -z ${DATABASE_PORT+x}] then echo "Link alias 'database' was not
       set!" exit else exec "$@" fi 

       执行下面命令查看脚本的效果 
       
       创建有效的链接目标
       docker run -d --name mydb --expose 3360 \
       alpine:latest nc -l 0.0.0.0:3306
       
       测试不建立链接的情况
       docker run -it --rm \
       dockerinaction/ch5_ff echo This "shouldn't" work 

       测试不正确的链接的链接别名的情况
       docker run -it --rm \
       --link mydb:wrongalias \
       dockerinaction/ch5_ff echo Wrong 

       测试错误别名
       docker run -it --rm \
       --link mydb:database \
       dockerinaction/ch5_ff echo It worked 
       
       停止并删除链接目标容器
       docker stop mydb && docker rm mydb

    4. [ ] 环境变量的改动
       创建有效的链接目标
       docker run -d --name mydb \
       --expose 2222 --expose 3333 --expose 4444/udp \
       alpine:latest nc -l 0.0.0.0:2222 
       
       创建链接并且列出所有的环境变量
       docker run -it --rm \
       --link mydb:database \
       dockerinaction/ch5_ff env 

       执行结果 
       DATABASE_PORT_4444_UDP_ADDR=172.17.0.3
       DATABASE_PORT_2222_TCP=tcp://172.17.0.3:2222
       DATABASE_PORT_4444_UDP_PORT=4444
       HOSTNAME=31df01912af0
       SHLVL=1
       DATABASE_PORT_4444_UDP_PROTO=udp
       HOME=/root
       DATABASE_PORT=tcp://172.17.0.3:2222
       DATABASE_PORT_3333_TCP=tcp://172.17.0.3:3333
       DATABASE_NAME=/zen_mccarthy/database
       DATABASE_PORT_4444_UDP=udp://172.17.0.3:4444
       TERM=xterm
       PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
       DATABASE_PORT_2222_TCP_ADDR=172.17.0.3
       PWD=/
       DATABASE_PORT_2222_TCP_PORT=2222
       DATABASE_PORT_3333_TCP_ADDR=172.17.0.3
       DATABASE_PORT_2222_TCP_PROTO=tcp
       DATABASE_PORT_3333_TCP_PORT=3333
       DATABASE_PORT_3333_TCP_PROTO=tcp
       
       上面的结果可以看到,有多个环境变量由于链接的创建而创建。所有跟某一具体链
       接相关的变量都会使用该链接别名作为前缀。其中还会有一个以_NAME 结尾的变量,
       它由当前容器的 名字,一个斜杠和链接的别名组成。对于每个被链接的容器开发的
       端口,都会有四个单独的环境变量,并且环境变量的名字包含了对应的开放的端口。
       模式大概如下:
       
       <ALIAS>_PORT_<PROT NUMBER>_<PRO  TOCOL TCP OR UDP>_PORT
       这个变量仅仅包含了端口数字。这看起来非常古怪,因为端口已经被包含在变量名
       中了。当你在过滤那些包含有 TCP_PROT 字符串的环境变量时,这是非常有用的。
       这么做会得到所有端口的列表

       <ALIAS>_PORT_<PORT NUMBER>_<PRO TOCOL TCP or UDP>_ADDR 
       这种变量的值表示接收网络连接的 IP 地址。如果别名一样,那么它们会拥有一样
       的值。
       
       <ALIAS>_PORT_<PORT NUMBER>_<PRO TOCOL TCP or UDP>_PROTO 
       就像以_PORT 为后缀的变量一样,值的信息也被包含在变量名中。认识到协议不仅
       仅可以是 TCP 是非常重要的。UPD 也是支持的

       <ALIAS>_PORT_<PORT NUMBER>_<PRO TOCOL TCP OR UDP> 
       这种变量包含了上面所有的信息,并且以 URL 格式表示

       还有一个额外的环境变量,模式为 <ALIAS>_<PORT>, 它包含其中一个开发端口的链
       接信息 ,格式为 URL。

       链接不具有传递性
       
       停止删除容器
       docker stop mydb && docker rm mydb

    5. [ ] 链接的本质和缺点
       1. 链接的本质就是静态的,具有方向性和无传递性的依赖。无传递性意味着被链接
          的容器不会继承链接。更具体的说,如果我链接了 B 到 A,并且链接了 C 到 B,
          那么 C 和 A 之间不存在链接。

       2. 链接通过检测目的容器的网络信息( IP 地址和开发端口),然后将这些信息注
          入新容器中。由于这个操作过程是在容器创建期间进行的,并且在一个容器运行
          之间,Docker 不知道该容器的 IP 地址,因此链接值能在新容器到已存在的容
          器之间的构建。当然,这并不是说通信是单向的,而是说服务发现是单向的。这
          也意味着如果某个依赖由于某些原因停止了,则这条链接也会被破坏。记住,只
          有容器在运行状态,它才能维持 IP 地址的租约。因此一旦容器被停止或者重启
          了,那么它将失去 IP 地址租约并且任何链接到该容器的容器保留的都是过期的
          链接信息。
          
    6. [ ] 小结        

       1. [ ] Docker 提供了四个 网络容器的原型
          
       2. [ ] Docker 创建的网桥网络会将参与的容器相互绑定到一起,并且绑定到主机
          所依附的网络上 
          
       3. [ ] 当 Docker 后台进程启动时,可以使用 docker 命令行选项用自定义的网桥
          接口替代 Docker 创建的网桥接口 
          
       4. [ ] docker run 命令的选项能够被用来开发容器接口的端口,绑定容器开发端
          口到主机的网络接口上,还能链接其他的容器

       5. [ ] 禁止任意的跨容器通信是非常简单的,并且能够构建一个深度防御的系统。

       6. [ ] 使用链接能够提供一个低负载的本地服务发现机制,并且映射具体的容器依
          赖
                                 
*** 6. 发现网络上的其他容器
** 6. 隔离-限制危险 
*** 1. 限制资源
    1. [ ] 8 个命名空间
       1. MNT: 文件系统访问和结构
       2. chroot():控制文件系统根目录的位置
       3. Cgroups: 资源保护
       4. PID: 进程标识符和进程能力
       5. NET: 网络访问和结构
       6. UTS: 主机和域名
       7. USR: 用户名和标识符
       8. IPC: 通过共享内存进行通信

    2. [ ] 内存限制 
       1. docker run 或 docker create  命令使用 -m 或--memory 选项来设置内存
          这个选项会接受一个值和一个基础单元作为参数。格式如下
          where unit = b, k, m or g
          b 表示字节
          k 表示千字节
          m 表示兆字节
          g 表示千兆字节          
          
       2. 
          docker run -d --name ch6_mariadb \
          --memory 256m \
          --cpu-shares 1024 \
          --user nobody \
          --cap-drop all \
          dockerfile/mariadb 

    3. [ ] CPU
       1. 指定容器的相对权重
          这个百分比是相对所有对容器可用的处理器的 CPU 周期的总和来计算的 

          docker run -d -P --name ch6_wordpress \
          --memory 512m \
          --cpu-shares 512 \
          --user nobody \
          --cap-drop net_raw \
          --link ch6_mariadb \
          wordpress:4.1

       2. 为一个容器指定一个具体的,可使用的 CPU 集合。 --cpuset-cups 选项来限制
          容器只能在一个指定的 cpu 核集合中执行 

          限制只能使用标号为 0 的 CPU 
          docker run -d \
          --cpuset-cpus 0 \
          --name ch6_stresser dockerinaction/ch6_stresser 

          docker run -it --rm dockerinaction/ch6_htop

    4. [ ] 设备的访问权
       1. [ ] 当且仅当 /dev/video0 上有一个网络摄像头设备时,这个例子才能正确运
          行
          docker run -it --rm \
          --device /dev/video0:/dev/video0 \   // 挂载在 video0 设备
          ubuntu:latest ls -al /dev 

    5. [ ] 共享内存 
       1. [ ] 默认情况下,Docker 为每一个容器创建了一个独立且唯一的 IPC 命名空间。
          IPC 命名空间的作用就是防止一个容器中的进程访问主机或者其他容器的内存

       2. 跨容器的进程间通信
          启动生成者进程
          docker run -d -u nobody --name ch6_ipc_producer \
          dockerinaction/ch6_ipc -producer 

          启动消费者
          docker run -d -u nobody --name ch6_ipc_consumer \
          dockerinaction/ch6_ipc -consumer 
          
          docker logs ch6_ipc_producer 
          docker logs ch6_ipc_consumer 
          
          消费者容器永远不会再消息队列中看到消息, 原因在于,每一个容器都拥有它们
          自己的共享内存命名空间

          docker rm -v ch6_ipc_consumer

          启动新的消费者进程
          docker run -d --name ch6_ipc_consumer \
          --ipc container:ch6_ipc_producer \
          dockerinaction/ch6_ipc -consumer
          
          docker rm -vf ch6_ipc_producer ch6_ipc_consumer 

       3. 开发内存容器
          内存隔离是一种优良特性。如果你想要和主机运行在同一个命名空间中,你可以
          使用开放内存容器(open memory container)
          
          docker run -d --name ch6_ipc_producer \
          --ipc host \              // 使用开放内存容器
          dockerinaction\ch6_ipc -producer 
          
          docker run -d --name ch6_ipc_consumer \
          --ipc host \          // 使用开放内存容器
          dockerinaction/ch6_ipc -consumer 

          docker rm -vf ch6_ipc_producer ch6_ipc_consumer 
          
       4. [ ]

    6. [ ]  理解用户
       1. [ ] 默认情况下, Docker 以容器中的 root 用户来启动容器。root 用户几乎拥有
          对当前容器的所有访问特权。

       2. [ ] Linux 用户命令空间
          Docker 还没有集成 USER 命名空间。这意味着,如果一个容器的用户ID(数字,
          不是名字)和主机上的一个用户一样,那么该容器中的用户和主机上的用户拥有
          相同的文件权限

       3. [ ] run-as 用户
          docker create --name bob busybox:latest ping localhost
          
          docker inspect bob  //显示 bob 镜像的所有元数据
          
          docker inspect --format "{{.Config.User}}" bob  //如果显示空白,那么容
          器默认使用 root 用户来启动,如果结果不为空,要么镜像作者指定了一个默认
          的 run-as 用户,要么就是当你创建这个容器的时,你指定了run-as 用户。 
          -f 或 --format 选项允许你为输出结果指定模板。

          如果用户被脚本改变了,改动的结果并不会反馈到 docker inspect 命令的输出
          上。第二,你必须用镜像创建一个容器来获得这些信息。
          
          检查镜像用户
          docker run --rm --entrypoint busybox:latest whoami
          docker run --rm --entrypoint busybox:latest id

          创建容器的时候设置了 run-as 用户,那么就能够完全避免默认用户带来的问题,
          注意,指定的用户名必须在你使用的镜像中存在。不同的 Linux 发行版有不同
          的用户定义。获取镜像中的可用用户的列表 
          docker run --rm busybox:latest awk -F: '$0=$1' /etc/passwd

          设置 nobody 用户
          docker run --rm \
          --user nobody \
          busybox:latest id 
          
          用户设置为 nobody ,用户组为 default
          docker run --rm \
          -u nobody:default \
          busybox:latest id 
          
          docker run --rm \
          -u 10000:20000 \
          busybox:latest id
          
          root 提权
          docker run -it --name escalation -u nobody busybox:latest \
          /bin/sh -c "whoami; su -c whoami"
                    
       4. [ ] 用户和卷
          
          echo "e-mc^2" > garbage 
          
          chmod 600 garbage 
          
          sudo chown root:root garbage //将文件的所有者改为 root(假设你拥有 sudo
          权限))
          
          docker run --rm -v "$(pwd)"/garbage:/test/garbage \
          -u nobody \
          ubuntu:latest cat /test/garbage      // 尝试用 nobody 用户读取文件

          docker run --rm -v "$(pwd)"/garbage:/test/garbage \
          -u root ubuntu:latest cat /test/garbage   //尝试用容器 
          
         #Outputs: "e=mc^2"

         #cleanup that garbage 
         sudo rm -f garbage
         
         管理运行中用户的 ID 
         mkdir logFiles 
         sudo chown 2000:2000 logFiles 
         docker run --rm -v "$(pwd)"/logFiles:/logFiles \
         -u 2000:2000 ubuntu:latest \
         /bin/bash -c "echo This is important info > /logFiles/important.log"
         
         docker run --rm -v "$(pwd)"/logFiles:/logFiles \
         -u 2000:2000 ubuntu:latest \
         /bin/bash -c "echo More Info >> /logFiles/important.log" 
         
         sudo rm -r logFiles                  
             
       5. 能力--操作系统功能的授权
          1. [ ] 被去除能力的集合
             SETPCAP --- 修改进程
             SYS_MODULE --- 插入或移除内核模块
             SYS_RAWIO --- 修改内核内存
             SYS_PACCT --- 修改进程计数 
             SYS_NICE --- 修改进程优先级 
             SYS_RESOURCE --- 覆盖资源限制
             SYS_TIME --- 修改系统时钟 
             SYS_TTY_CONFIG --- 配置 TTY 设备 
             AUDIT_WIRTE --- 审计日志文件的写入 
             AUDIT_CONTROL --- 配置审计子系统 
             MAC_ADMIN --- 配置 MAC 
             SYSLOG --- 修改内核行为
             NET_ADMIN --- 修改内核打印行为 
             SYS_ADMIN --- 一系列管理功能的集合

          2. --cap-drop 选项来为容器去除能力
             docker run --rm -u nobody \
             ubuntu:latest \
             /bin/bash -c "capsh --print | grep net_raw"
             
             去除 NET_RAW 能力
             docker run --rm -u nobody \
             --cap-drop net_raw \
             ubuntu:latest \
             /bin/bash -c "capsh --print | grep net_raw"

          3. --cap-add 选项能够添加能力
             docker run --rm -u nobody \
             ubuntu:latest \
             /bin/bash -c "capsh --print | grep sys_admin" 

             docker run --rm -u nobody \
             --cap-add sys_admin \
             ubuntu:latest \
             /bin/bash -c "capsh --print | grep sys_admin"
             
       6. 运行特权容器
          1. [ ] --privileged 选项开启特权容器模式
             查看用户 ID 
             docker run --rm \
             --privileged \
             ubuntu:latest id 
             
             查看 Linux 能力
             docker run --rm \
             --privileged \
             ubuntu:latest capsh --print 

             查看已被挂载的设备
             docker run --rm \
             --privileged \
             ubuntu:latest ls /dev

             检测网络配置
             docker run --rm \
             --privileged \
             ubuntu:latest ifconfig

       7. [ ] 使用加强工具创建更健壮的容器
          1. [ ] AppArmor 和 SELinux
             
          2. [ ] 指定额外的安全选项
             LSM 是 Linux 采用的一个框架,用作操作系统和安全供应商之间的接口层
             
             AppArmor 和 SELinux 都是 LSM 的供应商。它们都提供了强制访问控制
             (MAC --- 定义访问规则的系统)来取代标准的 Linux 自主访问控制(文件
             所有者定义访问规则)

             --security-opt 选项来设置这些内容
             1. 使用格式 label:user:<USERNAME> 来设置 SELinux 用户标签,其中
                <USERNAME> 表示你想要使用的用户的名字
                
             2. 使用格式 label:role:<ROLE> 来设置 SELinux 角色标签,其中 <ROLE>
                表示你想要赋予给容器中进程的角色的名字

             3. 使用格式 label:type:<TYPE> 来设置 SELinux 类型标签,其中 <TYPE>
                表示容器中进程的类型的名字
                
             4. 使用格式 label:level:<LEVEL> 来设置 SELinux 级别标签,其中
                <LEVEL> 表示容器中进程应该运行的级别。级别格式为低-高。如果只是
                缩写为低级别,那么 SELinux 会将 这个范围解析为单个级别

             5. 使用格式 label:disable 禁止 SELinux 标签限制
                
             6. 使用格式 label:apparmor:<PROFILE> 来在容器上应用一个 Apparmor 配
                置,其中 <PROFILE> 就是配置文件的名字
             
          3. [ ] 微调 LXC 
             Docker 最开始被创建是为了使用名为 Linux 容器(LXC)的软件。LXC 是一
             个容器运行时(container runtime)供应商的一个工具,它能够工作在
             Linux 上创建命名空间和构建一个容器所需要的组件
             
             docker 自带 libcontainer. LXC 是一个比 libcontainer 更加成熟的库

             为了使用 LXC,你需要安装它,并且确保 Docker 后台进程程序启动时,LXC
             驱动被允许。当启动 Docker 后台进程时,使用 --exec-driver=lxc 来使用
             LXC。Docker 进程常常被作为系统服务来启动,因此请根据 www.docker.com
             上的安装指导来找到你使用的发行版的操作细节

             一旦 Docker 被配置为 LXC,你可以使用 docker run 或 docker create 命
             令的 --lxc-conf 选项来设置 LXC 的配置
             
             docker run -d  \
             --lxc-conf="lxc.cgroup.cpuset.cups=0,1" \ // 通过 LXC 将容器限制在
             两个 cpu 
             --ch6——stresser dockerinaction/ch6_stresser 

             docker run  --it --rm dockerinaction/ch6_htop 
             docker rm -vf ch6_stresser 
             
             使用 LXC 配置时需要处理跟标准容器改动产生的冲突
             
       8. [ ] 因地制宜地构建容器
          容器是一个横切关注点(cross-cutting-concern)
          1. 应用

          2. 高层的系统服务
             cron, syslogd, dbus, sshd, docker 

          3. 底层的系统服务
             
       9. [ ] 小结
          1. Docker 使用 cgroups 让用户能够设置内存限制, CPU权重, CPU核限制,还
             有设备访问限制

          2. 每个 Docker 容器都有各自的 IPC命名空间,它们能够被其他容器或主机共
             享,以此建立基于共享内存的高速通信。

          3. Docker 还支持 USR 命名空间,因此容器中的用户和用户组 ID 和主机上具
             有相同 ID 的用户和用户组是相等的。

          4. 你可以并且应该使用 docker run 和 docker create 命令的 -u 选项是的容
             器以非 root 用户运行

          5. 尽可能避免容器以特权模式运行

          6. Linux 能力提供了操作系统特性的授权模式。 Docker 去除某些能力是为了
             提供合理的默认隔离配置

          7. 使用 --cap-add 和 --cap-drop 选项来赋予或去除容器的能力

          8. Docker 提供了配置选项来集成加强隔离技术,比如 SELinux 和 AppArmor。
             这些都是非常强大的工具,任何认真严肃的 Docker  采用者都应该研究她们。
*** 2. 共享容器内存
*** 3. 用户,权限和管理员特权
*** 4. 授权访问某个具体的 Linux 功能
*** 5. 加强 Linux 隔离和安全的工具:SELinux 和 AppArmor

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值