第一本Docker书

第一本Docker书

自动精简配置: thin-provisioning
加载device-mapper模块
sudo mpdprobe dm_mod
查看docker进程
docker ps -l
docker ps
docker ps -a
启动一个容器
docker run -i -t ubuntu /bin/bash
docker attach 重新附加到容器上
查看日志
docker logs vID
docker logs -f ID/Name
-f 与tail -f 很相似
docker logs --tail 10 ID
docker logs --tail 0 -f ID
列出前几个镜像
docker top
执行命令
docker exec -d ID touch /etc/new_config_file
docker exec -t -i ID /bin/bash
docker stop
显示最后x个容器
docker ps -n -x
自动重启容器
docker run --restart=always --name test -d ubuntu /bin/bash-c "while true; do echo hello world; sleep 1; done;"
always
on-failure
--restart=on-failure:5 设置重启次数
查看更多内容
docker inspect
*inspect -f --format * 选定查看信息
查看状态
docker inspect --format '{{ .Status.Running}}' testname
docker inspect --format '{{ .NetworkSetting.IPAddress}}'
获取多个容器信息
docker inspect --format '{{ .Name}} {{ .Status.Running}}' testName1 testName2
容器存放位置
所有容器都存在/var/lib/docker/containers
删除容器
docker rm testname
运行中的容器如法删除
删除所有容器
docker rm ``docker ps -a -q\
-q 只返回容器ID
列出所有容器
docker images
镜像 仓库 Registry
通过在仓库后边加一个冒号和标签名来指定该仓库中的某个镜像
docker run -t -i -name new_container ubuntu:12.04 /bin/bash
仓库分类
用户仓库(user repository)
顶层仓库(top-level repository)
只看fedora镜像
docker images fedora
查找镜像
dockert search
docker search puppet
构建镜像
docker login
信息保存在$HOME/.dockercfg
docker commit 仓库名/镜像名
指定更多信息
docker commit -m 'A new custom image ' --author='James' ID 仓库名/镜像名:tag

Dockerfile
注释使用#
指定都是大写

Version 0.0.1

FROM ubuntu:14.04
MAINTAINER James "James@163.com"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hi , I am you container '> /usr/share/nginx/html/index.html
EXPOSE 80
默认使用 /bin/sh -c
也可以使用
RUN ['apt-get', 'install', '-y', 'nginx']
该容器内的应用程序将会使用容器的指定端口
Docker 不会自动打开端口, 可以向外部公开多个端口
docker build -t='repo/image'
.dockerignore 用来设置那些文件不会被商场到构建上下文中
不使用缓存机制
docker build --no-cache -t='repo/iamge'
ENV REFRESHED_AT 2014-07-01
列出构建历史
docker history testname
docker run -d -p 80 --name new_container repo/image nginx -g 'daemo off'
-p 用来控制docker 在运行时应该公开那些网络接口该外部使用
Docker分配端口

  1. Docker 可以在宿主机上随机选择一个位于49000-49900的一个较大的端口号来映射到容器上得80端口
  2. 可以在docker宿主机中指定一个具体的端口号来映射到容器中的80端口上
    docker port testname 80
    -p 80:80
    -p 8080:80
    127.0.0.1:80:80
    -P 公开在dockerfile中的EXPOSE指定的所有端口号

Docker指令
CMD
指定容器被启动时要运行的命令
会被覆盖, 只能使用最后一个
/bin/true
CMD["/bin/bash"]
推荐使用数组
Dockerfile中只能指定一条CMD指令
ENTRYPOINT
ENTRYPOINT ["/usr/sbin/nginx"]
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off"]
巧妙工作
docker run -t -i repo/image -g 'daemon off'
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
docker run --entrypoint 覆盖ENTRYPOINT
WORKDIR
在容器内部设置一个工作目录, ENTRYPOINT和CMD或/ 指定的程序会在整个目录下执行
WORKDIR /opt/webapp/db 
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT ["rackup"]
将容器内工作目录设为/var/log
docker run -ti -w /var/log ubuntu pwd
ENV
用来在镜像构建过程中设置环境变量
ENV RVM_PATH /home/rvm
可以在后续的任何RUN指令中使用
ENV TRAGET_DI /opt/app
WORKDIR TARGET_DIR
docker run -e
使用-e 传递环境变量
docker run -ti -e 'WEB_PORT=8080' ubuntu
USER
指定该镜像会以什么样的用户去执行
USER nginx
USER user
USER user:group
USER uid
USER uid:gid
USER uid:group
USER user:gid
VOLUME
像基于镜像创建的容器添加卷
一个卷可以存在于一个或者多个容器内的特定目录, 这个目录可以绕过联合文件系统并提供如下共享数据或者对数据进行持久化的功能

  1. 可以在容器间共享和重用
  2. 一个容器可以不是必须和其他容器共享卷
  3. 对卷的修改是立时生效的
  4. 对卷的修改不会对更新镜像产生影响
  5. 卷会一直存在知道没有任何容器再使用它
    为基于此镜像的容器创建一个名为/opt/project的挂载点 VOLUME ["/opt/project"]
    添加多个卷
    VOLUME ["/opt/project", "/data"]
    ADD
    将构建环境下的文件和目录复制到镜像中
    ADD software.lic /opt/application/software.lic
    以“/”结尾的判断为目录, 否为判断为文件
    ADD 会将合法的归档文件指定源文件的是时候,自动将归档文件进行解压
    木i的目录不存在的话,会创建全路径,权限为0755 UID=0 GID=0
    ADD会使构建缓存无效
    ADD添加文件爱你和目录, 使用Dockerfile中的后续指令不能继续使用
    COPY
    类似ADD
    只关心在构建上下文中复制本地文件, 为不会做文件提取和解压
    COPY conf.d/ /etc/apache2
    ONBUILD
    为镜像添加触发器 trigger
    当一个镜像被用作其他其他镜像的基础镜像时,该镜像中的触发器将会被执行
    触发器会在构建过程中插入新的指令
    ONBUILD ADD . /app/src
    ONBUILD RUN cd /app/src &&make
    ONBUILD 触发器会按照父镜像中指定的顺序执行并只能被继承一次
    只能在子镜像中执行, 不会在孙子镜像中执行
    *FROM MAINTAINER ONBUILD本身 不能放在ONBUILD中,防止产生递归

推送
docker push testname
docker push repo/image
自动构建
删除
docker rm repo/img
从容器中运行Registry
docker run -p 5000:5000 registry
docker tag docker.example.com:5000/repo/image
docker push docker.example.com:5000/repo/image
docker run -t -i docker.example.com:5000/repo/image /bin/bash
Quary 私有Registryr托管服务 现已被CoreOS收购
在测试中使用Docker
使用docker测试静态网站

mkdir sample  
cd sample  
touch Dockerfile
cd sample
mkdir nginx && cd nginx
touch global.conf
touch nginx.conf

Dokcerfile 文件
#A Dockerfile for nginx web
#Version 0.0.1
FROM ubuntu:14.04
MAINTAINER James James@example.com
ENV REFRESHED_AT 2014-07
RUN apg-get update
RUN apt-get -y install nginx
RUN mkdir -p /var/www/html
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
global.conf文件
server{
listen 0.0.0.0:80;
server_name _;
root /var/www/html/website;
index index.html index.htm;
access_log /var/log/nginx/default_access.log;
error_log /var/log/nginx/default_error.log;

}

nginx 文件
user www-data;
work_process 4;
pid /run/nginx.pid;
daemon off;
event{}
http{
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
type_hash_max_size 2048;
include /etc/nginx/mime.type;
default_type application/octect_stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_diable "mise6";
include /etc/nginx/conf/*.conf;
}
daemon off; 防止nginx进入后台, 强制其在前台运行, 要想保持dokcer的活跃状态,需要其中运行的进程不能中断
docker build -t jamtu01/nginx
创建网站
touch sample
mkdir website&&cd website
touch index.html
docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website jamtu01/nginx nginx
-v 参数指定了卷的源目录和容器中的目的目录,这两个目录通过: 来分隔 如果目的目录不存在 docker会自动创建一个
可以增加rw 或ro 来指定目的目录的读写状态
-v $PWD/website:/var/www/html/websit:ro
web应用
#Version 0.0.1
FROM ubuntu:14.04
MAINTAINER James james@example.com
ENV REFRESHED_AT 2016-05-01
RUN apt-get update
RUN apt-get -y install ruby ruby-dev build-essential redis-tools
RUN mkdir -p /opt/webapp
EXPOSE 4567
CMD ["/opt/webapp/bin/webapp"]
docker build jamtu01/sinatra
检查端口映射
docker port webapp 4567
Redis镜像
#version 0.0.1
FROM ubuntu:14.04
MAINTAINER jame james@example.com
ENV REFRESHED_AT 2016-5-1
RUN apt-get update
RUN apt-get -y install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
CMD []

docker有自己的网络

  • 端口绑定到本地网络
  • 内部网络 dokcer0

docker0接口有符合RFC1918的私有IP地址, 范围172.16~ 172.30 接口本身地址 172.17.42.1是一个Docker网络的网关地址 也是所有docker容器的网管地址
doker会默认使用172.17.x.x作为子网地址, 除非这个子网已被占用,如果整个子网被占用了,docker会在172.16.~172.30这个范围尝试创建子网
docker0是一个熏鸡的以太网桥, 用于连接容器和本地宿主网络
veth 开头的网络
docker每创建一个容器就会创建一组互联的网络接口,这组接口其中一端作为容器里的eth0接口, 而另一端同意命名为类似vethc6a这种名字, 作为宿主机的一个接口
防火墙规则和Nat配置
Docker容器互联
docker run -d --name redis jamtur01/redis
docker run -p 4567 --name webapp --link redis:db -t -i -v $PWD/webapp:/opt/webapp jamtru01/sinamtra /bin/bash

--link
创建了连个容器间的父子连接
需要两个参数 一个是要连接的容器的名字, 另一个是连接容器的别名
连接让父容器有能力访问子容器,并且把自容器的一些连接细节分享给父容器,这些细节有助于配置应用程序并使用这个连接
连接也能得到安全上的好处
出于安全原因, 可以强制docker之允许有连接的容器之间进行互相通信,需要在启动docker守护进程时加上--icc=false 关闭所有设有连接的容器间的通讯
被连接容器必须运行在同一个docker宿主机上,不同docker宿主机上运行的容器无法连接
修改容器中的hosts
cat /etc/hosts
172.17.0.33 id
172.17.0.31 db
--volume-from
把指定容器里的所有卷都加入新创建的容器里
如果删除了最后一个使用卷的容器, 卷就部存在了
docker start jame/blog 重启编译运行

docker run --rm 容器进程运行完毕后 自动删除容器
备份
docker run --rm --volume-from james-blog -v $PWD:/backup ubuntu tar cvf /back/james-blog/backup.tar /var/www/html/

-h 设置容器的主机名
服务发现
服务发现是分布式应用程序之间管理相互关系的一种机制, 一个分布式程序一般由多个组件组成, 这些组件可以都放在一台机器上,也可以分布在多个数据中心,甚至分布在不同的地理区域,这些组件通常可以为其他组件提供服务或者为其他组件消费服务。
服务发现允许某个组件在想要与其他组件自动找到对方, 由于这应用本身是分布式的, 服务发现机制也需要是分布式的,而且服务发现作为分布式应用不同组件之间的胶水, 其本身还需要是足够动态, 可靠, 适应性强, 而且可以快速且一直的共享于这些服务的数据
Docker 主要关注分布式应用以及面向服务架构与微服务架构
consul
Consul 是一个使用一致性算法的特殊数据存储器
Consul使用Raft一致性算法来提供确定的写入机制, consul暴露了键值存储系统和服务分类系统, 并提供高可用性,高容错性能力, 并保证强一致性服务,可以将自己注册到consul并且高可用分布式方式共享这些信息
Consul其他功能

  1. 提供了数据API进行服务分类, 代替了大部分传统服务发现工具的键值对存储
  2. 提供了两类接口来查询信息, 基于内置DNS服务的DNS查询接口和基于HTTP的RESTAPI查询接口, 选择合适的接口, 尤其是基于DNS的接口可以很方便的将Consul与发现环境集成
  3. 提供了服务监控,也称作Aka健康监控, Consul内置了强大的服务监控系统
    构建Consul
    mkdir consul
    cd consul
    touch Dockerfile
    #Version 0.0.1
    FROM ubuntu:14.04
    MAINTAINER james jam@example.com
    ENV REFREESHED_AT 2016-5-2
    RUN apt-get update
    RUN apt-get -qqy install url unzip
    ADD https://dl.binray.com/mitchellh/consul/0.3.1_linux_amd64.zip /tmp/consul.zip
    RUN cd /usr/sbin && unzip /tmp/consul.zip && chmod +x /usr/sbin/consul && rm /tmp/consul.zip
    ADD https://dl.binary.com/mitchellh/consul/0.3.1_web_ui.zip /tmp/webui.zip
    RUN cd /tmp && unzip webui.zip && mv dist/ webui/
    ADD consul.json /config/
    EXPOSE 53/udp 8300 8301 8301/udp 8302 8302/udp 8400 8500
    VOLUME ["/data"]
    ENTRYPOIN ["/usr/sbin/consul", "agent", "-config-dir=/config"]
    CMD []
    consul.json
    {
    "data_dir" : "/data",
    "UI_dir" : "/webui",
    "client_addr" : "0.0.0.0",
    "ports" : {
    "dns" : 53
    }
    "rescursor" : "8.8.8.8"
    }
  • 53/udp | DNS服务器
  • 8300 |服务器使用的RPC
  • 8301+udp |serf服务器使用LAN端口
  • 8302+udp |serf服务器使用的WAN端口
  • 8400 |命令行RPC接入点
  • 8500 |HTTPAPI 用于提供HTTP API和网页界面
    8301+udp 8302+udp 8300 8400 用于可处理后台通信将多个consul节点组成集群
    获取IP地址
    PUBLIC_IP="$(ifconfig eth0 |awk -F '*|:' '/inet addr/{print $4'})"
    /etc/default/docker
    DOCKER_OPTS='--dns 172.17.42.1 --dns 8.8.8.8 --dns-search service .consul'

Docker API
在Doker生态系统中有三种API

  1. Registry API : 提供了与存储Docker镜像的Docker Registry集成的功能
  2. DockerHub API 提供了与Docker Hub集成的功能
  3. Docker Remote API 提供了与Docker守护进程进行集成的功能
    都是RESTAPI风格
    绑定
    Docker守护进程绑定宿主机套接字 unix:///var/run/docker.sock
    Docker 需要root权限运行
    启动配置文件
  • /etc/default/docker
  • /etc/init/docker.conf
  • /etc/sysconfig/docker
  • /usr/lib/systemd/system/docker.service
    编辑启动项
    /usr/lib/systemd/system/docker.service
    ExecStart=/usrt/bin/docker -d --selinux enabled -H tcp://0.0.0.0:2375
    Docker 守护进程能够绑定到一个接口上
    重新加载和启动Docker守护集成
    systemctl --system daemon-reload
    Docker 守护进程之间的网络连接是没有经过认证的, 是对外开放的
    返回json散列数据
    curl htpp://docker.example.com:2375/info
    使用python的json工具, 对API返回结果进行格式化处理
    curl http://docker.example.com:2375/images/json|python -mjson.tool
    查询
    curl "http://docker.exampl.com:2375/images/search?term=jamtur01"|python -mjson.tool
    获取正在运行的容器
    curl -s "http://docker.example.com:2375/containers/json" |python -mjson.tool
    获取所有容器
    http://docker.example.com:2375/container/json?all=1
    创建容器
    curl -X POST "Content-Type: application/json http://docker.example.com:2375/containers/create -d {"image":"jamtur01/jenkyll"}" *创建容器并设置主机名*curl -X POST -H Content-Type: application/json http://docker.example.com:2375/containers/ID/start -d {"publishAllPorts":true} *使用容器ID查询容器*curl http://docker.example.com:2375/containers/containerID/json| python mjson.tool`

认证
采用TLS/SSL认证
建立证书授权中心
mkdir /etc/docker
cd /etc/docker
echo 01>|sudo tee ca.srl
openssl genrsa -des3 -out ca-key.pem
创建CA证书
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem
创建服务器密钥
openssl x509 -req -days 365 server-key.pem -out server.csr
common name 或CN 要么为Docker服务器的FQDN 要么为允许任何服务器上使用该服务器证书
对CSR进行签名
openssl x509 -req -days 365 -ins server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem
移除服务器端密钥
openssl rsa -in server-key.pem -out server-key.pem
chmod 0600 /etc/server-key.pem
chmod 0600 /etc/server-cert.pem
chmod 0600 /etc/ca-key.pem
chmod 0600 /etc/ca.pem
在systemd中启用dockerTLS
ExecStart = /usr/bin/docker -d -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/server-cert.pem --tlskey=/etc/docker/server-key.pem
创建客户端证书和密钥
创建客户端密钥
openssl genrsa -des3 -out client-key.pem
创建客户端csr
openssl req -new client-key.pem -out client.src
添加客户端认证属性
echo extendedKeyUsage=clientAuth > extfile.cnf
对客户端CSR进行签名
openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAKey ca-key.pem -out cient-cert.pem -extfile extfile.cnf
移除客户端密钥
openssl rsa -in client-key.pem -out client-key
复制ca.pem client-cert。pem client-key.pem到运行的Docker客户端的宿主机上
复制到~/.docker目录下

推荐阅读更多精彩内容