文章目录
- 快速入门
- 简介
- 安装
- 配置镜像加速
- 部署MySQL
- Docker基础
- 常见命令
- 案例-部署Nginx
- 命令别名
- 数据卷挂载
- 本地目录挂载
- DockerFile语法
- 自定义镜像
- 容器网络互联
- 项目部署
- 部署Java应用
- 部署前端
- DockerCompose
快速入门
简介
Docker是一个快速构建、运行、管理应用的工具。
传统的企业项目部署需要手动通过命令行安装环境和依赖,安装步骤复杂且常常会出现版本不兼容等问题。
借助Docker,我们只需要执行docker run命令即可一键让项目及其依赖在服务器成功运行起来。
由于项目通常在Linux系统部署,正式学习Docker前可以通过Vmware虚拟机安装CentOS系统。
Docker官网:https://www.docker.com/
安装
-
配置Docker的yum仓库:
yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
-
安装Docker:
yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
-
启动和校验:
# 启动Docker systemctl start docker # 停止Docker systemctl stop docker # 重启 systemctl restart docker # 设置开机自启 systemctl enable docker # 执行docker ps命令,如果不报错,说明安装启动成功 docker ps
配置镜像加速
推荐使用阿里云的镜像加速服务。
-
找到产品->容器->容器镜像服务ACR。
-
找到镜像加速器并按照文档配置即可:
部署MySQL
用Docker部署MYSQL只需要执行如下命令即可:
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
mysql
当我们执行这个命令时,Docker会自动搜索并下载镜像(Image)。镜像不仅包含应用本身,还包含应用所需的环境、配置、函数库。
Docker会在运行镜像时创建一个隔离环境,称为容器(Container)。Docker官方维护了一个公共镜像Docker Hub用于存储和管理镜像。
下面详细介绍这条命令:
- docker run:创建并运行一个容器,-d是让容器在后台运行。
- –name mysql:给容器命名,必须唯一。
- -p 3306:3306:端口映射,容器有自己的隔离网络系统,通过-p [宿主机端口]:[容器端口]可以进行端口映射。
- -e:设置环境变量,-e KEY=VALUE。根据镜像的文档说明设置。
- mysql:镜像名称,完整写法:[repository]:[tag],即[镜像名]:[版本]。默认是latest最新版本。
Docker基础
常见命令
我们可以从镜像仓库拉取镜像或向镜像仓库推送镜像,常用命令:
-
docker push:将镜像推送到镜像仓库
-
docker pull:从镜像仓库拉取镜像到本地仓库
-
docker images:查看本地镜像
-
docker rmi:删除本地镜像
除了在镜像仓库获取镜像,也可以在本地编写dockerfile构建自己的镜像:
- docker build:根据编写的dockerfile构建镜像
为了方便镜像传输,可以将镜像打包成压缩文件,常用命令:
- docker save:将镜像打包成压缩文件保存在本地
- docker load:将压缩文件加载到docker本地镜像仓库
有了镜像,可以通过如下命令创建容器并启动镜像:
- docker run:创建并运行容器(如本地没有镜像会自动拉取)
对容器进行管理的常用命令:
- docker stop:停止容器进程
- docker start:启动容器进程
- docker ps:查看运行中的容器
- docker rm:删除容器
- docker logs:查看容器运行日志
- docker exec:进入容器内部执行命令
案例-部署Nginx
掌握了Docker的常见命令,现在来完成一个简单的案例,创建并运行Nginx容器。
-
首先,在Docker Hub搜索Nginx镜像,查看镜像名称。
-
通过docker pull nginx拉取镜像到本地,然后使用docker images查看本地镜像:
-
通过docker run -d --name nginx -p 80:80 nginx命令创建并运行容器,然后使用docker ps查看运行中的容器:
-
通过docker stop nginx停止容器,然后通过docker ps -a查看所有容器:
-
通过docker start nginx再次启动容器:
-
通过docker logs -f nginx查看日志,-f指follow持续输出日志(无法访问请关闭centos防火墙):
-
通过docker exec -it nginx bash命令进入容器并使用bash进行交互,-it指可交互终端。
命令别名
前面我们通过docker ps可以查看容器运行状态,但是输出的信息比较多。ps命令可以通过–format参数自定义输出格式:
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
但是这个命令太复杂,我们可以借助Linux的别名来简化这个操作。通过vim ~/.bashrc命令编辑加入下面的指令:
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
然后执行source ~/.bashrc,测试dps命令:
数据卷挂载
我们以一个需求为例:
- 创建nginx容器,修改nginx容器内的html目录下的index.html文件。
- 将静态资源部署到nginx的html目录。
在Docker Hub查看nginx镜像官方文档,发现/usr/share/nginx/html为静态资源目录:
通过docker exec -it nginx bash进入容器内部控制台,查看静态资源目录:
通过vi index.html修改静态资源,发现不存在vi指令。原因是docker镜像包含的是服务运行所必须的依赖,而vi是非必须的。
为了解决这个问题,我们可以使用数据卷:数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。
我们创建数据卷,会在宿主机系统的/var/lib/docker/volumes/目录下创建相应的文件夹。容器和宿主机文件系统就可以实现双向绑定。
如何创建数据卷呢?Docker为我们提供了相应的命令:
命令 | 说明 |
---|---|
docker volume create | 创建数据卷 |
docker volume ls | 查看所有数据卷 |
docker volume rm | 删除指定数据卷 |
docker volume inspect | 查看某个数据卷详情 |
docker volume prune | 清除数据卷 |
在执行docker run命令时,使用 -v 数据卷:容器内目录 可以完成数据卷挂载。如果挂载的数据卷不存在会自动创建数据卷。
执行docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx命令,挂载数据卷且创建并运行容器。
然后通过docker volume ls和docker volume inspect查看数据卷,容器内的html文件夹会映射到/var/lib/docker/volumes/html/_data文件夹。
我们只需要修改/var/lib/docker/volumes/html/_data下的index.html即可:
本地目录挂载
通过docker inspect可以查看容器详情,其中的Mounts代表挂载的数据卷,查看mysql容器:
我们创建mysql容器时虽然没有创建和指定数据卷,但它还是挂载到了一个数据卷。这个数据卷由容器自行创建,称为匿名卷。
匿名卷映射了容器的/var/lib/mysql目录,这个目录存储了mysql的数据。匿名卷目录名非常长,不方便数据访问,最好在启动mysql容器时指定数据卷。
除了之前的数据卷挂载,我们也可以挂载到宿主机的本地目录:docker run -v 本地目录:容器内目录 可以完成本地目录挂载。
对于mysql,除了数据目录,还需要挂载配置文件、初始化脚本目录,这些目录可以查询Docker Hub的官方文档:
我们可以执行如下挂载:
- /root/mysql/data -> /var/lib/mysql
- /root/mysql/init -> /docker-entrypoint-initdb.d
- /root/mysql/conf -> /etc/mysql/conf.d
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123 \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/init:/docker-entrypoint-initdb.d \
-v /root/mysql/conf:/etc/mysql/conf.d \
mysql
DockerFile语法
DockerFile是一个文本文件,包含一个个指令说明执行什么操作来构建镜像。常见指令:
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:6 |
ENV | 设置环境变量,可在后面指令使用 | ENV key:value |
COPY | 拷贝本地文件到镜像的指定目录 | COPY ./jre11.tar.gz /tmp |
RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN tar -zxvf /tmp/jre11.tar.gz && EXPORTS path=/tmp/jre11:$path |
EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
例:
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]
也可以直接基于JDK为基础镜像,省略一些步骤:
# 基础镜像
FROM openjdk:11.0-jre-buster
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
自定义镜像
编写完DockerFile后可以通过 docker build -t [repository]:[tag] . 命令构建镜像。
- -t:给镜像起名,不指定tag时默认为latest。
- .:指定DockerFile所在目录,如果在当前目录就使用"."。
容器网络互联
每个容器都有自己的隔离网络。默认情况下,所有容器都是以bridge方式连接到Docker的一个虚拟网桥上。
Docker的默认网桥是docker0:172.17.0.1/16,如图所示:
默认情况下,不同容器是可以相互连通的。但是容器的ip地址是动态的,每次重启可能会发生变化。
我们可以自定义网络,加入自定义网络的容器可以通过容器名互相访问,Docker的网络操作命令如下:
命令 | 说明 |
---|---|
docker network create | 创建一个网络 |
docker network ls | 查看所有网络 |
docker network rm | 删除指定网络 |
docker network prune | 清除未使用的网络 |
docker network connet | 使指定容器加入某网络 |
docker network disconnect | 使指定容器离开某网络 |
docker network inspect | 查看网络详细信息 |
-
通过docker network create创建网络并通过docker network ls查看网络:
-
通过ip addr指令查看发现多了一个虚拟网桥:
-
通过docker network connect simplicity mysql将mysql容器加入simplicity网络,然后通过docker inspect mysql查看网络信息:
此时,同网络中的其它容器可以通过 ping mysql 来访问这个容器。
除了这种方式,还可以在创建容器时加入 –network simplicity 使容器加入指定网络。
项目部署
部署Java应用
-
配置数据库信息。实际项目开发中的数据库连接参数通常用$来引用其它配置文件的变量,根据运行环境不同选择不同的数据库配置信息:
例如,在本地(local)使用测试数据库,而在服务器(dev)正式部署使用mysql容器名:
-
通过Maven的package命令打包:
-
编写DockerFile:
# 基础镜像 FROM openjdk:11.0-jre-buster # 设定时区 ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 拷贝jar包 COPY service.jar /app.jar # 入口 ENTRYPOINT ["java", "-jar", "/app.jar"]
-
将jar包和DockerFile放到同一个目录下,然后使用docker build -t “mall” .构建镜像:
-
通过docker run -d --name mall -p 8080:8080 --network simplicity mall创建并运行容器并通过docker logs -f mall查看日志:
-
此时,后端项目在Docker部署成功。我们可以将DockerFile和jar文件打包,其它人就可以构建镜像并运行容器。
部署前端
部署完毕后端,我们再来部署前端。通常使用nginx作为前端服务,我们需要部署静态资源目录html和conf配置文件。
对于nginx的conf,我们常在其中配置静态资源的目录、端口和代理。如:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/json;
sendfile on;
keepalive_timeout 65;
server {
listen 18080;
# 指定前端项目所在的位置
location / {
root /usr/share/nginx/html/mall-portal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://mall:8080;
}
}
server {
listen 18081;
# 指定前端项目所在的位置
location / {
root /usr/share/nginx/html/mall-admin;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://mall:8080;
}
}
}
上面的配置说明了用户端和管理端项目的端口、目录。此外,还通过代理转发了api接口到后端地址,解决了跨域的问题。
通过查询Docker Hub的官方文档,我们知道了nginx配置文件挂载方式:
下面,开始通过Docker部署前端项目:
docker run -d \
--name nginx \
-p 18080:18080 \
-p 18081:18081 \
-v /root/nginx/html:/usr/share/nginx/html \
-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \
--network simplicity \
nginx
DockerCompose
尽管通过DockerFile直接Build镜像并创建容器运行已经很方便,但是对于Java项目我们需要分别运行Mysql、后端、前端的Docker容器,还是很繁琐。
DockerCompose为我们解决了这个问题,它通过一个单独的docker-compose.yml模板文件定义一组关联的容器,帮助我们实现多个关联容器的快速部署。
DockerCompose的简易模板:
对于前面的项目部署,我们可以编写docker-compose.yml如下所示:
version: "3.8"
services:
mysql:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123
volumes:
- "./mysql/conf:/etc/mysql/conf.d"
- "./mysql/data:/var/lib/mysql"
- "./mysql/init:/docker-entrypoint-initdb.d"
networks:
- mall-net
mall:
build:
context: .
dockerfile: Dockerfile
container_name: mall
ports:
- "8080:8080"
networks:
- mall-net
depends_on:
- mysql
nginx:
image: nginx
container_name: nginx
ports:
- "18080:18080"
- "18081:18081"
volumes:
- "./nginx/nginx.conf:/etc/nginx/nginx.conf"
- "./nginx/html:/usr/share/nginx/html"
depends_on:
- mall
networks:
- mall-net
networks:
mall-net:
name: mall
可以看出来,compose只是整合了我们docker run中的参数。其中,depends_on指明了创建顺序。
我们只需要将相关的文件和Dockerfile和docker-compose.yml放到同一目录下,然后使用如下命令一键部署:
# 创建并启动所有容器(-f 指定composer文件位置,默认当前目录。-p 指定项目名。)
docker compose up -d
# 一键停止并移除所有容器、网络
docker compose down
# 查看容器进程状态
docker compose ps
以上利用composer实现了一键部署。DockerComposer的功能远不止于此,它可以借助DockerSwarm实现项目集群部署、多实例应用和负载均衡等。
在实际的项目开发中,程序员只需要编写好Dockerfile文件,会由专业的运维人员负责编写Composer实现集群和负载均衡的部署。
更进一步,在有些大企业中,程序员将后端代码push到仓库中后,会触发运维人员编写的自动化脚本对项目进行编译和部署。