工具 / 运维

Docker 学习总结

csxiaoyao · 6月6日 · 2020年本文共13740个字 · 预计阅读46分钟508次已读

Docker 学习总结

Write By CS逍遥剑仙
我的主页: csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: sunjianfeng@csxiaoyao.com
QQ: 1724338257

1. docker 架构

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][username]/[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上去的容器csxiaoyao.com必须正在运行,可以同时连接上同一个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 --usercsxiaoyao.comname=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

11. swarm & kubernetes

参考 github-csxiaoyaojianxian-DockerStudy

12. docker cloud

Docker 学习总结

13. 参考

Docker 学习总结

Docker 学习总结

Docker 学习总结

0 条回应