Docker容器技术

什么是Docker?

Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和其他的基础应用平台。(来自官方)

Docker的应用场景

1.产品交付:将环境和代码打包成镜像进行交付,交付更方便也更安全。
2.开发环境配置:将环境做成镜像,开发人员直接启动容器载入镜像,开发环境就有了。
3.多版本测试:由于Docker隔离了系统环境,同时容器直接互不影响,可以启动多个容器同时进行多版本的测试。
4.集群环境部署:Docker可以有效保证环境的一致,同时以容器为单位部署服务更加方便,也有利于及时回滚和服务的迁移。
5.架构扩容和自动化运维:Docker快速启动和低资源消耗的优点在弹性云平台和自动化运维方面有很好的应用前景。

Docker的三大组件

1.容器:容器是服务运行的载体。容器为服务虚拟出独立的环境,容器之间互不影响。创建容器需要加载镜像,容器本身也可打包为镜像。
2.镜像:镜像是容器状态的快照。创建一个容器必须要有一个镜像,比如centos镜像、lnmp镜像等。镜像包含完整的应用和环境,启动容器即可运行。
3.仓库:仓库是镜像存放的地点。镜像构建时需要指定仓库,远程仓库可以实现镜像共享,例如DockerHub以及私有化仓库Harbor。

Docker与Openstack的不同点

 
Docker
Openstack
部署难度
非常简单,yum安装docker即可
部署复杂,组件较多
启动速度
秒级
分钟级
处理性能
和物理机几乎一致
有一定的性能损失
镜像体积
MB级别
GB级别
管理效率
管理简单
管理复杂,组件相互依赖
隔离性
不完全隔离
彻底隔离
进程数目
单进程、不建议启动SSH
多进程,完整的系统管理
网络连接
比较弱
Neutron组件可以灵活构建网络架构

Docker容器和虚拟机原理及性能比较

1.上图分别是虚拟机和Docker容器的实现框架,显然虚拟机比容器要多一个GuestOS操作系统层,于是得到第一个区别:虚拟机调用的是完整的自身的操作系统内核,而Docker容器是直接调用物理机内核。
2.Hypervisor是一个硬件虚拟化平台,它为虚拟机内核提供底层驱动,于是得到第二个区别:虚拟机的资源隔离是利用独立OS和虚拟化硬件来实现,而DockerEngine则是利用物理机内核支持的LXC技术(LinuxContainer),通过namespace、cgroup等机制实现环境和资源的隔离。LXC是内核支持的一种轻量级虚拟化技术,与传统虚拟化技术的最大的区别在于不需要解释机制可直接在物理CPU中运行指令,LXC建立在cgroup基础上,包括cgroup、namespace、chroot、veth以及用户态控制脚本等。cgroup(control groups)是Linux内核提供的一种可以限制、记录进程组所使用物理资源的资源管理机制。namespace(命名空间)是Linux内核提供的一种资源隔离机制,将不同的进程和资源划分在一个局部作用域内,内部的进程和资源只在当前作用域生效,类似于局部变量。
通过上面的比较,我们可以得出结论:
1.Docker直接调用物理机内核,而不需要GuestOS。因此,由于不需要加载内核,创建一个容器只需几秒钟。同时,由于不需要操作系统,docker更加节省资源。
2.Docker直接使用物理机硬件,比虚拟机有更少的抽象层。因此,docker的计算性能和效率几乎和物理机相同。

Docker的优点和缺点

1.优点:Docker和虚拟机一样可以实现资源和环境的隔离,启动速度快,资源消耗低,处理性能强。
2.缺点:Docker的资源隔离不如虚拟机彻底,不能隔绝其他程序占用自己的资源。
       Docker不能分辨用户权限,用户只要能执行docker,就可以对容器进行任何操作。
 

Docker快速入门

cat /etc/redhat-release
uname -rm
yum install -y docker
systemctl start docker
ifconfig  #docker启动会创建一个虚拟网卡
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        ether 02:42:55:0d:41:e1  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker images  #查看当前镜像
docker search centos  #去dockerhub上搜索镜像
INDEX       NAME                                         DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/centos                             The official build of CentOS.                   3625      [OK]
docker.io   docker.io/ansible/centos7-ansible            Ansible on Centos7                              100                  [OK]

docker pull centos  #获取镜像
docker load --input centos.tar   # 导入镜像
docker save -o centos.tar centos  # 导出镜像
docker rmi centos #删除镜像
docker run centos /bin/echo "Hello world"  # 创建一个容器并运行一条命令 centos是镜像名称,名称必须在所有选项后面 命令可以有可以没有
docker ps -a # 查看容器的状态 -a表示所有容器,运行和不运行的    默认只显示运行的容器状态
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                          PORTS               NAMES
d989b65f0158        centos              "/bin/echo 'Hello wor"   2 minutes ago       Exited (0) About a minute ago                       pedantic_banach

[root@docker ~]# docker run --name mydocker -t -i centos /bin/bash  # 表示启动一个容器,名称命名为mydocker -t分配一个伪终端tty -i表示打开标准输入 centos是镜像名称 /bin/bash 是要执行的命令 如果centos不存在会自动去dockerhub上pull下来
#/bin/bash进程必须分配伪终端才能运行,同时打开标准输入后才可以输入命令
[root@7fbe5da0c90f /]#
[root@7fbe5da0c90f /]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 02:20 ?        00:00:00 /bin/bash       # 正常虚拟机PID=1的程序是systemd或者/sbin/init,显然docker容器不是虚拟机
root         16      1  0 02:22 ?        00:00:00 ps -ef
[root@7fbe5da0c90f /]# cat /proc/cpuinfo  # 这里显示的是物理机的硬件信息,所以docker并没有虚拟出硬件
[root@7fbe5da0c90f /]# exit # 退出容器
[root@docker ~]# docker start mydocker  #启动已经创建好的docker容器,run是创建实例,start是容器关闭后再打开 mydocker是容器名称,我们可以通过ID或名称来启动容器
[root@docker ~]# docker --help  # 帮助信息
[root@docker ~]# docker attach mydocker  # 进入正在运行的容器,需要容器内运行/bin/bash进程  这个命令不靠谱,退出容器后容器就停了 
#注:容器内必须有一个前台进程在运行,否则容器就会停止       更好的进入容器方法是nsenter命令,如果没有就yum install -y util-linux
[root@docker ~]# docker inspect -f "{{.State.Pid}}" mydocker  # 获取容器里面进程在物理机上的PID,注意要先启动容器
9515
[root@docker ~]# ps -ef | grep 9515
root       9515   9505  0 11:22 pts/0    00:00:00 /bin/bash
root       9553   8900  0 11:22 pts/1    00:00:00 grep --color=auto 9515
[root@docker ~]# nsenter --help
[root@docker ~]# nsenter -t 9515 -m -u -i -n -p   # 进入容器
[root@7fbe5da0c90f /]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 03:22 ?        00:00:00 /bin/bash
root         13      0  0 03:51 ?        00:00:00 -bash    # nsenter进入后重新创建了bash
root         28     13  0 03:54 ?        00:00:00 ps -ef
[root@7fbe5da0c90f /]# exit
[root@docker ~]# docker ps       # nsenter进入然后退出,只是-bash进程结束,/bin/bash进程不会受到影响,所以容器不会关闭
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
7fbe5da0c90f        centos              "/bin/bash"         About an hour ago   Up 33 minutes                           mydocker
[root@docker ~]# docker exec mydocker whoami  #  往docker容器里面传递命令,容器执行后返回结果
root
[root@docker ~]# docker exec -it mydocker /bin/bash  #  这样也能进入容器,原理和nsenter不同,但效果相同,但建议使用nsenter,这是进入容器的最佳实践
[root@7fbe5da0c90f /]# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 03:22 ?        00:00:00 /bin/bash
root         50      0  0 04:10 ?        00:00:00 /bin/bash
root         62     50  0 04:10 ?        00:00:00 ps -ef
[root@docker ~]# docker rm mydocker  # 删除容器,rmi是删除镜像
[root@docker ~]# docker rm -f mydocker  # 可以删除正在运行的容器 -f force强制删除
[root@docker ~]# docker run --rm centos /bin/echo "hehe"  # 启动一个容器,进程运行完毕后自动删除

命令总结:
镜像:docker search  # 搜索镜像
     docker pull # 获取镜像
     docker images # 查看镜像
     docker rmi  # 删除镜像
     docker load --input / docker load <  # 导入镜像
     docker save -o  # 导出镜像
容器:docker run --name -h hostname ...  # 创建容器
     docker start [ID/NAME] # 启动容器
     docker stop [ID/NAME]  # 停止容器
     docker ps -a # 查看容器
     docker exec | docker attach | nsenter # 进入容器
     docker rm  # 删除容器

Docker网络访问

网络访问:随机映射 docker run -P
         指定映射 docker run -p hostPort:containerPort
                 docker run -p ip:hostPort:containerPort
                 docker run -p ip::containerPort   # 不加端口就随机
                 docker run -p hostPort:containerPort:udp
                 docker run -p 81:80 -p 443:443

#Docker网络访问 docker启动时创建了一个桥接网卡docker0
[root@docker ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242550d41e1    no        veth1a9fdb5
[root@docker ~]# docker run -d -P nginx       # 随机映射端口    -d 后台启动
5038a3bd7de1dd28b303b8a61df12b4352f3e3c0f779ec148106c8ab652ebd45
[root@docker ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
5038a3bd7de1        nginx               "nginx -g 'daemon off"   43 seconds ago      Up 39 seconds       0.0.0.0:32768->80/tcp   elated_mcclintock
#容器的80端口映射到本地的32768端口,我们可以访问看看 http://172.16.1.5:32768

[root@docker ~]# docker run -d -p 10.0.0.5:82:80 --name mynginx nginx
6386aaef223f9965217053b82dacda0527fbf7edba7da90f0db6ba798cf2b276
[root@docker ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                 NAMES
6386aaef223f        nginx               "nginx -g 'daemon off"   About a minute ago   Up About a minute   10.0.0.5:82->80/tcp   mynginx
[root@docker ~]# docker port mynginx  # 查看容器端口
80/tcp -> 10.0.0.5:82

 编写进入容器的脚本

[root@docker ~]# vim docker_in.sh
#进入docker的脚本
#!/bin/bash

# Use nsenter to access docker

docker_in(){
   NAME_ID=$1
   PID=$(docker inspect -f "{{.State.Pid}}" $NAME_ID)
   nsenter -t $PID -m -u -i -n -p

}

docker_in $1

[root@docker ~]# chmod +x docker_in.sh
[root@docker ~]# ./docker_in.sh mydocker

 Docker数据管理

容器的镜像是分层的,我们可以在一个镜像基础上再开发,再生成镜像,生成的镜像只保存修改的内容,最终导出的镜像包含基础镜像和添加的镜像两层,下载镜像时也是分层下载。
数据管理:如果要在容器里写数据,为了防止数据写到一半容器故障,我们需要将数据写到物理机上,即需要挂载物理机目录到docker容器里即数据卷技术,同时docker支持容器之间的数据卷共享,其它容器通过访问某个已挂载容器的挂载目录来间接将数据写入物理机,即数据卷容器技术。

数据卷
#挂载目录到容器
[root@docker ~]# docker run -d --name nginx-volumn-test2 -v /data nginx
[root@docker ~]# ls /var/lib/docker/volumes/e44afc282ef635422705e42a5292602e4f6ca807a750af2983c86638adbf3751/
_data   # 被挂载的源目录,不指定时源目录时docker在物理机上自动创建             不同的容器对应不同的编号
[root@docker ~]# docker run -d --name nginx-volumn-test2 -v /data/docker-volumn-nginx:/data nginx  # 指定源目录  也可以挂载文件   src:dst
[root@docker ~]# docker run -d --name nginx-volumn-test2 -v /data/docker-volumn-nginx:/data:ro nginx  #只读

数据卷容器:挂载了目录(数据卷)的容器就称为数据卷容器。所以可以专门起一个容器用来挂载,其它的容器直接访问这个容器而不再需要挂载
[root@docker volumes]# docker run -it --name volumn-test3 --volumes-from nginx-volumn-test2 centos /bin/bash   # --volumes-from 直接访问另一个容器的挂载目录,自己不用挂载。即使test2停了也不影响test3访问

 Docker镜像构建

1.手动构建

[root@docker ~]# docker ps -a -q   # 获取所有容器的ID
e37a40ec1008
2d136a4b9ce8
37a1c1e3e151
b82da6090e37
6386aaef223f
01736bd88a27
5038a3bd7de1
f23d23271be7
7fbe5da0c90f
d989b65f0158
[root@docker ~]# docker kill $(docker ps -a -q)   # 停止所有容器
[root@docker ~]# docker rm $(docker ps -a -q)  # 删除所有容器
[root@docker ~]# docker run --name mynginx -it centos
[root@e570416e1c12 /]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@e570416e1c12 /]# yum install -y nginx
[root@e570416e1c12 /]# vi /etc/nginx/nginx.conf
daemon off;        # 关闭守护进程,就可以在前台运行
[root@e570416e1c12 /]# exit
[root@docker ~]# docker commit -m "my nginx" e570416e1c12 peter/mynginx:v1   # 提交当前容器状态,即创建镜像   跟git很像,git的提交也是对当前目录状态做一个镜像
sha256:390ac6fdc28ae5650673fea9695719c1c3185f7f9cc4eb25233259de158258c0
[root@docker ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
peter/mynginx       v1                  390ac6fdc28a        3 minutes ago       354.4 MB        # peter是仓库名称 mynginx是镜像名称 v1是标签
[root@docker ~]# docker run --name mynginxv1 -d -p 81:80 peter/mynginx:v1 nginx  # 从镜像中创建一个容器

 2.使用Dockerfile自动构建

[root@docker nginx]# pwd
/opt/dockerfile/nginx
[root@docker nginx]# vim Dockerfile   # 名称是固定的

# This Dockerfile

#Base image
FROM centos          #基础镜像是什么即在什么镜像基础上构建

#Maintainer
MAINTAINER Peter.Wang   #维护者是谁

#Commands
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum install -y nginx && yum clean all
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
ADD index.html /usr/share/nginx/html/index.html  # 添加一个文件
EXPOSE 80   # 对外的端口是什么
CMD ["nginx"]  # 启动时执行的命令是什么

[root@docker nginx]# echo "nginx in docker hahaha" > index.html   # 注意与Dockerfile在同一目录下
[root@docker nginx]# docker build -t mynginx:v2 .   # .表示在当前目录下找Dockerfile
[root@docker nginx]# docker images
[root@docker nginx]# docker run  --name mynginxv2 -d -p 82:80 mynginx:v2

 Dockerfile的生产实践

#分层设计,先构建系统层的镜像,然后构建运行环境层的镜像,然后构建应用服务层的镜像。docker镜像是分层设计的,并且这种方式使得镜像可以重复使用,耦合度较低
[root@docker docker]# tree
.
├── app
│   ├── xxx-admin
│   └── xxx-api
├── runtime
│   ├── java
│   ├── php
│   └── python
└── system
    ├── centos
    ├── centos-ssh
    └── ubuntu

[root@docker centos]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@docker centos]# cp /etc/yum.repos.d/epel.repo .
[root@docker centos]# ls
epel.repo
#系统环境centos [root@docker centos]# vim Dockerfile # Docker for CentOS #Base image FROM centos #Maintainer MAINTAINER Peter.Wang xxx@gmail.com #EPEL ADD epel.repo /etc/yum.repos.d/ #Base pkg RUN yum install -y wget mysql-devel supervisor git redia tree net-tools sudo psmisc && yum clean all [root@docker centos]# docker build -t peter/centos:base . [root@docker centos]# docker images

 

#python环境
[root@docker python]# vim Dockerfile

#Base image
FROM peter/centos:base

#Maintainer
MAINTAINER Peter.Wang

# Python env
RUN yum install -y python-devel python-pip supervisor

# Update pip
RUN pip install --upgrade pip

[root@docker python]# docker build -t peter/python .

 

#centos-ssh

[root@docker centos-ssh]# vim Dockerfile

# Docker for CentOS

#Base image
FROM centos

#Maintainer
MAINTAINER Peter.Wang xxx@gmail.com

#EPEL
ADD epel.repo /etc/yum.repos.d/

#Base pkg
RUN yum install -y openssh-clients openssl-devel openssh-server wget mysql-devel supervisor git redia tree net-tools sudo psmisc && yum clean all

#For SSHD
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN echo "root:123456" | chpasswd

[root@docker centos-ssh]# docker build -t peter/centos-ssh .

 

#python-ssh

[root@docker python-ssh]# vim Dockerfile

#Base image
FROM peter/centos-ssh

#Maintainer
MAINTAINER Peter.Wang

# Python env
RUN yum install -y python-devel python-pip supervisor
#supervisor是一个进程管理工具,可以同时管理多个进程
#vim /etc/supervisord.conf
#[include]
#files = supervisord.d/*.ini         # 我们只需要在/etc/supervisord.d目录下添加ini文件即可

# Update pip
RUN pip install --upgrade pip

[root@docker python-ssh]# docker build -t peter/python-ssh .

 

#shop.api

[root@docker shop-api]# vim app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
        return 'Hello World!'

if __name__ == "__main__":
        app.run(host="0.0.0.0",debug=True)

[root@docker shop-api]# yum install -y python-pip
[root@docker shop-api]# pip install flask
[root@docker shop-api]# python app.py
#http://192.168.1.5:5000

[root@docker shop-api]# vim Dockerfile

#Base image
FROM peter/python-ssh

#Maintainer
MAINTAINER Peter.Wang

# Python env
RUN useradd -s /sbin/nologin -M www

ADD app.py /opt/app.py
ADD requirements.txt /opt/
ADD supervisord.conf /etc/supervisord.conf
ADD app-supervisor.ini /etc/supervisord.d/

# Update pip
RUN /usr/bin/pip2.7 install -r /opt/requirements.txt

# Prot
EXPOSE 22 5000

#CMD
CMD ["/usr/bin/supervisord","-c","/etc/supervisord.conf"]

[root@docker shop-api]# cp /etc/supervisord.conf .

[root@docker shop-api]# vim app-supervisor.ini

[program:shop-api]
command=/usr/bin/python2.7 /opt/app.py
process_name=%(program_name)s
autostart=true
user=www
stdout_logfile=/tmp/app.log
stderr_logfile=/tmp/app.error

[program:sshd]
command=/usr/sbin/sshd -D
process_name=%(program_name)s
autostart=true

[root@docker shop-api]# vim supervisord.conf

nodaemon=true             ; (start in foreground if true;default false)

[root@docker shop-api]# docker build -t peter/shop-api .
[root@docker shop-api]# docker run --name shop-api -d -p 88:5000 -p 8022:22 peter/shop-api

 企业级Docker镜像仓库Harbor

官方网站
官方安装说明
官方使用说明
mkdir -p /server/tools
cd /server/tools
yum install docker-compose -y
# Compose是Docker的管理工具,主要用来构建基于Docker的复杂应用,Compose 通过一个配置文件来管理多个Docker容器,非常适合组合使用多个容器进行开发的场景。
wget https://github.com/vmware/harbor/releases/download/v1.2.0/harbor-offline-installer-v1.2.0.tgz
tar xf harbor-online-installer-v1.2.0.tgz
cd harbor
vim harbor.cfg

#The IP address or hostname to access admin UI and registry service.
#DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
hostname = docker.test.com      # 主机名

#The protocol for accessing the UI and token/notification service, by default it is http.
#It can be set to https if ssl is enabled on nginx.
ui_url_protocol = http   # 协议

#The password for the root user of mysql db, change this before any production use.
db_password = 123456   # 数据库root密码

./install.sh

#在Windows做hosts解析
10.0.0.5 docker.test.com

[root@lb01 harbor]# docker-compose ps
       Name                     Command               State                                Ports
------------------------------------------------------------------------------------------------------------------------------
harbor-adminserver   /harbor/harbor_adminserver       Up
harbor-db            docker-entrypoint.sh mysqld      Up      3306/tcp
harbor-jobservice    /harbor/harbor_jobservice        Up
harbor-log           /bin/sh -c crond && rm -f  ...   Up      127.0.0.1:1514->514/tcp
harbor-ui            /harbor/harbor_ui                Up
nginx                nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp
registry             /entrypoint.sh serve /etc/ ...   Up      5000/tcp

#http://docker.test.com

 

posted @ 2017-09-18 11:16  Peterer~王勇  阅读(533)  评论(0编辑  收藏  举报