Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件。
容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。
是,但是也不完全是。虚拟化使得许多操作系统可同时在单个系统上运行。容器则可共享同一个操作系统内核,将应用进程与系统其他部分隔离开。
让多个操作系统在单个虚拟机监控程序上运行以实现虚拟化,并不能达成和使用容器同等的轻量级效果。
Linux 容器可从单个操作系统运行,在所有容器中共享该操作系统,因此应用和服务能够保持轻量级,并行快速运行。
“docker” 一词指代多种事物,包括开源社区项目、开源项目使用的工具、主导支持此类项目的公司 Docker Inc. 以及该公司官方支持的工具。
借助 docker ,可将容器当做重量轻、模块化的虚拟机使用。同时,还能获得高度的灵活性,从而实现对容器的高效创建、部署及复制,并能将其从一个环境顺利迁移至另一个环境。
docker 技术使用 Linux 内核和内核功能(例如 Cgroups 和 namespaces)来分隔进程,以便各进程相互独立运行。
容器工具(包括 docker)可提供基于镜像的部署模式。这使得它能够轻松跨多种环境,与其依赖程序共享应用或服务组。docker 还可在这一容器环境中自动部署应用程序(或者合并多种流程,以构建单个应用程序)。
否。docker 技术最初是基于 LXC 技术构建(大多数人都会将这一技术与“传统的” Linux 容器联系在一起),但后来它逐渐摆脱了对这种技术的依赖。
就轻量级虚拟化这一功能来看,LXC 非常有用,但它无法提供出色的开发人员或用户体验。除了运行容器之外,docker 技术还具备其他多项功能,包括简化用于构建容器、传输镜像以及控制镜像版本的流程。
传统的 Linux 容器使用 init 系统来管理多种进程。这意味着,所有应用程序都作为一个整体运行。与此相反,docker 技术鼓励应用程序各自独立运行其进程,并提供相应工具以实现这一功能。这种精细化运作模式自有其优势。
docker的主要目标是"Build,Ship and Run any App,Angwhere",构建,运输,处处运行。
每一个容器,他都有自己的文件系统rootfs。
docker支持通过扩展现有镜像,创建新的镜像。实际上,Docker Hub 中 99% 的镜像都是通过在base镜像中安装和配置需要的软件构建出来的。
从上图可以看到,新镜像是从base镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。
镜像分层最大的一个好处就是共享资源。
比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是不会被修改的,修改只会被限制在单个容器内。这就是容器 Copy-on-Write 特性。
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。
文件操作
文件操作 | 说明 |
---|---|
添加文件 | 在容器中创建文件时,新文件被添加到容器层中 |
读取文件 | 在容器中读取某个文件时,docker会从上往下依次往各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。 |
修改文件 | 在容器中修改已存在的文件时,docker会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改。 |
删除文件 | 在容器中修改已存在的文件时,docker也是从上往下依次在各镜像层中查找此文件。找到后,会在容器层记录下此删除操作。(只是记录删除操作) |
只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
这样就解释了前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
为了更加详细地了解docker的网络运行原理,下面挑选几种较为重要的网络模型进行研究。
类型 | 说明 |
---|---|
None | 不为容器配置任何网络功能,没有网络 --net=none |
Container | 与另一个运行中的容器共享Network Namespace,–net=container:containerID |
Host | 与主机共享Network Namespace,–net=host |
Bridge | docker设计的NAT网络模型(默认类型) |
Bridge默认docker网络隔离基于网络命名空间,在物理机上创建docker容器时会为每一个docker容器分配网络命名空间,并且把容器IP桥接到物理机的虚拟网桥上。
此模式下创建容器是不会为容器配置任何网络参数的,如:容器网卡、IP、通信路由等,全部需要自己去配置。
[root@docker ~]# docker run -it --network none busybox:latest /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
此模式和host模式很类似,只是此模式创建容器共享的是其他容器的IP和端口而不是物理机,此模式容器自身是不会配置网络和端口,创建此模式容器进去后,会发现里边的IP是你所指定的那个容器IP并且端口也是共享的,而且其它还是互相隔离的,如进程等。
[root@docker ~]# docker run -it --network container:mywordpress_db_1 busybox:latest /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
105: eth0@if106: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
此模式创建的容器没有自己独立的网络命名空间,是和物理机共享一个Network Namespace,并且共享物理机的所有端口与IP,并且这个模式认为是不安全的。
[root@docker ~]# docker run -it --network host busybox:latest /bin/sh
如上图所示为docker中bridge
驱动模式的示意图,其中最下面的模块表示主机上的网卡。当docker启动时会自动在主机上创建一个虚拟网桥docker0
,使用默认网络模式创建docker容器时会自动创建一对veth pair
接口,一端连接在docker容器中(如图容器中的eth0
),一端连接在虚拟网桥docker0
上(如图veth
)。这种veth pair
是一种虚拟网络设备,主要用于不同namespace中(意味着网络隔离)的网络通信,它总是成对存在的。在这里可以把它想象成一对靠虚拟网线连接起来的两个虚拟网卡,一端连接着docker容器,一端连接着虚拟网桥docker0
。
通过这种方式,不同docker容器之间可以通过ip地址互相通信,也可以通过虚拟网桥访问主机上的网络eth0
(添加iptables规则,将docker容器对目标地址发出的访问通过地址伪装的方式修改为主机对目标地址进行访问)。
如果想要外界网络访问docker容器时,需要在docker容器启动时加上参数-p [主机端口]:[容器端口]
进行端口映射,原理也是通过修改iptables规则将访问[主机端口]的数据转发到docker容器的[容器端口]中,但是这种做法也存在着占用主机有限的端口资源的缺点。
查看网络列表
[root@docker ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
b15e8a720d3b bridge bridge local
345d65b4c2a0 host host local
bc5e2a32bb55 mywordpress_default bridge local
ebf76eea91bb none null local
查看docker相关信息
[root@docker ~]# docker version
Client:
Version: 17.12.0-ce
API version: 1.35
Go version: go1.9.2
Git commit: c97c6d6
Built: Wed Dec 27 20:10:14 2017
OS/Arch: linux/amd64
Server:
Engine:
Version: 17.12.0-ce
API version: 1.35 (minimum version 1.12)
Go version: go1.9.2
Git commit: c97c6d6
Built: Wed Dec 27 20:12:46 2017
OS/Arch: linux/amd64
Experimental: false
配置docker镜像加速
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
启动容器
[root@docker ~]# docker run -d -p 80:80 nginx
Unable to find image 'nginx:latest' locally
Trying to pull repository docker.io/library/nginx ...
latest: Pulling from docker.io/library/nginx
a2abf6c4d29d: Pull complete
f3409a9a9e73: Pull complete
9919a6cbae9c: Pull complete
fc1ce43285d7: Pull complete
1f01ab499216: Pull complete
13cfaf79ff6d: Pull complete
Digest: sha256:d13dca1855de09e2fe392c58a66dd73d4ff4b71da4d1720bcf3f47b48c53ca1d
Status: Downloaded newer image for docker.io/nginx:latest
137a747ae587441071913b20874c981203d86e758a8f5309b577d2b924ad5114
# 容器启动后,在浏览器进行访问测试
参数说明
参数 | 说明 |
---|---|
run | 创建并运行一个容器 |
-d | 放入后台 |
-p | 端口映射 |
nginx | 镜像名称 |
搜索官方仓库镜像
[root@docker ~]# docker search centos
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/centos The official build of CentOS. 6936 [OK]
docker.io docker.io/ansible/centos7-ansible Ansible on Centos7 135 [OK]
......
参数说明
参数 | 说明 |
---|---|
NAME | 镜像名称 |
DESCRIPTION | 镜像说明 |
STARS | 点赞数量 |
OFFICIAL | 是否是官方的 |
AUTOMATED | 是否是自动构建的 |
获取镜像
根据镜像名称拉取镜像
[root@docker ~]# docker pull centos
Using default tag: latest
Trying to pull repository docker.io/library/centos ...
latest: Pulling from docker.io/library/centos
a1d0c7532777: Pull complete
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for docker.io/centos:latest
查看当前主机镜像列表
[root@docker ~]# docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/nginx latest f6987c8d6ed5 About an hour ago 141 MB
docker.io/centos latest 5d0da3dc9764 3 months ago 231 MB
拉第三方镜像方法
docker pull index.tenxcloud.com/tenxcloud/httpd
导出镜像
# 导出
[root@docker ~]# docker image save centos > docker-centos.tar.gz
删除镜像
[root@docker ~]# docker image rm centos:latest
Untagged: centos:latest
Untagged: docker.io/centos@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Deleted: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6
Deleted: sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59
[root@docker ~]# docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/nginx latest f6987c8d6ed5 About an hour ago 141 MB
[root@VM-4-4-centos ~]#
导入镜像
[root@docker ~]# docker image load -i docker-centos.tar.gz
74ddd0ec08fa: Loading layer 238.6 MB/238.6 MB
Loaded image: docker.io/centos:latest
[root@docker ~]# docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/nginx latest f6987c8d6ed5 About an hour ago 141 MB
docker.io/centos latest 5d0da3dc9764 3 months ago 231 MB
查看镜像的详细信息
[root@docker ~]# docker image inspect centos
[
{
"Id": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
"RepoTags": [
"docker.io/centos:latest"
],
......
最简单的运行一个容器
[root@docker ~]# docker run nginx
创建容器,两步走(不常用)
[root@docker ~]# docker create centos:latest /bin/bash
bb7f32368ecf0492adb59e20032ab2e6cf6a563a0e6751e58930ee5f7aaef204
[root@docker ~]# docker start stupefied_nobel
stupefied_nobel
快速启动容器方法
[root@docker ~]# docker run centos:latest /usr/bin/sleep 20;
容器内的第一个进程必须一直处于运行的状态,否则这个容器,就会处于退出状态!
查看正在运行的容器
[root@VM-4-4-centos ~]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
137a747ae587 nginx "/docker-entrypoin..." 17 minutes ago Up 17 minutes 0.0.0.0:80->80/tcp vigilant_goldstine
查看你容器详细信息/ip
[root@docker ~]# docker container inspect 容器名称/id
查看你所有容器(包括未运行的)
[root@VM-4-4-centos ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
137a747ae587 nginx "/docker-entrypoin..." 17 minutes ago Up 17 minutes 0.0.0.0:80->80/tcp vigilant_goldstine
停止容器
[root@docker ~]# docker stop 容器名称/id
或
[root@docker ~]# docker container kill 容器名称/id
启动时进入容器方法
[root@docker ~]# docker run -it #参数:-it 可交互终端
[root@docker ~]# docker run -it nginx:latest /bin/bash
root@79241093859e:/#
退出/离开容器
ctrl+p & ctrl+q
启动后进入容器的方法
启动一个docker
[root@docker ~]# docker run -it centos:latest
[root@1bf0f43c4d2f /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 15:47 pts/0 00:00:00 /bin/bash
root 13 1 0 15:47 pts/0 00:00:00 ps -ef
attach进入容器,使用pts/0 ,会让所用通过此方法进如放入用户看到同样的操作。
[root@docker ~]# docker attach 1bf0f43c4d2f
[root@1bf0f43c4d2f /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 15:47 pts/0 00:00:00 /bin/bash
root 14 1 0 15:49 pts/0 00:00:00 ps -ef
自命名启动一个容器 --name
[root@docker ~]# docker attach 1bf0f43c4d2f
[root@1bf0f43c4d2f /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 15:47 pts/0 00:00:00 /bin/bash
root 14 1 0 15:49 pts/0 00:00:00 ps -ef
exec 进入容器方法**(推荐使用)**
[root@docker ~]# docker exec -it clsn1 /bin/bash
[root@b20fa75b4b40 /]# 重新分配一个终端
[root@b20fa75b4b40 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 16:11 pts/0 00:00:00 /bin/bash
root 13 0 0 16:14 pts/1 00:00:00 /bin/bash
root 26 13 0 16:14 pts/1 00:00:00 ps -ef
删除所有容器
[root@docker ~]# docker rm -f `docker ps -a -q`
# -f 强制删除
启动时进行端口映射
-p参数端口映射
[root@docker ~]# docker run -d -p 8888:80 nginx:latest
287bec5c60263166c03e1fc5b0b8262fe76507be3dfae4ce5cd2ee2d1e8a89a9
不同指定映射方法
参数 | 说明 |
---|---|
-p hostPort:containerPort | 端口映射 -p 8080:80 |
-p ip:hostPort:containerPort | 配置监听地址 -p 10.0.0.100:80 |
-p ip::containerPort | 随机分配端口 -p 10.0.0.100::80 |
-p hostPort:containerPort:udp | 指定协议 -p 8080:80:tcp |
-p 81:80 -p 443:443 | 指定多个 |
随机映射
docker run -P (大P)# 需要镜像支持
挂载时创建卷
挂载卷
[root@docker ~]# docker run -d -p 80:80 -v /data:/usr/share/nginx/html nginx:latest
079786c1e297b5c5031e7a841160c74e91d4ad06516505043c60dbb78a259d09
容器内站点目录: /usr/share/nginx/html
在宿主机写入数据,查看
[root@docker ~]# echo "http://www.baidu.com" >/data/index.html
[root@docker01 ~]# curl 10.0.0.100
http://www.baidu.com
设置共享卷,使用同一个卷启动一个新的容器
[root@docker ~]# docker run -d -p 8080:80 -v /data:/usr/share/nginx/html nginx:latest
351f0bd78d273604bd0971b186979aa0f3cbf45247274493d2490527babb4e42
[root@docker01 ~]# curl 10.0.0.100:8080
http://www.baidu.com
查看卷列表
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
创建卷后挂载
创建一个卷
[root@docker ~]# docker volume create
f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
指定卷名
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local clsn
local f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
查看卷路径
[root@docker ~]# docker volume inspect clsn
[
{
"CreatedAt": "2018-02-01T00:39:25+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/clsn/_data",
"Name": "clsn",
"Options": {},
"Scope": "local"
}
]
使用卷创建
[root@docker ~]# docker run -d -p 9000:80 -v clsn:/usr/share/nginx/html nginx:latest
1434559cff996162da7ce71820ed8f5937fb7c02113bbc84e965845c219d3503
# 宿主机测试
[root@docker ~]# echo 'blog.baidu.com' >/var/lib/docker/volumes/clsn/_data/index.html
[root@docker01 ~]# curl 10.0.0.100:9000
blog.baidu.com
设置卷
[root@docker ~]# docker run -d -P --volumes-from 079786c1e297 nginx:latest
b54b9c9930b417ab3257c6e4a8280b54fae57043c0b76b9dc60b4788e92369fb
查看使用的端口
[root@docker ~]# netstat -lntup
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1400/sshd
tcp 0 0 10.0.0.100:2375 0.0.0.0:* LISTEN 26218/dockerd
tcp6 0 0 :::9000 :::* LISTEN 32015/docker-proxy
tcp6 0 0 :::8080 :::* LISTEN 31853/docker-proxy
tcp6 0 0 :::80 :::* LISTEN 31752/docker-proxy
tcp6 0 0 :::22 :::* LISTEN 1400/sshd
tcp6 0 0 :::32769 :::* LISTEN 32300/docker-proxy
[root@docker ~]# curl 10.0.0.100:32769
http://www.baidu.com
docker build
命令来自动化地从 dockerfile 所描述的步骤来构建自定义的 Docker镜像,这比去命令行一条条指令执行的方式构建高效得多。官方构建dockerffile文件参考
https://github.com/CentOS/CentOS-Dockerfiles
dockerfile主要组成部分:
dockerfile常用指令:
dockerfile其他指令:
创建第一个Dockerfile文件
# 创建目录
[root@docker base]# cd /opt/base
# 创建Dcokerfile文件,注意大小写
[root@docker01 base]# vim Dockerfile
FROM centos:6.8
RUN yum install openssh-server -y
RUN echo "root:123456" |chpasswd
RUN /etc/init.d/sshd start
CMD ["/usr/sbin/sshd","-D"]
构建docker镜像
[root@docker base]# docker image build -t centos6.8-ssh .
-t 为镜像标签打标签 . 表示当前路径
使用自构建的镜像启动
[root@docker base]# docker run -d -p 2022:22 centos6.8-ssh-b
dc3027d3c15dac881e8e2aeff80724216f3ac725f142daa66484f7cb5d074e7a
就典型的Linux基础镜像来说,大小关系如下:
Ubuntu > CentOS > Debian
docker build -t=“centos:wordpress" .
例如上面的这个centos镜像是用来做wordpress用的,所以已经集成了wordpress功能,这一看就很清晰明了
FROM debian:twelveguo
什么是镜像缓存?
由 dockerfile 最终构建出来的镜像是在基础镜像之上一层层叠加而得,因此在过程中会产生一个个新的镜像层。docker daemon 在构建镜像的过程中会缓存一系列中间镜像。
docker build镜像时,会顺序执行dockerfile中的指令,并同时比较当前指令和其基础镜像的所有子镜像,若发现有一个子镜像也是由相同的指令生成,则命中缓存,同时可以直接使用该子镜像而避免再去重新生成了。
为了有效地使用缓存,需要保证 Dockerfile 中指令的连续一致,尽量将相同指令的部分放在前面,而将有差异性的指令放在后面
**举例:**假如想用dockerfile方式 基于最基本的centOS 镜像来构建两个不同的镜像时,两个dockerfile的开头可以相同:
FROM centos:latest
# 下面安装两个常用的工具
RUN yum install -y net-tools.x86_64
RUN yum install lrzsz
######## 上面为两个Dockerfile文件中相同的部分########
###### 下面为两个Dockerfile文件中不同的部分######
......
虽然两者都可以添加文件到镜像中,但在一般用法中,还是推荐以COPY指令为首选,原因在于ADD指令并没有COPY指令来的纯粹,ADD会添加一些额外功能,典型的如下 ADD一个压缩包时,其不仅会复制,还会自动解压,而有时并不需要这种额外的功能。
ADD twelveguo.tar.gz /path
除此之外,在需要添加多个文件到镜像中的时候,不要一次性集中添加,而是选择 按需 在必要时 逐个 添加即可,因为这样有利于利用镜像缓存。
虽然上面说推荐通过 COPY 命令来向镜像中添加多个文件,然而实际情况中,若文件大而多的时候还是应该优先用 docker-v
命令来挂载文件,而不是依赖于 ADD 或者 COPY
dockerfile 制作镜像时,会组合 CMD 和 ENTRYPOINT 指令来作为容器运行时的默认命令:即 CMD + ENTRYPOINT。此时的默认命令组成中:
docker run
命令中提供的参数会覆盖CMD的指令内容。举个例子:
FROM centos:latest
MAINTAINER [email protected]
ENTRYPOINT [ "ls", "-l"]
CMD ["-a"]
若以默认命令运行容器,可以发现,执行的是 ls-a-l
命令:
若 docker run
中增加参数 -t
docker run -it --rm --name test centos:twelveguo -t
也可以发现执行的是 ls-l-t
,即 Dockerfile 中的 CMD 原参数被覆盖了:
因此推荐的使用方式是:
dockerfile 可以通过EXPOSE指令将容器端口映射到主机端口上,但这样会导致镜像在一台主机上仅能启动一个容器!
所以应该在 docker run
命令中来用 -p
参数来指定端口映射,而不要将该工作置于 dockerfile 之中:
#尽量避免这种方式
EXPOSE 8080:8899
#选择仅仅暴露端口即可,端口映射的任务交给docker run去做
EXPOSE 8080