前端 Docker 基本教程
为什么要学习 Docker ?
每学一个东西,我们肯定是基于某个需求去学习的,众所周知,软件开发最麻烦的是环境配置,开发好好的,部署出问题就很难受,所以为了确保开发、测试、部署环境一致,且高效的部署所以选择了容器技术而非 VM ,而 Docker 是基于 Linux 容器技术的开源项目,它的口头禅就是:“一次构建,处处运行”,具有轻量,速度,社区活跃,且拓展性高。
安装
点我进入官网安装
Docker 支持 Linux、Mac、 Window,直接去官网下载安装就行了
快速开始
学习新的技术都需要一个 Hello World,让我们快速开始体验一下 Docker
直接进入终端开始吧!
# 拉取nginx镜像
docker pull nginx
# 创建一个nginx容器
docker run -d --name test-nginx -p 3000:80 nginx
然后打开 localhost:3000 即可访问到熟悉的 nginx 页面了
是不是非常简单?那我们接下来具体说一说 Docker 的组成
镜像 image
镜像是一个二进制文件,里面具有应用程序以及依赖,只有通过它,Docker 才能够生成容器,相当于模具一样,同一个镜像文件可以生成多个容器实例
对于如何制作镜像,一般来说我们都是通过加工别人基础镜像来生成我们自己的镜像,而不是从零开始,而且我们也可以在这里共享我们的镜像,这也是我们选择它的理由之一,我们可以享受社区的贡献
镜像常用的命令
# 列出本机的所有 image 文件。
$ docker image ls
# 拉取镜像
$ docker pull [imageName]
# 删除 image 文件
$ docker image rm [imageName]
用 Dockerfile 构建一个镜像
在前面我们已经使用过nginx的镜像了,这次我们尝试用 Dockerfile 构建一个自己的镜像
新建一个 docker-test 文件夹,在里面新建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello World</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
然后新建一个 Dockerfile 文件
# 声明基于 nginx 最新的镜像 这里说一下镜像名:后面的是标签 默认是latest
FROM nginx:latest
# 把刚才 index.html 复制到 nginx 的 html 路径去
COPY index.html /usr/share/nginx/html/index.html
# 声明暴露80端口
EXPOSE 80
运行命令
# 生成一个名为 nginx 标签为0.0.1 的镜像 ,注意最后还有一个 .
docker image build -t nginx:0.0.1 .
# 根据刚生成的镜像 启动容器
docker run -d --name test-nginx1 -p 3001:80 nginx:0.0.1
这时候再次访问 locahost:3001 已经不是默认的 nginx 的访问页面了
这里解释一下参数
- -p 参数是指容器的 80 端口映射到本机的 3001 端口
- -d 守护式运行(适合运行应用程序和服务)
- --name 容器名称 如果不指定则是一个随机的名称
容器 container
容器常用的命令
# 查看正在运行的容器
docker ps
# 查看所有创建过的容器(运行或者关闭)
docker ps -a
# 停止容器
docker stop [container]
# 启动容器
docker start [container]
# 删除容器
docker rm [container]
# 查看后台运行的日志
docker logs [containe]
可以先用这些容器命令操作一下我们之前创建的容器,尝试一下
那么在上一节我们自己构建了一个镜像代替了 nginx 的默认页面,那如果我们还想改怎么办?除了新构建一个镜像之外,我们还可以直接进入容器里进行修改
docker container exec -it [containe] /bin/bash
这样就进去了容器的 shell ,可以随意进行操作,当然就算你误操作比如运行了经典的了,也没有关系
在这里我们是使用了 -it 这个参数 它和 -d 区别就在于
-d => 使用 pm2 start index.js
-it => 使用 node.js
当然,真正开发的时候,肯定不可能是这样去修改了,好比之前的端口映射,也可以将容器的内部的目录也映射出来实现共共享,这就是接下来要说的 Volume
数据卷 Volume
回到我们 docker-test 文件夹,然后重新启动一个nginx容器(这里使用的端口和容器名与快速开始的创建的容器相同,请同学们复习一下容器命令先自行删除再创建)
docker run -d --name test-nginx -v $PWD/index.html:/usr/share/nginx/html/index.html -p 3000:80 nginx
我们打开 locahost:3000 修改一下 index.html 的内容为 Hello Volume ,刷新一下网页即可看到我们修改的内容
在这里
很容易发现我们和之前的区别就是使用了 -v参数 本地路径:容器路径 通过这样进行映射,这样当我们修改本地的文件的时候就等于修改了容器内的文件
那么就当在一个项目中,nginx部署好了前端,那么我们需要一个服务提供数据,在 docker-test 的文件夹下面
创建一个 mysql 文件夹 在mysql文件夹中创建一个data存储数据,创建一个my.cnf用作配置
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
启动容器
docker run --name test-mysql -it -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v $PWD/mysql/my.cnf:/etc/mysql/my.cnf -v $PWD/mysql/data:/var/lib/mysql -d mysql
然后我们通过上一节学会的命令进入 test-mysql 容器中创建数据库以及创建表 加插入一条数据
docker container exec -it test-mysql /bin/bash
# 下面是 test-mysql 容器中的 shell
# 登陆 输入密码
mysql -u root -p
# 创建数据库 test
create databse test;
# 进入 test 数据库
use test;
# 进入 PEOPLE 数据表 字段为 name age
Create Table PEOPLE (name VARCHAR(20), age CHAR(20));
# 插入一行数据到 PEOPLE
Insert into PEOPLE Values('kdq', '24')
然后我们退出容器,再 docker-test 下创建一个文件夹 node ,并创建一个 index.js 文件
const http = require("http");
const mysql = require("mysql");
const connection = mysql.createConnection({
host: "test-mysql",
user: "root",
password: "root",
database: "test"
});
connection.connect();
const server = http.createServer().listen(3001);
server.on("request", (req, res) => {
if (/testapi/.test(req.url)) {
try {
var sqlstr = "Select * From PEOPLE";
connection.query(sqlstr, function(err, result) {
if (err) {
console.log("SELECT ", err.message);
return;
}
res.end(JSON.stringify(result));
});
} catch (error) {
res.end("404 not found");
}
} else {
res.end("404 not found");
}
});
然后启动容器
docker run -it --name test-node -v $PWD/node:/var/www/node -v $PWD/mysql/data:/var/lib/mysql -p 3001:3001 node /bin/bash
# 下面是 test-node 容器中的 shell
cd /var/www/node
node index.js
我们发现 mysql 的操作报错了 connect ECONNREFUSED 127.0.0.1:3306 ,原来是因为我们的 mysql 和 node 是两个容器,而容器是相互隔离的,无法 ping 通
那么难道我们得把 mysql 和 node 都得部署在一个容器里?其实 Docker 更建议一个容器做一件事情,而这种理念其实在我们的开发中也是随处可见的,所以 Docker 可以使用 Networking 进行通信
网络 Networking
首先创建一个网络
docker network create test-net
然后把上一节启动 test-mysql 和 test-node 的容器的命令改成
# test-mysql
docker run --name test-mysql -it -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v $PWD/mysql:/etc/mysql --network test-net -d mysql
# test-node
docker run -it --name test-node -v $PWD/node:/var/www/node --network test-net -p 3001:3001 node /bin/bash
这时候我们在 node 的 index.js 中修改一下连接 mysql 的 host
const connection = mysql.createConnection({
host: "test-mysql",
user: "root",
password: "root",
database: "test"
});
然后我们启动一下 node 服务 访问 localhost:3001/testapi 就能看到我们查询到的数据了
那么我们也可以将 nginx 加入 test-net 网络中互相通信,具体就留给你们自己去做了
组合 docker-compose
经过上一节我们发现启动容器要输入这么多参数,而且多个容器服务就要启动多次,不仅繁琐,还容易出错
我们可以写 Dockerfile 写一层层命令来构建镜像,是不是也可以用一个配置去启动容器?
答案是可以的,这时候就需要 docker-compose ,当然,如果你的 docker 安装的时候自带就不用再次安装了,具体可以观看文档 点我下载
安装之后 在我们的 docker-test 文件夹下新建一个 docker-compose.yml
version: "3.7"
services:
test-mysql:
image: mysql
volumes:
- ./mysql/my.cnf:/etc/mysql/my.cnf
- ./mysql/data:/var/lib/mysql
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=root
networks:
- test-net
test-node:
image: node
volumes:
- ./node:/var/www/node
ports:
- 3001:3001
command:
- /bin/bash
- -c
- |
cd /var/www/node
node index.js
tail -f /dev/null
networks:
- test-net
depends_on:
- test-mysql
test-nginx:
image: nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/index.html:/usr/share/nginx/html/index.html
ports:
- 3000:80
networks:
- test-net
depends_on:
- test-node
networks:
test-net:
driver: bridge
目前的 docker-test 情况
|-- mysql
|-- data
|-- my.cnf
|-- nginx
|-- index.html
|-- nginx.conf
|-- node
|-- node_modules
|-- index.js
|-- package.json
|-- package-lock.json
|-- docker-compose.yml
通过 docker-compose 启动容器(先把之前启动的三个容器关了)
docker-compose up -d
打开 localhost:3001/testapi 发现依然查询到了数据,就这样一个简单的项目环境准备好了
总结
简单的介绍一下如何使用Docker以及基本的命令,相信大家已经对 Docker 有所了解,对于相关配置想知道更多的可以查看官网文档,如果想看书的同学,可以看第一本Docker书
前端 Docker 基本教程的更多相关文章
- [转帖]Docker 入门教程
Docker 入门教程 http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html 自己学的还是太肤浅啊.. 作者: 阮一峰 日期: 201 ...
- Docker简明教程
Docker简明教程 [编者的话]使用Docker来写代码更高效并能有效提升自己的技能.Docker能打包你的开发环境,消除包的依赖冲突,并通过集装箱式的应用来减少开发时间和学习时间. Docker作 ...
- Docker入门教程(九)10个镜像相关的API
Docker入门教程(九)10个镜像相关的API [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第九篇,重点介绍了镜像相关的Docker Remote ...
- Docker入门教程(八)Docker Remote API
Docker入门教程(八)Docker Remote API [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第八篇,重点介绍了Docker Remote ...
- Docker入门教程(七)Docker API
Docker入门教程(七)Docker API [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第七篇,重点介绍了Docker Registry API和 ...
- Docker入门教程(六)另外的15个Docker命令
Docker入门教程(六)另外的15个Docker命令 [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第六篇,继续介绍Docker命令.之前的第二篇文章 ...
- Docker入门教程(五)Docker安全
Docker入门教程(五)Docker安全 [编者的话]DockOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第五篇,介绍了Docker的安全问题,依然是老话重谈,入门者可以通 ...
- Docker入门教程(四)Docker Registry
Docker入门教程(四)Docker Registry [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第四篇,介绍了Docker Registry,它 ...
- Docker入门教程(三)Dockerfile
Docker入门教程(三)Dockerfile [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第三篇,介绍了Dockerfile的语法,DockerOn ...
随机推荐
- java8新特性Lambda和Stream
Java8出来已经4年,但还是有很多人用上了jdk8,但并没用到里面的新东西,那不就等于没用?jdk8有许多的新特性,详细可看下面脑图 我只讲两个最重要的特性Lambda和Stram,配合起来用可以极 ...
- JSONArray 与 List 互转
List 转 JSONArray // 通过JSONPath获取其中数据,也可以说自己生成的List List<JSONObject> caseList = JsonPath.read(r ...
- Math.Atan2 方法
返回正切值为两个指定数字的商的角度. public static double Atan2 ( double y, double x ) 参数 y 点的 y 坐标. x 点的 x 坐标. 返回值 角 ...
- epel-release的卸载重装
1.yum remove epel-release 2.清空epel目录:rm -rf /var/cache/yum/x86_64/6/epel/ 3.安装,yum install epel-rel ...
- php--->查询超大文件(12G)
今天遇到一个要在一个12G日志中查询数据的需求,手中暂时没有查询这种超大文件的工具,于是自己写了一个程度来读这个超大文件 其整体思路就是一行一行地去读取超大文件中的数据,然后将拿出的一行数据做相应的查 ...
- vue响应式原理的实现
响应式实现的原理---如何监控数据的变化:两种方法 Vue 2.x defineProperty(es5) Vue 3.x Proxy(es6) 语法:Object.defineProperty(参数 ...
- Vue中的计算属性
一.什么是计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护. 二.计算属性的用法 在一个计算属性里可以完成各种复杂的逻辑,包括运算.函 ...
- Spring 核心功能演示
Spring 核心功能演示 Spring Framework 简称 Spring,是 Java 开发中最常用的框架,地位仅次于 Java API,就连近几年比较流行的微服务框架 SpringBoot, ...
- C++ 强制类型转换详解
类型转换只不过是让编译器以另外一种方式解释一块内存而已.C++兼容C语言的强制类型转换方式,同时也提供了新型的基于模板的类型转换方式,来提供更多的安全性. 一.C风格的强制类型转换 double k ...
- 快乐编程大本营【java语言训练班】 6课:用java的对象和类编程
快乐编程大本营[java语言训练班] 6课:用java的对象和类编程 第1节. 什么是对象和类 第2节. 对象的属性和方法 第3节. 类的继承 第4节. 使用举例:创建类,定义方法,定义属性 第5节. ...