В части 1 этой серии мы узнали об образе Docker и контейнере, в этой серии мы погрузимся в то, как создать наш собственный образ Docker и как мы можем создать приложение и предоставить его через порт. В первую очередь мы рассмотрим Dockerfile и то, как указывать команды в Dockerfile.
В первой серии первое, что мы сделали, это набрали docker run hello-world и напечатали hello world с некоторой другой информацией. Что изображено на изображении hello-world? Если вам интересно, посетите здесь, чтобы узнать, как он был создан.
Dockerfile hello-world выглядит так
FROM scratch COPY hello / CMD ["/hello"]
Здесь мы видим, что они в основном создают образ Docker с нуля, вы всегда должны иметь "FROM" в начале вашего файла Dockerfile. В нашем случае мы не будем начинать с нуля, потому что у нас есть замечательные люди, которые создали образ Docker почти для всего, что нам нужно, в Docker hub, и мы можем начать создавать свой собственный образ ИЗ образа, который они создали.
Вторая строка «COPY» двоичного файла «hello» из того же каталога, что и Dockerfile, и копирует его в корневую папку контейнера.
Третья строка CMD выполняет двоичный файл и выводит на терминал «привет, мир» и другую информацию.
Хорошо, давайте создадим собственное изображение Hello world
Чтобы создать наш первый образ, нам нужно создать Dockerfile.
В настоящее время я нахожусь в папке docker-not-rocket-science, пожалуйста, не обращайте внимания на «01 -building-docker-image», это ветка git, в которой я сейчас нахожусь, README.md также не имеет значения.
Первым делом нужно создать Dockerfile:
touch Dockerfile
и добавьте приведенный ниже код в Dockerfile.
FROM ubuntu CMD ["/bin/echo", "Hello world"]
Чтобы создать наш образ, мы запускаем его в терминале
docker build .
Для просмотра созданного изображения
docker images
Сборка образа Docker прошла успешно, и мы видим, что идентификатор образа, сгенерированный в терминале, коррелирует с идентификатором в «образах докеров». Обратите внимание, что у нас нет тега, и здесь не указывается репозиторий. Чтобы запустить наш новый образ докеры:
docker run --rm 860085349447
Мы видим, что в консоли распечатано «Hello world».
Запуск bash по умолчанию в Docker Image
мы можем заставить наш образ докера перетащить нас в ubuntu bash, обновив наш Dockerfile до:
FROM ubuntu CMD ["/bin/bash"]
а затем мы можем создать новый образ, запустив
docker build --tag my-docker-image .
Теперь мы видим, что новое изображение создано с указанным нами тегом. Проверять
docker images
Для взаимодействия с созданным новым изображением
docker run -it --rm my-docker-image
Хорошо, Уэйл, я уже знаю, как взаимодействовать с изображениями и контейнерами из части 1 описания, я хочу сделать что-нибудь забавное, черт возьми!
Хорошо, давайте разберемся, как мы можем привязать наш код в нашей системе к контейнеру, хахаха !!
Давайте создадим в нашем каталоге папку src
mkdir src echo "I am file a" > src/a.txt echo "I am file b" > src/b.txt ls src
Мы создали эту папку src, в которой живет наш код, теперь мы хотим привязать код для запуска в нашем контейнере ubuntu.
docker run -it --rm --mount type=bind,source="$(pwd)"/src,target=/src my-docker-image
Подкоманда $(pwd)
расширяется до текущего рабочего каталога на хостах Linux или macOS.
Обратите внимание, что теперь у нас есть папка «src» в корне нашей машины ubuntu, и мы связали папку «src» в нашем проекте с корневым каталогом нашего контейнера.
Теперь, чтобы подтвердить, что наша папка src на нашем компьютере привязана к контейнеру, мы можем добавить текстовый файл в контейнер, а затем внести в него изменения на нашем хост-компьютере.
В контейнере проверьте наличие изменений в src / a.txt
tail -f src/a.txt
Теперь на моей машине, если я изменю текст в src / a.txt, он немедленно синхронизируется с контейнером докера.
Исходный код для этого можно найти здесь, чтобы также проверить разницу между тем, что у нас было в мастере и первой части, посетите здесь.
Давайте немного переключимся и запустим простое приложение Nodejs в Docker.
Мы настроим простое приложение Nodejs, соберем его и запустим в контейнере Docker. Посмотреть стартовый код можно здесь.
“npm init -y” npm install express
Это структура папок загруженного мной приложения. Посмотреть хеш-коммит можно на Github.
Давайте поговорим о том, что у нас в папке
- «Src / index.js»: здесь я раскручиваю экспресс-сервер. Обратите внимание, что экспресс-сервер будет работать на порту 8080, а хост - «localhost», который мы обозначили здесь как «0.0.0.0».
- «.Dockerignore»:
- «.Gitignore»: .dockerignore и .gitignore в этом случае имеют одинаковое содержимое. Мы просим git и docker игнорировать файлы, потому что мы не хотим отправлять наши node_modules в наш репозиторий, и мы определенно не хотим риска копирования node_modules на нашем хост-компьютере в docker-контейнер node_modules.
node_modules npm-debug.log
- package.json: это информация о нашем приложении и зависимостях.
- Dockerfile: содержимое нашего Dockerfile изменилось с ubuntu на node: 10
FROM node:10
Мы запускаем наше приложение с официального образа Nodejs в Dockerhub. Этот образ уже будет предустановлен вместе с Node и Npm. Все, о чем нам нужно беспокоиться, - это установить зависимости нашего приложения.
FROM node:10 COPY package*.json ./
На следующем этапе мы копируем package.json и package-lock.json с нашего хоста в образ Docker, который мы создаем.
FROM node:10 COPY package*.json ./ RUN npm install
Следующее, что мы хотим сделать, это установить наши зависимости в контейнер Docker. Если мы запускаем производственный код, мы должны использовать RUN npm ci - only = production.
FROM node:10 COPY package*.json ./ RUN npm install COPY src src
Теперь мы в основном копируем папку src в папку src на машине. Обычно вы увидите «КОПИРОВАТЬ. . » что означает копирование того, что находится в рабочем каталоге хост-машины, в корень контейнера докеров. Обратите внимание, что мы уже скопировали файлы пакета, единственное, что нам нужно, это скопировать папку src. Вы спросите, почему я добавил «.dockerignore», если я делаю это вручную, просто в учебных целях.
Если на этом этапе вы создадите образ докеры и запустите контейнер, передав «bash» в качестве CMD из терминала, вы увидите, что у нас есть файл package.json, package-lock.json и наша папка src.
FROM node:10 COPY package*.json ./ RUN npm install COPY src src CMD [ "node", "src/index.js" ]
Следующее, что нам нужно сделать, это запустить экспресс-сервер в src / index.js. Пришло время создать наш образ и запустить контейнер.
docker build --tag my-first-node-app .
Чтобы запустить изображение
docker run -it --rm --publish 3000:8080 my-first-node-app
Теперь мы увидели новый аргумент командной строки - publish, обратите внимание, что мы запускаем наш сервер Express на порту 8080, - publish позволяет нам открыть порт в нашем контейнере, чтобы мы могли получить к нему доступ на нашем хост-компьютере через порт. 3000. Теперь мы можем посетить http: // localhost: 3000 /, чтобы просмотреть наше приложение Nodejs. Посетите здесь, чтобы просмотреть изменения кода.
Примечание: «cntr + c» не остановит работающий экспресс-сервер, чтобы остановить его, нам нужно будет проверить процесс докера и убить процесс. Чтобы мы могли убить экспресс-сервер с помощью «cntr + c», передайте флаг инициализации.
docker run -it --init --rm --publish 3000:8080 my-first-node-app
Согласно Докеру: «Вы можете использовать флаг - init, чтобы указать, что процесс init должен использоваться как PID 1 в контейнере. Указание процесса инициализации гарантирует, что обычные обязанности системы инициализации, такие как получение зомби-процессов, будут выполняться внутри созданного контейнера ».
Что такое многоэтапная сборка с помощью миниатюрного приложения React
Мы будем использовать подход многоэтапной сборки для начальной загрузки нашего минифицированного приложения, готового к производству. Согласно Docker, при многоступенчатой сборке вы используете несколько операторов FROM в своем Dockerfile. Каждая инструкция FROM может использовать другую базу, и каждая из них начинает новый этап сборки. Вы можете выборочно копировать артефакты с одного этапа на другой, оставляя все нежелательное в конечном изображении.
Первое, что нам нужно сделать, это загрузить приложение React, я буду использовать приложение create-response-app.
Согласно документации React, Create React App - это удобная среда для изучения React, и это лучший способ начать создание нового одностраничного приложения в React. Он настраивает вашу среду разработки так, чтобы вы могли использовать новейшие функции JavaScript, предоставляя удобные возможности разработчика и оптимизируя ваше приложение для производства. На вашем компьютере должны быть Node ›= 8.10 и npm› = 5.6. Чтобы настроить среду разработки, зайдите на github и запустите:
npx create-react-app my-app
cd my-app
npm start
В моем случае у меня уже есть рабочий каталог, инициализированный с помощью git, мне пришлось удалить весь код в моем каталоге, а затем создать приложение для реагирования в моем текущем каталоге. Проверить хеш-коммит можно здесь.
npx create-react-app . npm start
ПРИМЕЧАНИЕ. Чтобы понять, как работает многоступенчатая сборка, я решил использовать три этапа:
- Первый этап: я буду использовать образ node: 12-stretch для создания минифицированного приложения Nodejs.
- Второй этап: я создам еще один образ из Ubuntu и скопирую уже миниатюрный вывод кода из «первого этапа» в контейнер на втором этапе.
- Третий этап: я создам еще один образ из контейнера Nginx, и мы скопируем миниатюрный код в папку, чтобы позволить Nginx запустить наше приложение.
Из приведенного выше шага вы заметите, что второй этап является избыточным, у меня уже есть минимизированный код с первого этапа, и я могу скопировать минимизированный код непосредственно в контейнер Nginx. Причина, по которой я добавил второй этап, состоит в том, чтобы позволить нам немного погрузиться в WORKDIR, а также для нас, чтобы мы знали, что контейнер - это не что иное, как набор файлов в папке, как описано в части 1 серии статей о Docker.
FROM node:12-stretch AS FIRST_STAGE WORKDIR /code/ COPY package*.json /code/ RUN npm ci --only=production COPY . /code/ RUN npm run build
ПРИМЕЧАНИЕ. Мы называем этот этап «FIRST_STAGE» (вы можете называть его как хотите), затем мы копируем наш package.json, package-lock.json, устанавливаем нашу зависимость, а затем создаем миниатюрную версию нашего приложения для реагирования.
Мы использовали WORKDIR, чтобы указать, где мы хотим использовать докер в качестве каталога.
На этом этапе мы попытаемся создать приложение, а затем постараемся убедиться, что минимизированный код находится в папке сборки.
docker build --tag my-minified-react-app . docker run -it --init --rm my-minified-react-app bash
Теперь мы можем подтвердить, что минимизированный код находится в сборке «ls / code / build /».
Второй этап
Здесь мы скопируем миниатюрный код из первой стадии сборки в образ, который мы создадим из Ubuntu.
FROM node:12-stretch AS FIRST_STAGE WORKDIR /code/ COPY package*.json /code/ RUN npm ci --only=production COPY . /code/ RUN npm run build # stage two FROM ubuntu:18.04 AS SECOND_STAGE COPY --from=FIRST_STAGE /code/build /minified/
ПРИМЕЧАНИЕ. Я использовал Ubuntu на втором этапе здесь, потому что я хочу, чтобы мы могли запускать bash и гарантировать, что наш миниатюрный код был скопирован из первого этапа / кода / сборки в нашу миниатюрную папку на втором этапе. В производстве вы хотите использовать альпийский контейнер.
docker build --tag my-minified-react-app . docker run -it --rm my-minified-react-app bash
Теперь мы можем подтвердить, что минимизированный код находится на втором этапе «ls / minified /».
Третий этап
Теперь мы хотим скопировать миниатюрный код из нашего второго этапа на сервер Nginx, чтобы Nginx мог обслуживать приложение.
FROM node:12-stretch AS FIRST_STAGE WORKDIR /code/ COPY package*.json /code/ RUN npm ci --only=production COPY . /code/ RUN npm run build # stage two FROM ubuntu:18.04 AS SECOND_STAGE COPY --from=FIRST_STAGE /code/build /minified/ #stage three FROM nginx AS my_nginx COPY --from=SECOND_STAGE /minified/ /usr/share/nginx/html
Чтобы проверить это
docker build --tag my-minified-react-app . docker run -it --rm --publish 3000:80 my-minified-react-app # Now visit http://localhost:3000/
Заключение
Можно с уверенностью сказать, что Docker - это просто набор файлов, помещенных в папку, как описано в первой части этой статьи. Обратите внимание, что для упрощения мы выполняем все команды от имени пользователя root, чтобы избежать смены владельца папок и всей связанной с этим сложности. Следующая часть статьи будет посвящена тому, как управлять пользователями в контейнере Docker, как объединять контейнеры, тома в сеть, и мы погрузимся в Docker-Compose, создав простое приложение CRUD с Nodejs, Mongo и React, работающими в контейнерах.
Спасибо за прочтение.