Docker | 学习总结
Docker | 学习总结
1. docker 架构
2. 环境安装
docker分为企业版(EE)和社区版(CE),社区版链接 docker-ce
有3种常用的安装方式:
- vagrant + VirtualBox
- docker-machine + VirtualBox
- docker playground
推荐安装 vagrant + VirtualBox 快速搭建 docker host,不推荐直接使用 Docker for Mac
# 初始化 Vagrantfile 文件
$ vagrant init centos/7
# 安装 centos7 虚拟机
$ vagrant up
# ssh进入虚拟机
$ vagrant ssh
> sudo yum update
> exit # 退出
vagrant 虚拟机镜像管理
$ vagrant status
# 停止
$ vagrant halt
# 删除
$ vagrant destroy
(1) 【推荐】虚拟机中 docker 安装方式1
按照官方教程安装 docker https://docs.docker.com/engine/install/centos/ 并验证
# 查看docker版本
$ sudo docker version
$ sudo docker info
# 启动 docker
$ sudo systemctl start docker
# $ sudo docker run hello-world
(2) 虚拟机中 docker 安装方式2
还可以直接通过配置 Vagrantfile 文件
config.vm.provision "shell", inline: <<-SHELL
sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
SHELL
然后启动
$ vagrant up
(3) docker-machine工具
可以不通过 vagrant 创建虚拟机,但是同样依赖 virtualbox
# 创建 docker - demo
$ docker-machine create demo
$ docker-machine ls
$ docker-machine ssh demo
$ exit
$ docker-machine stop demo
$ docker-machine env demo
3. image 镜像
image 的获取途径
- Dockerfile 构建
- pull from registry —— dockerhub
镜像由多层组成,容器其实就是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层里。如果容器删除了,最上面的读写层也就删除了,改动也就丢失了。可以通过 docker history <ID/NAME>
查看镜像中各层内容及大小,每层对应着 Dockerfile
中的一条指令。
3.1 查看镜像
$ docker image ls
3.2 查找镜像
$ docker search hello-world
字段 | 含义 |
---|---|
NAME | 名称 |
DESCRIPTION | 描述 |
STARTS | 星星的数量 |
OFFICIAL | 是否官方源 |
3.3 拉取镜像
$ docker pull docker.io/hello-world
# docker 官方提供的 image 文件都放在 docker.io 默认组里,可以省略
$ docker pull hello-world
3.4 删除镜像
$ docker rmi hello-world
3.5 其他
命令 | 含义 | 案例 |
---|---|---|
history | 查看镜像历史 | docker history imageName |
inspect | 显示一个或多个镜像详细信息 | docker inspect imageName |
push | 推送一个镜像到镜像仓库 | docker push imageName |
prune | 移除未使用的镜像,没有被标记或补任何容器引用 | docker image prune |
tag | 标记本地镜像,将其归入某一仓库 | docker image tag imageName/repository:tag |
build | 根据Dockerfile构建镜像 | |
export | ||
import | ||
save | ||
load |
4. container 容器
4.1 container 命令总览
命令 | 含义 | 案例 |
---|---|---|
run | 从镜像运行一个容器 | docker run ubuntu /bin/echo 'hello-world' |
ls | 列出容器 | docker container ls |
inspect | 显示一个或多个容器详细信息 | docker inspect |
attach | 要attach上去的容器必须正在运行,可以同时连接上同一个container来共享屏幕 | docker attach |
stats | 显示容器资源使用统计 | docker container stats |
top | 显示一个容器运行的进程 | docker container top |
update | 显示一个容器运行的进程 | docker container update |
port | 更新一个或多个容器配置 | docker container port |
ps | 查看当前运行的容器 | docker ps -a -l |
kill containerId | 终止容器(发送SIGKILL ) | docker kill containerId |
rm containerId | 删除容器 | docker rm containerId |
start containerId | 启动已经生成、已经停止运行的容器文件 | docker start containerId |
stop containerId | 终止容器运行 (发送 SIGTERM ) | docker stop containerId |
logs containerId | 查看 docker 容器的输出 | docker logs containerId |
exec containerId | 进入一个正在运行的 docker 容器执行命令 | docker container exec -it containerId /bin/bash |
cp containerId | 从正在运行的 Docker 容器里面,将文件拷贝到本机 | docker container cp containerId:app/package.json . |
commit containerId | 创建一个新镜像来自一个容器 | docker commit -a "victors" -m "test" abcd123efg test:v1 |
4.2 从 image 运行容器
docker run
命令会从 image
文件生成一个正在运行的容器实例,若发现本地没有指定的 image 文件,就会从 Docker Hub 仓库自动抓取。
# 启动镜像 ubuntu 并在启动的容器里执行命令 /bin/echo "Hello world"
$ docker run ubuntu /bin/echo "Hello world"
# 运行交互式的容器,允许对容器内的标准输入(STDIN)进行交互
$ docker run -it ubuntu /bin/bash
# 可以通过运行exit命令或者使用 CTRL+D 来退出容器
输出提示以后,hello world 会停止运行,容器自动终止(有些容器不会自动终止),关闭容器并不会删除容器文件,只是容器停止运行。
Docker以ubuntu镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果
参数 | 含义 |
---|---|
-i --interactive | 交互式 |
-t --tty | 分配一个伪终端 |
-d --detach | 运行容器到后台 |
-a --attach list | 附加到运行的容器 |
-e --env list | 设置环境变量 |
-p --publish list | 发布容器端口到主机 |
-P | --publish-all |
--mount mount | 挂载宿主机分区到容器 |
-v --volumn list | 挂载宿主机分区到容器 |
4.3 启动容器
$ docker start [containerId]
4.4 停止容器
$ docker stop [containerId]
4.5 进入一个容器 attach
$ docker attach [containerID]
4.6 进入运行中的容器 exec
$ docker container -exec -it [containerID] /bin/bash
$ docker exec -it [containerID] ip a # 打印容器ip
4.7 查看容器
$ docker ps
# 显示所有的容器,包括已停止的
$ docker container ls -a
# 显示最新的那个容器
$ docker container ls -l
字段 | 含义 |
---|---|
CONTAINER ID | 容器ID |
IMAGE | 使用的镜像 |
COMMAND | 使用的命令 |
CREATED | 创建时间 |
STATUS | 状态 |
PORTS | 端口号 |
NAMES | 自动分配的名称 |
4.8 后台运行容器
$ docker run --detach centos ping www.csxiaoyao.com
# 查看 docker 容器的输出
$ docker logs --follow abcd123efg
4.9 kill
$ docker kill abcd123efg
4.10 删除容器
# 删除容器
$ docker rm [containerId]
# 删除镜像
$ docker rmi [imageId]
# 删除所有停止运行的容器
$ docker rm $(docker ps -a -q)
4.11 拷贝文件
$ docker container cp [containerId] /README.md .
4.12 自动删除
$ docker run --rm ubuntu /bin/bash
4.13 容器资源限制
--memory
、--cpu-shares
设置内存和权重
$ docker run --cpu-shares=10 --memory=200M xxx/ubuntu-stress --vm 1 --verbose
4.14 查看容器内进程
$ docker top [CONTAINER ID/NAMES]
5. 制作个性化镜像 commit
# $ docker commit -m "test commit" -a "csxiaoyao" [containerId] csxiaoyao/testimage:1.0.0
$ docker commit -m "test commit" -a "csxiaoyao" [containerId] csxiaoyao/testimage:latest
$ docker image ls
$ docker run csxiaoyao/testimage /bin/bash
$ docker rm [containerId]
$ docker rmi [imageId]
6. Dockerfile
Docker 的镜像是用一层一层的文件组成的,docker inspect
命令可以查看镜像或容器
$ docker inspect centos
6.1 Dockerfile 语法总览
命令 | 含义 | 案例 |
---|---|---|
FROM | 继承的镜像 | FROM node |
LABEL | 定义 image 的 metadata,类似注释 | LABEL version="1.0" |
COPY | 从宿主机拷贝文件 | COPY ./app /app |
ADD | 拷贝文件或目录到镜像中,如果是URL或者压缩包会自动下载和解压 | ADD https://xxx.com/file.tar.gz /var/www/html |
WORKDIR | 设置 RUN CMD ENTRYPOINT COPY ADD 的工作目录 | WORKDIR /app |
RUN | 编译打包阶段运行命令,创建新的 layer,尽量合并一行,避免无用分层 | RUN npm install |
CMD | 容器运行阶段运行命令,可被 docker run 参数覆盖 | CMD npm run start |
ENTRYPOINT | 配置容器启动时运行的命令 | ENTRYPOINT /bin/bash -c '/start.sh' |
MAINTAINER | 作者 | MAINTAINER csxiaoyao |
EXPOSE | 暴露端口 | EXPOSE 3000 |
ENV | 设置容器内环境变量 | ENV MYSQL_ROOT_PASSWORD 19931128 |
VOLUME | 指定容器挂载点到宿主自动生成的目录或其它容器 | VOLUME "/var/lib/mysql" |
USER | 为 RUN CMD 和 ENTRYPOINT 执行命令指定运行用户 | USER csxiaoyao |
HEALTHCHECK | 健康检查 | HEALTHCHECK --interval=5m --timeout=3s --retries=3 CMS curl -f htp://localhost |
ARG | 在构建镜像时指定一些参数 | ARG user |
FROM,尽量使用官方 image 作为 base image
FROM centos # 制作 base image
FROM ubuntu:14.04
LABEL,定义 image 的 Metadata,类似注释,不可少
LABEL version="1.0"
LABEL description="test"
RUN,执行命令并创建新的 Image Layer,尽量合并一行,避免无用分层,为了美观,复杂的 RUN 可使用反斜线换行
RUN yum update && yum install -y vim \
python-dev
WORKDIR,不要用 RUN cd,尽量用绝对路径
WORKDIR /test
WORKDIR demo
RUN pwd # 输出 /test/demos
ADD / COPY,把本地文件添加到指定位置,ADD 相比 COPY 还可以自动解压缩,添加远程文件/目录使用 curl / wget
ADD test.tar.gz / # 添加到根目录并解压
WORKDIR /root
ADD hello test/ # /root/test/hellos
ENV,常量,增强可维护性
ENV MYSQL_VERSION 5.6 # 设置常量
RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \
&& rm -rf /var/lib/apt/lists/*
CMD,设置容器启动后默认执行的命令和参数,若 docker run 指定了其他命令,CMD 会被忽略,若定义了多个 CMD,只有最后一个会执行
两种写法:shell (完整的一行)、exec (参数数组的形式)
...
CMD ["python", "app.py"]
ENTRYPOINT,设置容器启动时运行的命令,让容器以应用程序或服务的形式运行,不会被忽略,推荐写一个 shell 脚本作为 entrypoint
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["mongod"]
6.2 忽略文件 .dockerignore
用于记录需要排除(不打包到 image 中)的文件的路径
.git
node_modules
6.3 demo
6.3.1 demo1
# 安装 npm
# 安装 node
$ npm install n -g
$ n latest
$ npm init -y
$ vi server.js
# ...
编写名为 Dockerfile
的文件
FROM node
COPY ./app /app
WORKDIR /app
RUN npm install
EXPOSE 3000
CMD node server.js
6.3.2 demo2
准备工作,本地app目录下新建 express 项目
$ npm install express-generator -g
$ express app
编写名为 Dockerfile
的文件
FROM node
COPY ./app /app
WORKDIR /app
RUN npm install
EXPOSE 3000
CMD npm start
6.3.3 指令说明
- FROM 该镜像继承的镜像
- COPY 将当前目录下app目录下面的文件拷贝到image里的/app目录中
- WORKDIR 指定工作路径,类似于执行
cd
命令 - RUN npm install 在image文件构建阶段在/app目录下安装依赖,执行结果会打包进入image文件
- EXPOSE 暴露3000端口,允许外部连接这个端口
- CMD npm start 在容器启动后执行,一个 Dockerfile 可以包含多个RUN命令,但是只能有一个CMD命令,指定了CMD命令后,docker container run 命令就不能附加命令(如 /bin/bash),否则会覆盖CMD命令
6.4 创建 image
# -t 指定image镜像的名称,后面还可以加冒号指定标签,如果不指定默认是latest
# . 表示 Dockerfile 文件所在路径,. 表示当前路径
$ docker build -t csxiaoyao/express-demo:latest .
$ docker image ls
6.5 使用新镜像运行容器
# -p 将容器的3000端口映射为本机的3333端口
# /bin/bash 容器启动后执行的第一个命令,会覆盖文件中配置的CMD
# --rm 在容器终止运行后自动删除容器文件
$ docker container run -p 3333:3000 -it express-demo /bin/bash
$ curl localhost:3333
6.6 发布 image
# 登录 dockerhub 账户
$ docker login
$ docker login --username=csxiaoyao --password=19931128
# build
# docker image build -t [username]/[repository]:[tag] .
# 镜像打标签
# docker image tag [imageName] [username]/[repository]:[tag]
$ docker tag express-demo:v1 csxiaoyao/express-demo:v1
# 上传
$ docker push csxiaoyao/express-demo:v1
7. 数据盘
有两种持久化存储方式:
- Data Volume 关联容器文件路径到主机,删除容器不会删除 Vloume,可以设置别名,如 nginx-vol
- Bind Mouting 可以实现绑定本地文件夹,实现开发调试
7.1 Data Volume
若想在删除容器时保留文件数据,如Web服务器日志,数据库数据等,可以为容器创建一个数据盘 volume,管理宿主机文件系统的一部分 (/var/lib/docker/volumes),如果没有指定卷,则会自动创建。
7.1.1 创建数据卷
$ docker volume --help
$ docker volume create nginx-vol
$ docker volume ls
$ docker volume inspect nginx-vol
7.1.2 挂载数据卷
# 把新建的 nginx-vol 数据卷挂载到 /usr/share/nginx/html
# 建议使用 --mount,更通用
$ docker run -d -it --name=nginx1 --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
$ docker run -d -it --name=nginx2 -v nginx-vol:/usr/share/nginx/html nginx
# mysql
$ docker run -d --name mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress mysql
7.1.3 删除数据卷
# 停止容器
$ docker container stop nginx1
# 删除容器
$ docker container rm nginx1
# 删除数据卷
$ docker volume rm nginx-vol
7.1.4 管理数据卷
# 列出所有的数据盘
$ docker volume ls
# 列出已经孤立的数据盘
$ docker volume ls -f dangling=true
# 删除数据盘
$ docker volume rm xxxx
7.2 Bind Mouting
此方式与Linux系统的mount方式相似,即是会覆盖容器内已存在的目录或文件,但并不会改变容器内原有的文件,当umount后容器内原有的文件就会还原。通过在创建容器时通过 -v
或 --volumn
指定数据盘路径,bind mounts
可以存储在宿主机系统的任意位置。如果源文件/目录不存在,不会自动创建,会抛出一个错误;如果挂载目标在容器中为非空目录,则该目录现有内容将被隐藏。
7.2.1 使用默认数据盘
$ docker run -v /mnt -it --name logs centos bash
> cd /mnt
> echo 1 > 1.txt
> exit
$ docker inspect logs
查看容器信息,Source
是给容器指定的数据盘在主机上的位置,Destination
是数据盘在容器上的位置
...
"Mounts": [
{
"Source":"/var/lib/docker/volumes/xxxxxxxx/_data",
"Destination": "/mnt",
}
]
...
7.2.2 指定数据盘
# demo1: $(pwd):/app
$ docker run -d -p 80:5000 -v $(pwd):/app --name app xxx/test-image
# demo2: ~/data:/mnt
$ docker run -v ~/data:/mnt -ti --name logs2 centos bash # 把当前用户目录中的data目录映射到/mnt上
> cd /mnt
> echo 2 > 2.txt
> exit
$ cat ~/data/2.txt
7.2.3 指定使用某容器的数据盘
# 创建 logger 容器
$ docker create -v /mnt --name logger centos
# 创建 logger1 容器使用 logger 的数据盘
$ docker run --volumes-from logger --name logger1 -i -t centos bash
> cd /mnt
> touch logger1
# 创建 logger2 容器使用 logger 的数据盘
$ docker run --volumes-from logger --name logger2 -i -t centos bash
> cd /mnt
> touch logger2
8. 网络 network
docker里有一个DNS服务,可以通过容器名称访问主机,分三种网络类型:
- none 无网络,对外界完全隔离
- host 主机网络
- bridge 桥接网络(默认),适用于日常需要连接网络的容器,如http容器、web容器...
# ping 验证IP可达性,telnet 验证服务(端口)可用性
$ ping 192.168.205.10
$ telnet 192.168.205.10 80
8.1 bridge
$ docker network ls
$ docker inspect bridge
$ docker run -d --name server1 nginx
$ docker run -d --name server2 nginx
$ docker exec -it server1 bash
> ping server2
8.2 none
$ docker run -d --name server_none --net none nginx
$ docker inspect none
$ docker exec -it server_none bash
> ip addr
8.3 host
$ docker run -d --name server_host --net host nginx
$ docker inspect none
$ docker exec -it server_host bash
> ip addr
8.4 访问桥接网络里的服务(端口绑定)
# 随机分配主机端口,容器内部端口随机映射到主机高端口
$ docker run -d -P [CONTAINER ID/NAMES] python app.py
# 指定主机端口
$ docker run -d -p 80:5000 [CONTAINER ID/NAMES] python app.py
# 指定host
$ docker run -d -p 127.0.0.1:80:5000 [CONTAINER ID/NAMES] python app.py
# 指定协议,默认TCP
$ docker run -d -p 127.0.0.1:80:5000/udp [CONTAINER ID/NAMES] python app.py
例如
# 访问主机的 8080 端口会被定向到容器的 80 端口
$ docker run -d --name server_nginx -p "8080:80" nginx
# 查看主机绑定的端口
$ docker port server_nginx
8.5 创建自定义网络
$ docker network create --driver bridge web
$ docker network inspect web
$ docker run -d --name webserver --net web nginx
$ docker network connect web webserver1
$ docker network disconnect web webserver2
9. docker-compose.yml
9.1 安装
compose 通过一个配置文件来管理多个 docker 容器,但是 只适用于单机,linux 需要独立安装 compose
$ pip install docker-compose
9.1 基础语法
services 代表一个 container,启动 service 类似 docker run
version: '2'
services:
test1:
image: nginx
port:
- "8080:80"
test2:
image: nginx
port:
- "8081:80"
9.2 操作指令
# 启动所有的服务,docker会创建默认的网络
$ docker-compose up
# 后台启动所有的服务
$ docker-compose up -d
# 打印所有的容器
$ docker-compose ps
# 停止所有服务
$ docker-compose stop
# 开始所有服务
$ docker-compose start
# 持续跟踪日志
$ docker-compose logs -f
# 进入容器
$ docker-compose exec test1 bash
# 通过服务名连接
> ping test2
# 删除服务容器
$ docker-compose rm
# 网络不会删除
$ docker network ls
# stop 并删除资源
$ docker-compose down
9.3 配置数据卷
version: '2'
services:
test1:
image: nginx
ports:
- "8080:80"
networks:
- "demo_network"
volumes:
- "access:/mnt"
test2:
image: nginx
ports:
- "8081:80"
networks:
- "demo_network"
volumes:
- "access:/mnt"
test3:
image: nginx
ports:
- "8082:80"
networks:
- "default"
- "demo_network"
networks:
demo_network:
driver: bridge
volumes:
access:
driver: local
9.4 配置根目录
version: '2'
services:
test1:
image: nginx
ports:
- "8080:80"
networks:
- "demo_network"
volumes:
- "access:/mnt"
- "./test1:/usr/share/nginx/html"
test2:
image: nginx
ports:
- "8081:80"
networks:
- "demo_network"
volumes:
- "access:/mnt"
- "./test2:/usr/share/nginx/html"
test3:
image: nginx
ports:
- "8082:80"
networks:
- "default"
- "demo_network"
networks:
demo_network:
driver: bridge
volumes:
access:
driver: local
9.5 配置环境变量
version: '3'
services:
wordpress:
image: wordpress
ports:
- 8080:80
depends_on:
- mysql
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- my-bridge
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-bridge
volumes:
mysql-data:
networks:
my-bridge:
driver: bridge
9.6 通过 Dockerfile build
参考 10.1.3 docker-compose.yml
9.7 水平扩展和负载均衡
version: "3"
services:
redis:
image: redis
web:
build:
context: .
dockerfile: Dockerfile
ports: ["8080"]
environment:
REDIS_HOST: redis
lb:
image: dockercloud/haproxy
links:
- web
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
--scale
,如 lb service 使用了 image: dockercloud/haproxy
$ docker-compose up --scale web=3 -d
10. Demo
10.1 nodeapp
10.1.1 相关服务
db(mariadb)、node、web server(nginx)
10.1.2 目录结构
-- app/
|-- web/ # 应用代码
-- services/ # 相关服务
-- images/ # 编译的脚本和镜像
|-- nginx/config/default.conf # nginx 配置文件
|-- node/Dockerfile
-- docker-compose.yml
10.1.3 docker-compose.yml
version: '2'
services:
node:
build:
context: ./images/node
dockerfile: Dockerfile
volumes:
- ./app/web:/web
depends_on:
- db
web:
image: nginx
ports:
- "8080:80"
volumes:
- ./images/nginx/config:/etc/nginx/conf.d
- ./app/web/views:/mnt/views
depends_on:
- node
db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: "root"
MYSQL_DATABASE: "node"
MYSQL_USER: "csxiaoyao"
MYSQL_PASSWORD: "19931128"
volumes:
- db:/var/lib/mysql
volumes:
db:
driver: local
10.1.4 app/web/server.js
let http=require('http');
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'db',
user : 'csxiaoyao',
password : '19931128',
database : 'node'
});
connection.connect();
let server=http.createServer(function (req,res) {
// ...
});
server.listen(3000);
10.1.5 package.json
"scripts": {
"start": "node server.js"
},
"dependencies": {
"mysql": "^2.16.0"
}
10.1.6 images/node/Dockerfile
FROM node
MAINTAINER csxiaoyao <x@csxiaoyao.com>
WORKDIR /web
RUN npm install
CMD npm start
10.1.7 images/nginx/config/default.conf
upstream backend {
server node:3000;
}
server {
listen 80;
server_name localhost;
root /mnt/views;
index index.html index.htm;
location /api {
proxy_pass http://backend;
}
}
10.2 搭建 LNMP-wordpress
10.2.1 关闭防火墙
功能 | 命令 |
---|---|
停止防火墙 | systemctl stop firewalld.service |
永久关闭防火墙 | systemctl disable firewalld.service |