docker入门实践
写在前面
之前看docker的时候,记录在了docx文档上,今天翻阅的时候看到了。
简介
docker有点像虚拟机技术那样,而虚拟机是模拟了全部或部分的硬件,有一整套自己的操作系统,而docker只是进程,这个进行也叫容器,隔离了linux内核有自己的空间。
注:ubuntu14的虚拟机,高版本的ubuntu操作起来估计坑应该会少一点。
安装
sudo curl -sSL https://get.daocloud.io/docker | sh
可能遇到curl 命令不存在,使用sudo apt-get install curl安装curl命令,如果报错了一串404的话。如图:
可以先更新:
sudo apt-get update
然后再安装:
sudo apt-get install
curl之后再安装docker:
sudo curl -sSL https://get.daocloud.io/docker | sh
安装验证:
jonsen@ubuntu:/etc/init.d$ sudo docker version
Client:
Version: 18.06.3-ce
API version: 1.38
Go version: go1.10.3
Git commit: d7080c1
Built: Wed Feb 20 02:27:13 2019
OS/Arch: linux/amd64
Experimental: false
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
发现问题:发现没有启动。
启动命令: ubuntu 14.04的系统:sudo /etc/init.d/docker start ubuntu 16.04的系统:sudo systemctl status docker.service
这里是unbuntu14的虚拟机,因为18的在硬盘忘记拉出来了,这里都会以14操作。
jonsen@ubuntu:/etc/init.d$ sudo /etc/init.d/docker start
* Docker is managed via upstart, try using service docker
发现并没有成功? 根据提示改用service的方式启动:
sudo service docker start
查看状态:
jonsen@ubuntu:/etc/init.d$ sudo service docker status
docker start/running, process 12695
已经启动ok了。
更换加速器
jonsen@ubuntu:/etc/init.d$ sudo mkdir -p /etc/docker
jonsen@ubuntu:/etc/init.d$ sudo tee /etc/docker/daemon.json <<-'EOF'
> "registry-mirrors": ["https://oyukeh0j.mirror.aliyuncs.com"]
"registry-mirrors": ["https://oyukeh0j.mirror.aliyuncs.com"]
}
国内的加速器有好多,我们使用阿里云的加速器。 https:// cr.console.aliyun.com/# /imageList
重启
sudo su
root@ubuntu:/etc/docker# service docker restart
docker stop/waiting
docker start/running, process 13092
注:这里已经切换了root用户了,后面可能操作不会带sudo
安装一个hello-world项目
root@ubuntu:/etc/docker# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
1b930d010525: Pull complete
安装gitlab的项目:
docker pull sameersbn/gitlab
可以看到安装一个项目只要pull即可。
这里以一个webserver为例
root@ubuntu:/etc/docker# docker run --name webserver -d -p 80:80 nginx
da42a076cd2fbc351e5a80e0426e4351cec52eecf8f859d7aea3f9c37b5abf38
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:297: copying bootstrap data to pipe caused \"write init-p: broken pipe\"": unknown.
可能遇到的问题: 由于每次安装运行服务的时候,总会报错,导致并没有运行成功。 因为之前安装了是最新版的18.06.3的版本。
jonsen@ubuntu:/etc/init.d$ sudo docker version
Client:
Version: 18.06.3-ce
API version: 1.38
Go version: go1.10.3
Git commit: d7080c1
Built: Wed Feb 20 02:27:13 2019
OS/Arch: linux/amd64
Experimental: false
Server:
Engine:
Version: 18.06.3-ce
API version: 1.38 (minimum version 1.12)
Go version: go1.10.3
Git commit: d7080c1
Built: Wed Feb 20 02:25:38 2019
OS/Arch: linux/amd64
Experimental: false
没有以上问题的可以跳过下面一部分 核心的降级 这里为了可以使用docker,先进行一个核心的降级:
root@ubuntu:/etc/docker# apt-get install docker-ce=17.12.1~ce-0~ubuntu
然后删掉服务,重新安装运行:
root@ubuntu:/etc/docker# docker rm -f webserver
webserver
root@ubuntu:/etc/docker# docker run --name webserver -d -p 80:80 nginx
45d84e3d0bd19bc954ba4043e080d2c3c403024760e336de145f054b00b3cc4f
root@ubuntu:/etc/docker# docker version
Client:
Version: 17.12.1-ce
这里使用浏览器进行测试验证,发现已经把docker的运行起来了,把docker的一个nginx附加到了80的端口上。
然后我们进入到该容器上:
docker exec -it webserver bash
-i: 交互式操作。
-t: 终端。
/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
此时可以看到这时候多了一个nginx的容器目录:
修改容器:
x.html5d84e3d0bd1:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index
root@45d84e3d0bd1:/# exit
exit
再次访问刚才的地址:
使用diff命令查看修改的目录:
root@ubuntu:/usr/share# docker diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
我们可以把这个容器的改变保存下来,成为一个镜像,下次就能直接运行了。
root@ubuntu:/usr/share# docker commit \
> --author "jonsen <jonsen.test@qq.com>" \
> --message "修改了默认网页" \
> webserver \
> nginx:v2
sha256:17472653ad3e57d352e5ad61f98a80cadb9b4234abae4ef99033325e33626d28
root@ubuntu:/usr/share# docker images nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 17472653ad3e 13 seconds ago 126MB
nginx latest 540a289bab6c 2 weeks ago 126MB
访问81端口:
慎用 docker commit 使用docker commit虽然能保存镜像或创建镜像,但是一般情况下我们不会这么用,我们会使用Dockerfile来构建镜像。 docker commit有一些不好的地方,上面的例子也可以看出来,我只是改了一个文件,上面却显示改了很多,比如临时文件,记录命令的文件(.bash_history),还有pid文件啥的。 而且使用docker commit创建的镜像,别人也不无法知道你是如何创建的,万一里面藏着什么东西,别人都不知道,很不安全。我们接下来会使用Dockerfile来创建镜像。
Dockerfile
Dockerfile是一个纯文本文件,内容是可读的,就是把构建镜像的指令一条条放在这个文件中,然后就可以build出来一个镜像。
root@ubuntu:/usr/dorcker# mkdir mynginx
root@ubuntu:/usr/dorcker# cd mynginx/
root@ubuntu:/usr/dorcker/mynginx# touch Dockerfile
root@ubuntu:/usr/dorcker/mynginx# vi Dockerfile
输入:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
然后保存。 执行命令:
docker build -t nginx:v3
新建一个docker镜像,并命名为nginx:v3 查看:
docker images
常用基本指令介绍: FROM:代表的是基础镜像。 RUN:表示的是运行的命令。 ARG:设置一个参数,在build的时候由外面通过命令行参数的形式传过来。 ENV:是设置环境变量,以后这个变量可以传到docker容器内部中去。 EXPOSE:表示暴露的端口号。 WORKDIR:就是指定工作目录啦。 CMD:是容器启动的命令。
部署node应用例子
这里目录为 /usr/docker/ 执行:
mkdir express-docker
cd express-docker
touch server.js
touch package.json
touch .dockerignore
touch Dockerfile
package.json:
{
"name": "docker_demo",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "node server.js"
"dependencies": {
"express": "^4.16.1"
}
server.js
'use strict';
const express = require('express');
// Constants
const PORT = 8889;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', async(req, res) => {
res.send('Hello world1\n');
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
Dockerfile:
#制定node镜像的版本
FROM node:latest
#声明作者
MAINTAINER onsen
#移动当前目录下面的文件到app目录下
COPY . /app/
#进入到app目录下面,类似cd
WORKDIR /app
#安装依赖
RUN npm install
#对外暴露的端口
EXPOSE 8889
#程序启动脚本
CMD ["npm", "start"]
.dockerignore:
# Logs
*.log
npm-debug.log*
# Runtime data
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
# Optional REPL history
.node_repl_history
.idea
.node_modules
node_modules
.vscode
在命令行上执行打包(注:后面的点不能忽略了):
docker build -t express_docker .
等待安装完毕后执行后台运行:
docker run -d -p 8889:8889 --name express_docker express_docker
设置docker自动重启 修改server.js:
'use strict';
const express = require('express');
require('./utils');
// Constants
const PORT = 8889;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', async(req, res) => {
res.send('Hello world1\n');
setTimeout(function(){
console.info(aa);
},2000)
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
重启docker并更新:
docker container update --restart=always 容器名字
如果成功没有启动过的话:
docker run --restart=always -d -p 8889:8889 --name express_docker express_docker
查看日志
docker logs -f -tail 100 express_docker
访问127.0.0.1:8889,这里是使用浏览器,如图:
因为程序访问后两秒后就会报一个模拟的错误,如下图:
可以看到,报错日志自动退出后,再使用:
docker ps
查看,容器又启动起来了。
使用pm2维护node 修改Dockerfile:
#制定node镜像的版本
FROM node:latest
#声明作者
MAINTAINER onsen
#移动当前目录下面的文件到app目录下
COPY . /app/
#进入到app目录下面,类似cd
WORKDIR /app
#安装依赖
RUN npm install
#对外暴露的端口
EXPOSE 8889
#程序启动脚本
#CMD ["npm", "start"]
RUN npm install pm2 -g
CMD ["pm2-runtime","server.js","-i","2", "--watch", "--name", "myapp"]
删除掉之前建立的容器,可能用到: docker stop <容器名> docker rm <容器名> docker rmi <容器image id>
root@ubuntu:/usr/dorcker/express_docker# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0119f838abe2 a8bb429e3a5a "docker-entrypoint.s…" 5 minutes ago Up 4 minutes 0.0.0.0:8889->8889/tcp express_docker
b99024f811fe docker_demo "npm start" 12 hours ago Up 12 hours 0.0.0.0:9000->3000/tcp stupefied_clarke
9fadc8ea7d03 training/postgres "su postgres -c '/us…" 15 hours ago Up 15 hours 5432/tcp db1
bc48f74c2092 training/postgres "echo Data-only cont…" 15 hours ago Exited (0) 15 hours ago dbdata
c2841ff00e6d nginx "nginx -g 'daemon of…" 15 hours ago Up 15 hours 0.0.0.0:80->80/tcp webserver
root@ubuntu:/usr/dorcker/express_docker# docker stop express_docker
express_docker
root@ubuntu:/usr/dorcker/express_docker# docker rm express_docker
express_docker
root@ubuntu:/usr/dorcker/express_docker# docker run -d -p 8889:8889 --name express_docker express_docker
1606b78e66dabef5817fe077fa74818dea06cf55b867c1a7e31a7f09a94b8f14
root@ubuntu:/usr/dorcker/express_docker# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
express_docker latest 45e2e40bb21c 52 seconds ago 972MB
<none> <none> a8bb429e3a5a 6 minutes ago 972MB
docker_demo latest 3e6b13bae720 12 hours ago 69.7MB
nginx v2 17472653ad3e 19 hours ago 126MB
node latest 1a77bcb355eb 4 days ago 933MB
sameersbn/gitlab latest 0d004485cc7b 7 days ago 2.59GB
nginx latest 540a289bab6c 2 weeks ago 126MB
hello-world latest fce289e99eb9 10 months ago 1.84kB
node 8.9-alpine 406f227b21f5 20 months ago 68.1MB
training/postgres latest 6fa973bb3c26 5 years ago 365MB
root@ubuntu:/usr/dorcker/express_docker# docker rmi a8bb429e3a5a
root@ubuntu:/usr/dorcker/express_docker# docker exec -it express_docker bash
然后重新build,之后再像上面一样启动起来容器,进入容器,查看pm2命令,如图:
进入容器使用pm2 logs myapp查看日志,使用浏览器访问127.0.0.1:8889。两秒后,收到了报错,pm2重启了node项目。
注:网上有人说容器中部署了pm2再用pm2管理node项目,如果项目报错重启,会导致容器重启,然而这里的实践发现,pm2重启了node的项目并没有导致容器重启。
链接数据库例子 这里以mysql为例, 如果需要查看版本镜像,可以查阅 https:// hub.docker.com/ 这里直接安装最新版本的mysql。
执行安装:
docker pull mysql
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d mysql
进行容器中查看mysql:
root@ubuntu:/usr/dorcker# docker exec -it mysql bash
root@db3b62dedbc7:/# mysql --version
root@db3b62dedbc7:/# mysql -u root -p
mysql> use mysql
mysql 8.0 默认使用 caching_sha2_password 身份验证机制 —— 从原来的 mysql_native_password 更改为 caching_sha2_password。
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
通过ip访问确认链接:
192.168.42.133是我本地虚拟机的ip,具体以自己实践结果为准。 我们知道如果需要链接到一个数据库的话需要知道该数据库的地址: 获取该容器的ip,进行数据库的链接
root@ubuntu:/usr/dorcker# docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql
172.17.0.6
注:具体的ip以自己机器上的准。
新建链接数据库的测试文件:
root@ubuntu:/usr/dorcker/express_docker# touch utils.js
utils.js:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '172.17.0.6',
user : 'root',
password : 'password'
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;
console.log('connected as id ' + connection.threadId);
});
修改express_docker 容器的文件package.json:
{
"name": "docker_demo",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "node server.js"
"dependencies": {
"express": "^4.16.1",
"mysql": "^2.17.1"
}
server.js:
'use strict';
const express = require('express');
require('./utils');
// Constants
const PORT = 8889;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', async(req, res) => {
res.send('Hello world1\n');
setTimeout(function(){
console.info(aa);