手动部署搞烦了?我花一个周末学的Docker,到现在用了三年
先吐槽一句。以前我部署一个项目,流程是这样的:买服务器、装系统、装nginx、装MySQL、装PHP或者Node、配环境变量、改权限、调配置……一套下来少说俩小时。而且每次换服务器或者帮朋友搭环境,同样的步骤重复一遍,烦得要死。
后来有个同事跟我说,你试试Docker。我说那玩意儿不是大厂才用的吗?他说你学一个下午就懂了。
结果我真花了一个周末,把Docker啃了一遍。从那以后,部署项目就是一条命令的事。今天就把这套东西用大白话讲清楚,不绕弯子。
先搞明白Docker是干啥的
不用背概念。你就这么理解:
以前部署像是搬家——锅碗瓢盆、被子衣服,每样都得单独打包、单独搬运,到了新家还得重新归置。
Docker就像是把整个房间(包括操作系统、软件、配置)直接装进一个集装箱。搬到哪台服务器上,打开就能用,环境一模一样。
所以它解决的核心问题是:环境不一致。本地跑得好好的,上了服务器就报错?Docker能把这个破事干掉。
实战:把一个Node.js项目容器化
我从最简单的例子开始。假设你有一个Express写的API服务,目录结构大概这样:
text
my-app/ ├── app.js ├── package.json └── package-lock.json
第一步:写Dockerfile
在项目根目录新建一个文件,就叫Dockerfile(没有后缀)。内容如下:
dockerfile
# 用官方Node镜像作为基础 FROM node:18-alpine # 设置工作目录 WORKDIR /app # 先复制package文件,这样可以利用缓存 COPY package*.json ./ # 安装依赖 RUN npm ci --only=production # 复制源代码 COPY . . # 暴露端口(你的应用监听的端口) EXPOSE 3000 # 启动命令 CMD ["node", "app.js"]
解释一下:alpine版本很小,最终镜像不到100MB,下载和启动都快。npm ci比npm install更适合自动化环境。
第二步:建一个.dockerignore
跟.gitignore一个意思,把不需要打包进镜像的文件排除掉:
text
node_modules .git .gitignore .env Dockerfile .dockerignore logs
第三步:构建镜像
bash
docker build -t my-api:v1 .
第一次会慢一点,因为要下载基础镜像。之后再构建就快了。
第四步:运行容器
bash
docker run -d -p 3000:3000 --name my-api-container my-api:v1
参数说明:-d后台运行,-p 3000:3000把宿主机的3000端口映射到容器的3000端口,--name给容器起个名字。
跑起来之后,访问http://你的服务器IP:3000就能看到服务了。
实战:用docker-compose管理多个服务
单个容器还好说,但真实项目往往需要数据库、缓存、应用一起跑。这时候用docker-compose,一个配置文件全搞定。
在项目根目录新建docker-compose.yml:
yaml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
depends_on:
- mysql
- redis
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
restart: always
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=yourpassword
- MYSQL_DATABASE=myapp
volumes:
- mysql-data:/var/lib/mysql
restart: always
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
restart: always
volumes:
mysql-data:
redis-data:
然后启动只需要一条命令:
bash
docker-compose up -d
所有服务一起起来,数据卷自动挂载,重启策略也配好了。停止用docker-compose down。
注意:volumes那块是持久化数据的关键。如果不挂载,容器删了数据就没了。生产环境一定得挂载。
镜像仓库:把镜像存到云上
镜像做好了,怎么迁移到另一台服务器?不需要重新build,直接把镜像推到仓库。
我一般用阿里云容器镜像服务(免费额度够个人用)。操作步骤:
bash
# 登录仓库
docker login --username=你的账号 registry.cn-hangzhou.aliyuncs.com
# 给镜像打tag
docker tag my-api:v1 registry.cn-hangzhou.aliyuncs.com/你的命名空间/my-api:v1
# 推送
docker push registry.cn-hangzhou.aliyuncs.com/你的命名空间/my-api:v1
然后在另一台服务器上:
bash
docker pull registry.cn-hangzhou.aliyuncs.com/你的命名空间/my-api:v1
docker run -d -p 3000:3000 上面那个镜像名
整个迁移过程,从拉到跑起来,一分钟不到。
生产环境的一些小建议
Docker本身不难,但真正用起来有几个坑,提前说一下。
1. 日志别让它无限增长
容器默认会把所有输出存到本地,跑久了能撑爆磁盘。启动的时候加个参数限制日志大小:
bash
docker run -d --log-opt max-size=10m --log-opt max-file=3 your-image
或者在docker-compose里配置:
yaml
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
2. 时区问题
很多基础镜像是UTC时间,你打印日志发现比北京时间慢8小时。解决办法:挂载宿主机的时区文件。
yaml
volumes: - /etc/localtime:/etc/localtime:ro
或者Dockerfile里设置环境变量:
dockerfile
ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
3. 别用root用户跑容器
默认容器里是root,如果被入侵,风险很大。建一个普通用户:
dockerfile
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 -G nodejs
USER nodejs
4. 更新代码不用重新build整个镜像
如果只是代码变了,不想重新build依赖,可以挂载代码目录。开发环境很方便:
yaml
volumes: - ./:/app
但生产环境不建议,直接build新镜像重启最稳妥。
我现在的工作流
经过这几年摸索,我的标准流程是:
-
写代码,本地用docker-compose跑起全套环境
-
测试通过后,build镜像并推送到仓库(给镜像打上git commit hash作为tag)
-
登录生产服务器,docker pull新镜像
-
停止旧容器,启动新容器(用同样的数据卷和环境变量)
整个发布过程不到30秒,回滚更简单——把旧tag重新跑起来就行。
说实话,Docker是这几年学的最值的一个工具。以前每次部署都提心吊胆,现在基本不慌。你如果还在手动部署,真心建议花一天时间入门,后面省下的时间不止一百倍。
更多推荐
所有评论(0)