предисловие
Благодаря появлению нод и росту фронтенд-инжиниринга, интерфейсная экологическая цепочка претерпела коренные изменения, будь то модель разработки или структура разработки.В то же время интерфейс медленно начинается для изучения других областей, и развертывание проекта является одной из них.
В эпоху подсечки, при реализацииnpm run build
После того, как сгенерированный продукт передан в эксплуатацию и техническое обслуживание, фронтальная задача завершается, и студенты, изучающие эксплуатацию и техническое обслуживание, записывают путь к продукту в файл конфигурации nginx на рабочем сервере, тем самым завершая «простое» развертывание.
С непрерывной итерацией проекта интерфейс начал обнаруживать серьезность проблемы, и каждый раз на упаковку уходило много времени,开发5分钟,打包半小时的情况屡见不鲜
, Кроме того, различия в собственной среде разработчика приведут к тому, что конечный продукт будет другим.
Но метод всегда сложнее, чем сложность, например, вы можете поставить операцию упаковки на удаленный сервер, или вы можете объединить описанный выше процесс с репозиторием git для достижения автоматического развертывания.
Основываясь на «парадигме байтов» без границ, эта статья начнется с нуля, реализует процесс автоматизированного развертывания переднего плана и откроет «черный ящик» развертывания проекта.
Используемый стек технологий выглядит следующим образом:
- docker
- node
- pm2
- shell
- webhook
文章中的命令大部分为 linux 命令,本地是 windows 系统的读者请使用 git bash
Представляем докер
Прежде чем приступить к разработке, давайте на этот раз представим главного героя.docker
что такое докер
Короче говоря, докер может гибко создавать/уничтожать/управлять несколькими «серверами», эти «серверы» называются容器 (container)
В контейнере вы можете делать все, что может делать сервер, например, запускать в контейнере со средой узла.npm run build
Упакуйте проект, разверните проект в контейнере со средой nginx, сохраните данные в контейнере со средой mysql и т. д.
После установки докера на сервер вы можете свободно создавать столько контейнеров, сколько хотите. Логотип докера на картинке выше показывает взаимосвязь между ними. 🐳 — это докер, а контейнеры выше — это контейнеры.
установить докер
Чтобы облегчить локальную отладку, вы можете сначала установить докер локально
Мак:скачать.docker.com/Mac/stable/…
Окна:скачать.docker.com/win/stable/…
Линукс:get.docker.com/
После загрузки и установки щелкните значок докера, чтобы запустить докер, затем вы можете использовать операции, связанные с докером, в терминале.
Когда происходит следующее, проверьте, нормально ли запускается приложение Docker.
docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
основная концепция
В Docker есть три важных понятия
- изображение
- контейнер
- склад (хранилище)
Если контейнер сравнивать с легковесным сервером, то образ — это шаблон для его создания, Docker-образ может создавать несколько контейнеров, и их отношения аналогичны отношениям между классами и экземплярами в JavaScript.
Есть два способа получить изображение
- Файл Dockerfile создан
- Непосредственно используйте существующие образы в dockerHub или других репозиториях.
Dockerfile
Dockerfile — это файл конфигурации, похожий на .gitlab-ci.yml/package.json, который определяет, как создавать образы.
Попробуйте создать образ докера с помощью Dockerfile
Создать файл
Сначала создайтеhello-docker
каталог, создать в каталогеindex.html
а такжеDockerfile
документ
<!--index.html-->
<h1>Hello docker</h1>
# Dockerfile
FROM nginx
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
- ОТ nginx: на основе официального зеркала nginx
- скопируйте index.html /usr/share/nginx/html/index.html:Замените index.html в текущем каталоге файлом /usr/share/nginx/html/index.html в контейнере,
/usr/share/nginx/html
Это каталог по умолчанию, в котором nginx хранит файлы веб-страниц в контейнере. Доступ к порту 80 контейнера отобразит файл index.html в этом каталоге. - EXPOSE 80: Контейнер предоставляет порт 80, который в основном используется для объявления.Реальное сопоставление портов также необходимо определить при создании контейнера.
Другие ссылки на конфигурацию Dockerfileофициальная документация
На этом этапе ваша файловая структура должна быть
hello-docker
|____index.html
|____Dockerfile
Создать образ
После создания Dockerfile выполните следующую команду в текущем каталоге, чтобы создать образ Docker.
docker build . -t test-image:latest
- build: создать образ докера
- .: использовать файл dockerfile в текущем каталоге
- -t: пометить версию тегом
- test-image:latest: Создайте файл с именем
test-image
, отмечен как последняя (последняя) версия
пройти черезdocker images
команда для просмотра всех изображений
Создать контейнер
После успешного создания образа выполните следующую команду, чтобы создать контейнер докеров.
docker run -d -p 80:80 --name test-container test-image:latest
- run: создать и запустить контейнер докеров
- -d: запустить контейнер в фоновом режиме
- 80:80: сопоставление порта 80 текущего сервера (80 до двоеточия) с портом 80 контейнера (80 после двоеточия)
- --name: Дайте контейнеру имя, которое будет удобно для последующего поиска контейнера.
- тестовое изображение: последнее: на основе
test-image
Последняя версия образа для создания контейнера
пройти черезdocker ps -a
команда для просмотра всех контейнеров
Поскольку локальный порт 80 сопоставлен с портом 80 контейнера, при входеlocalhost
, будет отображаться содержимое файла index.html
dockerHub
Если github — это репозиторий для хранения кода, тоdockerhubэто репозиторий для хранения изображений
Разработчики могут загружать изображения, сгенерированные Dockerfile, в dockerhub для хранения пользовательских изображений или напрямую использовать официальные изображения.
docker pull nginx
docker run -d -p 81:80 --name nginx-container nginx
Первый шаг — загрузить официальный образ nginx, а второй шаг — создать файл с именем на основе официального образа nginx.nginx-container
контейнер
Поскольку локальный порт 80 в предыдущей операции уже занят, здесь мы используем порт 81 для сопоставления с портом 80 контейнера, и доступlocalhost:81
Вы можете увидеть стартовую страницу nginx
Зачем использовать докер
Поймите концепцию и использование докера, а затем расскажите, зачем его использовать.
Некоторые люди спросят, я могу установить среду на свой собственный сервер, зачем мне помещать ее в контейнер? Вот несколько преимуществ использования докера
Экологическое единство
Появление докера решает вековую проблему:在我电脑上明明是好的
:)
Разработчики могут загружать образ Docker для среды разработки в репозиторий Docker, извлекать и запускать тот же образ в производственной среде и поддерживать согласованность среды.
docker push yeyan1996/docker-test-image:latest
локальный коммит с именемdocker-test-image
, имя образа должно начинаться с учетной записи dockerhub.
docker pull yeyan1996/docker-test-image:latest
Учетная запись сервераyeyan1996
внизdocker-test-image
зеркало
легко откатиться назад
Подобно git, docker также имеет контроль версий.
При создании образа можно использовать тег для отметки версии, если есть проблема с окружением определенной версии, можно быстро откатиться на предыдущую версию
Экологическая изоляция
Использование докера может сделать ваш сервер чище, а среды сборки можно размещать в контейнерах.
Эффективность и экономия ресурсов
В отличие от реальных серверов/виртуальных машин, контейнеры не содержат операционной системы, что означает эффективность создания/уничтожения контейнеров.
Автоматическое развертывание переднего плана
После введения докера мы внедрим автоматизированное развертывание переднего плана с нуля.
Перед миграцией Docker, если я хочу обновить содержимое онлайн-сайта, мне необходимо:
- работать локально
npm run build
Создание продуктов сборки - Загрузить продукт на сервер через ftp и т.п.
-
git push
Отправить код в репозиторий
После реализации внешнего автоматизированного развертывания:
-
git push
Отправить код в репозиторий - Сервер автоматически обновляет зеркало
- Автозапуск в зеркале
npm run build
Создание продуктов сборки - Сервер автоматически создает контейнер
Можно обнаружить, что после реализации автоматического развертывания интерфейса все, что нужно сделать разработчикам, — это отправить код на склад, а все остальное можно сделать с помощью скрипта автоматизации на сервере.
Облачный сервер
我免费申请的服务器到期了...
Во-первых, у вас должен быть сервер -. -
Поскольку это личный проект, требования к облачным серверам невысоки, и большинство поставщиков предоставят новым пользователямпроституцияБесплатная пробная версия на 1-2 недели, здесь я выбираю Tencent CloudCentOS 7.6 64位
операционная система, конечно, Alibaba Cloud или другие облачные серверы тоже вполне в порядке
Войдите на облачный сервер
熟悉云服务器配置或者不是腾讯云的读者可以跳过这章
Операции, связанные с регистрацией, не подробно описаны, обратитесь к руководству поставщика, а затем войдите в консоль, чтобы увидеть общедоступный IP-адрес текущего облачного сервера.Например, общедоступный IP-адрес сервера на рисунке ниже: 118.89. 244,45
Публичный IP-адрес используется для отправки запроса на веб-перехватчик.
Затем нам нужно войти на облачный сервер.Как правило, есть два способа локального входа на облачный сервер: вход с паролем и вход по ssh (или использование инструмента ssh, система Windows может использоватьxhell, macOS может использоватьputty)
Первый не нужно настраивать, но каждый раз при входе нужно вводить пароль от учетной записи, а второй нужно прописывать ключ ssh, но тогда вы сможете войти на облачный сервер без пароля. Лично я предпочитаю последнее, поэтому сначала зарегистрируйте ключ ssh в консоли.
Метод генерации ключа такой же, как у git.Если он был сгенерирован ранее, выполните следующую команду локально, чтобы просмотреть его
less ~/.ssh/id_rsa.pub
Вы можете выполнить следующую команду локально без создания ключа, см.Git на сервере - сгенерировать открытый ключ SSH
$ ssh-keygen -o
Generating public/private rsa key pair.
Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
Created directory '/home/schacon/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/schacon/.ssh/id_rsa.
Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaCxxxxxxxxxxxxxxxxxxxxxxxxBWDSU
GPl+nafzlHDTYxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPppSwg0cda3
Pbv7kOdJ/MxxxxxxxxxxxxxxxxxxxxxxxxxxxQwdsdMFvSlVK/7XA
t3FaoJoxxxxxxxxxxxxxxxxxxxxx88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTxxxxxxxxxxxxxxxxxxo1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== schacon@mylaptop.local
Поместите сгенерированный открытый ключ в иконку консоли облачного сервера и нажмите OK.
Помимо регистрации публичного ключа, его также необходимо привязать к инстансу,实例关机并进行绑定
После завершения привязки перезагрузите машину, и теперь вы можете войти на облачный сервер локально через команду ssh.
ssh <username>@<hostname or IP address>
Среда установки
Затем установите базовую среду для облачного сервера.
docker
Раньше я устанавливал докер локально, но он недоступен на облачном сервере по умолчанию, поэтому мне нужно установить и для него среду докера.
Существуют некоторые различия между установкой облачного сервера и локальнойофициальный сайт докераДля руководства по установке выполните следующую команду
# Step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils
# Step 2: 添加软件源信息,使用阿里云镜像
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 安装 docker-ce
sudo yum install docker-ce docker-ce-cli containerd.io
# Step 4: 开启 docker服务
sudo systemctl start docker
# Step 5: 运行 hello-world 项目
sudo docker run hello-world
выскакиватьHello from Docker!
Докажите, что Docker успешно установлен~
git
Автоматическое развертывание предполагает извлечение самого последнего кода, поэтому вам необходимо установить среду git.
yum install git
Поскольку метод SSH также требует регистрации открытого ключа на github, для удобства мы выберем метод HTTPS для клонирования хранилища позже.
node
Поскольку это автоматизированное внешнее развертывание, соответствующая логика обработки на облачном сервере написана на js, поэтому необходимо установить среду узла.Здесь nvm используется для управления версией узла.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
Затем вам нужно установить nvm в качестве переменной среды.
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
Установите последнюю версию узла через nvm
nvm install node
После завершения установки узла вам также необходимо установитьpm2
, который позволяет вашему js-скрипту работать в фоновом режиме на облачном сервере.
npm i pm2 -g
Создать демонстрационный проект
Просто используйте vue-cli для локального создания проекта.
vue create docker-test
И загрузите демонстрационный проект на github, готовый к настройке вебхука.
webhook
Хук переводится как «крючок» и может пониматься также как «обратный вызов».
Ссылаясь на жизненный цикл Vue, когда компонент смонтирован, будет запущен смонтированный хук.В хуке вы можете написать логику обратного вызова, такую как извлечение внутренних данных или рендеринг страниц, а веб-хук github отправит сообщение, когда определенные события срабатывают на текущем складе HTTP-запрос в виде поста
当仓库有提交代码时,通过将 webhook 请求地址指向云服务器 IP 地址,云服务器就能知道项目有更新,之后运行相关代码实现自动化部署
Настройка веб-перехватчиков
Add webhook
docker-test
http://118.89.244.45:3000
repository.name
# dockerfile
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
-
lts-alpine
build-stage
npm install
npm run build
-
stable-alpine
production-stage
build-stage
-
nginx -g daemon off
一旦 CMD 对应的命令结束,容器就会被销毁
scp
scp ./Dockerfile root@118.89.244.45:/root
# .dockerignore
node_modules
COPY
scp ./.dockerignore root@118.89.244.45:/root
const http = require("http")
http.createServer((req, res) => {
console.log('receive request')
console.log(req.url)
if (req.method === 'POST' && req.url === '/') {
//...
}
res.end('ok')
}).listen(3000,()=>{
console.log('server is ready')
})
const http = require("http")
+ const {execSync} = require("child_process")
+ const path = require("path")
+ const fs = require("fs")
+ // 递归删除目录
+ function deleteFolderRecursive(path) {
+ if( fs.existsSync(path) ) {
+ fs.readdirSync(path).forEach(function(file) {
+ const curPath = path + "/" + file;
+ if(fs.statSync(curPath).isDirectory()) { // recurse
+ deleteFolderRecursive(curPath);
+ } else { // delete file
+ fs.unlinkSync(curPath);
+ }
+ });
+ fs.rmdirSync(path);
+ }
+ }
+ const resolvePost = req =>
+ new Promise(resolve => {
+ let chunk = "";
+ req.on("data", data => {
+ chunk += data;
+ });
+ req.on("end", () => {
+ resolve(JSON.parse(chunk));
+ });
+ });
http.createServer(async (req, res) => {
console.log('receive request')
console.log(req.url)
if (req.method === 'POST' && req.url === '/') {
+ const data = await resolvePost(req);
+ const projectDir = path.resolve(`./${data.repository.name}`)
+ deleteFolderRecursive(projectDir)
+ // 拉取仓库最新代码
+ execSync(`git clone https://github.com/yeyan1996/${data.repository.name}.git ${projectDir}`,{
+ stdio:'inherit',
+ })
}
res.end('ok')
}).listen(3000, () => {
console.log('server is ready')
})
data.repository.name
Создание образов и контейнеров
Перед созданием нового контейнера необходимо сначала уничтожить старый контейнер.Вот несколько используемых команд докера:
docker ps -a -f "name=^docker" --format="{{.Names}}"
Просмотрите все контейнеры докеров, имена которых начинаются с докера, и выведите только имя контейнера.
docker stop docker-container
Остановите контейнер с именем docker-container
docker rm docker-container
Удалить контейнер с именем docker-container (можно удалить только контейнер в остановленном состоянии)
Затем добавьте логику, связанную с докером, в index.js.
const http = require("http")
const {execSync} = require("child_process")
const fs = require("fs")
const path = require("path")
// 递归删除目录
function deleteFolderRecursive(path) {
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file) {
const curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
const resolvePost = req =>
new Promise(resolve => {
let chunk = "";
req.on("data", data => {
chunk += data;
});
req.on("end", () => {
resolve(JSON.parse(chunk));
});
});
http.createServer(async (req, res) => {
console.log('receive request')
console.log(req.url)
if (req.method === 'POST' && req.url === '/') {
const data = await resolvePost(req);
const projectDir = path.resolve(`./${data.repository.name}`)
deleteFolderRecursive(projectDir)
// 拉取仓库最新代码
execSync(`git clone https://github.com/yeyan1996/${data.repository.name}.git ${projectDir}`,{
stdio:'inherit',
})
+ // 复制 Dockerfile 到项目目录
+ fs.copyFileSync(path.resolve(`./Dockerfile`), path.resolve(projectDir,'./Dockerfile'))
+ // 复制 .dockerignore 到项目目录
+ fs.copyFileSync(path.resolve(__dirname,`./.dockerignore`), path.resolve(projectDir, './.dockerignore'))
+ // 创建 docker 镜像
+ execSync(`docker build . -t ${data.repository.name}-image:latest `,{
+ stdio:'inherit',
+ cwd: projectDir
+ })
+ // 销毁 docker 容器
+ execSync(`docker ps -a -f "name=^${data.repository.name}-container" --format="{{.Names}}" | xargs -r docker stop | xargs -r docker rm`, {
+ stdio: 'inherit',
+ })
+ // 创建 docker 容器
+ execSync(`docker run -d -p 8888:80 --name ${data.repository.name}-container ${data.repository.name}-image:latest`, {
+ stdio:'inherit',
+ })
+ console.log('deploy success')
res.end('ok')
}
}).listen(3000, () => {
console.log('server is ready')
})
Оператор linux pipe иxargs
команда для фильтрации контейнеров, начинающихся с docker-test (сdocker-test
Контейнеры, созданные зеркалированием кода репозитория), останавливайте, удаляйте и пересоздавайте их
Также скопируйте на облачный сервер через scp
scp ./index.js root@118.89.244.45:/root
запустить скрипт узла
Запустите index.js в качестве фонового скрипта на облачном сервере через ранее установленный pm2
pm2 start index.js
После успешного запуска посетите порт 8888 облачного сервера, чтобы увидеть развернутый демонстрационный проект (перед доступом убедитесь, что сервер открыл порт 8888).
try it
Давайте попробуем проверить, правильно ли работает процесс автоматического развертывания.
Первый запуск на облачном сервереpm2 logs
Просмотрите вывод журнала с помощью index.js, а затем добавьте его локально.hello docker
Скопируйте и отправьте на github
Неудивительно, что pm2 выведет лог клонированного проекта.
После клонирования поместите Dockerfile и .dockerignore в файл проекта и обновите образ.
Затем уничтожьте старый контейнер и создайте контейнер с обновленным образом.
Наконец, посетите порт 8888, чтобы увидеть обновленную копию.
Вы закончили~
исходный код
Следите за файлами Dockerfile , .dockerignore , index.js
написать на обороте
Приведенная выше демонстрация создает только один контейнер Docker.При обновлении проекта, поскольку контейнер должен пройти процесс уничтожения и создания, может возникнуть ситуация, когда страница не будет доступна в течение определенного периода времени.
При фактическом запуске в производство обычно создается несколько контейнеров, и каждый контейнер постепенно обновляется.При балансировке нагрузки пользовательские запросы сопоставляются с контейнерами с разными портами, чтобы гарантировать, что онлайн-сервисы не будут отключены из-за обновлений контейнеров.
Кроме того, существуют очень зрелые инструменты CI/CD, основанные на платформе github, такие как
Упростите описанные выше шаги по регистрации веб-перехватчика и написанию скрипта index.js, который обновляет контейнер через файл конфигурации yml.
# .travis.yml
language: node_js
node_js:
- 8
branchs:
only:
- master
cache:
directories:
- node_modules
install:
- yarn install
scripts:
- yarn test
- yarn build
Кроме того, по мере расширения среды количество контейнеров будет постепенно увеличиваться, и в Docker также появился лучший способ управления несколькими контейнерами.docker-compose
Однако целью этой статьи является изучение принципа, и рекомендуется использовать вышеуказанные платформы для поддержки зрелых проектов с открытым исходным кодом.
Спасибо, что вы здесь, я надеюсь, что это поможет вам ~
использованная литература
Практическое руководство по Docker, написанное для внешнего интерфейса