docker in all

世有因果知因求果發表於2019-03-03

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不佔用頻寬,非常快速好用

 

相關文章