docker容器与镜像

docker镜像:业务代码和运行环境进行整体的打包.

1.1快速创建环境(开发,测试,生产)

1.2整体交付(运行环境+代码(之前只是代码))

1.3可保证环境(开发,测试,生产)的一致性

1.4更好的完成devops

镜像(Image)就是一堆只读层(read-only layer)的统一视角

这些层是Docker内部的实现细节,并且能够在主机的文件系统上访问到,统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统,你可以在你的主机文件系统上找到有关这些层的文件.它们存在于/var/lib/docker/aufs目录下

1
sudo tree -L 1 /var/lib/docker/

容器=镜像+读写层.并且容器的定义并没有提及是否要运行容器

1
2
3
4
5
6
# 创建容器
docker create <image_id>
# 运行容器
docker start <image_id>
# 上两条命令组合下如下命令
docker run <image_id>

所以容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的.

镜像分层技术:AUFS (Another UnionFS)支持将多个目录挂载到同一个虚拟目录下.已构建的镜像会设置成只读模式,read-write操作是在read-only上的一种增量操作.

把一个镜像创造成一个容器才能往里写东西.
所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中.UnionFS的一个最主要的应用是,把一张CD/DVD和一个硬盘目录给联合 mount在一起
然后,你就可以对这个只读的CD/DVD上的文件进行修改(当然,修改的文件存于硬盘上的目录里)

docker容器管理技术,是内核技术,容器本身是进程 其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西

它并不是虚拟技术(VM),docker最关键的点是提出了docker images标准化,image打包了应用:如nginx镜像,通过镜像启动一个nginx容器,其实就是在主机上启动了一个nignx进程.
容器不等于微服务,推荐只运行一个服务,如果运行多个服务,需要结合进程管理工具(supervisor,S6),
因为容器本身就是进程,所以数据库容器也可以运行,但需要对数据做好保护(volume).

既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数.CMD 指令就是用于指定默认的容器主进程的启动命令的,
在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash.

容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念.

ubuntu install docker

1
2
3
4
5
6
7
8
9
10
11
12
sudo apt-get update
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-cache policy docker-ce
sudo apt-get install -y docker-ce
sudo systemctl status docker
sudo usermod -aG docker $(whoami)
service docker start
docker images
# 虚拟机是用此方法下载 当前用户就可以使用whoami

debain install docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 坑一
sudo apt-get update
sudo apt-get install docker.io
apt-get purge docker.io*
# 坑二
apt-get dist-upgrade -y
apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys \
58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo debian-jessie main" | \
tee /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install docker-engine -y
apt-get purge docker-engine -y
# 成功
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 \
--recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main " > \
/etc/apt/sources.list.d/docker.list
apt-get update
apt-get install docker-engine=1.10.1-0~trusty
apt install docker.io
docker -v
# 本机是此方法下载docker 必须是root身份才能使用

启动docker服务

docker官方镜像 类似AppStore 是由各自官方机构做的镜像认证后,上传到docker官方镜像的,这个仓库的地址是在国外,所以下载的速度会慢点
利用镜像的分层技术,如果主机上已有layer(层)存在,那只会下载新增加的layer(类似git代码提交机制)

1
2
3
4
5
6
7
8
9
service docker start
docker port nostalgic_morse 5000 # 查看主机端口在容器的映射情况
# modify docker daemon启动参数 Docker的配置文件
vim /usr/lib/systemd/system/docker.service
# centos ubuntu:/lib/systemd/system/docker.service
ExecStart=之前的不要动加上 --insecure-registry=0.0.0.0/0
systemctl daemon-reload
systemctl restart docker
ps aux |grep docker #看参数有没有正确的添加.

容器操作命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
docker run -dit -p 80:80 --name app nginx
# -d后台运行 -it此时的容器就有了标准的输入和输出
docker create -p 80:80 --name app nginx
# 创建容器
docker start app #运行容器
docker commit <container-id>
# 将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像
# 用Dockerfile 生成镜像,命名为app
docker build -t app .
docker exec -it app bash
# 在运行中的容器执行一个新进程
# 以bash的方式登陆到容器里面 exit 从容器中退出
docker exec -it app bash ls /tmp
docker rename app new_app #重命容器名
docker stop/kill/pause/unpause/restart <container_id>
# 对容器中的进程发信号
docker inspect/stats/port/ps/top/dip/dpid <container-id>
# 查看容器状态
docker top app #查看当前容器跑了多少进程
docker ps # 列出所有运行中的容器,这隐藏了非运行态容器的存在
docker ps -a # 查看所有容器
docker inspect nginx/app |grep -i memory # 查看指定镜像/容器
# 会提取出容器或者镜像最顶层的元数据
docker stats $(docker ps | awk 'NR>1 {print $NF}')
docker update -m 256m app #更新容器信息
docker rm -f -v app # 移除构成容器的可读写层
# -f 强制删除 -v 连同Volumes也一起删除
# -v如果这个容器有Volume这个文件存在的话,也一起删除掉
docker copy 本地文件 容器路径
docker copy app:/usr/share/ngxin/html/index.html .
docker copy index.html app:/tmp
docker export app (把容器保存成tar文件)
docker import app.tar (把tar文件导会到镜像列表)

镜像命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker commit -a admin@femnyy.com -m 'app commit image' app app:2.0
# -a作者 -m提交时的标记 app容器名 app:2.0镜像名
# 用容器创建镜像 推荐用Dockerfile
docker save nginx >nginx.tar
# 镜像 -->tar包
docker rmi nginx
# 删除镜像
# 1.此镜像是其他镜像的父镜像
# 2.有容器使用镜像 镜像已经被创建为容器 不能删除本地镜像
docker load < nginx.tar
# tar包--导入到镜像列表中
docker images
# 列出了所有顶层(top-level)镜像
docker history <image_id>
# 列出镜像的所有层
docker pull nginx
# 下载镜像
docker tag nginx 192.168.1.116:5000/nginx:last
# 镜像的重命名(标记镜像)
docker push 192.168.1.116:5000/nginx:last # 本机测试的成功
# 上传镜像
docker search nginx
# 查看镜像

容器生成镜像

# 进入容器
docker exec -it docker_web_1 bash
# 在容器中进行操作
pip install gevent==1.3.3
# 退出容器
exit

docker ps |grep docker_web
# 得到 conta id: fdeb8e0c67ee 
docker images |grep docker_web 
# 得到repository:docker_web:latest

docker history docker_web:latest |wc -l
docker commit -m "pip install gevent==1.3.3" fdeb8e0c67ee docker_web:latest
docker history docker_web:latest |wc -l

容器与镜像

镜像不能直接访问,只有启动成容器,才可以访问.镜像和容器的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体.容器可以被创建、启动、停止、删除、暂停等.同一个 Images 可生成多个不同配置的 Container.

容器连接(相当于进程间的通信吧)

1
2
3
4
5
6
sudo docker run -d --name db training/postgres
# 指定了 training/webapp 镜像,并且这个镜像已经包含了简单的 Python Flask web 应用程序
# 最后,我们指定了我们容器要运行的命令: python app.py.这样我们的 web 应用就启动了
sudo docker run -d -P --name web --link db:db training/webapp python app.py
docker ps
# db和web,我们还在名字列中可以看到web容器也显示db/web.这告诉我们web容器和db容器是父/子关系.

Volume(持久化容器数据,以及容器之间共享数据)

保留容器的数据

当容器删除时,就是杀死进程,只要我们的数据在,当再启动时,数据又回来了,但我们想在容器中产生的数据保留起来

容器间的数据共享(root用户下,主机巻)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker run -d -v /web_data:/tmp:ro --name data-container nginx
cd /web_data
mkdir files
touch test.file
docker exec -it data-container ls /tmp
#第二个容器的数据来自 第一个容器的数据
docker run -d --volumes-from data-container --name web-container nginx
#第三个容器的数据来自 第一个容器的数据
docker run -d --volumes-from data-container --name web-container-2 nginx
# 查看容器 有没有第一个容器中的文件
docker exec -it web-container ls /tmp
# 验证只读
docker exec -it web-container bash
cd /tmp
touch a.txt
exit
# 在宿主机添加一个新文件,相当于往三个容器分发信息.
cd /web_data
touch a.txt

Volume分为容器卷(在Dockerfile写了VOLUME)和主机卷

容器卷的定义的目录,将会自动保存在宿主机的/var/lib/docker/volumes目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mkdir docker
cd docker && vim Dockerfile
FROM centos:7
RUN yum -y install epel-release && \
yum -y install nginx && \
yum clean all
EXPOSE 80 443
VOLUME ["/usr/share/nginx/html"]
CMD ["nginx","-g", "daemon off;"]
# 用Dockerfile 生成镜像app
docker build -t app .
cd /var/lib/docker/volumes
docker rm -f web-container data-container
docker run -d --name app app
ll #会有一个随机生成字符串的目录 VOLUME的东西,将会同步到这个目录下的_data目录下
docker rm -f -v app # -v

主机卷(/web-data),这个不仅仅会在/var/lib/docker/volumes:/usr/share/nginx/html保留,双保险.

1
docker run -d -p 80:80 --name app -v /web-data:/tmp app

备份app容器中 /dbdata容器巻中的数据–>打包在容器中的/backup/backup.tar–>将此包copy到主机的当前目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 通过ubuntu这个镜像生成一个容器
# --rm 这个容器退出 就把这个容器删掉
# 使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器:app
docker run --rm --volume-from app -v ${pwd}:/backup ubuntu \
tar cvfz /backup/backup.tar /dbdata
# 1.数据恢复
docker run --rm --volume-from app -v ${pwd}:/backup busybox \
bash -c "cd /dbdata && tar zxvf /backup/backup.tar --strip 1"
# 2
# 如果要恢复数据到一个容器,首先创建一个带有空数据卷的容器 dbdata2
sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
# 然后创建另一个容器,挂载 dbdata2 容器卷中的数据卷
# 并使用 untar 解压备份文件到挂载的容器卷中.
sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf
/backup/backup.tar
#为了查看/验证恢复的数据,可以再启动一个容器挂载同样的容器卷来查看
sudo docker run --volumes-from dbdata2 busybox /bin/ls /dbdata

Docker的网络模式:nat,host,container,none,overlay

默认启动的是nat

nat需要进行端口的管理,通过docker容器转发的功能,转发到宿主机的网卡上,每次启动容器的时候,它的IP会变:

1
2
3
4
5
6
7
8
9
10
11
docker run -d -p 80:80 --name app nginx
ps -aux |grep docker
# 得到如下信息
/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 \
-container-ip 172.17.0.3 -container-port 80
# 但我试了两次 IP都没有变
docker restart app
/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 \
-container-ip 172.17.0.3 -container-port 80
docker stop app
docker start app

host:容器中的网卡直接与宿主机的网卡沟通:只能使用80端口,但可以固定容器的IP.攻击了容器也就相当于攻击了宿主机.

1
2
3
4
docker run -d --name app2 --net=host nginx
docker ps -a
# 查看端口是否已经冲突,如果冲突则这个容器会启动失败 状态为Exited
docker start -a app2 # 端口也占用

container模式,用于容器之间经常通信的情况:

1
2
3
4
5
6
7
8
docker pull busybox
docker run -it --name busybox1 busybox sh
ip addr
ctrl+p+q # 不使用exit,让容器在后台运用
docker run -it --name busybox2 --net=container:busybox1 busybox sh
ip addr
# 此两个的容器的IP是一模一样的,而且还可以相互通信
ping busybox1

none模式:没有IP

1
docker run -it --name none --net=none busybox sh

overlay模式,实现跨主机的通信,docker:1.10+,才有的模式.

  1. 约定的容器启动时 会被当作一个服务被consul发现,从而分发IP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    vim /lib/systemd/system/docker.service
    # 注意改自己的IP地址,和网卡的名称
    # 多台也是这个的一样的配置(不需要去改变IP地址和网卡名称)
    # 使用hostA做为consul的服务端 hostA的IP;192.168.1.116 网卡名称为:enp2s0
    # hostA hostB都要操作
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 \
    -H unix:///var/run/docker.sock --cluster-store=consul://192.168.1.116:8500 \
    --cluster-advertise=enp2s0:2376 --insecure-registry=0.0.0.0/0
    systemctl daemon-reload
    systemctl restart docker
    ps -aux |grep docket
  2. 在consul的主机执行就可以:hostA

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    docker pull progrium/consul
    # -h 取名 -server服务端 并把ui打开
    docker run -d -p 8400:8400 -p 8500:8500 -p 8600:53/udp -h consul progrium/consul \
    -server -bootstrap -ui-dir /ui
    docker ps -a
    # 在浏览器中打开
    192.168.1.116:8500
    # 在KEY/VALUE中 会显示有多少台主机进行着通信 hostA,hostB
    # create overlay network 创建IP地址池 会用consul定义一个IP地址池(如10.0.9.0/24)
    docker network ls
    # -d overlay使用overlay驱动 --subnet 网段 edu_net为名称
    # 会同步到多台主机上(hostA,hostB)
    docker network create -d overlay --subnet=10.0.9.0/24 edu-net
  3. 当约定好的主机启动容器时,consul服务就会从地址池中取得IP,给容器使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # hostA
    docker run -d --name app1 --net=edu-net nginx
    # 登陆进出
    docker exec -it app1 bash
    ip addr
    ping www.baidu.com
    # hostB 没有虚拟技术 所以没有实做
    docker run -d --name app2 --net=edu-net nginx
    docker exec -it app2 bash
    ping app1

官方文档

一个中文的docker文档

对docker的解释

Share Comments