docker简介
特点
灵活性:即使是最复杂的应用也可以集装箱化
轻量级:容器利用并共享主机内核
可互换:您可以即时部署更新和升级
便携式:您可以在本地构建,部署到云,并在任何地方运行
可扩展:您可以增加并自动分发容器副本
可堆叠:您可以垂直和即时堆叠服务
概念 镜像
容器
1 是镜像的可运行实例,本质是进程,但是有独立的命名空间,有独立的网络配置,文件系统,进程空间等等... 相当于在一个独立的隔离环境
registry
1 存储镜像的一个仓库,Docker hub官网提供了一个公共的仓库,而且也可以单独的运行一个私有仓库
容器和虚拟机 1 2 虚拟机:是一个独立的操作系统,占用资源较多,完全隔离,所以比较安全 容器:与宿主机共享主机的内核,是一个独立的进程,不占用其他执行文件,比较轻量
docker的底层 Namespace和CGroup
UnionFS
1 2 3 4 5 docker镜像是由一系列的层组成,每层代表 Dockerfile 中的一条指令 镜像中每层都是只读的,在运行容器时,就可以在基础层上添加新的可写层,也就是通常说的容器层,对于运行中的容器所做的更改都会写入容器层 容器与镜像之间的主要区别就是在镜像之上有一个可写层,在容器中的所有操作都会存储在这个容器层中,删除容器后,容器层也会被删除,但是镜像不会变化
docker架构 1 Docker 使用 C/S体系的架构,Docker客户端与Docker守护进程(Dockerd)通信,Docker守护进程负责构建,运行和分发 Docker 容器。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API 通过 UNIX 套接字或网络接口进行通信
安装 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 yum remove docker docker-client docker-client-latest docker-common \ docker-latest docker-latest-logrotate docker-logrotate docker-engine yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io yum list docker-ce --showduplicates | sort -r yum install docker-ce-18.09.9 docker-ce-cli-18.09.9 containerd.io -y systemctl start docker systemctl enable docker docker info mkdir -p /data/docker cat > /etc/docker/daemon.json <<EOF { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": {"max-size": "100m"}, "registry-mirrors" : ["https://ot2k4d59.mirror.aliyuncs.com/"], "graph": "/data/docker" } EOF systemctl daemon-reload systemctl restart docker docker info docker run hello-world yum remove docer-ce rm -rf /var/lib/docker echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.confecho "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.confecho "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.confsysctl --system sysctl -p sysctl -p net.ipv4.ip_forward = 1 sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory modprobe br_netfilter
基本操作 镜像管理 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 pull ubuntu:18.04 docker push docker images docker image ls docker image rm xxx docker rmi -f xxx docker tag nginx nginx:test docker image save nginx > /tmp/nginx.tar.gz docker load < /tmp/nginx.tar.gz docker image inspect nginx:latest
容器管理 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 40 41 42 43 44 45 46 47 48 49 50 docker run -itd --name bs busybox:latest docker run -it --rm ubuntu:18.04 /bin/bash docker attach bs ctrl + p ctrl + q docker exec -it bs sh docker ps docker ps -a docker ps -a -q docker stop `docker ps -a -q` docker rm -f `docker ps -a -q` docker rm -f 562faccc3106 docker run --name test ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" docker run -d --name test ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" docker logs -f test docker stats -it -d --name --restart=always -h x.x.x.x --dns x.x.x.x --dns-search --add-host hostname:IP --rm
网络管理 端口暴露 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 docker run --name webserver -d nginx docker ps docker inspect webserver |grep IPAddress curl 172.17.0.2 docker rm -f webserver docker run --name webserver -d -p 8080:80 nginx curl localhost:8080
Bridge模式 docker启动会产生一个docker0的网桥,启动的容器会连接到这个网桥上,类似物理交换机
1 2 3 4 5 6 ip address yum install -y bridge-utils brctl show
bridge 模式是 docker 的默认网络模式,使用docker run -p
时,实际上是通过 iptables 做了DNAT
规则,实现端口转发功能。可以使用iptables -t nat -vnL查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 brctl show docker run -tid --net=bridge --name docker_bri busybox top brctl show docker exec docker_bri ifconfig -a docker exec docker_bri route -n ip link show docker exec docker_bri cat /sys/class/net/eth0/iflink
自定义网络模式 可以通过自定义的 Docker 网络来连接多个容器,而不是使用--link
命令
--link
方式1 2 3 4 5 6 7 8 9 10 11 12 docker run -tid --link docker_bri --name docker_bri1 busybox top docker exec docker_bri1 ping docker_bri docker exec docker_bri ping docker_bri1 docker exec docker_bri cat /etc/hosts docker exec docker_bri1 cat /etc/hosts
自定义网络方式 1 2 3 4 5 6 7 8 9 10 11 12 13 docker network list docker network create -d bridge my-net docker run -itd --name busybox1 --network my-net busybox top docker run -itd --name busybox2 --network my-net busybox top docker exec busybox1 ping busybox2 docker exec busybox2 ping busybox1
Host模式 使用 host 模式,这个容器不会获得一个独立的Network Namespace
,而是和宿主机共用一个 Network Namespace,和宿主机共用端口。但是其他资源依旧是隔离的
Container 模式 新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享
新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等
其他资源依旧隔离,文件系统、进程列表等
1 2 3 4 --net=container:目标容器名
None模式 None模式不会为容器创建任何的网络配置,没有路由、IP等信息
数据持久化 数据卷 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 volume create my-vol docker volume ls docker volume inspect my-vol docker run -d -p 8080:80 --name web -v my-vol:/usr/share/nginx/html nginx docker run -d -p 8080:80 --name web --mount source =my-vol,target=/usr/share/nginx/html nginx ls /data/docker/volumes/my-vol/_data/ curl localhost:8080 echo "Hello Docker" > /data/docker/volumes/my-vol/_data/index.htmlcurl localhost:8080 docker volume prune
挂载主机目录 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 -v --mount echo 'hello' > /mnt/test.txtdocker run -it -v /mnt/:/usr/mnt busybox /bin/sh cat /usr/mnt/test.txt 默认挂载的路径权限为读写,如果指定为只读使用 ro ,例如 -v /mnt/:/usr/mnt:ro 容器目录不可以为相对路径 宿主机目录如果不存在,则会自动生成 挂载宿主机已存在目录后,在容器内对其进行操作,报“Permission denied”。可通过两种方式解决: 1.关闭selinux 临时关闭:setenforce 0 永久关闭:修改 /etc/sysconfig/selinux 文件,将 SELINUX 的值设置为disabled 2.以特权方式启动容器 指定 --privileged 参数,如: docker run -it --privileged=true -v /test :/soft centos /bin/bash
Dockerfile 镜像的定制实际上就是定制镜像的每一层所添加的配置、文件等信息
当在容器内添加或修改了一些文件后,可以通过docker commit
命令来生成一个新的镜像(可用来还原场景等),一般不使用该方式制作镜像,而是使用Dockerfile的方式,还可以作为版本记录的追踪
FROM 1 2 3 4 5 6 7 指定基础镜像,而且必须是第一条指令 scratch镜像,这个镜像是一个虚拟的镜像,并不实际存在,表示一个空白的镜像,不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在 FROM centos:7 ... FROM scratch
RUN 1 2 3 4 5 6 7 8 9 10 执行命令的指令,有两种格式 1.shell格式 ... RUN echo 'hello world' > /usr/share/nginx/html/index.html ... 2.exec格式 RUN ["可执行文件" , "参数1" , "参数2" ] 在Dockerfile中RUN指令尽量使用 && 将命令拼接在一起,因为每一个指令就是一层,合并在一层优化层数,并且有 UnionFS 层数限制,不得超过127层
WORKDIR 1 2 指定工作目录,后续的指令都会在当前工作目录,如果该目录不存在会创建该目录 格式: WORKDIR /path/
ADD和COPY 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 这两个指令都可以将主机上的资源复制到容器镜像上 COPY 1.只能将宿主机上的资料复制到镜像里 格式: COPY <src> <dest> ADD 1.可以通过URL从远程服务器上复制到镜像上 2.可以将宿主机上的压缩包解压缩后复制到镜像上 格式: ADD <src> <dest> ADD http://test.com/nginx.tar.gz /tools/ ADD nginx.tar.gz /workdir/ 使用注意事项 1.源路径可以有多个 2.源路径是相对于执行 build 的相对路径 3.源路径如果是本地路径,必须是构建上下文中的路径 4.源路径如果是一个目录,则该目录下的所有内容都将被加入到容器,但是该目录本身不会 5.目标路径必须是绝对路径,或相对于 WORKDIR 的相对路径 6.目标路径如果不存在,则会创建相应的完整路径 7.目标路径如果不是一个文件,则必须使用/结束 8.路径中可以使用通配符
构建镜像 1 2 docker build -t nginx:v1 .
构建上下文
推送镜像 私有仓库 客户端设置
1 2 3 4 5 6 7 8 9 cat /etc/docker/daemon.json { "insecure-registries" : ["127.0.0.1:5000" ]} systemctl daemon-reload systemctl restart docker
Dockerfile实践 docker-compose 安装 1 2 3 4 5 curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.3/docker-compose-`uname -s`-`uname -m` > /usr/local /bin/docker-compose chmod +x /usr/local /bin/docker-compose docker-compose --version
管理参数 1 2 3 4 5 6 7 8 9 10 11 -f ps restart logs config -q stop start up -d pause UNpause rm
wordpress 1 2 3 4 5 6 docker-compose config -q docker-compose up -d
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 version: '2' services: db: image: mysql:5.7 restart: always environment: MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db image: wordpress:latest restart: always ports: - "8080:80" environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress
python-web 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 mkdir pythonweb && cd pythonweb touch app.py touch docker-compose.yaml touch Dockerfile docker pull python:3.6-alpine docker pull redis:alpine import redis import time from flask import Flask app = Flask(__name__) cache = redis.Redis(host='redis' ,port=6379) def get_count(): retries = 5 while True: try: return cache.incr('hits' ) except redis.exceptions.ConnectionError as exc: if retries == 0: raise exc retries -= 1 time.sleep(0.3) @app.route('/' ) def hello(): cnt = get_count() return 'Hello World! cnt={}\n' .format(cnt) if __name__ == '__main__' : app.run(host='0.0.0.0' ,debug=True) FROM python:3.6-alpine ADD . /code WORKDIR /code RUN pip install redis flask -i https://pypi.douban.com/simple CMD ["python" ,"app.py" ] version: '3' services: web: build: . ports: - "5000:5000" volumes: - ./code redis: image: redis:alpine docker-compos up