docker in all

时间:2023-03-09 18:31:23
docker in all

docker vs hyper-v,vmware,xen,kvm

docker in all

docker in all

docker host, docker container, docker engineen, docker image

docker in all

images = stopped container

container = running images

docker操作示意圖

docker in all

workflow

docker in all

docker in all

开始使用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:
. The Docker client contacted the Docker daemon.
. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
. 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 in all

docker image到底包含了什么?

强烈建议: https://www.****.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 months ago .84kB
docker4w/nsenter-dockerd latest 2f1c802f322f 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" minutes ago Exited () 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 weeks ago .1MB
alpine latest caf27325b298 weeks ago .53MB
hello-world latest fce289e99eb9 months ago .84kB
docker4w/nsenter-dockerd latest 2f1c802f322f 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 : 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: --listen-addr xxx:
# engine port , secure engine port: , swarm port:

docker in all

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

Container

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

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

docker in all

engine

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

docker in all

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

docker in all

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

docker in all

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": ,
"VirtualSize": ,
"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 .0242bd712cd8 no
br-9694b511a9af .0242e7c72a3d no
br-81195db0babc .0242d6feb257 no veth375600f
vethbc86c59
br-c301fa0c30d5 .024241d93a8e no veth73040a3
veth72eebce
vethd5af9cd
veth12d8ab4
veth6d89a9d

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

$ brctl show
bridge name bridge id STP enabled interfaces
docker0 .0242bd712cd8 no
br-9694b511a9af .0242e7c72a3d no
br-81195db0babc .0242d6feb257 no veth375600f
vethbc86c59
br-c301fa0c30d5 .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…" hours ago Up hours 0.0.0.0:->/tcp, 0.0.0.0:->/tcp laradock_nginx_1
a2070a01035c laradock_php-fpm "docker-php-entrypoi…" hours ago Up hours /tcp laradock_php-fpm_1
d1f9327cb61c laradock_workspace "/sbin/my_init" hours ago Up hours 0.0.0.0:->/tcp laradock_workspace_1
a70f2b180a0d laradock_mysql "docker-entrypoint.s…" hours ago Up hours 0.0.0.0:->/tcp, /tcp laradock_mysql_1
01f438a6efa9 docker:dind "dockerd-entrypoint.…" hours ago Up hours /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 .0242bd712cd8 no
br-9694b511a9af .0242e7c72a3d no
br-81195db0babc .0242d6feb257 no veth375600f
vethbc86c59
br-c301fa0c30d5 .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
[
{
"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
[
{
"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
[
{
"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": ""
},
"Labels": {}
}
]

关于环境变量env

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

关于volumes

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

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

docker in all

容器中进程启动的两种模式: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://*.com/questions/41935435/understanding-volume-instruction-in-dockerfile

docker from development to production

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

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

一个比较好的策略是

docker-compose.yml中这样定义:

version: ''
services:
app:
build: .
image: app:1.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: ''
services:
app:
build: .
image: app:1.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://*.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://*.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执行顺序及变更

 FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install nginx
RUN apt-get install php5

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

Docker AUFS原理

docker in all

docker in all

使用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