В части 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

ПРИМЕЧАНИЕ. Чтобы понять, как работает многоступенчатая сборка, я решил использовать три этапа:

  1. Первый этап: я буду использовать образ node: 12-stretch для создания минифицированного приложения Nodejs.
  2. Второй этап: я создам еще один образ из Ubuntu и скопирую уже миниатюрный вывод кода из «первого этапа» в контейнер на втором этапе.
  3. Третий этап: я создам еще один образ из контейнера 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, работающими в контейнерах.

Спасибо за прочтение.