一. Docker概述与安装
开发一个新项目后,会涉及到环境部署的问题(如MySQL、Redis等),会耗费比较多时间,因此急需一种可以快速打包部署项目并上线的方法。
Docker(一种容器化技术)因此而生。它的思想是隔离,将项目与环境进行打包、装箱,将服务器性能利用到极致。它是一个快速交付、运行应用的技术。
Docker运行到不同操作系统时,打包时会将系统库函数一同打包,最后借助Linux内核运行,实现Linux内核系统的跨平台。
官方文档:https://docs.docker.com/
Docker 和 虚拟机技术有何不同?
Docker架构
镜像(image):类似一个模板,可以通过模板来创建容器服务。通过一个镜像可以创建多个容器。它是一种轻量级、可执行的独立软件包,包含运行软件所需的运行时、库、环境变量和配置环境等。
容器(container):独立运行一个或一组应用,可以理解为一个简易的linux系统
仓库(repository):存放镜像的位置
Docker安装
环境查看
1 2 3 4 5 6
| [root@iZ2ze7qxbl1r54zhx9fbs3Z ~] 3.10.0-1127.10.1.el7.x86_64
[root@iZ2ze7qxbl1r54zhx9fbs3Z /]
|
安装帮助文档:https://docs.docker.com/engine/install/
按照文档安装完成后
1 2 3 4 5 6 7 8 9 10 11
| systemctl start docker
docker version
docker run hello-world
docker images
|
配置阿里云镜像加速服务(控制台->产品与服务->弹性计算->容器镜像服务->镜像加速器)
1 2 3 4 5 6 7 8 9 10 11
| sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://7nonk1j6.mirror.aliyuncs.com"] } EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
|
二. Docker常用命令
帮助命令
1 2 3 4
| docker version docker info docker help docker 命令 --help
|
官方文档:https://docs.docker.com/engine/reference/run/
镜像命令
docker images:查看本地主机上的镜像
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 7 months ago 13.3kB
# 说明 REPOSITORY 镜像的仓库源 TAG 镜像标签 IMAGE ID 镜像ID CREATED 镜像创建时间 SIZE 镜像大小
# 常用可选项 -a , --all # 列出所有镜像 -q , --quiet # 只显示镜像ID
|
docker search:搜索镜像
1 2 3 4
| [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker search mysql
# 常用可选项,使用filter限制搜索条件(例如下方搜索3000赞以上的镜像) [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker search mysql --filter=STARS=3000
|
docker pull:下载镜像
1 2
| # 如果不写tag默认latest docker pull 镜像名[:tag] # 例如 docker pull mysql:5.7
|
docker rmi:删除镜像
1 2
| docker rmi -f 镜像ID或镜像名 docker rmi -f $(docker images -aq)
|
docker save / docker load:保存或加载镜像
1 2
| docker save -o redis.tar redis:latest docker load -i redis.tar
|
容器命令
首先,下载一个centos镜像进行测试
新建容器并启动
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 run [参数] image
# 常用参数 --name # 容器名 --detach # 后台方式运行 -d -it # 使用交互方式运行 -p # 指定容器端口 # -p ip:主机端口:容器端口 # -p 主机端口:容器端口 # -p 容器端口 -P # 随机指定端口(大写) --ip # 指定容器运行的ip,要注意user specified IP address is supported on user defined networks only -net # 运行在指定的网络的名称
# 启动并进入容器 [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker run -it centos /bin/bash
# 退出容器 [root@274f17eaf88a /]# exit
# 查看正在运行容器 [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker ps -a # 列出历史+现在正在运行的容器 -n=? # 显示最近个数 -q # 只显示容器ID
|
查看所有容器
1 2
| docker container ls -a # 列出所有容器 docker system df -v # 占用空间情况
|
退出容器
1 2
| exit # 容器停止并退出 Ctrl+P+Q # 容器不停止并退出
|
删除容器
1 2
| docker rm 容器ID # 删除容器 docker rm -f $(docker ps -aq) # 删除所有容器
|
启动与停止容器
1 2 3 4 5 6
| docker start 容器ID # 启动 docker restart 容器ID # 重启容器 docker stop 容器ID # 停止容器 docker kill 容器ID # 强制停止 docker pause 容器ID # 暂停容器(将进程挂到内存) docker unpause 容器ID # 恢复暂停的容器
|
其他命令
后台启动容器
1 2
| [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker run -d centos # 注意:使用后台运行,需要有一个前台进程,否则会自动停止
|
日志查看
1 2 3
| # docker logs -tf --tail 条数 容器ID # -t为显示时间戳, -f为跟踪日志输出 [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker logs -tf --tail 10 f6baeea082b1
|
查看容器中进程信息
1 2
| # docker top 容器ID [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker top f6baeea082b1
|
查看镜像数据源
1 2
| # docker inspect 容器ID [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker inspect f6baeea082b1
|
进入当前正在运行容器
1 2 3 4
| # docker exec -it 容器ID /bin/bash # docker attach 容器ID [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker exec -it f6baeea082b1 /bin/bash [root@iZ2ze7qxbl1r54zhx9fbs3Z ~]# docker attach f6baeea082b1
|
从容器内拷贝文件到主机
1
| docker cp 容器ID:文件路径 主机路径
|
容器内安装 sudo
1 2
| apt-get update apt-get -y install sudo
|
查看网络信息
1
| docker network inspect bridge
|
创建网络
1
| docker network create -d bridge --subnet 172.20.0.0/24 network_name
|
三. 安装练习
仓库推荐:DockerHub
可视化面板推荐:portainer
安装Nginx
1 2 3 4 5 6 7 8 9
| # 1.搜索并拉取镜像 docker search nginx docker pull nginx
# 2.新建容器并启动 -d为后台运行,名字为nginx01,本机端口3344,容器端口80 docker run -d --name nginx01 -p 3344:80 nginx
# 3.访问测试 curl localhost:3344
|
安装Tomcat
1 2 3 4 5 6 7 8
| # 直接安装 docker run -it --rm tomcat:9.0 # 测试,用完即删
# 下载再启动 docker pull tomcat
# 启动 docker run -d --name tomcat01 -p 3345:80 tomcat
|
安装ES+head插件+Kibana
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 启动 docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.8.1
# 注意:安装完后会发现内存占用很大,服务器十分卡顿 docker stats # 查看服务器资源状态
# 启动时限制内存在64MB-512MB -e ES_JAVA_OPTS="-Xms64m -Xmx512m" docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.8.1
# 拉取并安装head监控管理插件 docker pull mobz/elasticsearch-head:5 docker run -d -p 9100:9100 docker.io/mobz/elasticsearch-head:5
# 安装kibana docker run --name kibana -e ELASTICSEARCH_URL=http://39.97.107.13:9200 -p 5601:5601 -d kibana:7.8.1
|
补充:在使用宝塔 Docker 部署 ES 时,要注意填写的内存大小,资源不足会导致容器停止运行。对于2G内存服务器来说,建议填写400MB限制,同时对镜像提供运行环境参数:discovery.type=single-node
,ES_JAVA_OPTS=-Xms64m -Xmx160m
安装portainer
在网上发现一个不错的Docker可视化工具
1 2 3
| docker volume create portainer_data
docker run -d -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
|
四. 镜像原理与数据卷
镜像加载
联合文件系统(UnionFS)
是一种分层、轻量级且高性能的文件系统,支持对文件系统的修改作为一次提交来以层层叠加。它是Docker镜像的基础。
Docker镜像加载原理
Docker镜像实际上由一层层文件系统组成,这种层级文件系统就是 UnionFS。
总的来说,镜像是将应用程序的及其需要的系统函数库、环境、配置和依赖打包而成。
Q:为什么CentOS镜像那么小?
A:精简后的OS,rootfs(root file system)可以非常小,只需包含基本命令、工具和程序库。对于不同Linux发行版,bootfs(boot file system)基本一致,只是rootfs有差别。
特点
Docker镜像都是只读,当容器启动时一个新的 可写层 会被加载到镜像顶部。
这一层也被称为 容器层,容器以下的都是 镜像层。
镜像结构:入口(Entryppoint)、层(Layer)、基础镜像(BaseImage)
镜像提交
1
| docker commit -m="描述信息" -a="作者名" 容器id 目标镜像名:[TAG]
|
容器数据卷管理
将Docker容器内产生的数据同步到本地,相当于挂载到宿主机上。这样一来,就算将容器不小心删除了,我们的数据还是能得到保存。(容器与数据分离,解耦合,方便操作容器内数据,保证数据安全)
需要注意的是,-v是将本地目录挂载到容器内,如果本地目录为空,在容器中查看对应的目录也会为空。
方法一:目录或文件直接挂载
1 2 3 4 5 6 7
| docker run -it -v 主机目录:容器内目录 -p
# 查看挂载情况 "Mounts"属性 docker inspect 容器ID
# MySQL数据挂载测试,注意-e配置参数,参考dockerhub docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw=123456 --name mysql01 mysql:5.7
|
方法二:数据卷挂载
1 2 3 4 5 6
| # 创建数据卷 docker volume create html # 挂载,如果没创建数据卷Docker会自动创建 docker run --name mn -v html:/usr/share/nginx/html -p 80:80 -d nginx # 或者通过 --volumes-from 实现两个容器的数据卷同步 docker run -it --name docker-2 --volumes-from docker-1 92f4cfd1ccbe
|
方法三:启动容器后追加挂载
第一步:使用docker ps
查看正在运行的容器id(2831a679ab1b)
第二步:找到配置文件/var/lib/docker/containers/<容器ID>/config.v2.json
,注意macOS上的Docker需要用特殊方法找到配置文件。
第三步:修改配置文件,这里暂时还没试过,TODO。
匿名挂载与具名挂载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 匿名挂载,只写容器内路径 docker run -d -P --name nginx01 -v /etc/nginx nginx # 查看卷情况 [root@iZ2ze7qxbl1r54zhx9fbs3Z /]# docker volume ls DRIVER VOLUME NAME local 5c0348ebc465674851391d48a81cecdd91a9f4c52ec09209a49e68ab4ce33f22
# 具名挂载 卷名:容器内路径 docker run -d -P --name nginx01 -v nginx_juan:/etc/nginx nginx
# 可以通过 ro rw 改变容器读写权限 ro readonly 只读 rw readwrite 可读写 # 例如 docker run -d -P --name nginx01 -v nginx_juan:/etc/nginx:ro nginx
|
DockerFile简介
它是一个构建docker镜像的文件,是命令参数脚本。
步骤:
- 编写一个 dockerfile 文件
- docker build 构建一个新镜像
- docker run 运行镜像
- docker push 发布镜像(dockerhub、阿里云镜像仓库等)
举例
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
| # 创建dockerfile FROM centos VOLUME ["volume01","volume02"] # 挂载目录,启动后会在根目录创建 CMD echo "---end---" CMD /bin/bash
# 测试新建容器 -f dockerfile文件名 -t 镜像名:版本号 [root@iZ2ze7qxbl1r54zhx9fbs3Z ceshi]# docker build -f /home/ceshi/dockerfile1 -t banana_centos:1.0 . Sending build context to Docker daemon 3.072kB Step 1/4 : FROM centos ---> 831691599b88 Step 2/4 : VOLUME ["volume01","volume02"] ---> Running in 8ca74a22f596 Removing intermediate container 8ca74a22f596 ---> 665e599d4961 Step 3/4 : CMD echo "---end---" ---> Running in 5a0de7cd643b Removing intermediate container 5a0de7cd643b ---> 23d0d3685d01 Step 4/4 : CMD /bin/bash ---> Running in 84671b255a0b Removing intermediate container 84671b255a0b ---> 92f4cfd1ccbe Successfully built 92f4cfd1ccbe Successfully tagged banana_centos:1.0
|
构建过程
基础知识:
- 每个关键字指令必须是大写字母
- 从上到下按顺序执行
- 注释符号是 #
- 每个指令都会创建一个新的镜像层
DockerFile指令与编写
1 2 3 4 5 6 7 8 9 10 11 12
| FROM # 从基础镜像开始构建 MAINTAINER # 维护者信息,姓名+邮箱 RUN # 运行的命令 ADD # 添加文件 如tomcat WORKDIR # 设置镜像工作目录 VOLUME # 挂载的目录 EXPOSE # 指定对外端口 CMD # 容器运行时要运行的命令,只有最后一个生效 ENTRYPOINT # 容器运行时要运行的命令,可以追加命令 ONBUILD # 构建一个被继承DockerFile时会被触发 COPY # 将文件拷贝到镜像中 ENV # 设置环境变量
|
创建一个自己的CentOS镜像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # 1.编写dockerfile文件 FROM centos MAINTAINER banana<798998087@qq.com>
ENV MYPATH /usr/local # 设置工作目录 WORKDIR $MYPATH
RUN yum -y install vim RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH CMD echo "---end---" CMD /bin/bash
# 2.通过文件构建镜像 docker build -f dockerfile文件名 -t 镜像名:版本号 .
|
创建一个Tomcat镜像
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
| # 1.准备好tomcat与jdk安装包 [root@iZ2ze7qxbl1r54zhx9fbs3Z tomcat]# ls apache-tomcat-8.5.50.tar.gz jdk-8u241-linux-x64.tar.gz
# 2.编写文件Dockerfile(这是官方命名,用这个名字后构建镜像无需-f指定) FROM centos MAINTAINER banana<798998087@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u241-linux-x64.tar.gz /usr/local ADD apache-tomcat-8.5.50.tar.gz /usr/local
RUN yum -y install vim
ENV MYPATH /usr/local WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.50 ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.50 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-8.5.50/bin/startup.sh
# 3.构建镜像,点代表当前所在目录 docker build -t mytomcat .
# 4.创建容器,启动(同时将目录挂载出来方便部署) docker run -d -p 9090:8080 --name bananaTomcat -v /home/ceshi/tomcat/test:/usr/local/apache-tomcat-8.5.50/webapps/test -v /home/ceshi/tomcat/logs:/usr/local/apache-tomcat-8.5.50/logs mytomcat
|
创建一个Java项目镜像
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
| # 1.指定镜像基础 FROM ubuntu:16.04
# 2.配置环境变量,JDK的安装目录 ENV JAVA_DIR=/usr/local
# 3.拷贝JDK和Java项目将 COPY ./jdk8.tar.gz $JAVA_DIR/
# 4.安装JDK RUN cd $JAVA_DIR/ \ && tar -xf ./jdk8.tar.gz \ && mv ./jdk1.8.0_144 ./java8
# 5.配置环境变量 ENV JAVA_HOME=$JAVA_DIR/java8 ENV PATH=$PATH:$JAVA_HOME/bin
# 6.放入项目 COPY ./docker-demo.jar /tmp/app.jar
# 7.暴露端口 EXPOSE 8080
# 8.入口 ENTRYPOINT java -jar /tmp/app.jar
|
实际上,1-5步骤基本不会变,所以可以先打包成一个镜像,然后再根据这个镜像去构建,后期调整会方便很多。在DockerHub上已经有人做了这些事,并上传了这样的镜像(例如 java:8-alpine)。
发布镜像
DockerHub
在 https://hub.docker.com/ 注册账号后
服务器上提交镜像
1 2 3 4 5 6 7 8 9 10 11
| # 登录 docker login -u 账号
# 推送镜像前要记得修改tag docker tag 镜像ID 账号ID/镜像名:版本号
# 推送(例子) docker push tj20185584/tomcat:1.0
# 查看仓库 https://hub.docker.com/r/tj20185584/tomcat
|
阿里云镜像服务
产品与服务->弹性计算->容器镜像服务->命名空间(创建)->镜像仓库(创建)
然后在镜像仓库页面中点击“管理”,查看操作指南即可
添加映射卷
先停止容器
停止docker服务
1
| sudo snap stop docker # 我的树莓派用的是snap安装
|
查找config.v2.json文件
1 2 3 4
| # snap cd /var/snap/docker/common/var-lib-docker/containers/容器ID # 正常 cd /var/lib/docker/containers/容器ID/
|
修改MountPoints
下的配置,要注意bind和volume的区别,bind是容器以宿主机文件夹为准;
而volume是 ① 宿主有数据时,以宿主机为准 ②宿主无数据,从容器复制过来,再以宿主机为准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| "/var/www/html/pan": { "Source": "/media/banana", // 宿主机路径 "Destination": "/var/www/html/pan", // 容器内路径 "RW": true, "Name": "", "Driver": "", "Type": "bind", "Propagation": "rprivate", "Spec": { "Type": "bind", "Source": "/media/banana", "Target": "/var/www/html/pan" }, "SkipMountpointCreation": false }
|
启动docker服务
启动容器
五. SpringBoot打包Docker镜像
1.将项目打包 maven package
2.在idea中安装Docker插件
3.在target文件夹内编写Dockerfile文件
1 2 3 4 5 6 7 8 9
| FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
|
4.将jar包(或war包)和Dockerfile上传至服务器
5.打包镜像
6.生成容器并运行
1
| docker run -d -P --name 容器名 镜像名
|
六. Mac上的容器访问问题
在macOS上的Docker无法通过容器IP直接访问,因为系统问题,造成Docker底层实现有差距,
原因参考:https://blog.csdn.net/luo_cs_dn/article/details/122880902
解决方法是安装docker-connector:
1 2 3 4 5 6 7 8 9 10 11 12
| brew tap wenjunxiao/brew brew install docker-connector
docker network ls --filter driver=bridge --format "{{.ID}}" | xargs docker network inspect --format "route {{range .IPAM.Config}}{{.Subnet}}{{end}}" >> "$(brew --prefix)/etc/docker-connector.conf"
sudo brew services start docker-connector
docker pull wenjunxiao/mac-docker-connector
docker run -it -d --restart always --net host --cap-add NET_ADMIN --name connector wenjunxiao/mac-docker-connector
|
然后就可以通过IP地址访问容器了。
七. Docker Compose部署微服务案例
Docker Compose可以基于Compose文件快速部署分布式应用,无需一个个手动创建和运行容器。
下面以部署gateway、order-service和user-service为例,目录结构如下图。
其中gateway、order-service、user-service 这三个文件夹中都有一个Dockerfile文件,内容如下:
1 2 3
| FROM java:8-alpine COPY ./app.jar /tmp/app.jar ENTRYPOINT java -jar /tmp/app.jar
|
后面只要将打包好的微服务jar包命名为app.jar放进去即可。
然后是最外层的docker-compose.yml文件:
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: "3.2"
services: nacos: image: nacos/nacos-server environment: MODE: standalone ports: - "8848:8848" mysql: image: mysql:5.7.25 environment: MYSQL_ROOT_PASSWORD: 123 volumes: - "$PWD/mysql/data:/var/lib/mysql" - "$PWD/mysql/conf:/etc/mysql/conf.d/" userservice: build: ./user-service orderservice: build: ./order-service gateway: build: ./gateway ports: - "10010:10010"
|
打包时要注意把项目的数据库、nacos地址都命名为docker-compose中的服务名。
然后在cloud-demo目录下执行:
1 2 3 4
| docker-compose up -d
docker-compose restart gateway userservice orderservice
|
八. 私有镜像仓库
搭建
首先编写docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| version: '3.0' services: registry: image: registry volumes: - ./registry-data:/var/lib/registry ui: image: joxit/docker-registry-ui:static ports: - 8080:80 environment: - REGISTRY_TITLE=扎克蕉的私人仓库 - REGISTRY_URL=http://registry:5000 depends_on: - registry
|
运行docker-compose up -d
指令,随后访问该机器对应IP的8080端口,如下图所示。
由于本地测试没有HTTPS协议,需要配置信任地址。
Linux:vim /etc/docker/daemon.json
Mac:vim ~/.docker/daemon.json
最外层添加内容:“insecure-registries”:[“http://172.16.155.19:8080”]
然后重启Docker即可。
上传与拉取镜像
1 2 3 4 5 6 7 8
| docker tag nginx:latest 172.16.155.19:8080/nginx:1.0
docker push 172.16.155.19:8080/nginx:1.0
docker pull 172.16.155.19:8080/nginx:1.0
|