One place for hosting & domains

      Cómo disponer en contenedor una aplicación de Ruby on Rails para el desarrollo con Docker Compose


      Introducción

      Si desarrolla activamente una aplicación, usando Docker, puede simplificar su flujo de trabajo y el proceso de implementación de su aplicación para producción. El trabajo con contenedores en tareas de desarrollo tiene los siguientes beneficios:

      • Los entornos son uniformes, lo cual significa que puede elegir los lenguajes y las dependencias que desee para su proyecto sin tener que preocuparse por posibles conflictos del sistema.
      • Los entornos están aislados. Esto facilita la resolución de problemas y la admisión de nuevos miembros del equipo.
      • Los entornos son portátiles; esto permite empaquetar y compartir su código con otros.

      A través de este tutorial, verá la forma de configurar un entorno de desarrollo para una aplicación de Ruby on Rails usando Docker. Con Docker Compose, creará varios contenedores para la propia aplicación, la base de datos de PostgreSQL, Redis y un servicio Sidekiq. La configuración hará lo siguiente:

      • Sincronizar el código de la aplicación del host con el código del contenedor para facilitar los cambios durante el desarrollo.
      • Conservar los datos de la aplicación entre reinicios del contenedor.
      • Configurar trabajadores de Sidekiq para que procesen las tareas como se espera

      Al finalizar este tutorial, contará con una aplicación de información sobre tiburones en funcionamiento en contenedores de Docker:

      Inicio de la aplicación de Sidekiq

      Requisitos previos

      Para este tutorial, necesitará lo siguiente:

      Paso 1: Clonar el proyecto y agregar dependencias

      Nuestro primer paso será clonar el repositorio de rails-sidekiq de la cuenta de GitHub comunitaria de DigitalOcean. En este repositorio se incluye el código de la configuración descrita en el artículo Cómo agregar Sidekiq y Redis a una aplicación de Ruby on Rails, en el que se explica la forma de añadir Sidekiq a un proyecto de Rails 5 existente.

      Clone el repositorio en un directorio llamado rails-docker:

      • git clone https://github.com/do-community/rails-sidekiq.git rails-docker

      Diríjase al directorio rails-docker​​​:

      En este tutorial, usaremos PostgreSQL como base de datos. Para trabajar con PostgreSQL en lugar de SQLite 3, deberá añadir la gema pg a las dependencias del proyecto, que se enumeran en su Gemfile. Abra ese archivo para la edición usando nano o su editor favorito:

      Añada la gema en cualquier punto de las dependencias del proyecto principal (encima de las dependencias de desarrollo):

      ~/rails-docker/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      gem 'pg', '~>1.1.3'
      
      group :development, :test do
      . . .
      

      También se puede excluir la gema sqlite, ya que dejaremos de usarla:

      ~/rails-docker/Gemfile

      . . .
      # Use sqlite3 as the database for Active Record
      # gem 'sqlite3'
      . . .
      

      Por último, excluya la gema spring-watcher-listen en development:

      ~/rails-docker/Gemfile

      . . .
      gem 'spring'
      # gem 'spring-watcher-listen', '~> 2.0.0'
      . . .
      

      Si no desactivamos esta gema, veremos mensajes de error persistentes al acceder a la consola de Rails. Estos mensajes de error derivan del hecho de que esta gema hace que Rails use listen para mantenrse informado sobre cambios en el desarrollo, en lugar de sondear el sistema de archivos en busca de cambios. Debido a que esta gema supervisa la raíz del proyecto, incluido el directorio node_modules, emitirá mensajes de error sobre los directorios controlados, lo que sobrecargará la consola. Sin embargo, si se ocupa de conservar los recursos de CPU, es posible que la desactivación de esta gema no funcione para usted. En este caso, puede ser recomendable actualizar su aplicación de Rails a Rails 6.

      Guarde y cierre el archivo cuando concluya la edición.

      Una vez que se establezca el repositorio de su proyecto, se añada la gema pg a su Gemfile y se excluya la gema spring-watcher-listen, estará listo para configurar su aplicación y trabajar con PostgreSQL.

      Paso 2: Configurar la aplicación para que funcione con PostgreSQL y Redis

      Para trabajar con PostgreSQL y Redis en proyectos de desarrollo, nos convendrá hacer lo siguiente:

      • Configurar la aplicación para que funcione con PostgreSQL como adaptador predeterminado.
      • Añadir un archivo .env al proyecto con el nombre de usuario y la contraseña de nuestra base de datos y el host de Redis.
      • Crear una secuencia de comandos init.sql a fin de generar un usuario sammy para la base de datos.
      • Añadir un inicializador para Sidekiq, de modo que pueda funcionar con nuestro servicio redis en contenedor.
      • Añadir el archivo .env y otros archivos pertinentes a los archivos gitignore y dockerignore del proyecto.
      • Crear semillas de base de datos para que nuestra aplicación tenga algunos registros con los que podamos trabajar cuando la iniciemos.

      Primero, abra el archivo de configuración de su base de datos, ubicado en config/database.yml:

      Actualmente, en el archivo se incluye la siguiente configuración predeterminada, que se aplica en caso de que no haya otra disponible:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: sqlite3
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      

      Debemos cambiarla para que refleje el hecho de que usaremos el adaptador postgresql, ya que crearemos un servicio PostgreSQL con Docker Compose para conservar los datos de nuestra aplicación.

      Elimine el código que establece SQLite como adaptador y sustitúyalo por la siguiente configuración, que acondicionará el adaptador de forma adecuada junto con las demás variables necesarias para establecer conexión:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: postgresql
        encoding: unicode
        database: <%= ENV['DATABASE_NAME'] %>
        username: <%= ENV['DATABASE_USER'] %>
        password: <%= ENV['DATABASE_PASSWORD'] %>
        port: <%= ENV['DATABASE_PORT'] || '5432' %>
        host: <%= ENV['DATABASE_HOST'] %>
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      . . .
      

      A continuación, modificaremos la configuración del entorno development, ya que este es el entorno que usaremos en esta configuración.

      Elimine la configuración de base de datos de SQLite existente de modo que la sección tenga el siguiente aspecto:

      ~/rails-docker/config/database.yml

      . . .
      development:
        <<: *default
      . . .
      

      Por último, elimine la configuración de database para los entornos production y test:

      ~/rails-docker/config/database.yml

      . . .
      test:
        <<: *default
      
      production:
        <<: *default
      . . .
      

      Estas modificaciones de la configuración de nuestra base de datos predeterminada nos permitirán establecer la información de nuestra base de datos de forma dinámica usando variables de entorno definidas en los archivos .env, que no se asignarán al control de versiones.

      Guarde y cierre el archivo cuando concluya la edición.

      Tenga en cuenta que si crea un proyecto de Rails desde cero, puede configurar el adaptador con el comando de rails new, como se indica en el Paso 3 de Cómo usar PostgreSQL con su aplicación de Ruby on Rails en Ubuntu 18.04. De esta forma, su adaptador quedará configurado en config/database.yml y se agregará automáticamente la gema pg al proyecto.

      Ahora que hicimos referencia a nuestras variables de entorno, podemos crear un archivo para ellas con nuestra configuración preferida. Extraer ajustes de configuración de esta manera forma parte del enfoque de 12 factores para el desarrollo de aplicaciones, que define las prácticas recomendadas para lograr resiliencia en aplicaciones en entornos distribuidos. Ahora, cuando configuremos nuestro entorno de producción y prueba en el futuro, definir los ajustes de nuestra base de datos implicará crear archivos adicionales .env y hacer referencia al archivo correspondiente de nuestros archivos de Docker Compose.

      Abra un archivo .env:

      Añada los siguientes valores al archivo:

      ~/rails-docker/.env

      DATABASE_NAME=rails_development
      DATABASE_USER=sammy
      DATABASE_PASSWORD=shark
      DATABASE_HOST=database
      REDIS_HOST=redis
      

      Además de establecer el nombre, el usuario y la contraseña de nuestra base de datos, fijamos un valor para DATABASE_HOST. El valor, database, hace referencia al servicio database de PostgreSQL que crearemos con Docker Compose. También configuramos un REDIS_HOST para especificar nuestro servicio de redis.

      Guarde y cierre el archivo cuando concluya la edición.

      Para crear el usuario de base de datos sammy, podemos escribir una secuencia de comandos init.sql que podemos montar en el contenedor de base de datos cuando se inicia.

      Abra el archivo de secuencia de comandos:

      Añada el siguiente código para crear un usuario sammy con privilegios administrativos:

      ~/rails-docker/init.sql

      CREATE USER sammy;
      ALTER USER sammy WITH SUPERUSER;
      

      Esta secuencia de comandos creará el usuario apropiado en la base de datos y otorgará a este privilegios administrativos.

      Establezca los permisos apropiados en la secuencia de comandos:

      A continuación, configuraremos Sidekiq para que funcione con nuestro servicio de redis en contenedor. Podemos añadir un inicializador al directorio config/initializers, en el que Rails busca ajustes de configuración una vez que se cargan los marcos y los complementos, con lo cual se establece un valor para un host de Redis.

      Abra un archivo sidekiq.rb para especificar estas configuraciones:

      • nano config/initializers/sidekiq.rb

      Añada el siguiente código al archivo para especificar valores de REDIS_HOST y REDIS_PORT:

      ~/rails-docker/config/initializers/sidekiq.rb

      Sidekiq.configure_server do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      
      Sidekiq.configure_client do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      

      Al igual que los ajustes de configuración de nuestra base de datos, estos ajustes nos permiten establecer los parámetros de hosts y puertos de forma dinámica, lo cual nos permite sustituir los valores apropiados en el tiempo de ejecución sin necesidad de modificar el propio código de la aplicación. Además de un REDIS_HOST, tenemos un valor predeterminado establecido para REDIS_PORT en caso de que no esté configurado en otro lugar.

      Guarde y cierre el archivo cuando concluya la edición.

      A continuación, para garantizar que los datos confidenciales de nuestra aplicación no se copien al control de versiones, podemos añadir .env al archivo .gitignore de nuestro proyecto, lo cual indica a Git los archivos que se deben ignorar en nuestro proyecto. Abra el archivo para editarlo:

      En la parte inferior del archivo, añada una entrada para .env:

      ~/rails-docker/.gitignore

      yarn-debug.log*
      .yarn-integrity
      .env
      

      Guarde y cierre el archivo cuando concluya la edición.

      A continuación, crearemos un archivo .dockerignore para establecer lo que no debe copiarse a nuestros contenedores. Abra el archivo para editarlo:

      Añada al archivo el siguiente código, el cual indica a Docker que ignore algunas de las cosas que no necesitamos copiar a nuestros contenedores:

      ~/rails-docker/.dockerignore

      .DS_Store
      .bin
      .git
      .gitignore
      .bundleignore
      .bundle
      .byebug_history
      .rspec
      tmp
      log
      test
      config/deploy
      public/packs
      public/packs-test
      node_modules
      yarn-error.log
      coverage/
      

      Añada .env al final de este archivo:

      ~/rails-docker/.dockerignore

      . . .
      yarn-error.log
      coverage/
      .env
      

      Guarde y cierre el archivo cuando concluya la edición.

      Como paso final, crearemos algunos datos semilla de modo que nuestra aplicación tenga algunos registros cuando la iniciemos.

      Abra un archivo para los datos semilla del directorio db:

      Añada el siguiente código al archivo para crear cuatro tiburones y una publicación de muestra:

      ~/rails-docker/db/seeds.rb

      # Adding demo sharks
      sharks = Shark.create([{ name: 'Great White', facts: 'Scary' }, { name: 'Megalodon', facts: 'Ancient' }, { name: 'Hammerhead', facts: 'Hammer-like' }, { name: 'Speartooth', facts: 'Endangered' }])
      Post.create(body: 'These sharks are misunderstood', shark: sharks.first)
      

      Estos datos semilla crearán cuatro tiburones y una publicación asociada al primero.

      Guarde y cierre el archivo cuando concluya la edición.

      Una vez que configure su aplicación para que funcione con PostgreSQL y cree sus variables de entorno, estará listo para escribir el Dockerfile de su aplicación.

      Paso 3: Escribir las secuencias de comandos de Dockerfile y puntos de entrada.

      Su Dockerfile especifica lo que se incluirá en el contenedor de su aplicación cuando se cree. Usar Dockerfile le permite definir el entorno de su contenedor y evitar discrepancias con las dependencias o versiones de tiempo de ejecución.

      Siguiendo estas directrices sobre la creación de contenedores optimizados, haremos que nuestra imagen sea lo más eficiente posible usando una base de Alpine e intentando minimizar las capas de nuestra imagen en general.

      Abra un Dockerfile en su directorio actual:

      Las imágenes de Docker se crean usando una sucesión de imágenes en capas que se acumulan unas sobre otras. El primer paso será añadir la imagen de base para nuestra aplicación, que formará el punto de partida de la compilación de la aplicación.

      Añada el siguiente código al archivo para agregar la imagen alpine de Ruby como base:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      

      La imagen alpine se deriva del proyecto Alpine Linux, y nos ayudará a mantener el tamaño de las imágenes bajo. Para obtener más información sobre si la imagen alpine es la opción correcta para su proyecto, consulte la discusión completa en la sección de variantes de imagen de la página de imágenes de Ruby de Docker Hub.

      Algunos factores que es necesario tener en cuenta a la hora de usar alpine para el desarrollo:

      • Una imagen de tamaño reducido hará que se acorten los tiempos de carga de páginas y recursos, en particular si también mantiene los volúmenes en valores mínimos. Esto le permite lograr una experiencia de usuario de desarrollo rápida y más cercana a la que habría disponible si trabajara a nivel local en un entorno no dispuesto en contenedor.
      • La paridad entre imágenes de desarrollo y producción facilita la implementación correcta. Debido a que en los equipos se suele optar por usar imágenes de Alpine en la producción para lograr beneficios en términos de velocidad, el desarrollo con una base de Alpine ayuda a resolver problemas al realizar la transición hacia la producción.

      A continuación, establezca una variable de entorno para especificar la versión de Bundler:

      ~/rails-docker/Dockerfile

      . . .
      ENV BUNDLER_VERSION=2.0.2
      

      Este es uno de los pasos que daremos para evitar conflictos de versiones entre la versión predeterminada de bundler disponible en nuestro entorno y el código de nuestra aplicación, que requiere de Bundler 2.0.2.

      A continuación, añada al Dockerfile los paquetes que necesita para trabajar con la aplicación:

      ~/rails-docker/Dockerfile

      . . .
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      

      Entre estos paquetes se incluyen nodejs y yarn. Debido a que nuestra aplicación proporciona activos con webpack, debemos incluir Node.js y Yarn para que funcione como se espera.

      Tenga en cuenta que el contenido de la imagen alpine es extremadamente reducido: los paquetes que se mencionan aquí no contemplan por completo lo que usted podría desear o necesitar durante el desarrollo, cuando disponga en un contenedor su propia aplicación.

      A continuación, instale la versión de bundler apropiada:

      ~/rails-docker/Dockerfile

      . . .
      RUN gem install bundler -v 2.0.2
      

      Este paso garantizará la paridad entre nuestro entorno en contenedor y las especificaciones del archivo Gemfile.lock de este proyecto.

      Ahora, establezca el directorio de trabajo para la aplicación en el contenedor:

      ~/rails-docker/Dockerfile

      . . .
      WORKDIR /app
      

      Copie su Gemfile y Gemfile.lock:

      ~/rails-docker/Dockerfile

      . . .
      COPY Gemfile Gemfile.lock ./
      

      Si se copian estos archivos como paso independiente y luego se aplica bundle install, no es necesario volver a compilar las gemas del proyecto cada vez que se hacen cambios en el código de su aplicación. Esto funcionará junto con el volumen de gema que incluiremos en nuestro archivo Compose, que montará gemas en el contenedor de su aplicación en los casos en que se vuelva a crear el servicio, pero las gemas del proyecto siguen siendo iguales.

      A continuación, establezca las opciones de configuración para la compilación de la gema nokogiri:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle config build.nokogiri --use-system-libraries
      . . .
      

      Con este paso se crea nokigiri con las versiones de bibliotecas libxml2 y libxslt que añadimos al contenedor de la aplicación en el paso anterior con apk RUN add

      A continuación, instale las gemas del proyecto:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle check || bundle install
      

      Antes del proceso, esta instrucción verifica que las gemas no estén ya instaladas.

      A continuación, repetiremos el mismo procedimiento que usamos con gemas con nuestros paquetes y dependencias de JavaScript. Primero, copiaremos los metadatos de paquetes, luego instalaremos dependencias y, por último, copiaremos el código de la aplicación a la imagen del contenedor.

      Para comenzar con la sección Javascript de nuestro Dockerfile, copie el package.json y yarn.lock desde el directorio de su proyecto actual del host al contenedor:

      ~/rails-docker/Dockerfile

      . . .
      COPY package.json yarn.lock ./
      

      A continuación instale los paquetes necesarios con yarn install:

      ~/rails-docker/Dockerfile

      . . .
      RUN yarn install --check-files
      

      Esta instrucción incluye una bandera --check-files con el comando yarn, una función que garantiza que no se hayan eliminado archivos previamente instalados. Como en el caso de nuestras gemas, administraremos la persistencia de los paquetes en el directorio node_modules con un volumen cuando escribamos nuestro archivo Compose.

      Por último, copie el resto del código de la aplicación e iniícela con una secuencia de comandos de punto de entrada:

      ~/rails-docker/Dockerfile

      . . .
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      El uso de una secuencia de comandos de punto de entrada nos permite ejecutar el contenedor como un ejecutable.

      El Dockerfile final tendrá el siguiente aspecto:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      
      ENV BUNDLER_VERSION=2.0.2
      
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      
      RUN gem install bundler -v 2.0.2
      
      WORKDIR /app
      
      COPY Gemfile Gemfile.lock ./
      
      RUN bundle config build.nokogiri --use-system-libraries
      
      RUN bundle check || bundle install
      
      COPY package.json yarn.lock ./
      
      RUN yarn install --check-files
      
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      Guarde y cierre el archivo cuando concluya la edición.

      A continuación, cree un directorio llamado entry points para las secuencias de comandos de punto de entrada:

      En este directorio se incluirán nuestra secuencia de comandos de punto de entrada principal y una secuencia de comandos para nuestro servicio Sidekiq.

      Abra el archivo de la secuencia de comandos de punto de entrada de la aplicación:

      • nano entrypoints/docker-entrypoint.sh

      Añada el siguiente código al archivo:

      rails-docker/entrypoints/docker-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec rails s -b 0.0.0.0
      

      La primera línea importante es set -e, e indica al shell /bin/sh que ejecuta la secuencia de comandos que genere un fallo inmediato si hay problemas más adelante en la secuencia de comandos. A continuación, la secuencia de comandos verifica que tmp/pids/server.pid no esté presente para garantizar que no se produzcan conflictos de servidores cuando iniciemos la aplicación. Por último, la secuencia de comandos inicia el servidor de Rails con el comando bundle exec rails s. Usamos la opción -b con este comando para vincular el servidor a todas las direcciones IP y no a la predeterminada, localhost. Esta invocación hace que el servidor de Rails dirija las solicitudes a la dirección IP del contenedor en lugar de usar el localhost predeterminado.

      Guarde y cierre el archivo cuando concluya la edición.

      Haga que la secuencia de comandos sea ejecutable:

      • chmod +x entrypoints/docker-entrypoint.sh

      A continuación, crearemos una secuencia de comandos para iniciar nuestro servicio sidekiq que procesará nuestros trabajos de Sidekiq. Para obtener más información sobre la forma en que esta aplicación usa Sidekiq, consulte Cómo agregar Sidekiq y Redis a una aplicación de Ruby on Rails.

      Abra un archivo para la secuencia de comandos de punto de entrada de Sidekiq:

      • nano entrypoints/sidekiq-entrypoint.sh

      Añada el siguiente código al archivo para iniciar Sidekiq:

      ~/rails-docker/entrypoints/sidekiq-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec sidekiq
      

      Esta secuencia de comandos inicia Sidekiq en el contexto de nuestro paquete de aplicaciones.

      Guarde y cierre el archivo cuando concluya la edición. Haga que sea ejecutable:

      • chmod +x entrypoints/sidekiq-entrypoint.sh

      Con sus secuencias de comandos de punto de entrada y Dockerfile, estará listo para definir sus servicios en su archivo de Compose.

      Paso 4: Definir servicios con Docker Compose

      Usando Docker Compose, podremos ejecutar los diferentes contenedores necesarios para nuestra configuración. Definiremos nuestros servicios de Compose en nuestro archivo docker-compose.yml principal: Un servicio en Compose es un contenedor en ejecución y las definiciones del servicio, que incluirá en su archivo docker-compose.yml, contienen información sobre cómo se ejecutará cada imagen del contenedor. La herramienta Compose le permite definir varios servicios para crear aplicaciones en diferentes contenedores.

      En la configuración de nuestra aplicación se incluirán los siguientes servicios:

      • La propia aplicación
      • La base de datos de PostgreSQL
      • Redis
      • Sidekiq

      También incluiremos un montaje bind como parte de nuestra configuración, de modo que cualquier cambio de código que hagamos durante el desarrollo se sincronice de inmediato con los contenedores que necesiten acceso a este código.

      Tenga en cuenta que no definiremos un servicio test, ya que estas encuentran fuera del alcance de este tutorial y esta serie, pero podría hacerlo siguiendo el anterior que usaremos aquí para el servicio sidekiq.

      Abra el archivo docker-compose.yml:

      Primero, añada la definición del servicio de la aplicación:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      

      La definición del servicio app incluye las siguientes opciones:

      • build: define las opciones de configuración, incluido el context y dockerfile, que se aplicarán cuando Compose cree la imagen de la aplicación. Si desea utilizar una imagen existente de un registro como Docker Hub, podría utilizar la instrucción image como alternativa, con información sobre su nombre de usuario, repositorio y etiqueta de imagen.
      • context: esto define el contexto de compilación para la compilación de la imagen; en este caso, el directorio del proyecto actual.
      • dockerfile: esto especifica el Dockerfile del directorio actual de su directorio como el archivo que Compose usará para complilar la imagen de la aplicación.
      • depends_on: esto establece primero database y los contenedores de redis para que estén listos y en ejecución antes que app.
      • ports: asigna el puerto 3000 del host al puerto 3000 del contenedor.
      • volumes: incluiremos dos tipos de montajes aquí:
        • El primero es un montaje bind, que monta el código de nuestra aplicación del host en el directorio /app del contenedor. Esto facilitará un desarrollo rápido, ya que cualquier cambio que realice a su código de host se completará de inmediato en el contenedor.
        • El segundo es un volumen con nombre: gem_cache. Cuando la instrucción bundle install se ejecute en el contenedor, instalará las gemas del proyecto. Añadir este volumen significa que si recrea el contenedor, las gemas se montarán en el nuevo contenedor. Para este montaje se prevé que no se produjeron cambios en el proyecto, de modo que si realiza cambios en las gemas de su proyecto, deberá recordar eliminar este volumen antes de recrear el servicio de su aplicación.
        • El tercero es un volumen con nombre para el directorio node_modules. En lugar de contar con node_modules montados en el host, que pueden generar discrepancias de paquetes y conflictos de permisos durante el desarrollo, este volumen garantizará que los paquetes de este directorio se mantengan y reflejen el estado actual del proyecto. Una vez más, si modifica las dependencias del proyecto de Node, deberá eliminar y recrear este volumen.
      • env_file: indica a Compose que deseamos añadir variables de entorno de un archivo llamado .env, ubicado en el contexto de compilación.
      • environment: usar esta opción nos permite establecer una variable de entorno no sensible y pasar información sobre el entorno de Rails al contenedor.

      A continuación, debajo de la definición del servicio app, añada el siguiente código para definir su servicio database:

      ~/rails-docker/docker-compose.yml

      . . .
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      

      A diferencia del servicio app, el servicio database obtiene una imagen de postgres directamente del Hub de Docker. Tenga en cuenta que también anclaremos la versión aquí, en lugar de fijarla en latest o no especificarla (su valor predeterminado es latest). De esta forma, podemos garantizar que esta configuración funcione con las versiones aquí especificadas y evitar sorpresas inesperadas con cambios por código dañado en la imagen.

      Aquí, también incluiremos un volumen db_data, que conservará los datos de nuestra aplicación entre uno y otro inicio del contenedor. Además, montamos nuestra secuencia de comandos de inicio init.sql en el directorio apropiado, docker-entrypoint-initdb.d/ del contenedor, a fin de crear nuestro usuario de base de datos sammy. Después de que el punto de entrada de imagen cree el usuario y la base de datos de postgres predeterminados, ejecutará cualquier secuencia de comandos del directorio docker-entrypoint-initdb.d/ que puede usar para las tareas de inicialización necesarias. Para obtener más información, consulte la sección Secuencias de comandos de inicialización de la documentación de imagen de PostgreSQL.

      A continuación, añada la definición del servicio de redis:

      ~/rails-docker/docker-compose.yml

      . . .
        redis:
          image: redis:5.0.7
      

      Al igual que el servicio database, el servicio de redis utiliza una imagen del Hub de Docker. En este caso, no conservaremos la caché de trabajo de Sidekiq.

      Por último, añada la definición del servicio sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      

      Nuestro servicio sidekiq se asemeja a nuestro servicio app en algunos aspectos: el contexto y la imagen de compilación, las variables de entorno y el volumen que usa son los mismos. Sin embargo, depende de los servicios app, redis y database, y se iniciará en último lugar. Además, utiliza un entrypoint que anulará el punto de entrada establecido en el Dockerfile. Este ajuste entrypoint apunta a entrypoints/sidekiq-entrypoint.sh, que incluye el comando indicado para iniciar el servicio sidekiq.

      Como paso final, añada las definiciones de volumen que se encuentran debajo de la definición del servicio sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
      volumes:
        gem_cache:
        db_data:
        node_modules:
      

      Nuestra clave de volúmenes de nivel superior define lo volúmenes gem_cache, db_data y node_modules. Cuando Docker crea volúmenes, el contenido de estos se almacena en una parte del sistema de archivos de host, /var/ib/docker/volume/, que Docker administra. El contenido de cada volumen se almacena en un directorio en /var/lib/docker/volume/ y se monta en cualquier contenedor que utilice el volumen. De esta forma, los datos sobre tiburones que nuestros usuarios crearán persistirán en el volumen db_data, incluso si eliminamos y volvemos a crear el servicio database.

      El archivo terminado tendrá este aspecto:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:     
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      
        redis:
          image: redis:5.0.7
      
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      
      volumes:
        gem_cache:
        db_data:
        node_modules:     
      

      Guarde y cierre el archivo cuando concluya la edición.

      Una vez escritas sus definiciones de su servicios, estará listo para iniciar la aplicación.

      Paso 5: Probar la aplicación

      Una vez implementado su archivo docker-compose.yml, puede crear sus servicios con el comando docker-compose up y propagar su base de datos. También puede comprobar que sus datos persisitirán deteniendo y eliminando sus contenedores con docker-compose down y volviendo a crearlos.

      Primero, compile las imágenes del contenedor y cree los servicios ejecutando docker-compose up con el indicador -d, que luego ejecutará los contenedores en segundo plano.

      Verá un resultado que indicará la creación de sus servicios:

      Output

      Creating rails-docker_database_1 ... done Creating rails-docker_redis_1 ... done Creating rails-docker_app_1 ... done Creating rails-docker_sidekiq_1 ... done

      También puede obtener información más detallada sobre los procesos de inicio mostrando el resultado del registro de los servicios:

      Si todo se inició de forma correcta, verá algo similar a esto:

      Output

      sidekiq_1 | 2019-12-19T15:05:26.365Z pid=6 tid=grk7r6xly INFO: Booting Sidekiq 6.0.3 with redis options {:host=>"redis", :port=>"6379", :id=>"Sidekiq-server-PID-6", :url=>nil} sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-musl] sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: See LICENSE and the LGPL-3.0 for licensing details. sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org app_1 | => Booting Puma app_1 | => Rails 5.2.3 application starting in development app_1 | => Run `rails server -h` for more startup options app_1 | Puma starting in single mode... app_1 | * Version 3.12.1 (ruby 2.5.1-p57), codename: Llamas in Pajamas app_1 | * Min threads: 5, max threads: 5 app_1 | * Environment: development app_1 | * Listening on tcp://0.0.0.0:3000 app_1 | Use Ctrl-C to stop . . . database_1 | PostgreSQL init process complete; ready for start up. database_1 | database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv6 address "::", port 5432 database_1 | 2019-12-19 15:05:20.163 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" database_1 | 2019-12-19 15:05:20.182 UTC [63] LOG: database system was shut down at 2019-12-19 15:05:20 UTC database_1 | 2019-12-19 15:05:20.187 UTC [1] LOG: database system is ready to accept connections . . . redis_1 | 1:M 19 Dec 2019 15:05:18.822 * Ready to accept connections

      También puede verificar el estado de sus contenedores con docker-compose ps:

      Verá un resultado que indicará que sus contenedores están en ejecución:

      Output

      Name Command State Ports ----------------------------------------------------------------------------------------- rails-docker_app_1 ./entrypoints/docker-resta ... Up 0.0.0.0:3000->3000/tcp rails-docker_database_1 docker-entrypoint.sh postgres Up 5432/tcp rails-docker_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp rails-docker_sidekiq_1 ./entrypoints/sidekiq-entr ... Up

      A continuación, cree y propague su base de datos, y ejecute migraciones en ella con el siguiente comando docker-compose exec:

      • docker-compose exec app bundle exec rake db:setup db:migrate

      El comando docker-compose exec le permite ejecutar comandos en sus servicios; lo usaremos aquí para que ejecute rake db:setup y db:migrate en el contexto del paquete de nuestra aplicación, a fin de crear y sembrar la base de datos y ejecutar migraciones. A medida que trabaje en el desarrollo, docker-compose exec será útil para usted cuando desee ejecutar migraciones con su base de datos de desarrollo.

      Verá el siguiente resultado después de ejecutar este comando:

      Output

      Created database 'rails_development' Database 'rails_development' already exists -- enable_extension("plpgsql") -> 0.0140s -- create_table("endangereds", {:force=>:cascade}) -> 0.0097s -- create_table("posts", {:force=>:cascade}) -> 0.0108s -- create_table("sharks", {:force=>:cascade}) -> 0.0050s -- enable_extension("plpgsql") -> 0.0173s -- create_table("endangereds", {:force=>:cascade}) -> 0.0088s -- create_table("posts", {:force=>:cascade}) -> 0.0128s -- create_table("sharks", {:force=>:cascade}) -> 0.0072s

      Una vez que sus servicios estén en ejecución, puede visitar localhost:3000 o http://your_server_ip:3000 en el navegador. Verá una página de aterrizaje similar a esta:

      Inicio de la aplicación de Sidekiq

      Ahora, podemos probar la persistencia de datos. Cree un nuevo tiburón haciendo clic en el botón Get Shark Info, que lo dirigirá a la ruta sharks/index:

      Página de índice de tiburones con datos propagados

      Para verificar que la aplicación funcione, podemos añadirle información de prueba. Haga clic en New Shark. Gracias a la configuración de autenticación del proyecto, se le solicitará un nombre de usuario (sammy) y una contraseña (tiburón).

      En la página New Shark, ingrese “Mako” en el campo Name y “Fast” en el campo Facts.

      Haga clic en el botón Create Shark para crear el tiburón. Una vez que haya creado el tiburón, haga clic en Home en la barra de navegación del sitio para volver a la página de aterrizaje de la aplicación. Ahora, podemos probar que Sidekiq funciona.

      Haga clic en el botón** ¿Which Sharks Are In Danger?** . Debido a que no cargó ningún tiburón en peligro, esto lo dirigirá a la vista de endangered index:

      Vista del índice de tiburones en peligro

      Haga clic en Import Endangered Sharks para importar los tiburones. Verá un mensaje de estado que le indicará que los tiburones se importaron:

      Inicio de la importación

      También verá el comienzo de la importación. Actualice su página para ver la tabla entera:

      Actualización de tabla

      Gracias a Sidekiq, realizamos la carga del gran lote de tiburones en peligro sin cerrar el navegador ni interferir con otras funciones de la aplicación.

      Haga clic en el botón Home en la parte inferior de la página, que lo llevará de vuelta a la página principal de la aplicación:

      Inicio de la aplicación de Sidekiq

      Aquí, haga clic en Which Sharks Are in Danger? nuevamente. Verá los tiburones cargados otra vez.

      Ahora que sabemos que nuestra aplicación funciona correctamente, podemos probar la persistencia de nuestros datos.

      Cuando regrese a su terminal, escriba el siguiente comando para detener y eliminar sus contenedores:

      Tenga en cuenta que no incluiremos la opción --volumes; por lo tanto, no se eliminará nuestro volumen db_data.

      El siguiente resultado confirma que se eliminaron sus contenedores y su red:

      Output

      Stopping rails-docker_sidekiq_1 ... done Stopping rails-docker_app_1 ... done Stopping rails-docker_database_1 ... done Stopping rails-docker_redis_1 ... done Removing rails-docker_sidekiq_1 ... done Removing rails-docker_app_1 ... done Removing rails-docker_database_1 ... done Removing rails-docker_redis_1 ... done Removing network rails-docker_default

      Vuelva a crear los contenedores:

      Abra la consola de Rails en el contenedor app con docker-compose exec y bundle exec rails console:

      • docker-compose exec app bundle exec rails console

      En el símbolo de sistema, inspeccione el registro last de Tiburón de la base de datos:

      Verá el registro que acaba de crear:

      IRB session

      Shark Load (1.0ms) SELECT "sharks".* FROM "sharks" ORDER BY "sharks"."id" DESC LIMIT $1 [["LIMIT", 1]] => "#<Shark id: 5, name: "Mako", facts: "Fast", created_at: "2019-12-20 14:03:28", updated_at: "2019-12-20 14:03:28">"

      Luego, puede verificar que sus tiburones endangered se hayan conservado con el siguiente comando:

      IRB session

      (0.8ms) SELECT COUNT(*) FROM "endangereds" => 73

      Su volumen db_data se montó correctamente en el servicio database recreado, lo cual posibilitó que su servicio app accediera a los datos guardados. Si accede directamente a la página index shark visitando localhost:3000/sharks o http://your_server_ip:3000/sharks, también verá el registro:

      Página de índice de tiburones con Mako

      Sus tiburones en peligro también se encontrarán en la vista localhost:3000/endangered/data o http://your_server_ip:3000/endangered/data:

      Actualización de la tabla

      Su aplicación ahora se ejecuta en los contenedores de Docker con la persistencia de datos y la sincronización de códigos habilitadas. Puede probar los cambios de código local en su host, que se sincronizarán con su contenedor gracias al montaje que definimos como parte del servicio de app.

      Conclusión

      Siguiendo este tutorial, creó una configuración de desarrollo para su aplicación de Rails usando contenedores de Docker. Hizo que su proyecto fuera más modular y portátil mediante la extracción de información confidencial y la desvinculación del estado de su aplicación de su código. También configuró un archivo docker-compose.yml estándar que podrá revisar a medida que sus necesidades y requisitos de desarrollo cambien.

      A medida que realice desarrollos, es posible que le interese aprender más sobre cómo diseñar aplicaciones para flujos de trabajo en contenedores y de Cloud Native. Consulte Crear aplicaciones para Kubernetes y Modernizar aplicaciones para Kubernetes para obtener más información sobre estos temas. O bien, si desea invertir en una secuencia de aprendizaje de Kubernetes, consulte Kubernetes para desarrolladores Full-Stack.

      Para obtener más información sobre el código de la aplicación en sí, consulte los otros tutoriales de esta serie:



      Source link

      Контейнеризация приложения Ruby on Rails для разработки с помощью Docker Compose


      Введение

      Если вы активно занимаетесь разработкой приложений, использование Docker может упростить ваш рабочий процесс и процесс развертывания вашего приложения в продакшен. Работа с контейнерами в процессе разработки предоставляет следующие преимущества:

      • Среды окружения остаются согласованными, что означает, что вы можете выбирать языки и зависимости, которые вам нужны для вашего проекта, не беспокоясь о системных конфликтах.
      • Среды окружения изолированы, что упрощает устранение проблем и включение в работу новых участников групп.
      • Среды окружения портативны, что позволяет вам упаковывать свой код и делиться им с другими.

      Это обучающее руководство поможет вам научиться настраивать среду разработки для приложений Ruby on Rails с использованием Docker. Мы создадим контейнеры для самого приложения, базы данных PostgreSQL, Redis и службы Sidekiq с помощью Docker Compose. Настройка обеспечит следующее:

      • Синхронизацию кода приложения на хосте с кодом в контейнере, чтобы облегчить внесение изменений во время разработки.
      • Сохранение данных приложения между перезапусками контейнера.
      • Настройку рабочих элементов Sidekiq для обработки заданий ожидаемым образом.

      После изучения данного руководства у вас будет рабочее приложение с информацией об акулах, работающее на контейнерах Docker:

      Домашняя директория приложения Sidekiq

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      Шаг 1 — Клонирование проекта и добавление зависимостей

      Прежде всего мы клонируем репозиторий rails-sidekiq из учетной записи сообщества DigitalOcean на GitHub. Этот репозиторий содержит код установки, описанный в обучающем руководстве Добавление Sidekiq и Redis в приложение Ruby on Rails, где объясняется процедура добавления Sidekiq в существующий проект Rails 5.

      Клонируйте репозиторий в директорию rails-docker:

      • git clone https://github.com/do-community/rails-sidekiq.git rails-docker

      Перейдите в директорию rails-docker:

      В этом обучающем руководстве мы будем использовать базу данных PostgreSQL. Для работы с PostgreSQL вместо SQLite 3 нам потребуется добавить зависимость pg в список зависимостей проекта в файле Gemfile. Откройте этот файл в nano или другом текстовом редакторе по вашему выбору:

      Добавьте зависимость в любое место в списке основных зависимостей проекта (над зависимостями разработки):

      ~/rails-docker/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      gem 'pg', '~>1.1.3'
      
      group :development, :test do
      . . .
      

      Также можно выделить зависимость sqlite как комментарий, поскольку мы больше не будем ее использовать:

      ~/rails-docker/Gemfile

      . . .
      # Use sqlite3 as the database for Active Record
      # gem 'sqlite3'
      . . .
      

      Наконец, мы превратим в комментарий зависимость spring-watcher-listen в разделе development:

      ~/rails-docker/Gemfile

      . . .
      gem 'spring'
      # gem 'spring-watcher-listen', '~> 2.0.0'
      . . .
      

      Если мы не отключим эту зависимость, мы будем постоянно получать сообщения об ошибке при доступе к консоли Rails. Эти сообщения об ошибке связаны с тем, что с этой зависимостью Rails использует listen для отслеживания изменений разработки, а не запрашивает изменения в файловой системе. Поскольку эта зависимость следит за корневой директорией проекта, включая директорию node_modules, она выводит сообщения об ошибке, связанные с отслеживаемыми директориями, засоряя ими консоль. Если вам требуется экономить ресурсы процессора, отключение этой зависимости может вам не подойти. В этом случае имеет смысл обновить приложение Rails до версии Rails 6.

      Сохраните и закройте файл после завершения редактирования.

      Мы подготовили репозиторий проекта, добавили зависимость pg в файл Gemfile и выделили зависимость spring-watcher-listen как комментарий. Теперь мы можем перейти к настройке приложения для работы с PostgreSQL.

      Шаг 2 — Настройка приложения для работы с PostgreSQL и Redis

      Для работы с PostgreSQL и Redis во время разработки нам потребуется следующее:

      • Настройте приложение для работы с PostgreSQL как с адаптером по умолчанию.
      • Добавьте в проект файл .env с именем пользователя и паролем базы данных и хостом Redis.
      • Создайте скрипт init.sql для создания пользователя sammy для базы данных.
      • Добавьте инициализатор Sidekiq для обеспечения работы со службой redis в контейнере.
      • Добавьте файл .env и другие требуемые файлы в файлы gitignore и dockerignore нашего проекта.
      • Создайте исходные записи базы данных, чтобы у нашего приложения были записи, с которыми мы можем работать при запуске.

      Откройте файл конфигурации базы данных в директории config/database.yml:

      В этом файле содержатся следующие параметры по умолчанию, которые применяются при отсутствии других настроек:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: sqlite3
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      

      Нам нужно изменить их и указать, что мы используем адаптер postgresql, поскольку мы будем создавать службу PostgreSQL с помощью Docker Compose для постоянного хранения данных нашего приложения.

      Удалите код, задающий SQLite в качестве адаптера, и замените его следующими настройками, которые будут задавать адаптер и другие переменные, необходимые для подключения:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: postgresql
        encoding: unicode
        database: <%= ENV['DATABASE_NAME'] %>
        username: <%= ENV['DATABASE_USER'] %>
        password: <%= ENV['DATABASE_PASSWORD'] %>
        port: <%= ENV['DATABASE_PORT'] || '5432' %>
        host: <%= ENV['DATABASE_HOST'] %>
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      . . .
      

      Затем мы изменим настройки среды development, которую будем использовать в данном примере.

      Удалите существующую конфигурацию базы данных SQLite, чтобы раздел выглядел следующим образом:

      ~/rails-docker/config/database.yml

      . . .
      development:
        <<: *default
      . . .
      

      Удалите параметры database для сред production и test:

      ~/rails-docker/config/database.yml

      . . .
      test:
        <<: *default
      
      production:
        <<: *default
      . . .
      

      Эти изменения параметров базы данных по умолчанию помогут нам динамически настраивать информацию базы данных с помощью переменных среды из файлов .env, для которых не будет применяться контроль версий.

      Сохраните и закройте файл после завершения редактирования.

      Следует отметить, что при создании проекта Rails с нуля можно задать адаптер с помощью команды rails new, как описано в шаге 3 обучающего руководства Использование PostgreSQL с приложением Ruby on Rails в Ubuntu 18.04. Эта команда задаст адаптер в файле config/database.yml и автоматически добавит зависимость pg в наш проект.

      Мы уже разместили ссылку на переменные среды, и теперь нам нужно создать для них файл с предпочитаемыми настройками. Такое извлечение параметров конфигурации является частью 12-факторного подхода к разработке приложений, который определяет лучшие практики обеспечения надежности приложений в распределенных средах. При будущей настройке рабочей среды и среды тестирования для изменения настроек базы данных потребуется создание дополнительных файлов .env и размещение ссылок на соответствующие файлы в наших файлах Docker Compose.

      Откройте файл .env:

      Добавьте в файл следующие значения:

      ~/rails-docker/.env

      DATABASE_NAME=rails_development
      DATABASE_USER=sammy
      DATABASE_PASSWORD=shark
      DATABASE_HOST=database
      REDIS_HOST=redis
      

      В дополнение к настройке имени базы данных, имени пользователя и пароля мы также зададим значение DATABASE_HOST. Значение database относится к службе database PostgreSQL, которую мы создадим с помощью Docker Compose. Также мы задаем REDIS_HOST для определения службы redis.

      Сохраните и закройте файл после завершения редактирования.

      Чтобы создать пользователя базы данных sammy, мы можем написать скрипт init.sql для последующего монтирования в контейнер базы данных при его запуске.

      Откройте файл скрипта:

      Добавьте следующий код для создания пользователя sammy с привилегиями администратора:

      ~/rails-docker/init.sql

      CREATE USER sammy;
      ALTER USER sammy WITH SUPERUSER;
      

      Этот скрипт создаст соответствующего пользователя базы данных и предоставит ему привилегии администратора.

      Задайте в скрипте подходящие разрешения:

      Далее мы настроим Sidekiq для работы с нашей службой redis в контейнере. Мы можем добавить инициализатор в директорию config/initializers, где Rails ищет параметры конфигурации после загрузки структур и надстроек. Этот инициализатор будет задавать значение для хоста Redis.

      Откройте файл sidekiq.rb для указания этих настроек:

      • nano config/initializers/sidekiq.rb

      Добавьте в файл следующий код, чтобы задать значения REDIS_HOST и REDIS_PORT:

      ~/rails-docker/config/initializers/sidekiq.rb

      Sidekiq.configure_server do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      
      Sidekiq.configure_client do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      

      Как и наши параметры конфигурации базы данных, они позволяют выполнять динамическую настройку параметров хоста и портов, заменяя соответствующие значения во время исполнения без изменения кода приложения. В дополнение к REDIS_HOST мы задаем значение по умолчанию для REDIS_PORT, если оно больше нигде не задано.

      Сохраните и закройте файл после завершения редактирования.

      Чтобы важные данные приложения не копировались в систему контроля версий, мы можем добавить .env в файл .gitignore нашего проекта, указывая Git, какие файлы нашего проекта нужно игнорировать. Откройте файл для редактирования:

      Добавьте в конце файла запись для .env:

      ~/rails-docker/.gitignore

      yarn-debug.log*
      .yarn-integrity
      .env
      

      Сохраните и закройте файл после завершения редактирования.

      Далее мы создадим файл .dockerignore и зададим, что не следует копировать в наши контейнеры. Откройте файл для редактирования:

      Добавьте в файл следующий код, предписывающий Docker игнорировать элементы, которые мы не хотим копировать в наши контейнеры:

      ~/rails-docker/.dockerignore

      .DS_Store
      .bin
      .git
      .gitignore
      .bundleignore
      .bundle
      .byebug_history
      .rspec
      tmp
      log
      test
      config/deploy
      public/packs
      public/packs-test
      node_modules
      yarn-error.log
      coverage/
      

      Добавьте .env в конец файла:

      ~/rails-docker/.dockerignore

      . . .
      yarn-error.log
      coverage/
      .env
      

      Сохраните и закройте файл после завершения редактирования.

      На последнем шаге нам потребуется создать исходные данные, чтобы у нашего приложения было несколько записей для работы сразу после запуска.

      Откройте файл для исходных данных в директории db:

      Добавьте в файл следующий код для создания четырех демонстрационных акул и одного образца сообщения:

      ~/rails-docker/db/seeds.rb

      # Adding demo sharks
      sharks = Shark.create([{ name: 'Great White', facts: 'Scary' }, { name: 'Megalodon', facts: 'Ancient' }, { name: 'Hammerhead', facts: 'Hammer-like' }, { name: 'Speartooth', facts: 'Endangered' }])
      Post.create(body: 'These sharks are misunderstood', shark: sharks.first)
      

      Исходные данные создадут четыре акулы и одно сообщение, связанное с первой акулой.

      Сохраните и закройте файл после завершения редактирования.

      Мы настроили приложение для работы с PostgreSQL и создали переменные среды. Теперь мы готовы к написанию файла Dockerfile для нашего приложения.

      Шаг 3 — Написание файла Dockerfile и скриптов точек входа

      Файл Dockerfile указывает, что будет включено в контейнер приложения при его создании. Использование Dockerfile позволяет определить среду контейнера и избежать расхождений с зависимостями и версиями модуля исполнения.

      Следуя этим указаниям по построению оптимизированных контейнеров, мы сделаем наш образ максимально эффективным, используя базовый образ Alpine и постаравшись минимизировать количество уровней образа.

      Откройте файл Dockerfile в текущей директории:

      Образы Docker создаются с использованием последовательности многослойных образов, накладывающихся друг поверх друга. Вначале мы добавим базовый образ нашего приложения, который станет начальной точкой сборки приложения.

      Добавьте в файл следующий код, чтобы добавить образ Ruby alpine как базовый:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      

      Образ alpine является производным проекта Alpine Linux, и это помогает уменьшить размер образа. Дополнительную информацию о том, подходит ли образ alpine для вашего проекта, можно найти в обсуждении в разделе Image Variants на странице образа Ruby на Docker Hub.

      При использовании alpine для разработки нужно учитывать ряд факторов:

      • Уменьшение размера образа ускорит загрузку страниц и ресурсов, особенно если вы хотите сохранить объемы минимальными. Это поможет ускорить пользовательский интерфейс при разработке и сделает его ближе к тому, как он бы выглядел при локальной работе в среде без контейнеров.
      • Паритет между образами для разработки и рабочими образами упрощает успешное развертывание. Поскольку команды разработчиков часто используют образы Alpine для ускорения работы, использование базового образа Alpine поможет компенсировать проблемы при переносе в рабочую среду.

      Далем мы зададим переменную среды, которая будет указывать версию Bundler:

      ~/rails-docker/Dockerfile

      . . .
      ENV BUNDLER_VERSION=2.0.2
      

      Это один из шагов, которые мы предпримем для предотвращения конфликтов версий между версией bundler по умолчанию в нашей среде и кодом нашего приложения, для которого требуется версия Bundler 2.0.2.

      Затем следует добавить в Dockerfile пакеты, необходимые для работы с приложением:

      ~/rails-docker/Dockerfile

      . . .
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      

      Это пакеты nodejs, yarn и другие. Поскольку наше приложение обслуживает ресурсы с помощью webpack, нам нужно добавить пакеты Node.js и Yarn для надлежащей работы приложения.

      Следует помнить, что образ alpine является минимальным: мы перечислили не все пакеты, которые вам можно или нужно будет установить в среде разработки при контейнеризации вашего приложения.

      Далее следует установить подходящую версию bundler:

      ~/rails-docker/Dockerfile

      . . .
      RUN gem install bundler -v 2.0.2
      

      Этот шаг гарантирует паритет контейнеризованной среды и спецификаций в файле Gemfile.lock нашего проекта.

      Теперь настройте рабочую директорию для приложения на контейнере:

      ~/rails-docker/Dockerfile

      . . .
      WORKDIR /app
      

      Скопируйте файлы Gemfile и Gemfile.lock:

      ~/rails-docker/Dockerfile

      . . .
      COPY Gemfile Gemfile.lock ./
      

      Копирование этих файлов — отдельный шаг, после которого выполняется команда bundle install. Благодаря этому зависимости проекта не нужно будет воссоздавать каждый раз при внесении изменений в код приложения. Это работает в сочетании с объемом зависимостей, который мы включаем в наш файл Compose, монтирующий зависимости в контейнер приложений, когда производится воссоздание службы, но зависимости остаются без изменений.

      Далее мы установим параметры конфигурации для сборки зависимостей nokogiri:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle config build.nokogiri --use-system-libraries
      . . .
      

      На этом шаге выполняется сборка nokogiri с версиями библиотек libxml2 и libxslt, которые мы добавили в контейнер приложений на шаге RUN apk add… выше.

      Далее мы установим зависимости проекта:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle check || bundle install
      

      Эта команда перед установкой зависимостей проверяет, не были ли они уже установлены.

      Далее мы повторяем эту же процедуру для пакетов и зависимостей JavaScript. Сначала мы копируем метаданные пакета, затем устанавливаем зависимости, а в заключение копируем код приложения в образ контейнера.

      Чтобы начать работать с разделом Javascript в нашем файле Dockerfile, нужно скопировать файлы package.json и yarn.lock из текущей директории проекта на хосте в контейнер:

      ~/rails-docker/Dockerfile

      . . .
      COPY package.json yarn.lock ./
      

      Затем мы установим требуемые пакеты с помощью команды yarn install:

      ~/rails-docker/Dockerfile

      . . .
      RUN yarn install --check-files
      

      Эта команда включает флаг --check-files с командой yarn так, чтобы ранее установленные файлы не удалялись. Как и в случае с зависимостями, мы будем управлять состоянием постоянного хранения пакетов в директории node_modules при написании файла Compose.

      В заключение мы скопируем остальной код приложения и запустим приложение с помощью скрипта точки входа:

      ~/rails-docker/Dockerfile

      . . .
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      Использование скрипта точки входа позволяет запускать контейнер как исполняемый файл.

      Итоговый файл Dockerfile будет выглядеть следующим образом:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      
      ENV BUNDLER_VERSION=2.0.2
      
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      
      RUN gem install bundler -v 2.0.2
      
      WORKDIR /app
      
      COPY Gemfile Gemfile.lock ./
      
      RUN bundle config build.nokogiri --use-system-libraries
      
      RUN bundle check || bundle install
      
      COPY package.json yarn.lock ./
      
      RUN yarn install --check-files
      
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      Сохраните и закройте файл после завершения редактирования.

      Далее мы создадим директорию entrypoints для скриптов точек входа:

      В этой директории будут храниться основной скрипт точки входа и скрипт для нашей службы Sidekiq.

      Откройте файл скрипта точки входа приложения:

      • nano entrypoints/docker-entrypoint.sh

      Добавьте в файл следующий код:

      rails-docker/entrypoints/docker-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec rails s -b 0.0.0.0
      

      Первая важная строка — это строка set -e, которая предписывает оболочке выполнения скрипта /bin/sh быстро прекратить работу при обнаружении любых проблем со скриптом. Далее скрипт проверяет отсутствие tmp/pids/server.pid для предотвращения конфликтов с сервером при запуске приложения. В заключение скрипт запускает сервер Rails с помощью команды bundle exec rails s. Мы используем с этой командой опцию -b для привязки сервера ко всем IP-адресам, а не только к адресу localhost по умолчанию. При таком вызове сервер Rails перенаправляет входящие запросы на IP-адрес контейнера, а не на адрес localhost по умолчанию.

      Сохраните и закройте файл после завершения редактирования.

      Создайте исполняемый скрипт:

      • chmod +x entrypoints/docker-entrypoint.sh

      Далее мы создадим скрипт для запуска службы sidekiq, который будет обрабатывать наши задания Sidekiq. Дополнительную информацию об использовании Sidekiq в этом приложении можно найти в обучающем руководстве Добавление Sidekiq и Redis в приложение Ruby on Rails.

      Откройте файл скрипта точки входа Sidekiq:

      • nano entrypoints/sidekiq-entrypoint.sh

      Добавьте в файл следующий код для запуска Sidekiq:

      ~/rails-docker/entrypoints/sidekiq-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec sidekiq
      

      Этот скрипт запускает Sidekiq в контексте комплекта нашего приложения.

      Сохраните и закройте файл после завершения редактирования. Сделайте его исполняемым:

      • chmod +x entrypoints/sidekiq-entrypoint.sh

      Со скриптами точки входа и файлом Dockerfile мы готовы определять службы в файле Compose.

      Шаг 4 — Настройка служб с помощью Docker Compose

      С помощью Docker Compose мы сможем запустить несколько контейнеров, необходимых для нашего приложения. Мы определим службы Compose в основном файле docker-compose.yml. Служба в Compose — это запущенный контейнер, а определения служб, которые вы будете добавлять в ваш файл docker-compose.yml, содержат информацию о том, как будет запускаться образ каждого контейнера. Compose позволяет вам определять различные службы для создания приложений с несколькими контейнерами.

      Установка приложения предусматривает следующие службы:

      • Само приложение
      • База данных PostgreSQL
      • Redis
      • Sidekiq

      Также мы добавим привязку монтирования, чтобы любые изменения кода при разработке немедленно синхронизировались с контейнерами, которым требуется доступ к этому коду.

      Обратите внимание, что мы не определяем службу test, поскольку в настоящем обучающем руководстве и серии материалов не описывается тестирование, но вы можете использовать для ее определения процедуру, аналогичную приведенной здесь для службы sidekiq.

      Откройте файл docker-compose.yml:

      Добавьте определение службы приложения:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      

      Определение службы приложения включает следующие параметры:

      • build: это определение параметров конфигурации, включая context и dockerfile, которые будут применяться при создании образа приложения Compose. Если вы хотите использовать существующий образ из реестра, например, из Docker Hub, вы можете использовать инструкцию image с информацией об имени пользователя, репозитория и теге образа.
      • context: это определение контекста сборки для сборки образа, в этом случае текущая директория проекта.
      • dockerfile: данный параметр определяет Dockerfile в текущей директории проекта в качестве файла, который Compose будет использовать для сборки образа приложения.
      • depends_on: настраивает контейнеры database и redis первыми, чтобы они запускались до приложения.
      • ports: сопоставляет порт 3000 хоста с портом 3000 контейнера.
      • volumes: мы используем два типа монтирования:
        • Первый тип — это связанное монтирование, которое подразумевает монтирование кода приложения на хост в директорию /app в контейнере. Это упрощает быструю разработку, поскольку любые изменения, которые вы вносите в код хоста, будут немедленно добавлены в контейнер.
        • Второй тип — это том с именем gem_cache. При запуске команды bundle install в контейнере она устанавливает зависимости проекта. Добавление этого тома означает, что при воссоздании контейнера зависимости монтируются в новый контейнер. Такое монтирование предполагает отсутствие изменений в проекте, поэтому если вы вносите изменения в зависимости проекта во время разработки, этот том нужно удалить до воссоздания службы приложения.
        • Третий том — это том с именем для директории node_modules. Поскольку монтирование node_modules на хост может привести к расхождениям с пакетом и конфликтам при разработке, этот том обеспечивает постоянство пакетов в данной директории и их соответствие текущему состоянию проекта. Если вы измените зависимости Node проекта, этот том нужно удалить и воссоздать.
      • env_file: указывает Compose, что мы хотим добавить переменные среды из файла .env в контексте сборки.
      • environment: данная опция позволяет установить некритичную переменную среды, передавая информацию о среде Rails в контейнер.

      Добавьте под определением службы app следующий код для определения службы database:

      ~/rails-docker/docker-compose.yml

      . . .
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      

      В отличие от службы app, служба database извлекает образ postgres непосредственно из Docker Hub. Здесь мы также закрепляем версию, а не устанавливаем последнюю версию и не указываем конкретную версию (по умолчанию — последнюю). Так мы обеспечим работу этой системы с указанными здесь версиями и сможем избежать непредвиденных сюрпризов при нарушениях изменений кода образа.

      Также мы добавляем здесь том db_data, который сохраняет в постоянном виде данные приложения в промежутках между запуском контейнеров. Также мы смонтировали скрипт пуска init.sql в соответствующую директорию контейнера docker-entrypoint-initdb.d/ для создания нашего пользователя базы данных sammy. Когда точка входа образа создает пользователя и базу данных postgres по умолчанию, она выполняет все скрипты из директории docker-entrypoint-initdb.d/, которые можно использовать для выполнения необходимых задач по инициализации. Более подробную информацию можно найти в разделе Скрипты инициализации в документации по образам PostgreSQL.

      Затем следует добавить определение службы redis:

      ~/rails-docker/docker-compose.yml

      . . .
        redis:
          image: redis:5.0.7
      

      Как и служба database, служба redis использует образ из Docker Hub. В этом случае мы не сохраняем кэш заданий Sidekiq.

      В заключение следует добавить определение службы sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      

      Наша служба sidekiq напоминает службу app в некоторых отношениях. В частности, она использует тот же контекст сборки и образ, те же переменные среды и тома. Однако она зависит от служб app, redis и database и поэтому запускается в последнюю очередь. Кроме того, она использует точку входа, заменяющую заданную в Dockerfile. Этот параметр точки входа указывает на файл entrypoints/sidekiq-entrypoint.sh, который включает команду для запуска службы sidekiq.

      На заключительном шаге мы добавим определения томов под определением службы sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
      volumes:
        gem_cache:
        db_data:
        node_modules:
      

      Наш ключ томов верхнего уровня определяет тома gem_cache, db_data и node_modules. Когда Docker создает тома, содержимое тома сохраняется в части файловой системы хоста, /var/lib/docker/volumes/, а данным процессом управляет Docker. Содержимое каждого тома сохраняется в директории /var/lib/docker/volumes/ и монтируется в любой контейнер, который использует том. Таким образом, данные информации об акулах, которые будут добавлять наши пользователи, будут сохраняться в томе db_data даже при удалении и последующем восстановлении службы database.

      Итоговый файл будет выглядеть примерно так:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:     
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      
        redis:
          image: redis:5.0.7
      
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      
      volumes:
        gem_cache:
        db_data:
        node_modules:     
      

      Сохраните и закройте файл после завершения редактирования.

      Когда ваши определения службы будут записаны, вы сможете запустить приложение.

      Шаг 5 — Тестирование приложения

      Имея в распоряжении файл docker-compose.yml, вы можете создать ваши службы с помощью команды docker-compose up и создать исходные записи базы данных. Также вы можете проверить сохранение данных, останавливая работу контейнеров и удаляя их с помощью docker-compose down, а затем воссоздавая их.

      Во-первых, необходимо выполнить сборку образов и создать службы, запустив docker-compose up с флагом -d, который будет запускать контейнеры в фоновом режиме:

      Вы увидите сообщение, подтверждающее успешное создание служб:

      Output

      Creating rails-docker_database_1 ... done Creating rails-docker_redis_1 ... done Creating rails-docker_app_1 ... done Creating rails-docker_sidekiq_1 ... done

      Также вы можете получить более подробную информацию о процессах запуска, отобразив вывод журнала из служб:

      Если запуск был выполнен корректно, вы должны увидеть примерно следующее:

      Output

      sidekiq_1 | 2019-12-19T15:05:26.365Z pid=6 tid=grk7r6xly INFO: Booting Sidekiq 6.0.3 with redis options {:host=>"redis", :port=>"6379", :id=>"Sidekiq-server-PID-6", :url=>nil} sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-musl] sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: See LICENSE and the LGPL-3.0 for licensing details. sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org app_1 | => Booting Puma app_1 | => Rails 5.2.3 application starting in development app_1 | => Run `rails server -h` for more startup options app_1 | Puma starting in single mode... app_1 | * Version 3.12.1 (ruby 2.5.1-p57), codename: Llamas in Pajamas app_1 | * Min threads: 5, max threads: 5 app_1 | * Environment: development app_1 | * Listening on tcp://0.0.0.0:3000 app_1 | Use Ctrl-C to stop . . . database_1 | PostgreSQL init process complete; ready for start up. database_1 | database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv6 address "::", port 5432 database_1 | 2019-12-19 15:05:20.163 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" database_1 | 2019-12-19 15:05:20.182 UTC [63] LOG: database system was shut down at 2019-12-19 15:05:20 UTC database_1 | 2019-12-19 15:05:20.187 UTC [1] LOG: database system is ready to accept connections . . . redis_1 | 1:M 19 Dec 2019 15:05:18.822 * Ready to accept connections

      Также вы можете проверить состояние ваших контейнеров с помощью docker-compose ps:

      Вы получите вывод, указывающий, что ваши контейнеры запущены:

      Output

      Name Command State Ports ----------------------------------------------------------------------------------------- rails-docker_app_1 ./entrypoints/docker-resta ... Up 0.0.0.0:3000->3000/tcp rails-docker_database_1 docker-entrypoint.sh postgres Up 5432/tcp rails-docker_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp rails-docker_sidekiq_1 ./entrypoints/sidekiq-entr ... Up

      Далее создайте базу данных, создайте для нее исходные записи и выполните миграцию с помощью следующей команды docker-compose exec:

      • docker-compose exec app bundle exec rake db:setup db:migrate

      Команда docker-compose exec позволяет запускать команды в службах. Здесь мы используем ее для запуска команд rake db:setup и db:migrate в контексте нашего приложения для создания базы данных, создания исходных записей и проведения миграции. Команда docker-compose exec полезна для проведения миграции с базой данных, используемой для разработки.

      После запуска этой команды вы увидите следующий экран:

      Output

      Created database 'rails_development' Database 'rails_development' already exists -- enable_extension("plpgsql") -> 0.0140s -- create_table("endangereds", {:force=>:cascade}) -> 0.0097s -- create_table("posts", {:force=>:cascade}) -> 0.0108s -- create_table("sharks", {:force=>:cascade}) -> 0.0050s -- enable_extension("plpgsql") -> 0.0173s -- create_table("endangereds", {:force=>:cascade}) -> 0.0088s -- create_table("posts", {:force=>:cascade}) -> 0.0128s -- create_table("sharks", {:force=>:cascade}) -> 0.0072s

      Оставив службы работать, откройте в браузере адрес localhost:3000 или http://your_server_ip:3000. Вы увидите стартовую страницу, которая будет выглядеть примерно так:

      Домашняя директория приложения Sidekiq

      Теперь мы можем протестировать сохранение данных. Создайте новую акулу, нажав кнопку Get Shark Info, после чего откроется путь sharks/index:

      Страница указателя акул с исходными данными

      Чтобы убедиться в работе приложения, добавим в него примеры данных. Нажмите New Shark. Вам будет предложено ввести имя пользователя (sammy) и пароль (shark) в связи с параметрами аутентификации проекта.

      На странице New Shark введите Mako в поле Name и Fast в поле Facts.

      Нажмите кнопку Create Shark для создания акулы. После создания акулы нажмите Home на панели навигации сайта, чтобы вернуться на главную страницу приложения. Теперь мы можем протестировать работу Sidekiq.

      Нажмите кнопку Which Sharks Are in Danger? Поскольку вы еще не выгрузили акул, находящихся под угрозой вымирания, сейчас откроется экран endangered index:

      Экран Endangered Index

      Нажмите Import Endangered Sharks для импорта акул. Вы увидите сообщение о состоянии, где будет указано, что акулы были импортированы:

      Начало импорта

      Также вы увидите начало импорта. Обновите страницу, чтобы увидеть таблицу целиком:

      Обновление таблицы

      Благодаря Sidekiq пакетная выгрузка акул, находящихся под угрозой вымирания, была проведена успешно без блокировки браузера и без помех для работы других приложений.

      Нажмите кнопку Home внизу страницы, чтобы вернуться на главную страницу приложения:

      Домашняя директория приложения Sidekiq

      Далее нажмите Which Sharks Are in Danger? еще раз. Вы снова увидите список выгруженных акул.

      Теперь мы знаем, что наше приложение работает хорошо и можем протестировать сохранение данных.

      Введите на терминале следующую команду для остановки и удаления контейнеров:

      Обратите внимание, что мы не используем параметр --volumes и поэтому наш том db_data не удаляется.

      Следующая информация подтверждает, что ваши контейнеры и сеть были удалены:

      Output

      Stopping rails-docker_sidekiq_1 ... done Stopping rails-docker_app_1 ... done Stopping rails-docker_database_1 ... done Stopping rails-docker_redis_1 ... done Removing rails-docker_sidekiq_1 ... done Removing rails-docker_app_1 ... done Removing rails-docker_database_1 ... done Removing rails-docker_redis_1 ... done Removing network rails-docker_default

      Повторно создайте контейнеры:

      Откройте консоль Rails в контейнере app с помощью команд docker-compose exec и bundle exec rails console:

      • docker-compose exec app bundle exec rails console

      Проверьте в командной строке последнюю запись Shark в базе данных:

      Вы увидите только что созданную запись:

      IRB session

      Shark Load (1.0ms) SELECT "sharks".* FROM "sharks" ORDER BY "sharks"."id" DESC LIMIT $1 [["LIMIT", 1]] => "#<Shark id: 5, name: "Mako", facts: "Fast", created_at: "2019-12-20 14:03:28", updated_at: "2019-12-20 14:03:28">"

      Вы можете проверить сохранение акул из списка Endangered с помощью следующей команды:

      IRB session

      (0.8ms) SELECT COUNT(*) FROM "endangereds" => 73

      Ваш том db_data был успешно смонтирован в воссозданную службу database, благодаря чему служба app смогла получить доступ к сохраненным данным. Если вы перейдете напрямую на страницу index shark по адресу localhost:3000/sharks или http://your_server_ip:3000/sharks, вы также увидите эту запись:

      Страница Sharks Index с Mako

      Находящиеся под угрозой исчезновения виды акул также можно будет посмотреть на экране localhost:3000/endangered/data или http://your_server_ip:3000/endangered/data:

      Обновление таблицы

      Ваше приложение сейчас запущено в контейнерах Docker с сохранением данных и активацией синхронизации кода. Далее вы можете протестировать локальные изменения кода на вашем хосте, который будет синхронизирован с контейнером благодаря привязке монтирования, определенной в рамках службы app.

      Заключение

      В этом обучающем руководстве вы создали систему разработки приложений Rails с использованием контейнеров Docker. Вы сделали проект более модульным и портативным посредством извлечения важной информации и отсоединения состояния приложения от кода. Вы также настроили шаблонный файл docker-compose.yml, который вы можете изменять в зависимости от потребностей разработки и изменений требований.

      В процессе разработки вы можете захотеть узнать больше о проектировании приложений для контейнеризованных рабочих процессов и процессов Cloud Native. Дополнительную информацию по этим темам вы можете посмотреть в статьях Разработка архитектуры приложений для Kubernetes и Модернизация приложений для Kubernetes. Если вы предпочитаете пройти обучение по Kubernetes, ознакомьтесь с нашим учебным планом по Kubernetes для комплексной разработки.

      Чтобы узнать больше о коде приложения, ознакомьтесь с другими обучающими руководствами этой серии:



      Source link

      Use Unicorn and Nginx to Configure Ruby on Rails Applications on Ubuntu 18.04


      Updated by Linode

      Written by Linode Community

      Ruby on Rails is a popular web-application framework that allows developers to create dynamic web applications. This guide describes how to deploy Rails applications on servers using Unicorn and nginx on Ubuntu 18.04.

      Unicorn is an HTTP server, just like Passenger or Puma. Since Unicorn cannot be accessed by users directly we will be using nginx as the reverse proxy that will buffer requests and response between users and Rails application.

      Before You Begin

      Before starting this guide, make sure that you have read through and completed our Getting Started and Securing Your Server guides.

      Note

      This guide is written for a non-root user. Commands that require elevated privileges are prefixed with sudo. If you’re not familiar with the sudo command, you can check our Users and Groups guide.
      • Before you install any package, ensure that your hostname is correct:

        hostname
        hostname -f
        
      • Make sure your system is up to date:

        sudo apt-get update && apt-get upgrade
        

      Install Node.js

      Some of the features in Rails, such as the Asset Pipeline, depend on a JavaScript Runtime and Node.js provides this functionality.

      1. Install Node.js using a PPA (personal package archive) maintained by NodeSource:

        curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh
        
      2. Run the script:

        sudo bash nodesource_setup.sh
        
      3. Install the Node.js package:

        sudo apt-get install nodejs
        
      4. Check the version of Node.js:

        nodejs -v
        

      Install Yarn

      1. Configure the repository to install Yarn using Debian package repository:

        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
        
      2. Install Yarn:

        sudo apt update && sudo apt install yarn
        

      Install Ruby

      1. Install Ruby dependencies:

        sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev libsqlite3-dev
        
      2. Download the latest version of Ruby. At the time of writing this article, the current, most recent and stable version is 2.7, but you can check for the latest version here:

        wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.0.tar.gz
        
      3. Unpack the tarball:

        tar -xzvf ruby-2.7.0.tar.gz
        
      4. Move to the extracted directory:

        cd ruby-2.7.0
        
      5. Configure and install Ruby from source:

        ./configure
        make
        sudo make install
        

      Install and Create a Rails Application

      1. Install Rails on the server using gem (the package management framework for Ruby):

        sudo gem install rails
        
      2. Before creating your project, move to the home directory:

        cd
        
      3. Create a new Rails project. You will be using example as your project name:

        rails new example
        
      4. Move to the project directory:

        cd example
        

      Install and Configure Unicorn

      1. Install Unicorn on the server using gem:

        sudo gem install unicorn
        
      2. Create the file config/unicorn.rb which contains the unicorn configuration and paste the following configuration in the file.

        /home/username/example/config/unicorn.rb
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        
        # set path to the application
        app_dir = File.expand_path("../..", __FILE__)
        shared_dir = "#{app_dir}/shared"
        working_directory app_dir
        
        # Set unicorn options
        worker_processes 2
        preload_app true
        timeout 30
        
        # Path for the Unicorn socket
        listen "#{shared_dir}/sockets/unicorn.sock", :backlog => 64
        
        # Set path for logging
        stderr_path "#{shared_dir}/log/unicorn.stderr.log"
        stdout_path "#{shared_dir}/log/unicorn.stdout.log"
        
        # Set proccess id path
        pid "#{shared_dir}/pids/unicorn.pid"
      3. Now, create the directories mentioned in the Unicorn config file:

        mkdir -p shared/pids shared/sockets shared/log
        

        Note

        Please note that we are still in the Rails application directory.

      Install and Configure Nginx

      1. Install nginx:

        sudo apt-get install nginx
        
      2. We need to configure nginx to work as the reverse proxy. Edit the config file /etc/nginx/nginx.conf and paste the following configuration in the HTTP block:

        /etc/nginx/nginx.conf
        1
        2
        3
        4
        
        upstream rails {
        # Path to Unicorn socket file
        server unix:/home/username/example/shared/sockets/unicorn.sock fail_timeout=0;
        }

        Note

        Edit username and example with appropriate values.

      3. Remove the default nginx site configuration:

        sudo rm /etc/nginx/sites-enabled/default
        
      4. Create new nginx site configuration file for the Rails application:

        /etc/nginx/sites-available/example
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        
        server {
        listen 80;
        server_name localhost;
        
        root /home/username/example;
        
        try_files $uri/index.html $uri @rails;
        
        location @rails {
           proxy_pass http://rails;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
           proxy_redirect off;
        }
        
        error_page 500 502 503 504 /500.html;
        client_max_body_size 4G;
        keepalive_timeout 10;
        }

        Note

        Make sure you change the username and example with the appropriate values.

      5. Create a symlink to nginx’s sites-enabled directory to enable your site configuration file:

        sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled
        
      6. Restart nginx:

        sudo service nginx restart
        

      Start Unicorn

      • To start Unicorn in the development environment:

        sudo unicorn -c config/unicorn.rb -E development -D
        
      • To start Unicorn in the production environment:

        sudo unicorn -c config/unicorn.rb -E production -D
        

        Note

        Make sure you are in the application directory; otherwise, you will need to type in the whole path name.

      • To stop Unicorn, issue the following command:

        sudo pkill unicorn
        

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      This guide is published under a CC BY-ND 4.0 license.



      Source link