docker in all

docker vs hyper-v,vmware,xen,kvm

docker host, docker container, docker engineen, docker image

images = stopped container

container = running images

 

docker操作示意圖

workflow

 

开始使用docker(以windows下为例)

PS G:\dockerdata> docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

PS G:\dockerdata> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 2 months ago 1.84kB
docker4w/nsenter-dockerd latest 2f1c802f322f 4 months ago 187kB

 

 

以上docker run hello-world命令本质上的执行过程:

1. docker client向docker daemon(engine)联络,告诉docker engine,请帮我运行一个hello-wold container

2. docker daemon(engine)收到该命令后先在本地查找是否有hello-world这个image,如果没有则从regisry查找并且pull下来

3. docker daemon以该image实例化一个container,并且运行该image定义的executable,而这个executable将产生output;

4. docker daemon streamed that output to the docker client,这样我们就看到了hello world的消息

docker image到底包含了什么?

强烈建议: https://www.csdn.net/article/2015-08-21/2825511

我们知道linux系统由内核+发行版组成,同样的内核比如3.8之上,我们可以有debian, ubuntu, centos等不同的发行版本。类似地,Docker镜像就是类似于“ubuntu操作系统发行版”,可 以在任何满足要求的Linux内核之上运行。简单一点有“Debian操作系统发行版”Docker镜像、“Ubuntu操作系统发行版”Docker镜 像;如果在Debian镜像中安装MySQL 5.6,那我们可以将其命名为Mysql:5.6镜像;如果在Debian镜像中安装有Golang 1.3,那我们可以将其命名为golang:1.3镜像;以此类推,大家可以根据自己安装的软件,得到任何自己想要的镜像。

修改默认pull image存放位置

在windows下本质上docker engine是工作在hyper-v虚拟机中,所有的docker客户端敲的命令在该虚拟机中运行,pull的image也放在该虚拟机中,因此我们要修改image保存的位置实际上只要修改hyper-v的MobyLinuxVM对应的vhdx文件的位置即可。

http://www.cnblogs.com/show668/p/5341283.html

 docker ps/docker images

PS G:\dockerdata> docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
hello-world                latest              fce289e99eb9        2 months ago        1.84kB
docker4w/nsenter-dockerd   latest              2f1c802f322f        4 months ago        187kB
PS G:\dockerdata> docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
PS G:\dockerdata> docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
135da1372a06        hello-world         "/hello"            24 minutes ago      Exited (0) 24 minutes ago                       modest_spence

 pull特定版本的image

docker pull ubuntu:14.04

Repository:是对一个docker image的存储定义

将docker hub mirror配置为阿里云加速器

删除本地的image

PS G:\dockerdata> docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
ubuntu                     latest              47b19964fb50        3 weeks ago         88.1MB
alpine                     latest              caf27325b298        4 weeks ago         5.53MB
hello-world                latest              fce289e99eb9        2 months ago        1.84kB
docker4w/nsenter-dockerd   latest              2f1c802f322f        4 months ago        187kB
PS G:\dockerdata> docker rmi ubuntu
Untagged: ubuntu:latest
Untagged: ubuntu@sha256:7a47ccc3bbe8a451b500d2b53104868b46d60ee8f5b35a24b41a86077c650210
Deleted: sha256:47b19964fb500f3158ae57f20d16d8784cc4af37c52c49d3b4f5bc5eede49541
Deleted: sha256:d4c69838355b876cd3eb0d92b4ef27b1839f5b094a4eb1ad2a1d747dd5d6088f
Deleted: sha256:1c29a32189d8f2738d0d99378dc0912c9f9d289b52fb698bdd6c1c8cd7a33727
Deleted: sha256:d801a12f6af7beff367268f99607376584d8b2da656dcd8656973b7ad9779ab4
Deleted: sha256:bebe7ce6215aee349bee5d67222abeb5c5a834bbeaa2f2f5d05363d9fd68db41

docker run detached mode启动一个web服务

PS G:\dockerdata> docker run -d --name web -p 9090:8080 nigelpoulton/pluralsight-docker-ci
Unable to find image 'nigelpoulton/pluralsight-docker-ci:latest' locally
latest: Pulling from nigelpoulton/pluralsight-docker-ci
a3ed95caeb02: Pull complete
3b231ed5aa2f: Pull complete
7e4f9cd54d46: Pull complete
929432235e51: Pull complete
6899ef41c594: Pull complete
0b38fccd0dab: Pull complete
Digest: sha256:7a6b0125fe7893e70dc63b2c42ad779e5866c6d2779ceb9b12a28e2c38bd8d3d
Status: Downloaded newer image for nigelpoulton/pluralsight-docker-ci:latest
27b4bc07a3e299e738ea8fc05bb6de9fa160c192a5ab71886b84e432d5422aea #这就是docker host主机上面的container id
PS G:\dockerdata> docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
27b4bc07a3e2 nigelpoulton/pluralsight-docker-ci "/bin/sh -c 'cd /src…" 4 minutes ago Up 4 minutes 0.0.0.0:9090->8080/tcp web

上面的命令执行后将在docker host主机上启动一个web服务器,使用http://localhost:9090就可以直接访问到该container的服务了!!

启动一个container并且在该container中执行bash

PS G:\dockerdata> docker run -it --name temp ubuntu:latest /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
6cf436f81810: Pull complete
987088a85b96: Pull complete
b4624b3efe06: Pull complete
d42beb8ded59: Pull complete
Digest: sha256:7a47ccc3bbe8a451b500d2b53104868b46d60ee8f5b35a24b41a86077c650210
Status: Downloaded newer image for ubuntu:latest
root@9b4970dcb02a:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

简单批量维护命令:

PS G:\dockerdata> docker ps -aq
9b4970dcb02a
27b4bc07a3e2
135da1372a06
PS G:\dockerdata> docker stop $(docker ps -aq)
9b4970dcb02a
27b4bc07a3e2
135da1372a06

 

swarm:

一群docker engines加入一个cluster分组就被称为swarm, a cluster = a swarm

swarm里面的engine工作于swarm mode

manager nodes维护swarm,

worker nodes 执行manager nodes分发过来的tasks

services: declarative/scalable

tasks: assigned to worker nodes ,means  ~ containers  currently

docker swarm init --advertise-addr xxx:2377 --listen-addr xxx:2377 
# engine port 2375, secure engine port: 2376, swarm port: 2377

docker service create --name web-fe --replicas 5 ...

 

Container

container is isolated area of an OS with resource usage limits applied.

它由name space和control group(限定cpu,ram,networking吞吐量,io吞吐量)约束形成的独立运行环境。

engine 

engine通过外部api接受命令负责屏蔽OS的namespace及cgroup,并且创建对应的container运行于host环境中

不同module协同工作实现的container运行过程

一旦container被启动运行后,containerd和它之间就可以没有了关系,以后可以通过发现过程来取得新的联系

image

image包含app运行所需的

1.OS Files library, objects;

2. app files

3. manifest-->定义这些文件是如何组织在一起工作的

image是层叠结构的文件系统.

docker image pull redis的工作分两步:第一步从registry这里获取到manifest文件;第二步pull layers

 

docker history redis  # 罗列出所有能够创建redis这个image的命令列表
$ docker image inspect redis
[
    {
        "Id": "sha256:0f55cf3661e92cc44014f9d93e6f7cbd2a59b7220a26edcdb0828289cf6a361f",
        "RepoTags": [
            "redis:latest"
        ],
        "RepoDigests": [
            "redis@sha256:dd5b84ce536dffdcab79024f4df5485d010affa09e6c399b215e199a0dca38c4"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2019-02-06T09:02:43.375297494Z",
        "Container": "1abd8103d4a4423fa8339aabdb3442026bf6b8e9dca21c4ed44973e73ffd90cf",
        "ContainerConfig": {
            "Hostname": "1abd8103d4a4",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "6379/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.10",
                "REDIS_VERSION=5.0.3",
                "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.3.tar.gz",
                "REDIS_DOWNLOAD_SHA=e290b4ddf817b26254a74d5d564095b11f9cd20d8f165459efa53eb63cd93e02"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"redis-server\"]"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:68d73e8c5e2090bf28a588569b92595ab2d60e38eb92ba968be552b496eb6ed3",
            "Volumes": {
                "/data": {}
            },
            "WorkingDir": "/data",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "18.06.1-ce",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "6379/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.10",
                "REDIS_VERSION=5.0.3",
                "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.3.tar.gz",
                "REDIS_DOWNLOAD_SHA=e290b4ddf817b26254a74d5d564095b11f9cd20d8f165459efa53eb63cd93e02"
            ],
            "Cmd": [
                "redis-server"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:68d73e8c5e2090bf28a588569b92595ab2d60e38eb92ba968be552b496eb6ed3",
            "Volumes": {
                "/data": {}
            },
            "WorkingDir": "/data",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 94993858,
        "VirtualSize": 94993858,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/1aeb385f6b9def8e0c2048213c6a68446b233f4d44c9230657859257505dace5/diff:/var/lib/docker/overlay2/5e8dc35e2ed45cee79a8b5108cc74bfe7000311e75db45bd83d254f21e1892e7/diff:/var/lib/docker/overlay2/bfb61b0335946076ea36f25716da9e43d133dd6e8cf0211e7abadb6a23c001f3/diff:/var/lib/docker/overlay2/591b4074f127d18d3b7d84078891e464eb9c808439bd70f78f653ece9fa1101e/diff:/var/lib/docker/overlay2/30c283b2c4910e51dc162b23d6344575697e9fb478aeccf330edcef05c90aeae/diff",
                "MergedDir": "/var/lib/docker/overlay2/358068125c47e5995e7b1308b71a7ba11dd1509a9a69b36c1495e5c23a5c71f0/merged",
                "UpperDir": "/var/lib/docker/overlay2/358068125c47e5995e7b1308b71a7ba11dd1509a9a69b36c1495e5c23a5c71f0/diff",
                "WorkDir": "/var/lib/docker/overlay2/358068125c47e5995e7b1308b71a7ba11dd1509a9a69b36c1495e5c23a5c71f0/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:0a07e81f5da36e4cd6c89d9bc3af643345e56bb2ed74cc8772e42ec0d393aee3",
                "sha256:943fb767d8100f2c44a54abbdde4bf2c0f6340da71125f4ef73ad2db7007841d",
                "sha256:16d37f04beb4896e44557df69c060fc93e1486391c4c3dedf3c6ebd773098d90",
                "sha256:5e1afad325f9c970c66dcc5db47d19f034691f29492bf2fe83b7fec680a9d122",
                "sha256:d98df0140af1ee738e8987862268e80074503ab33212f6ebe253195b0f461a43",
                "sha256:b437bb5668d3cd5424015d7b7aefc99332c4af3530b17367e6d9d067ce9bb6d5"
            ]
        },

        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

docker支持的网络模式

bridge模式: -net = bridge

这是默认网络,docker engine一旦启动后就会在宿主host上创建一个docker0的网桥(可以理解为switch),默认创建的容器都是添加到该网桥(switch)的网段中,可以想象这些容器就是连接在一个交换机的不同网口上,他们的网关就是docker0的ip(172.17.0.1)

host模式: -net = host

容器不会获得独立的network namespace,而是与宿主host主机共用一个,这也意味着container不会拥有自己的网卡信息,而是使用宿主机的。host模式的容器之间除了网络,其他都是隔离的。

none模式: -net = none

容器将获取独立的network namespace,但是不会为容器进行任何网络配置,需要我们自己去手工配置

container模式: -net = container:Name/ID

这种模式创建的容器将与指定的容器使用同一个network namespace,具有同样的网络配置信息,这种容器之间除了网络,其他都是隔离的。

自定义网络模式:

与默认的bridge原理一样,但自定义网络内部具备dns发现的能力,可以通过容器名或者主机名容器之间网络通信

docker logs通过查看容器log来定位调试问题

默认情况下docker logs和ldocker service logs命令显示命令执行的输出,就像是你在命令行直接执行该程序时的情形一样。unix和linux程序往往会打开三个I/O Streams,分别称为STDIN,STDOUT,STDERR。其中stdin是命令的input stream, 可以包含从键盘获得的input或者从其他命令的输出作为input;    stdout是应用程序的normal output.而stderr则被用于错误信息输出。默认情况下,docker logs将显示命令的stdout和stderr输出。基于以上信息,在多重场景下docker logs无法提供有效的log:

1. 如果你使用了一个logging driver(logging driver是docker提供的从运行的container或者service中获取有用信息的机制)将log发往一个文件,或者一个外部的主机,一个数据库或者其他的logging back-end,那么docker logs将不会显示任何有用的信息;

https://docs.docker.com/config/containers/logging/configure/

docker daemon有一个默认的logging driver,每个启动的容器都将使用它除非你配置了使用一个不同的logging driver.

比如,我们可以配置docker daemon使用syslog来做log的收集,他就会通过syslog将运行容器的stdout,stderr信息实时打印到远程服务器。在这种情况下,我们实际上就不可能使用docker logs来查看运行时的状态,而只能通过syslog服务器来获取信息;

2. 如果我们的image运行在non-interactive 进程中,比如web server或者database的进程,这种进程会将其输出信息直接送往log文件,而不是stdout或者stderr.

在这种情况下,我们一方面可以进入容器来查看类似nginx和myql的log文件获取运行时信息;另外一方面官方的nginx,httpd都提供了workaround方式,比如nginx image的构建中通过创建一个符号连接将 /var/log/nginx/access.log指向到/dev/stdout; 将/var/log/nginx/error.log指向到/dev/stderr的方式来解决。 httpd image则默认输出到/proc/self/fd/1 (stdout),   error则将写往 /proc/self/fd/2(stderr)

这样我们依然可以通过docker logs -tail 8 -f来实时查看log

docker networking

https://success.docker.com/article/networking

建议使用自定义网络,docker默认的docker0 bridge支持--link参数,但是--link参数将来也会废弃。

$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242bd712cd8       no
br-9694b511a9af         8000.0242e7c72a3d       no
br-81195db0babc         8000.0242d6feb257       no              veth375600f
                                                                vethbc86c59
br-c301fa0c30d5         8000.024241d93a8e       no              veth73040a3
                                                                veth72eebce
                                                                vethd5af9cd
                                                                veth12d8ab4
                                                                veth6d89a9d        

咱们来看一下使用laradock docker-compose up -d nginx mysql之后的网络拓补图分析过程:

$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242bd712cd8       no
br-9694b511a9af         8000.0242e7c72a3d       no
br-81195db0babc         8000.0242d6feb257       no              veth375600f
                                                        vethbc86c59
br-c301fa0c30d5         8000.024241d93a8e       no              veth73040a3
                                                        veth72eebce
                                                        vethd5af9cd
                                                        veth12d8ab4
                                                        veth6d89a9d
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                           NAMES
25dd9253f860        laradock_nginx       "/bin/bash /opt/star…"   2 hours ago         Up 2 hours          0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   laradock_nginx_1
a2070a01035c        laradock_php-fpm     "docker-php-entrypoi…"   2 hours ago         Up 2 hours          9000/tcp                           laradock_php-fpm_1
d1f9327cb61c        laradock_workspace   "/sbin/my_init"          2 hours ago         Up 2 hours          0.0.0.0:2222->22/tcp                       laradock_workspace_1
a70f2b180a0d        laradock_mysql       "docker-entrypoint.s…"   2 hours ago         Up 2 hours          0.0.0.0:3306->3306/tcp, 33060/tcp          laradock_mysql_1
01f438a6efa9        docker:dind          "dockerd-entrypoint.…"   2 hours ago         Up 2 hours          2375/tcp                           laradock_docker-in-docker_1
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
60e8d0d3dd8c        bridge              bridge              local
5130e0e1e134        host                host                local
c301fa0c30d5        laradock_backend    bridge              local
9694b511a9af        laradock_default    bridge              local
81195db0babc        laradock_frontend   bridge              local
cb098f68c7be        none                null                local
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242bd712cd8       no
br-9694b511a9af         8000.0242e7c72a3d       no
br-81195db0babc         8000.0242d6feb257       no              veth375600f
                                                        vethbc86c59
br-c301fa0c30d5         8000.024241d93a8e       no              veth73040a3
                                                        veth72eebce
                                                        vethd5af9cd
                                                        veth12d8ab4
                                                        veth6d89a9d
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network inspect c301
[
    {
        "Name": "laradock_backend",
        "Id": "c301fa0c30d5f44e8daab0ffecf8166012f63edee764ce2abeaf3e884ce54446",
        "Created": "2019-03-13T12:25:42.645372888Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.21.0.0/16",
                    "Gateway": "172.21.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "01f438a6efa996b4e5c8df8f36b742ae468bf09762a1e6eabdefd66f5c920e11": {
                "Name": "laradock_docker-in-docker_1",
                "EndpointID": "d01c244fc579cd288bf8b1e79a6e936486b348f3167db3e7034044e08beae44c",
                "MacAddress": "02:42:ac:15:00:02",
                "IPv4Address": "172.21.0.2/16",
                "IPv6Address": ""
            },
            "25dd9253f860588321b1ff05ae4b43226ae6c22f83044973b86c0c57871ed924": {
                "Name": "laradock_nginx_1",
                "EndpointID": "24b527973345960c10bf2f97a11612c33562a5146732e9c4049625fc99cadca8",
                "MacAddress": "02:42:ac:15:00:06",
                "IPv4Address": "172.21.0.6/16",
                "IPv6Address": ""
            },
            "a2070a01035cbd8c15005c074e9e19ea18f795cdf6a2bc48863d86cc638b35b5": {
                "Name": "laradock_php-fpm_1",
                "EndpointID": "b3071a2d3d019a6e10b0b778ce0b4f99efbaff28898d295d3829d41e840aa15c",
                "MacAddress": "02:42:ac:15:00:05",
                "IPv4Address": "172.21.0.5/16",
                "IPv6Address": ""
            },
            "a70f2b180a0dfcc18c26e4991897946b9389b678ce4ea2cd6527859c301bb78e": {
                "Name": "laradock_mysql_1",
                "EndpointID": "815e801431b16f4a245b0a243e08cc9642482b3933b09480928ae40fadd56b14",
                "MacAddress": "02:42:ac:15:00:03",
                "IPv4Address": "172.21.0.3/16",
                "IPv6Address": ""
            },
            "d1f9327cb61cbd26f43c55911cbffa1cd3f53b912f783725bbf73e0c6edad5ef": {
                "Name": "laradock_workspace_1",
                "EndpointID": "5bbe5ceae7d15ff3eb65236ab0243619591d69474f3a0a13df07e507d2e25a22",
                "MacAddress": "02:42:ac:15:00:04",
                "IPv4Address": "172.21.0.4/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "backend",
            "com.docker.compose.project": "laradock",
            "com.docker.compose.version": "1.23.2"
        }
    }
]
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network inspect 8119
[
    {
        "Name": "laradock_frontend",
        "Id": "81195db0babc4aff1b4ae09b2ad078038b74643c798b396409a46f2948ff89c8",
        "Created": "2019-03-13T12:25:42.057604176Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.20.0.0/16",
                    "Gateway": "172.20.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "25dd9253f860588321b1ff05ae4b43226ae6c22f83044973b86c0c57871ed924": {
                "Name": "laradock_nginx_1",
                "EndpointID": "e1ad08b19608cc3884a9da04e509a71566ca4847245db12310d77463bcb80814",
                "MacAddress": "02:42:ac:14:00:03",
                "IPv4Address": "172.20.0.3/16",
                "IPv6Address": ""
            },
            "d1f9327cb61cbd26f43c55911cbffa1cd3f53b912f783725bbf73e0c6edad5ef": {
                "Name": "laradock_workspace_1",
                "EndpointID": "64d65215f6e0d6135bb7dbf5f341bd858972bc8e869cd8a177991d27d5652491",
                "MacAddress": "02:42:ac:14:00:02",
                "IPv4Address": "172.20.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "frontend",
            "com.docker.compose.project": "laradock",
            "com.docker.compose.version": "1.23.2"
        }
    }
]
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network inspect 9694
[
    {
        "Name": "laradock_default",
        "Id": "9694b511a9afac9a43d3b45ae4296976bf193633148465141f5e0cd787b12082",
        "Created": "2019-03-13T12:25:41.924774946Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "laradock",
            "com.docker.compose.version": "1.23.2"
        }
    }
]
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network inspect 5130
[
    {
        "Name": "host",
        "Id": "5130e0e1e1340fb58d5704528257cfb0f7dc98e9f718055c3e32f96705355597",
        "Created": "2019-03-13T12:23:30.472608001Z",
        "Scope": "local",
        "Driver": "host",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
[node1] (local) root@192.168.0.13 ~/apiato/laradock
$ docker network inspect 60e8
[
    {
        "Name": "bridge",
        "Id": "60e8d0d3dd8c376a31a802f9965227301dc06a74910852895f9b010d07fd4417",
        "Created": "2019-03-13T12:23:30.540268336Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

关于环境变量env

https://vsupalov.com/docker-arg-env-variable-guide/

关于volumes

https://docs.docker.com/storage/volumes/

如果我们不需要永久持久化,但是又需要在运行时保存一些状态信息,可以考虑使用tmpfs mount直接mount到内存中,加快速度。

 

容器中进程启动的两种模式:shell模式和exec模式

docker容器内启动的所有进程全部都是宿主机上的独立进程;另外,该进程是不是docker容器进程本身(即:1号进程)取决于dockerfile的写法。

在ENTRYPOINT和CMD命令中,有两种不同的进程执行方式:shell和exec.

1.在shell方式中,CMD/ENTRYPOINT指令如下方式定义

CMD executable param1 param2

此时PID=1的进程为/bin/sh -c "executable param1 param2",真正的executable工作进程是其子进程

2.在exec方式中,CMD/ENTRYPOINT指令则如下方式定义:

CMD ["executable", "param1","param2"]

此时PID=1的进程直接是工作进程executable param1 param2

这两种启动模式还带来进程退出机制的区别,如果使用不当会造成僵尸进程。

docker提供了docker stop和docker kill两个命令向1号进程发送信号。当执行docker stop时,docker会先想PID1的进程发送一个SIGTERM信号,如果容器收到该信号后没有结束进程,则docker daemon会在等待10秒后发送SIGKILL信号,将容器进程杀死(PID1)并变为退出状态。

PID1的进程必须能够正确处理SIGTERM信号并通知所有子进程退出。如果用shell脚本启动容器,其1号进程为shell进程,而shell进程中并没有对SIGTERM信号的处理逻辑,因此会忽略接收到的SIGTERM信号,这样就无法实现优雅的退出(比如持久化数据),因此docker官方建议的模式是:令每个容器中只包含一个进程,同时采用exec模式启动进程。或者使用定制化shell脚本启动,需要能够接受SIGTERM信号并且分发该信号到所有的子进程,或者工作进程以exec方式启动,同时该工作进程能够处理SIGTERM并负责分发给子进程

docker daemon只监控1号进程。

Docker容器的运行时模型

linux中的父进程用fork命令创建子进程,然后调用exec执行子进程函数,每个进程都有一个PID。另外,除了常见的一般应用进程,操作系统中还有以下特殊的进程。

1. PID=0是调度进程,该进程是内核的一部分,不会执行磁盘上的任何程序;

2. PID=1为init进程,通常读取与系统有关的初始化文件/etc/rc*文件,/etc/inittab,/etc/init.d/中的文件

3. PID=2为页守护进程,负责支持虚拟存储系统的分页操作。

Docker启动时,利用fork命令从Docker-containerd进程中fork出一个子进程,然后以exec方式启动自己的程序。容器进程被fork之后便创建了namespace,下面就要执行一系列的初始化操作,该操作分为三个阶段,dockerinit负责初始化网络栈;ENTRYPOINT负责完成用户态配置;CMD负责启动入口。启动后的docker容器和docker daemon就通过sock文件描述符实现IPC通信。

docker volumes vs binding mount

docker数据持久化建议有两种或者说3种模式:

1. bind mounts;

2. named volumes

3. volumes in dockerfile

bind mounts的作用是将host的本地目录mount到container中,

docker run -v /hostdir:/containerdir IMAGE_NAME
docker run --mount type=bind,source=/hostdir,target=/containerdir IMAGE_NAME

named volumes是通过docker volume create volume_name的方式手工创建的volumes,他们都存储在/var/lib/docker/volumes目录下,可以仅仅使用volume name来引用。比如,如果我们创建了mysql_data这个volume,则可以在docker run -v mysql_data:/containerdata IMAGE_NAME来引用它。

而在dockerfile中定义的volumes,是使用VOLUME指令来创建的,他们也存储于/var/lib/docker/volumes中,但是他们没有一个自定义的名字,一般使用hash作为其名称,并且dockerfile中定义的volumes后续参数实际上是指定了在container中的路径,如果在image中已经populate了数据,则container执行后会自动将该目录数据copy到host自动创建的目录中(如果指定了host路径则不会覆盖host的数据!)

https://stackoverflow.com/questions/41935435/understanding-volume-instruction-in-dockerfile

docker from development to production

一般来说,我们在开发时希望通过一个volume来绑定host主机的source代码以方便即改即调的快捷流程,但是在production阶段

我们往往直接将代码复制到image中从而实现容器就是代码,独立于主机可以在任何地点运行的便捷。

一个比较好的策略是

docker-compose.yml中这样定义:

version: '2'
services:
    app:
        build: .
        image: app:1.0.0-test
        volumes:
        - ./host_src:/user/share/nginx/html
        ports:
        - "8080:80"

其中nginx app build时需要使用的Dockerfile可以简单定义如下:

FROM nginx
COPY host_src /usr/share/nginx/html

在nginx app中首先COPY host_src到container对应的目录中,随后在dev的compose yml中为方便实时修改代码和测试则mount了一个volume将host_src也映射到nginx app中相同目录下;

随后,在nginx app变为production时,我们可以这样创建一个docker-compose-production.yml

version: '2'
services:
    app:
        build: .
        image: app:1.0.0-production
        ports:
        - "80:80"

和dev的yml文件相比,我们仅仅剔除了volume的绑定,而是直接使用COPY到image中的代码去运行

是否可以修改从parent image中继承的volume data?

比如,A image的dockerfile如下:

FROM bash
RUN mkdir "/data" && echo "FOO" > "/data/test"
VOLUME "/data"

我们再定义一个B image,它继承于A,我们在dockerfile中希望修改A image中的“默认”数据:

FROM A
RUN echo "BAR" > "/data/test"

以上测试中B image中的/data/test实际上其值为FOO,并不是BAR

这实际上是Docker本身的特性使然,如何workaround?

1. 直接修改parent docker file,我们从google搜索以下信息

docker <image-name:version> source

我们就能够找到对应的父亲image的dockerfile,通过删除其volume来实现。

VOLUMES本身并不是IMAGE的一部分,因此我们需要通过seed data来实现上面的需求。当docker image被放到另外地方运行时,它将在启动后是一个空的volume,因此,如果你希望将数据和image一起打包,就不要使用volume,而应该使用copy.

如果你确实需要重新build新的image的话,你应该先将这个volume删除掉。

https://stackoverflow.com/questions/46227454/modifying-volume-data-inherited-from-parent-image

docker volume create$docker run -v /host_path:container_path$VOLUME in Dockerfile

使用volume是docker推荐的持久化数据的方式,但是volume的用法有很多种,他们之间到底有什么区别?

要回答这个问题先得明白"volume是一个持久化数据的目录,存在于/var/lib/docker/volumes/..."

这个事实。你可以:

1. 在Dockerfile中声明一个volume,这意味着每次从image中运行一个container时,该volume就将被created,但是确是(empty)空的,即便你并未使用一个-v参数在docker run -v命令中

2.你可以在运行时指定mount的volume:

docker run -v [host-dir:]container-dir
docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest
# -v和--mount有相同的效果,如果还不存在myvol2则创建一个volume到/var/lib/docker/volumes目录,随后mount到container中
docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

这种模式就结合了VOLUME in dockerfile和docker run -v两者的优点,他会将host folder mount到由container持久化并存储于/var/lib/docker/volumes/...的卷

3.docker volume create将创建一个命名式的volume,可以快速被其他容器来mount

https://stackoverflow.com/questions/34809646/what-is-the-purpose-of-volume-in-dockerfile

docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest
# 等价于以下命令,bind mount主机的目录到target机器上
docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest

 

dockerfile执行顺序及变更

1 FROM ubuntu:16.04
2 RUN apt-get update
3 RUN apt-get install nginx
4 RUN apt-get install php5

如果上面的dockerfile我们做过build,随后我们想把nginx换成apache,并重新build,则这时候第1和第2行不会再运行,因为都保存在cache中,但是第3和第4行都会重新执行,因为第3行做了变更,而第4行又依赖于第3行,因此第3和第4行都将重新执行最终构建出image

Docker AUFS原理

 

使用docker数据容器的备份策略

我们知道在网站日常运维中会有很多数据产生,包括数据库本身,很多配置文件,包括dockerfile, docker-compose等数据,如何备份这个数据是一个挑战。以前直接使用云主机提供商提供的数据卷镜像备份虽然可以work,但是往往备份了很多不必要的数据,额外占用的空间将产生额外的费用。而目前很多容器服务提供商能够免费提供私有数据容器存储,这又可以为我们节省一笔开支。

我的建议思路是:使用busybox基础镜像,COPY指令将需要备份的数据copy到镜像中,并且tag后push到私有仓库来存储。

FROM busybox:1.30
COPY ./datainhost /dataincontainer

需要注意的是./datainhost目录是相对于Dockerfile-databackup这个文件的相对路径。

如果需要copy不在build context中的目录到image中,可以这么做:

  • go to you build path
  • mkdir -p some_name
  • sudo mount --bind src_dir ./some_name

然后在dockerfile的copy指令中直接用some_name来引用外部文件夹并且实施copy即可。

 

随后在host上(包含dockerfile的那个目录上)执行以下shell命令:

docker build -f Dockerfile-databackup -t registry-internal.cn-shanghai.aliyuncs.com/namespace/reponame:$(date +"%F") .

该命令将会生成registry-internal.../reponame:2019-03-20类似的tag到构建好的image上去。

随后直接push一下就好了。

注意上述registry对于阿里云主机使用内网ip不占用带宽,非常快速好用

docker 子命令 tab completion

https://github.com/samneirinck/posh-docker

posted @ 2019-03-03 15:58  世有因果知因求果  阅读(1362)  评论(0编辑  收藏  举报