安装
太久没玩服务端了,心痒痒。上docker本地试试
Dockerfile
编写优雅地Dockerfile
编写优雅的Dockerfile主要需要注意以下几点:
- Dockerfile文件不宜过长,层级越多最终制作出来的镜像也就越大。
- 构建出来的镜像不要包含不需要的内容,如日志、安装临时文件等。
- 尽量使用运行时的基础镜像,不需要将构建时的过程也放到运行时的Dockerfile里。
只要记住以上三点就能写出不错的Dockerfile。
FROM node:10.15
MAINTAINER sunhengzhe@foxmail.com
COPY . /app/
WORKDIR /app
RUN npm install pm2 -g
EXPOSE 8003
CMD ["pm2-runtime", "ecosystem.config.js"]
FROM
指定基础镜像为 node 的 10.15 版本(node 官方版本可 在此查看)MAINTAINER
说明镜像的维护者COPY
命令将宿主机的文件拷贝到镜像中,格式为COPY [--chown=<user>:<group>] <源路径>... <目标路径>
,这里将项目目录下的所有文件都拷贝到镜像中的/app
目录下。如果目标路径不存在,docker 将自动创建。WORKDIR
用来指定工作目录,即是CMD
执行所在的目录。RUN
命令用来执行 shell 命令,这里用来安装pm2
EXPOSE
命令用来 声明 运行时容器提供服务端口,但要注意运行时并不会开启这个端口的服务。这个命令主要是帮助使用者理解这个镜像服务的守护端口,以方便配置映射;另外在使用随机端口映射时,会自动随机映射EXPOSE
的端口CMD
是默认的容器主进程的启动命令。 CMD 能够被 docker run 后面跟的命令行参数替换,有多个 CMD 指令时只有最后一个 CMD 有效
构建镜像
docker build -t express-app:v1 .
运行容器
docker run -p 8003:3000 --name="express-app" -d express-app:v1
docker run
是 docker create
和 docker start
两个命令的简写。
-d
即--detach
,代表让容器后台运行。-p
指定宿主机和容器的端口映射,左边为宿主机的端口,右边为容器的端口,也就是说访问宿主机的 8003 端口,会映射到容器内的 3000 端口。--name
设置容器别名,如果不指定,docker 会随机生成一个名字,比如tender_swirles
之类的。
查看运行镜像。docker ps [-a]
添加 -a
参数可以查看所有已启动容器。
进入容器
docker exec -it express-app bash
-i
与 -t
一般结合使用,-i
启动交互模式,-t
指定需要分配终端,可以自行尝试不传其中一个的效果
关闭操作
停止容器
docker stop express-app
删除容器
docker rm express-app
如果删除时容器还在运行,需要加上 -f
参数
删除镜像
docker rmi express-app:v1
常用合集
**docker cp 😗*用于容器与主机之间的数据拷贝。
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
将主机/www/runoob目录拷贝到容器96f7f14e99ab的/www目录下。
docker cp /www/runoob 96f7f14e99ab:/www/
将主机/www/runoob目录拷贝到容器96f7f14e99ab中,目录重命名为www。
docker cp /www/runoob 96f7f14e99ab:/www
将容器96f7f14e99ab的/www目录拷贝到主机的/tmp目录中。
docker cp 96f7f14e99ab:/www /tmp/
常用镜像
nginx
FROM nginx
COPY dist/ /usr/share/nginx/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf # 覆盖default.conf
docker run --name nginx -p 80:80 \
-v /home/docker-nginx/nginx.conf:/etc/nginx/conf.d/ \
-v /home/docker-nginx/log:/var/log/nginx \
-v /home/docker-nginx/www:/usr/share/nginx/html/ \
-d nginx
version:'3.9'
services:
testserver:
image:nginx
container_name: webserver
volumes:
- ./conf.d:/etc/nginx/conf.d/
- ./www:/usr/share/nginx/html/
- ./logs/:/var/log/nginx/
简单测试:
docker run -p 80:80 --name nginx -d nginx
--name 给你启动的容器起个名字,以后可以使用这个名字启动或者停止容器
-p 映射端口,将docker宿主机的80端口和容器的80端口进行绑定
-v 挂载文件用的
第一个-v 表示将你本地的nginx.conf覆盖你要起启动的容器的nginx.conf文件
第二个表示将日志文件进行挂载,就是把nginx服务器的日志写到你docker宿主机的/home/docker-nginx/log/下面
第三个-v 表示的和第一个-v意思一样的。
no2中挂载方式需要自己准备好nginx.conf 和default.conf文件,我是直接从容器里面复制的,然后根据自己的需要改的
而Nginx的配置文件会包含 default.conf 以及 nginx.conf ;二者的联系为 default.conf 会被 nginx.conf 引入,所以实际上我们只需要配置 default.conf ,然后挂载到 Nginx 容器中即可!
-d 表示启动的是哪个镜像
server {
listen 80;
server_name localhost;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /usr/share/nginx/html;#vue项目的打包后的dist
try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
index index.html index.htm;
}
#对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
#因此需要rewrite到index.html中,然后交给路由在处理请求资源
location @router {
rewrite ^.*$ /index.html last;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
本地copy到云端
Linux 对 Linux 免密登录
1.本地生成秘钥,如果本地有,直接2
ssh-keygen -t rsa
在 ~/.ssh directory
id_rsa : 生成的私钥文件
id_rsa.pub : 生成的公钥文件
know_hosts : 已知的主机公钥清单
2.通过ssh-copy-id的方式写入到服务器
命令: ssh-copy-id -i ~/.ssh/id_rsa.pub <username>@<remote_ip>
cp -r dir1 dir2
即可。
如果dir2目录已存在,则需要使用
cp -r dir1/. dir2
如果这时使用cp -r dir1 dir2,则也会将dir1目录复制到dir2中,明显不符合要求。
ps:dir1、dir2改成对应的目录路径即可。
cp -r /home/www/xxx/statics/. /home/www/statics
source=./dist/ target=/home/docker-nginx/dist/;scp -r $source root@47.95.236.69:$target
MySQL
拉取镜像。
docker pull mysql
创建容器。
docker run -di --name mysql8 -p 3307:3306 \
-v ~/docker_mysql/conf:/etc/mysql/conf.d \
-v ~/docker_mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 mysql
-p
:代表端口映射,格式为 宿主机映射端口:容器运行端口。-e
:代表添加环境变量MYSQL_ROOT_PASSWORD
是 root 用户的登陆密码。
连接容器中的 MySQL 时,只需要连接宿主机的 IP + 指定的映射端口即可。
连接mysql测试
mysql -h localhost -P 3308 -u root -p
也可以进入容器并使用 MySQL 命令打开客户端
# 进入容器
docker exec -it mysql8 /bin/bash
# 使用 MySQL 命令打开客户端
mysql -uroot -p1234 --default-character-set=utf8
1. 查看MySQL服务状态
sudo mysql.server status
2、重新启动MySQL服务
sudo mysql.server restart
Redis
拉取镜像
docker pull redis
创建容器。
docker run -i -t -p 6379:6379 --name myRedis
-v /Users/chengchaoyang/mydocker/conf/redis/redis.conf:/etc/redis/redis.conf
-v /Users/chengchaoyang/mydocker/data/redis:/data
-d redis redis-server /etc/redis/redis.conf
--appendonly yes
–appendonly yes 开启redis 持久化
连接容器中的 Redis 时,只需要连接宿主机的 IP + 指定的映射端口即可。
- 修改redis.conf配置文件中的下列项:
创建本地配置文件redis.conf,从官网下载
#注释默认配置中所有的绑定bind,以允许所有的IP都可以访问
#有的博客讲还需要加一条 bind 0.0.0.0 实测不加也可以,只要把原本所有的bind都注释即可
#bind 127.0.0.1
#远程访问
protected-mode no
# 取消注释,设置密码,因为允许任意IP访问,强烈建议配置密码避免redis裸奔
# 否则马上就有一些挖矿病毒进入迅速占据你的虚拟机
requirepass qwer1234
#持久化
appendonly no
#默认no,改为yes意为以守护进程方式启动,可后台运行,除非kill进程,改为yes会使配置文件方式启动redis失败
daemonize no
# 修改yes为no,关闭保护模式,允许远程任意IP访问
protected-mode no
远程连接
测试连接:redis-cli -h [localhost] -p IP地址
redis-cli -h 127.0.0.1 -p 6379
注意这里的-p是你隐射出来的ip
查看docker容器ip地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' ID/NAMES
# 查看 容器ip 地址
MongoDB
拉取镜像。
docker pull mongo
创建容器。
docker run -di --name mongo -p 27017:27017 mongo
连接容器中的 MongoDB 时,只需要连接宿主机的 IP + 指定的映射端口即可
Docker Compose
Docker Compose 是 Docker 容器进行编排的工具,定义和运行多容器的应用,可以一条命令启动多个容器。
命令
查看容器
docker-compose ps
删除窗口
docker-compose rm laradock_redis_1
拉取镜像
docker-compose pull nginx
重起项目的服务
docker-compose restart
Setup
Run docker-compose build
nginx
nginx:
image: nginx
container_name: nginx-start
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf
- ./dist/:/usr/share/nginx/html/
- ./logs/:/var/log/nginx/
ports:
- 8080:80
redis
redis:
image: redis
container_name: redis
command: redis-server /etc/redis/redis.conf #覆盖容器启动的redis.conf
volumes:
- ./redis.conf:/etc/redis/redis.conf
- ./data:/data
ports:
- 6380:6379
难点
VOLUME && -v && mount
一、通过docker run命令
运行命令:docker run --name test -it -v /home/xqh/myimage:/data ubuntu /bin/bash
其中的 -v 标记 在容器中设置了一个挂载点 /data(就是容器中的一个目录),并将主机上的 /home/xqh/myimage 目录中的内容关联到 /data下。
这样在容器中对/data目录下的操作,还是在主机上对/home/xqh/myimage的操作,都是完全实时同步的,因为这两个目录实际都是指向主机目录。
另一件只有-v
参数能够做到而Dockerfile是做不到的事情就是在容器上挂载指定的主机目录例如:
1. $ docker run -v /home/adrian/data:/data debian ls /data
"Mounts": [
{
"Name": "0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01",
主机"Source": "/home/adrian/data",
镜像"Destination": "/var/lib/docker/volumes/0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01/_data",
"Driver": "local",
"Mode": "",
"RW": true
}
“Destination” 值是容器的挂载点,"Source"值是对应的主机目录
-
运行命令:docker run --name test1 -it -v /data ubuntu /bin/bash
上面-v的标记只设置了容器的挂载点,并没有指定关联的主机目录。这时docker会自动绑定主机上的一个目录。通过docker inspect 命令可以查看到。
通过dockerfile的 VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。
还有一个区别是,通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。
VOLUME ["/data1","/data2"]
"Source": "/var/lib/docker/volumes/d411f6b8f17f4418629d4e5a1ab69679dee369b39e13bb68bed77aa4a0d12d21/_data",
"Destination": "/data1",
"Source": "/var/lib/docker/volumes/6d3badcf47c4ac5955deda6f6ae56f4aaf1037a871275f46220c14ebd762fc36/_data",
"Destination": "/data2",
容器共享卷(挂载点)
在 docker run中使用 --volumes-from标
docker run \
-p 3000:80 \
-d --name vuenginxnew \
--mount type=bind,source=$HOME/SelfWork/docker/vueclidemo/nginx,target=/etc/nginx/conf.d \
--mount type=bind,source=$HOME/SelfWork/docker/vueclidemo/dist,target=/usr/share/nginx/html \
nginx
纠错
安装过程中,如果发现容器启动失败,使用docker logs查看容器日志
本例中docker容器名为redis,查看日志命令为:docker logs -f -t --tail 100 redis
docker inspect [containerid]/[name]
docker inspect db ✔ 1638 07:36:14
[
{
"Id": "636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0",
"Created": "2021-04-27T12:35:11.5392669Z",
"Path": "docker-entrypoint.sh",
"Args": [
"--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci",
"--default-authentication-plugin=mysql_native_password"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 16597,
"ExitCode": 0,
"Error": "",
"StartedAt": "2021-04-27T23:37:32.9769096Z",
"FinishedAt": "2021-04-27T23:37:23.5432795Z"
},
"Image": "sha256:0627ec6901db4b2aed6ca7ab35e43e19838ba079fffe8fe1be66b6feaad694de",
"ResolvConfPath": "/var/lib/docker/containers/636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0/hostname",
"HostsPath": "/var/lib/docker/containers/636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0/hosts",
"LogPath": "/var/lib/docker/containers/636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0/636641cabcd1ee16092c1464b028679dbe9aff496bed9dfc240e1bed2dde42a0-json.log",
"Name": "/db",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/Users/cwz/kkk/mysql/data:/var/lib/mysql:rw",
"/Users/cwz/kkk/mysql/my.cnf:/etc/my.cnf:rw"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "kkk_app-network",
"PortBindings": {
"3306/tcp": [
{
"HostIp": "",
"HostPort": "33061"
}
]
},
"RestartPolicy": {
"Name": "always",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": [],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/6541b939a2c0ad3e9953bfe898edaf693764e2cde56d62ad36309d4fa25b17f1-init/diff:/var/lib/docker/overlay2/88bfd7c828014de3670d17a5ddaebe79c44ca14070a1c9e3f7a80a5cb58a3c79/diff:/var/lib/docker/overlay2/0ae5c16bb1f9df67091739900a3cfc4f09ba92b66eca862d2cc5fcbbfabc3146/diff:/var/lib/docker/overlay2/371f950b0cf38d53d9536c592a5c7d98289f38ab6ee9588801d70fd2d085d91c/diff:/var/lib/docker/overlay2/d37a862de9fc54f4840b78a31b0ea0e034d927e65b92f899c618f7fa944ff137/diff:/var/lib/docker/overlay2/b3e14412827965c50315c846afd34cd3a40e20801dbe2a7d427c3f3f995fb119/diff:/var/lib/docker/overlay2/a82739b5a1f2336d4f2a216b3f4a4f9ab55319ddcf8e72881112637ffecfb021/diff:/var/lib/docker/overlay2/3b57ff4c5068a67defb34afced98445d242307ccf0b64a6470be764f5863e745/diff:/var/lib/docker/overlay2/3daca23272ad0875c3ee39f06675959f3abab9b925efa342663fd51d8e3b30b5/diff:/var/lib/docker/overlay2/3474137cd32190ce06e45903d23aa7c777ab201474d364e09f49b7b931f0a7b2/diff:/var/lib/docker/overlay2/a89a925c53f43d2575d4328eaa4d26176a9a765260747bc195cd35d3934c795d/diff:/var/lib/docker/overlay2/b7ef041594eee10722a49f6fc209540c84b62ba453621ac95ca6b72ed3b89e87/diff:/var/lib/docker/overlay2/928afa9318fa8d78912ead936c9961e5318162c097f970cb744ed4eb7dd0fe82/diff",
"MergedDir": "/var/lib/docker/overlay2/6541b939a2c0ad3e9953bfe898edaf693764e2cde56d62ad36309d4fa25b17f1/merged",
"UpperDir": "/var/lib/docker/overlay2/6541b939a2c0ad3e9953bfe898edaf693764e2cde56d62ad36309d4fa25b17f1/diff",
"WorkDir": "/var/lib/docker/overlay2/6541b939a2c0ad3e9953bfe898edaf693764e2cde56d62ad36309d4fa25b17f1/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/Users/cwz/kkk/mysql/data",
"Destination": "/var/lib/mysql",
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/Users/cwz/kkk/mysql/my.cnf",
"Destination": "/etc/my.cnf",
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "636641cabcd1",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3306/tcp": {},
"33060/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"ccc=333",
"MYSQL_USER=test",
"MYSQL_PASSWORD=123456",
"MYSQL_ROOT_PASSWORD=123456",
"MYSQL_DATABASE=fendou",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"MYSQL_MAJOR=8.0",
"MYSQL_VERSION=8.0.24-1debian10"
],
"Cmd": [
"--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci",
"--default-authentication-plugin=mysql_native_password"
],
"Image": "mysql",
"Volumes": {
"/etc/my.cnf": {},
"/var/lib/mysql": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "aad574eb3639cb0a5bad29f1da465ec3c007f907d80bd6e6e61ed4f97a718885",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "kkk",
"com.docker.compose.project.config_files": "docker-compose.yml",
"com.docker.compose.project.working_dir": "/Users/cwz/kkk",
"com.docker.compose.service": "db",
"com.docker.compose.version": "1.28.5"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "d27be75574f4aad55ad31c491e9a12a762684e89f1bbcdaa240d0308fcb074ec",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"3306/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "33061"
}
],
"33060/tcp": null
},
"SandboxKey": "/var/run/docker/netns/d27be75574f4",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"kkk_app-network": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"636641cabcd1",
"db"
],
"NetworkID": "ea6bb64819f85349364e2256276296b6c05340ac1b03922fb86aeb0c95f382fa",
"EndpointID": "64ad84042834cfe99926ed02ca46c8fe4d69bc5034ade917d6477d78b4ca8b15",
"Gateway": "192.168.144.1",
"IPAddress": "192.168.144.2",
"IPPrefixLen": 20,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:c0:a8:90:02",
"DriverOpts": null
}
}
}
}
]
最终配置
version: '3.7'
services:
db:
image: mysql
container_name: db
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/my.cnf:/etc/my.cnf
# - ./mysql/init:/docker-entrypoint-initdb.d/
env_file: .env
restart: always
environment:
# - MYSQL_HOST=127.0.0.1
- MYSQL_USER=test #创建test用户
- MYSQL_PASSWORD=123456 #设置test用户的密码
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=fendou
command:
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
--default-authentication-plugin=mysql_native_password
# --init-file /data/application/init.sql
ports:
- '33061:3306'
networks:
- kkkproject
node3:
build: .
image: node
container_name: node3
restart: always #重启规则
volumes:
- .:/usr/node/app/
environment:
- DB_HOST=db
ports:
- 4000:3000
depends_on:
- db
networks:
- kkkproject
command: npm run dev
networks:
app-network:
name: kkkproject
# driver: bridge