docker可以将运行环境和代码打包成一个镜像,使用时直接运行镜像,可以免去不同机器部署时的麻烦。今天研究一下docker的基本使用。用docker打包一个node
node代码如下:
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3333);
然后在根目录创建dockerfile, 如下:
FROM node
COPY . .
RUN npm install
EXPOSE 3333
CMD ["npx", "pm2", "-n", "main", "restart", "/src/index.js"]
我的步骤很简单,使用node镜像提供node环境,然后把当前目录的内容复制到docker的容器里面。我把容器理解为使用本地的linux内核的空目录。因此拷贝过来之后,容器里面的目录结构跟我项目里的一样,我项目目录结构如下:
node-test
├─dockerfile
├─package-lock.json
├─package.json
├─src
| └index.js
node-test相当于容器根目录,那后续的话应该就是直接在容器根目录安装依赖。这里暴露出一个3333端口,因为listen就是3333端口,便于和外界端口做映射。最后跑npx pm2 -n main restart '/src/index.js'命令。
使用docker build -t node-test .命令打包,-t 用来命名打包出来的镜像名字,最后的.用来告知dockerfile的位置。命令运行后没有报错,然后我使用docker images查看,如下:
REPOSITORY TAG IMAGE ID CREATED SIZE
node-test latest b0cd73088ae0 27 minutes ago 963MB
node latest 1db64f55f800 3 days ago 936MB
node是我下载的镜像,node-test是刚刚打出来的。貌似离成功不远了?然后我运行docker run node-test,就报错了,提示/src/index.js不存在,看来是COPY那里出问题了。那我看看到底打了什么东西,改一下dockerfile,CMD改成 ['ls', '-al'],输出如下:
total 240
drwxr-xr-x 1 root root 4096 Jan 24 14:19 .
drwxr-xr-x 1 root root 4096 Jan 24 14:19 ..
-rwxr-xr-x 1 root root 0 Jan 24 14:19 .dockerenv
drwxr-xr-x 1 root root 4096 Jan 12 04:01 bin
drwxr-xr-x 2 root root 4096 Jul 10 2020 boot
drwxr-xr-x 5 root root 340 Jan 24 14:19 dev
-rw-r--r-- 1 root root 129 Jan 24 14:18 dockerfile
drwxr-xr-x 1 root root 4096 Jan 24 14:19 etc
drwxr-xr-x 1 root root 4096 Jan 12 10:30 home
drwxr-xr-x 1 root root 4096 Jan 12 04:01 lib
drwxr-xr-x 2 root root 4096 Jan 11 00:00 lib64
drwxr-xr-x 2 root root 4096 Jan 11 00:00 media
drwxr-xr-x 2 root root 4096 Jan 11 00:00 mnt
drwxr-xr-x 1 root root 4096 Jan 24 14:19 node_modules
drwxr-xr-x 1 root root 4096 Jan 20 20:26 opt
-rw-r--r-- 1 root root 154255 Jan 24 14:19 package-lock.json
-rw-r--r-- 1 root root 290 Jan 24 14:19 package.json
dr-xr-xr-x 110 root root 0 Jan 24 14:19 proc
drwx------ 1 root root 4096 Jan 24 14:19 root
drwxr-xr-x 3 root root 4096 Jan 11 00:00 run
drwxr-xr-x 1 root root 4096 Jan 12 04:00 sbin
drwxr-xr-x 2 root root 4096 Jan 24 13:45 src
drwxr-xr-x 2 root root 4096 Jan 11 00:00 srv
dr-xr-xr-x 13 root root 0 Jan 24 14:19 sys
drwxrwxrwt 1 root root 4096 Jan 20 20:26 tmp
drwxr-xr-x 1 root root 4096 Jan 11 00:00 usr
drwxr-xr-x 1 root root 4096 Jan 11 00:00 var
可以看到,docker还是加了一些很眼熟的文件夹,/var、/bin之类的。但是COPY指令的执行跟我想象的一致,项目的内容确实是直接拷贝到容器的根目录里面了(怪不得网上的例子都是新开一个目录,直接在根目录太乱了)。仔细一看,命令错了,pm2 应该用的start命令。重新来打包,然后运行docker run -p 8000:3333 node-test,这次成功了,输出了pm2的成功信息,然后访问浏览器的8000端口,无法访问。我使用docker ps查看在运行的docker进程,发现没有。也就是pm2挂掉了。百度了一下,大概就是说docker运行完CMD脚步之后就关掉了,就跟你打开一个终端,运行pm2,然后把终端关掉,pm2也就关掉了,度娘提供了几种办法,我唯一能看懂的就是在cmd后面加个tail -f命令,让他一直执行不完。于是我改成了这样:
FROM node
COPY . .
RUN npm install
EXPOSE 3333
CMD npx pm2 -n main start /src/index.js && npx pm2 logs main
运行成功,刷新浏览器8000端口,能看到hello world的正常输出。然后运行。
为了确认真的没有依赖本地的环境。我把本地npx改个名字,如下:
➜ bin cd /usr/local/bin
➜ bin ls
2to3 git kubectl.docker python3-32
2to3-3.7 git-credential-osxkeychain node python3-config
MotionPro git-cvsserver notary python3.7
com.docker.cli git-shell npm python3.7-32
docker gitk npx python3.7-config
docker-compose hub-tool pip3 python3.7m
docker-credential-desktop hyperkit pip3.7 python3.7m-config
docker-credential-ecr-login idle3 pydoc3 pyvenv
docker-credential-osxkeychain idle3.7 pydoc3.7 pyvenv-3.7
easy_install-3.7 kubectl python3 vpnkit
➜ bin npx -v
6.14.5
➜ bin mv npx npx2
➜ bin npx -v
zsh: command not found: npx
然后stop容器,重新运行,依然正常!
正在运行的容器还可以进去,跟平常的终端一样。
➜ docker docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9071e2fa8c20 node-test "docker-entrypoint.s…" 39 seconds ago Up 38 seconds 0.0.0.0:8000->3333/tcp upbeat_chatelet
➜ docker docker exec -it 9071 sh
# npx pm2 list
┌─────┬─────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼─────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ main │ default │ N/A │ fork │ 44 │ 72s │ 0 │ online │ 0% │ 40.8mb │ root │ disabled │
└─────┴─────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
#
一套demo下来3小时,感觉还挺简单入手的样子,有空再多多研究