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:
. 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 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 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 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吞吐量)约束形成的独立运行环境。
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": ,
"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到内存中,加快速度。
容器中进程启动的两种模式: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: ''
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://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执行顺序及变更
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数据容器的备份策略
我们知道在网站日常运维中会有很多数据产生,包括数据库本身,很多配置文件,包括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
docker in all的更多相关文章
- docker——容器安装tomcat
写在前面: 继续docker的学习,学习了docker的基本常用命令之后,我在docker上安装jdk,tomcat两个基本的java web工具,这里对操作流程记录一下. 软件准备: 1.jdk-7 ...
- Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境
首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...
- Docker 第一篇--初识docker
已经多年不写博客, 看完<晓松奇谈>最后一期猛然觉醒, 决定仔细梳理下自己这几年的知识脉络. 既然决定写, 那么首先就从最近2年热门的开源项目Docker开始.Docker 这两年在国内很 ...
- 在docker中运行ASP.NET Core Web API应用程序(附AWS Windows Server 2016 widt Container实战案例)
环境准备 1.亚马逊EC2 Windows Server 2016 with Container 2.Visual Studio 2015 Enterprise(Profresianal要装Updat ...
- docker for mac 学习记录
docker基本命令 docker run -d -p 80:80 --name webserver nginx 运行容器并起别名 docker ps 展示目前启动的容器 docker ps -a 展 ...
- scrapy爬虫docker部署
spider_docker 接我上篇博客,为爬虫引用创建container,包括的模块:scrapy, mongo, celery, rabbitmq,连接https://github.com/Liu ...
- [原][Docker]特性与原理解析
Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...
- 开发者的利器:Docker 理解与使用
困扰写代码的机器难免会被我们安装上各种各样的开发工具.语言运行环境和引用库等一大堆的东西,长久以来不仅机器乱七八糟,而且有些相同的软件还有可能会安装不同的版本,这样又会导致一个项目正常运行了,却不小心 ...
- 使用python自动生成docker nginx反向代理配置
由于在测试环境上用docker部署了多个应用,而且他们的端口有的相同,有的又不相同,数量也比较多,在使用jenkins发版本的时候,不好配置,于是想要写一个脚本,能在docker 容器创建.停止的时候 ...
- 微服务与Docker介绍
什么是微服务 微服务应用的一个最大的优点是,它们往往比传统的应用程序更有效地利用计算资源.这是因为它们通过扩展组件来处理功能瓶颈问题.这样一来,开发人员只需要为额外的组件部署计算资源,而不需要部署一个 ...
随机推荐
- Python基础内容
1.注释 #单行注释 ‘“多行注释”’ 2.变量 Python没有声明变量的过程(动态类型) 变量名=值,如果是浮点数就定义为浮点类型,如果是整型就定义为整型,如果是字符串就定义为字符串 3.输入和输 ...
- java学习碰到死胡同了
一个月前看了java的异常处理,有点凌乱,各种异常好不习惯,特别是那个throws和throw,现在好多了,编程中使用打异常抛出没啥问题了,只是线程和输出输入流又遇到问题了,线程刚学,了解了Threa ...
- CentOS QT can't find lGL
直接安装: yum install libGL, yum install libGL-devel 库即可.
- ResNet 论文研读笔记
Deep Residual Learning for Image Recognition 原文链接 摘要 深度神经网络很难去训练,本文提出了一个残差学习框架来简化那些非常深的网络的训练,该框架使得层能 ...
- async/await 与 generator、co 的对比
之前写过一个分批预加载资源的插件,其实质便是串行执行异步,使用的方法是generator + promise -- 前几天写了一个爬虫,抓取页面的n个页面的音频资源,其也是串行执行异步,但是在使用的a ...
- laravel 单词
return view('admin.user.login'); 返回 admin文件夹下, user文件夹中 login文件模板 setcookie 语法 setcookie(name,value, ...
- vs code 插件收集
名称 简述 Auto Close Tag 自动闭合HTML标签 Auto Import Typescript自动import提示 Auto Rename Tag 修改HTML标签时,自动修改匹配的标签 ...
- IIS 是如何处理 ASP.NET 请求的
#main{ width:1250px; } #mainContent{ width:915px } img#imgTop{ max-width:850px; } Web 服务器 VS Web 应用程 ...
- node.js遇到的问题
1.cann't find module 'request' 不能找到’request' 模块 解决方法:找到项目的根路径,cd到该路径,运行命令 npm install request 2.no ...
- CSS 样式属性
大小 width 宽 body { min-width:1200px; height 高 } ...