Docker容器基础介绍

Docker是PaaS 提供商 dotCloud 开源的一个基于 LXC 的高级容器引擎,源代码托管在 Github 上, 基于go语言并遵从Apache2.0协议开源。Docker是通过内核虚拟化技术(namespace以及cgroups等)来提供容器的资源隔离与安全保障。由于Docker通过操作系统层的虚拟化实现隔离,所以Docker容器在运行时,不需要类似虚拟机( VM)额外的操作系统开销,提高资源利用率。

Docker是使用Go语言编写的一个程序运行、测试、交付的开放平台,Docker被设计为能够使你快速地交付应用。在Docker中,你可以将你的程序分为不同的基础部分,对于每一个基础部分都可以当做一个应用程序来管理。Docker能够帮助你快速地测试、快速地编码、快速地交付,并且缩短你从编码到运行应用的周期。Docker使用轻量级的容器虚拟化平台,并且结合工作流和工具,来帮助你管理、部署你的应用程序。Docker在其核心,Docker实现了让几乎任何程序都可以在一个安全、隔离的容器中运行。安全和隔离可以使你可以同时在机器上运行多个容器。Docker容器轻量级的特性,意味着可以得到更多的硬件性能。

Docker原理建立-->传送-->运行

image

通过Docker Hub或者自己的Docker仓库分享Docker镜像, 从Docker镜像创建Docker容器, 在容器里运行应用程序。

Docker镜像是如何工作的?
Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层(layers)组成;Docker使用UnionFS(联合文件系统)来将这些层联合到一二镜像中,UnionFS文件系统允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。

正因为有了这些层(layers)的存在,Docker才会如此的轻量。当你改变了一个Docker镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。所以你不用重新发布整个镜像,只需要升级。层使得分发Docker镜像变得简单和快速。

每个镜像都是从一个基础的镜像开始的,比如ubuntu,一个基础的Ubuntu镜像,或者是Centos,一个基础的Centos镜像。你可以使用你自己的镜像作为新镜像的基础,例如你有一个基础的安装了Nginx的镜像,你可以使用该镜像来建立你的Web应用程序镜像。(Docker通常从Docker Hub获取基础镜像)

Docker镜像从这些基础的镜像创建,通过一种简单、具有描述性的步骤,我们称之为 指令(instructions)。每一个指令会在镜像中创建一个新的层,指令可以包含这些动作:
->  运行一个命令。
->  增加文件或者文件夹。
->  创建一个环境变量。
->  当运行容器的时候哪些程序会运行。
这些指令存储在Dockerfile文件中。当你需要建立镜像的时候,Docker可以从Dockerfile中读取这些指令并且运行,然后返回一个最终的镜像。

Docker仓库的用处?
Docker仓库是Docker镜像的存储仓库。可以推送镜像到Docker仓库中,然后在Docker客户端,可以从Docker仓库中搜索和拉取镜像。

Docker容器是如何工作的?
一个Docker容器包含了一个操作系统、用户添加的文件和元数据(meta-data)。每个容器都是从镜像建立的,镜像告诉Docker容器内包含了什么,当容器启动时运行什么程序,还有许多配置数据。Docker镜像是只读的,当Docker运行一个从镜像建立的容器,它会在镜像顶部添加一个可读写的层,应用程序可以在这里运行。

Docker容器运行时会做哪些事情?
使用docker命令时,Docker客户端都告诉Docker守护进程运行一个容器。
# docker run -i -t ubuntu /bin/bash
可以来分析这个命令,Docker客户端使用docker命令来运行,run参数表明客户端要运行一个新的容器。
Docker客户端要运行一个容器需要告诉Docker守护进程的最小参数信息是:
->  这个容器从哪个镜像创建,这里是ubuntu,基础的Ubuntu镜像。
->  在容器中要运行的命令,这里是/bin/bash,在容器中运行Bash shell。

那么运行这个命令之后在底层发生了什么呢?按照顺序,Docker做了这些事情:
->  拉取ubuntu镜像:Docker检查ubuntu镜像是否存在,如果在本地没有该镜像,Docker会从Docker Hub下载。如果镜像已经存在,Docker会使用它来创建新的容器。
->  创建新的容器:当Docker有了这个镜像之后,Docker会用它来创建一个新的容器。
->  分配文件系统并且挂载一个可读写的层:容器会在这个文件系统中创建,并且一个可读写的层被添加到镜像中。
->  分配网络/桥接接口:创建一个允许容器与本地主机通信的网络接口。
->  设置一个IP地址:从池中寻找一个可用的IP地址并且服加到容器上。
->  运行你指定的程序:运行指定的程序。
-> 捕获并且提供应用输出:连接并且记录标准输出、输入和错误让你可以看到你的程序是如何运行的。
由此就可以拥有一个运行着的Docker容器了!从这里开始你可以管理你的容器,与应用交互,应用完成之后,可以停止或者删除你的容器。

Docker与VM的区别:

image

docker与Openstack的对比

image

Docker用途:简单配置、代码流水线管理、开发效率、应用隔离、服务器整合、调试能力、多租户、快速部署

image

Docker可以快速交付应用程序
Docker可以为你的开发过程提供完美的帮助。Docker允许开发者在本地包含了应用程序和服务的容器进行开发,之后可以集成到连续的一体化和部署工作流中。举个例子,开发者们在本地编写代码并且使用Docker和同事分享其开发栈。当开发者们准备好了之后,他们可以将代码和开发栈推送到测试环境中,在该环境进行一切所需要的测试。从测试环境中,你可以将Docker镜像推送到服务器上进行部署。

Docker可以让开发和拓展更加简单
Docker的以容器为基础的平台允许高度可移植的工作。Docker容器可以在开发者机器上运行,也可以在实体或者虚拟机上运行,也可以在云平台上运行。Docker的可移植、轻量特性同样让动态地管理负载更加简单。你可以用Docker快速地增加应用规模或者关闭应用程序和服务。Docker的快速意味着变动几乎是实时的。

Docker可以达到高密度和更多负载
Docker轻巧快速,它提供了一个可行的、符合成本效益的替代基于虚拟机管理程序的虚拟机。这在高密度的环境下尤其有用。例如,构建你自己的云平台或者PaaS,在中小的部署环境下同样可以获取到更多的资源性能。

Docker改变了什么?
-> 面向产品:产品交付
-> 面向开发:简化环境配置
-> 面向测试:多版本测试
-> 面向运维:环境一致性
-> 面向架构:自动化扩容

Docker组件:镜像(Image)、容器(Container)、仓库(Repository)

Docker 架构C/S架构
-> Docker使用客户端-服务器(client-server)架构模式。
-> Docker 客户端会与Docker守护进程进行通信。Docker 守护进程会处理复杂繁重的任务,例如建立、运行、发布你的 Docker 容器。
-> Docker 客户端和守护进程可以运行在同一个系统上,当然也可以使用Docker客户端去连接一个远程的 Docker 守护进程。
-> Docker 客户端和守护进程之间通过socket或者RESTful API进行通信。

Docker守护进程
如上图所示,Docker守护进程运行在一台主机上。用户并不直接和守护进程进行交互,而是通过 Docker 客户端间接和其通信。

Docker 客户端
Docker 客户端,实际上是 docker 的二进制程序,是主要的用户与 Docker 交互方式。它接收用户指令并且与背后的 Docker 守护进程通信,如此来回往复。

Docker 内部
要理解Docker内部构建,需要理解以下三种部件:
Docker 镜像 - Docker Images
Docker 仓库 - Docker Registry
Docker 容器 - Docker Containers

Docker镜像是Docker容器运行时的只读模板,每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新 的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发 Docker 镜像变得简单和快速。

Docker仓库用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。

Docker容器和文件夹很类似,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。

libcontainer
Docker 从 0.9 版本开始使用 libcontainer 替代 lxc,libcontainer 和 Linux 系统的交互图如下:

下面说下Docker容器的底层技术:Namesapce(资源隔离)和 Cgroup(资源限制)

命名空间   [Namespaces]
=>   pid namespace:使用在进程隔离(Process ID)
不同用户的进程就是通过pid namespace隔离开的,且不同 namespace 中可以有相同 PID。
具有以下特征:
-> 每个namespace中的pid是有自己的pid=1的进程(类似 /sbin/init 进程)
-> 每个 namespace 中的进程只能影响自己的同一个 namespace 或子 namespace 中的进程
-> 因为 /proc 包含正在运行的进程,因此在 container 中的 pseudo-filesystem 的 /proc 目录只能看到自己namespace 中的进程
-> 因为 namespace 允许嵌套,父 namespace 可以影响子 namespace 的进程,所以子 namespace 的进程可以在父namespace中看到,但是具有不同的 pid

=>   mnt namespace:使用在管理挂载点(Mount)
类似 chroot,将一个进程放到一个特定的目录执行。mnt namespace 允许不同namespace的进程看到的文件结构不同,这样每个namespace 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个 namespace 中的 container 在 /proc/mounts 的信息只包含所在namespace的mount point。

=>   net namespace:使用在进程网络接口(Networking)
网络隔离是通过 net namespace 实现的, 每个 net namespace 有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每个 container 的网络就能隔离开来。 docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge 连接在一起。

=>   uts namespace:使用在隔离内核和版本标识 (Unix Timesharing System)
UTS ("UNIX Time-sharing System") namespace 允许每个 container 拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 Host 上的一个进程。

=>   ipc namespace:使用在管理进程间通信资源 (InterProcess Communication)
container 中进程交互还是采用 Linux 常见的进程间交互方法 (interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同 VM 不同,container 的进程间交互实际上还是 host 上具有相同 pid namespace 中的进程间交互,因此需要在IPC资源申请时加入 namespace 信息 - 每个 IPC 资源有一个唯一的 32bit ID。

=>  user namespace:使用在管理空户空间
每个 container 可以有不同的 user 和 group id, 也就是说可以以 container 内部的用户在 container 内部执行程序而非 Host 上的用户。

有了以上6种namespace从进程、网络、IPC、文件系统、UTS 和用户角度的隔离,一个 container 就可以对外展现出一个独立计算机的能力,并且不同container从OS层面实现了隔离。然而不同 namespace 之间资源还是相互竞争的,仍然需要类似ulimit 来管理每个container所能使用的资源。

资源配额 [cgroups]
Docker还使用到了cgroups技术来管理群组。使应用隔离运行的关键是让它们只使用你想要的资源。这样可以确保在机器上运行的容器都是良民(good multi-tenant citizens)。群组控制允许Docker分享或者限制容器使用硬件资源。例如,限制指定的容器的内容使用。

cgroups实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup 目录下新建一个文件夹即可新建一个 group,在此文件夹中新建 task 文件,并将 pid 写入该文件,即可实现对该进程的资源控制。具体的资源配置选项可以在该文件夹中新建子 subsystem ,{子系统前缀}.{资源项} 是典型的配置方法, 如 memory.usageinbytes 就定义了该 group 在 subsystem memory 中的一个内存限制选项。另外,cgroups 中的 subsystem 可以随意组合,一个 subsystem 可以在不同的 group 中,也可以一个 group 包含多个 subsystem - 也就是说一个 subsystem。

=>  memory
内存相关的限制

=>  cpu
在 cgroup 中,并不能像硬件虚拟化方案一样能够定义 CPU 能力,但是能够定义 CPU 轮转的优先级,因此具有较高 CPU 优先级的进程会更可能得到 CPU 运算。 通过将参数写入 cpu.shares ,即可定义改 cgroup 的 CPU 优先级 - 这里是一个相对权重,而非绝对值

=>  blkio
block IO 相关的统计和限制,byte/operation 统计和限制 (IOPS 等),读写速度限制等,但是这里主要统计的都是同步 IO

=>  devices
设备权限限制

Docker 联合文件系统
联合文件系统(UnionFS)是用来操作创建层的,使它们轻巧快速。Docker使用UnionFS提供容器的构造块。Docker可以使用很多种类的UnionFS包括AUFS, btrfs, vfs, and DeviceMapper。

Docker 容器格式
Docker连接这些组建到一个包装中,称为一个 container format(容器格式)。默认的容器格式是libcontainer。Docker同样支持传统的Linux容器使用LXC。在未来,Docker也许会支持其它的容器格式,例如与BSD Jails 或 Solaris Zone集成。

======================== Docker环境的安装部署 ==========================
环境准备(centos7)

1
2
3
# yum install -y docker
# systemctl start docker
# systemctl enable docker

镜像的查看(docker images信息结果包括:镜像仓库、标签、镜像ID、创建时间、镜像大小   )

1
2
3
4
5
[root@linux-node2 ~]# docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
docker.io/centos     latest              60e65a8e4030        36 hours ago        196.6 MB
docker.io/nginx      latest              813e3731b203        9 days ago          133.8 MB
docker.io/registry   latest              a8706c2bfd21        2 weeks ago         422.8 MB

镜像的导出、导入和下载(可以将本机下载的镜像导出,然后将导出文件上传到别的机器上,在别的机器上进行镜像导入)

1
2
3
4
5
[root@linux-node2 ~]# docker pull centos
[root@linux-node2 ~]# docker save centos > /opt/centos.tar.gz
 
将linux-node2的镜像导出文件上传到linux-node1机器上,然后在linux-node1机器上导入
[root@linux-node1 ~]# docker load < /opt/centos.tar.gz

镜像的删除(rmi后面可以跟多个id,用空格隔开)
docker rmi container_id

1
[root@linux-node2 ~]# docker rmi 813e3731b203

Docker重命名镜像名称和TAG
# docker tag IMAGEID(镜像id) REPOSITORY:TAG(仓库:标签)

1
2
3
4
5
6
7
[root@docker-test2 ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
<none>              <none>              93ec41b5ed04        About an hour ago   435.9 MB
[root@docker-test2 ~]# docker tag 93ec41b5ed04 kevin/nginx:v1
[root@docker-test2 ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
kevin/nginx         v1                  93ec41b5ed04        About an hour ago   435.9 MB

首次创建一个简单的容器

1
2
[root@linux-node2 ~]# docker run centos /bin/echo "hehe"
hehe

查看容器状态
可以使用docker ps只能看见存活的容器,docker ps -a 查看全部的容器,结果信息表示:
容器ID、使用的镜像、执行的命令、创建的时间、状态、端口、名称(如果不指定,自动生成)

1
2
3
[root@linux-node2 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
daeb4d7f7aab        centos              "/bin/echo hehe"    About a minute ago   Exited (0) About a minute ago                       insane_einstein

创建容器
--name:指定容器名称
-t :分配一个tty终端
-i :容器的标准输保持打开的状态

1
2
3
4
5
[root@linux-node2 ~]# docker run --name mydocker -t -i centos /bin/bash
[root@94ab7a046f7c /]# ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.1  11772  1872 ?        Ss   03:42   0:00 /bin/bash
root         14  0.0  0.0  35888  1472 ?        R+   03:43   0:00 ps aux

这种方式创建自动进入容器,开启的容器只执行/bin/bash;

在容器中查看主机名

1
2
3
[root@94ab7a046f7c /]# hostname
94ab7a046f7c
[root@94ab7a046f7c /]# exit

启动、停止容器

1
2
# docker stop  ID
# docker start  ID

进入容器

1
2
[root@linux-node2 ~]# docker attach 94ab7a046f7c
[root@94ab7a046f7c /]#

删除容器

[root@linux-node2 ~]# docker rm ID/名称
加-f 强制删除,包括正在运行中的容器

映射

image

随机映射 (端口的映射是系统自动分配的)

1
2
3
4
5
[root@linux-node2 ~]# docker run -d -P nginx
90316d97ee975b4e62e1927a9fb31f20703556b1a3ea07880d0c68dcb5bbd3bb
[root@linux-node2 ~]# docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                           NAMES
90316d97ee97        nginx               "nginx -g 'daemon off"   25 seconds ago      Up 23 seconds       0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp   ecstatic_almeida

指定映射 (如下,指定容器的80端口映射到主机的81端口上)

1
2
3
4
5
[root@linux-node2 ~]# docker run -d -p 81:80 nginx
0294a8f5b4fc81ba31383a8eb98ec62b136826eba92360c84afd87bf1bf819fc
[root@linux-node2 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                           NAMES
0294a8f5b4fc        nginx               "nginx -g 'daemon off"   11 seconds ago      Up 10 seconds       443/tcp, 0.0.0.0:81->80/tcp                     admiring_ramanujan

查看日志

1
命令为"docker logs +ID"

数据管理

image

数据卷
默认挂载目录
创建一个数据卷,名称是volume-test1,挂载到data下默认挂载目录

1
2
3
[root@linux-node2 ~]# docker run -it --name volume-test1 -v /data centos
[root@1768d6414cfc /]# ls -l /data/
total 0

列出容器的所有信息,查看mounts模块

1
2
3
4
5
6
7
8
9
10
11
[root@linux-node2 ~]# docker inspect 1768d6414cfc
 "Mounts": [
        {
            "Name""55c97df0276bb8879398b4e7286fc41f9872e9203267da7e23060e24ba06d167",
            "Source""/var/lib/docker/volumes/55c97df0276bb8879398b4e7286fc41f9872e9203267da7e23060e24ba06d167/_data",
            "Destination""/data",
            "Driver""local",
            "Mode""",
            "RW"true
        }
    ],

查找挂载点并进入

1
2
3
[root@linux-node2 ~]# ll /var/lib/docker/volumes/55c97df0276bb8879398b4e7286fc41f9872e9203267da7e23060e24ba06d167/_data
总用量 0
[root@linux-node2 ~]# cd /var/lib/docker/volumes/55c97df0276bb8879398b4e7286fc41f9872e9203267da7e23060e24ba06d167/_data

创建一个cgt测试,并重新回到容器中查看

1
2
3
4
5
6
[root@linux-node2 _data]# mkdir cgt
 
去容器中查看
[root@1768d6414cfc /]# ls -l /data/
total 4
drwxr-xr-x 2 root root 4096 Jan  4 14:04 cgt

指定挂载目录。 将容器的/opt映射到主机的/opt目录下(下面命令中前一个是主机路径,后一个是容器路径)

1
[root@linux-node2 ~]# docker run -it --name volume-test1 -v /opt:/opt centos

指定权限

只需要在挂载后面加上权限即可。rw为读写,ro为只读

1
[root@linux-node2 ~]# docker run -it --name volume-test1 -v /opt:/opt:rw centos

挂载单个文件

记录历史记录

1
[root@linux-node2 ~]# docker run -it -v ~/.bash_history:/.bash_history centos

数据卷容器

让一个容器可以访问另一个容器的数据卷。启动两个容器

1
2
3
4
5
6
7
8
启动nfs容器,挂在一个卷,使用-d直接在后台执行
[root@linux-node2 ~]# docker run -d --name nfs -v /data centos
209bc89b365ad6bc1eeae693ada01c04c2d08e9ee2b8816e624882944c116126
 
启动test1容器,挂载到nfs的数据卷容器上,
[root@linux-node2 ~]# docker run -it --name test1 --volumes-from nfs centos
[root@5e399198d6a8 /]# ls /data/
查看没内容

找到nfs容器的挂载点(可以使用名称,不仅仅是ID)

1
2
3
4
5
6
7
8
找到nfs容器的ID
[root@linux-node2 opt]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                     PORTS               NAMES
209bc89b365a        centos              "/bin/bash"         2 minutes ago        Exited (0) 2 minutes ago                       nfs
找到nfs容器的挂载点
[root@linux-node2 _data]# docker inspect nfs
[root@linux-node2 opt]# cd /var/lib/docker/volumes/3938c9b1143d41340e148a4c7bc12d13b53966b15380c5b958a9e035897450d5/_data
[root@linux-node2 _data]# touch cgt

在test1上查看

1
2
[root@5e399198d6a8 /]# ls /data/
cgt

注意:数据卷容器不论停止还是开启,不影响其他容器挂载使用
如何制作镜像
方式一:手动构建容器
1)创建一个容器mynginx,使用centos镜像

1
2
3
4
5
[root@linux-node2 ~]# docker run --name mynginx -it centos
[root@f9c7dfb6f552 /]# rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
[root@f9c7dfb6f552 /]# yum -y install nginx
[root@f9c7dfb6f552 /]# exit
exit

2)基于mynginx容器做一个镜像mynginx:v1

1
2
3
4
5
6
7
8
9
10
11
12
[root@linux-node2 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
f9c7dfb6f552        centos              "/bin/bash"         3 minutes ago       Exited (0) 15 seconds ago                       mynginx
基于mynginx这个容器做一个镜像
[root@linux-node2 ~]# docker commit -m "my nginx" f9c7dfb6f552 cgt/mynginx:v1
3f3adc859b77b2b47c3631229761bee6c7066f1c708bc01c5173c2ef5c0adce8
提交镜像,同时打一个标签叫mynginx:v1,cgt相当于你向github上提交的用户名
 
查看镜像
[root@linux-node2 ~]# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
cgt/mynginx                          v1                  3f3adc859b77        About a minute ago   326.4 MB

3)基于mynginx:v1创建一个容器mynginxv1,目的是修改nginx不让其在后台运行

1
2
3
4
5
6
7
8
9
[root@linux-node2 ~]# docker run -it --name nginxv1 cgt/mynginx:v1
[root@ea64c5855006 /]# vi /etc/nginx/nginx.conf
daemon off;      # 不再后台运行
[root@ea64c5855006 /]# exit
exit
 
[root@linux-node2 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
ea64c5855006        cgt/mynginx:v1      "/bin/bash"         2 minutes ago       Exited (0) 42 seconds ago                       nginxv1

4)基于mynginxv1提交mynginxv2版本

1
2
3
4
5
6
7
重新提交V2版本
[root@linux-node2 ~]# docker commit -m "my nginx" ea64c5855006  cgt/mynginx:v2
a480cdf9055ec4e640c65df6404c6ba42903ea77198a26cec75eef0e4965fe67
查看V2镜像
[root@linux-node2 ~]# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
cgt/mynginx                          v2                  a480cdf9055e        25 seconds ago

5)基于mynginxv2镜像,创建mynginxv2容器

1
2
3
4
5
6
7
8
启动容器,-d后台运行,-p指定端口 在后面是镜像,最后是命令(因为是yum安装的,可以直接写nginx,如果不是yum,那要写绝对路径)
[root@linux-node2 ~]# docker run -d -p 82:80 cgt/mynginx:v2 nginx
4eaf8a19034a673100f9355504628fad45e6ecbab91615afd6cb4e7a18b82171
 
[root@linux-node2 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                NAMES
4eaf8a19034a        cgt/mynginx:v2      "nginx"             15 seconds ago      Up 14 seconds       0.0.0.0:82->80/tcp   elegant_leakey
可以在浏览器访问82端口

方式二:Dockerfile

1)Dockerfile包含的信息
-  基础镜像信息
-  维护者信息
-  镜像操作指令
-  容器启动时执行指令

2)文件的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@linux-node2 ~]# mkdir /opt/dockerfile/nginx/ -p
[root@linux-node2 ~]# cd /opt/dockerfile/nginx/
将index.html上传到此处
[root@linux-node2 nginx]# vim Dockerfile
# This is docker file
# version v1
# Author wangshibo
# Base image(基础镜像)
FROM centos
 
# Maintainer(维护者信息)
MAINTAINER wangshibo  2134728394@qq.com
 
# Commands(执行命令)
RUN rpm -ivh  http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum -y install nginx
# Add(添加文件)
ADD index.html /usr/share/nginx/html/index.html       # index.html是自己编写的文件,放在后面的目录中,因为yum安装后Documentroot是在这里
RUN echo "daemon off;" >>/etc/nginx/nginx.conf
EXPOSE 80           # 对外的端口
CMD ['nginx']       # 执行的命令

3)构建容器,并运行。 建立newnginx容器,-t:标签,执行/opt/dockerfile/nginx/下面的默认的Dockerfile文件

1
2
[root@linux-node2 nginx]# docker build -t cgt/mynginx:v3 /opt/dockerfile/nginx/
[root@linux-node2 nginx]# docker run -d -p 83:80 cgt/mynginx:v3

Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。当你真正投入容器Docker的怀抱,不但可以发现它能解决很多问题,而且还具有众多的优点:
-> 它是不可变的-操作系统,库版本,配置,文件夹和应用都是一样的。您可以使用通过相同QA测试的镜像,使产品具有相同的表现。  
-> 它是轻量级的-容器的内存占用非常小。不需要几百几千MB,它只要对主进程分配内存再加上几十MB。  
-> 它很快速-启动一个容器与启动一个单进程一样快。不需要几分钟,您可以在几秒钟内启动一个全新的容器。

许多用户依然像对待典型的虚拟机那样对待容器,似乎都忘记了除了与虚拟机相似的部分,容器还有一个很大的优点:它是一次性的。这个"特性"本身促使用户改变他们关于使用和管理容器的习惯;下面将会说明下在容器中不应该做这些事,以确保最大地发挥容器的作用。
-> 不要在容器中存储数据 – 容器可能被停止,销毁,或替换。一个运行在容器中的程序版本1.0,应该很容易被1.1的版本替换且不影响或损失数据。有鉴于此,如果你需要存储数据,请存在卷中,并且注意如果两个容器在同一个卷上写数据会导致崩溃。确保你的应用被设计成在共享数据存储上写入。
-> 不要将你的应用发布两份 – 一些人将容器视为虚拟机。他们中的大多数倾向于认为他们应该在现有的运行容器里发布自己的应用。在开发阶段这样是对的,此时你需要不断地部署与调试;但对于质量保证与生产中的一个连续部署的管道,你的应用本该成为镜像的一部分。记住:容器应该保持不变。
-> 不要创建超大镜像 – 一个超大镜像只会难以分发。确保你仅有运行你应用/进程的必需的文件和库。不要安装不必要的包或在创建中运行更新(yum更新)。
-> 不要使用单层镜像 – 要对分层文件系统有更合理的使用,始终为你的操作系统创建你自己的基础镜像层,另外一层为安全和用户定义,一层为库的安装,一层为配置,最后一层为应用。这将易于重建和管理一个镜像,也易于分发。
-> 不要为运行中的容器创建镜像 – 换言之,不要使用"docker commit"命令来创建镜像。这种创建镜像的方法是不可重现的也不能版本化,应该彻底避免。始终使用Dockerfile或任何其他的可完全重现的S2I(源至镜像)方法。
-> 不要只使用"最新"标签 – 最新标签就像Maven用户的"快照"。标签是被鼓励使用的,尤其是当你有一个分层的文件系统。你总不希望当你2个月之后创建镜像时,惊讶地发现你的应用无法运行,因为最顶的分层被非向后兼容的新版本替换,或者创建缓存中有一个错误的"最新"版本。在生产中部署容器时应避免使用最新。
-> 不要在单一容器中运行超过一个进程-容器能完美地运行单个进程(http守护进程,应用服务器,数据库),但如果运行多个进程,管理、获取日志、独立更新都会遇到麻烦。
-> 不要在镜像中存储凭据。使用环境变量 –不要将镜像中的任何用户名/密码写死。使用环境变量来从容器外部获取此信息。
-> 使用非root用户运行进程 – "docker容器默认以root运行。(…)随着docker的成熟,更多的安全默认选项变得可用。现如今,请求root对于其他人是危险的,可能无法在所有环境中可用。你的镜像应该使用USER指令来指令容器的一个非root用户来运行。"
-> 不要依赖IP地址 – 每个容器都有自己的内部IP地址,如果你启动并停止它地址可能会变化。如果你的应用或微服务需要与其他容器通讯,使用任何命名与(或者)环境变量来从一个容器传递合适信息到另一个。

原文地址:https://www.cnblogs.com/chenyablog/p/15143952.html