One place for hosting & domains

      Compose

      How To Migrate a Docker Compose Workflow for Rails Development to Kubernetes


      Introduction

      When building modern, stateless applications, containerizing your application’s components is the first step in deploying and scaling on distributed platforms. If you have used Docker Compose in development, you will have modernized and containerized your application by:

      • Extracting necessary configuration information from your code.
      • Offloading your application’s state.
      • Packaging your application for repeated use.

      You will also have written service definitions that specify how your container images should run.

      To run your services on a distributed platform like Kubernetes, you will need to translate your Compose service definitions to Kubernetes objects. This will allow you to scale your application with resiliency. One tool that can speed up the translation process to Kubernetes is kompose, a conversion tool that helps developers move Compose workflows to container orchestrators like Kubernetes or OpenShift.

      In this tutorial, you will translate Compose services to Kubernetes objects using kompose. You will use the object definitions that kompose provides as a starting point and make adjustments to ensure that your setup will use Secrets, Services, and PersistentVolumeClaims in the way that Kubernetes expects. By the end of the tutorial, you will have a single-instance Rails application with a PostgreSQL database running on a Kubernetes cluster. This setup will mirror the functionality of the code described in Containerizing a Ruby on Rails Application for Development with Docker Compose and will be a good starting point to build out a production-ready solution that will scale with your needs.

      Prerequisites

      Step 1 — Installing kompose

      To begin using kompose, navigate to the project’s GitHub Releases page, and copy the link to the current release (version 1.22.0 as of this writing). Paste this link into the following curl command to download the latest version of kompose:

      • curl -L https://github.com/kubernetes/kompose/releases/download/v1.22.0/kompose-linux-amd64 -o kompose

      For details about installing on non-Linux systems, please refer to the installation instructions.

      Make the binary executable:

      Move it to your PATH:

      • sudo mv ./kompose /usr/local/bin/kompose

      To verify that it has been installed properly, you can do a version check:

      If the installation was successful, you will see output like the following:

      Output

      1.22.0 (955b78124)

      With kompose installed and ready to use, you can now clone the Node.js project code that you will be translating to Kubernetes.

      Step 2 — Cloning and Packaging the Application

      To use our application with Kubernetes, we will need to clone the project code and package the application so that the kubelet service can pull the image.

      Our first step will be to clone the rails-sidekiq repository from the DigitalOcean Community GitHub account. This repository includes the code from the setup described in Containerizing a Ruby on Rails Application for Development with Docker Compose, which uses a demo Rails application to demonstrate how to set up a development environment using Docker Compose. You can find more information about the application itself in the series Rails on Containers.

      Clone the repository into a directory called rails_project:

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

      Navigate to the rails_project directory:

      Now checkout the code for this tutorial from the compose-workflow branch:

      • git checkout compose-workflow

      Output

      Branch 'compose-workflow' set up to track remote branch 'compose-workflow' from 'origin'. Switched to a new branch 'compose-workflow'

      The rails_project directory contains files and directories for a shark information application that works with user input. It has been modernized to work with containers: sensitive and specific configuration information has been removed from the application code and refactored to be injected at runtime, and the application’s state has been offloaded to a PostgreSQL database.

      For more information about designing modern, stateless applications, please see Architecting Applications for Kubernetes and Modernizing Applications for Kubernetes.

      The project directory includes a Dockerfile with instructions for building the application image. Let’s build the image now so that you can push it to your Docker Hub account and use it in your Kubernetes setup.

      Using the docker build command, build the image with the -t flag, which allows you to tag it with a memorable name. In this case, tag the image with your Docker Hub username and name it rails-kubernetes or a name of your own choosing:

      • docker build -t your_dockerhub_user/rails-kubernetes .

      The . in the command specifies that the build context is the current directory.

      It will take a minute or two to build the image. Once it is complete, check your images:

      You will see the following output:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_user/rails-kubernetes latest 24f7e88b6ef2 2 days ago 606MB alpine latest d6e46aa2470d 6 weeks ago 5.57MB

      Next, log in to the Docker Hub account you created in the prerequisites:

      • docker login -u your_dockerhub_user

      When prompted, enter your Docker Hub account password. Logging in this way will create a ~/.docker/config.json file in your user’s home directory with your Docker Hub credentials.

      Push the application image to Docker Hub with the docker push command. Remember to replace your_dockerhub_user with your own Docker Hub username:

      • docker push your_dockerhub_user/rails-kubernetes

      You now have an application image that you can pull to run your application with Kubernetes. The next step will be to translate your application service definitions to Kubernetes objects.

      Step 3 — Translating Compose Services to Kubernetes Objects with kompose

      Our Docker Compose file, here called docker-compose.yml, lays out the definitions that will run our services with Compose. A service in Compose is a running container, and service definitions contain information about how each container image will run. In this step, we will translate these definitions to Kubernetes objects by using kompose to create yaml files. These files will contain specs for the Kubernetes objects that describe their desired state.

      We will use these files to create different types of objects: Services, which will ensure that the Pods running our containers remain accessible; Deployments, which will contain information about the desired state of our Pods; a PersistentVolumeClaim to provision storage for our database data; a ConfigMap for environment variables injected at runtime; and a Secret for our application’s database user and password. Some of these definitions will be in the files kompose will create for us, and others we will need to create ourselves.

      First, we will need to modify some of the definitions in our docker-compose.yml file to work with Kubernetes. We will include a reference to our newly-built application image in our app service definition and remove the bind mounts, volumes, and additional commands that we used to run the application container in development with Compose. Additionally, we’ll redefine both containers’ restart policies to be in line with the behavior Kubernetes expects.

      If you have followed the steps in this tutorial and checked out the compose-workflow branch with git, then you should have a docker-compose.yml file in your working directory.

      If you don’t have a docker-compose.yml then be sure to visit the previous tutorial in this series, Containerizing a Ruby on Rails Application for Development with Docker Compose, and paste the contents from the linked section into a new docker-compose.yml file.

      Open the file with nano or your favorite editor:

      The current definition for the app application service looks like this:

      ~/rails_project/docker-compose.yml

      . . .
      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
      . . .
      

      Make the following edits to your service definition:

      • Replace the build: line with image: your_dockerhub_user/rails-kubernetes
      • Remove the following context: ., and dockerfile: Dockerfile lines.
      • Remove the volumes list.

      The finished service definition will now look like this:

      ~/rails_project/docker-compose.yml

      . . .
      services:
        app:
          image: your_dockerhub_user/rails-kubernetes
          depends_on:
            - database
            - redis
          ports:
            - "3000:3000"
          env_file: .env
          environment:
            RAILS_ENV: development
      . . .
      

      Next, scroll down to the database service definition and make the following edits:

      • Remove the - ./init.sql:/docker-entrypoint-initdb.d/init.sql volume line. Instead of using values from the local SQL file, we will pass the values for our POSTGRES_USER and POSTGRES_PASSWORD to the database container using the Secret we will create in Step 4.
      • Add a ports: section that will make PostgreSQL available inside your Kubernetes cluster on port 5432.
      • Add an environment: section with a PGDATA variable that points to a directory inside /var/lib/postgresql/data. This setting is required when PostgreSQL is configured to use block storage, since the database engine expects to find its data files in a sub-directory.

      The database service definition should look like this when you are finished editing it:

      ~/rails_project/docker-compose.yml

      . . .
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
          ports:
            - "5432:5432"
          environment:
            PGDATA: /var/lib/postgresql/data/pgdata
      . . .
      

      Next, edit the redis service definition to expose its default TCP port by adding a ports: section with the default 6379 port. Adding the ports: section will make Redis available inside your Kubernetes cluster. Your edited redis service should resemble the following:

      ~/rails_project/docker-compose.yml

      . . .
        redis:
          image: redis:5.0.7
          ports:
            - "6379:6379"
      

      After editing the redis section of the file, continue to the sidekiq service definition. Just as with the app service, you’ll need to switch from building a local docker image to pulling from Docker Hub. Make the following edits to your sidekiq service definition:

      • Replace the build: line with image: your_dockerhub_user/rails-kubernetes
      • Remove the following context: ., and dockerfile: Dockerfile lines.
      • Remove the volumes list.

      ~/rails_project/docker-compose.yml

      . . .
        sidekiq:
          image: your_dockerhub_user/rails-kubernetes
          depends_on:
            - app
            - database
            - redis
          env_file: .env
          environment:
              RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      

      Finally, at the bottom of the file, remove the gem_cache and node_modules volumes from the top-level volumes key. The key will now look like this:

      ~/rails_project/docker-compose.yml

      . . .
      volumes:
        db_data:
      

      Save and close the file when you are finished editing.

      For reference, your completed docker-compose.yml file should contain the following:

      ~/rails_project/docker-compose.yml

      version: '3'
      
      services:
        app:
          image: your_dockerhub_user/rails-kubernetes
          depends_on:
              - database
              - redis
          ports:
              - "3000:3000"
          env_file: .env
          environment:
              RAILS_ENV: development
      
        database:
          image: postgres:12.1
          volumes:
              - db_data:/var/lib/postgresql/data
          ports:
              - "5432:5432"
          environment:
              PGDATA: /var/lib/postgresql/data/pgdata
      
        redis:
          image: redis:5.0.7
          ports:
              - "6379:6379"
      
        sidekiq:
          image: your_dockerhub_user/rails-kubernetes
          depends_on:
              - app
              - database
              - redis
          env_file: .env
          environment:
              RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      
      volumes:
        db_data:
      

      Before translating our service definitions, we will need to write the .env file that kompose will use to create the ConfigMap with our non-sensitive information. Please see Step 2 of Containerizing a Ruby on Rails Application for Development with Docker Compose for a longer explanation of this file.

      In that tutorial, we added .env to our .gitignore file to ensure that it would not copy to version control. This means that it did not copy over when we cloned the rails-sidekiq repository in Step 2 of this tutorial. We will therefore need to recreate it now.

      Create the file:

      kompose will use this file to create a ConfigMap for our application. However, instead of assigning all of the variables from the app service definition in our Compose file, we will only add settings for the PostgreSQL and Redis. We will assign the database name, username, and password separately when we manually create a Secret object in Step 4.

      Add the following port and database name information to the .env file. Feel free to rename your database if you would like:

      ~/rails_project/.env

      DATABASE_HOST=database
      DATABASE_PORT=5432
      REDIS_HOST=redis
      REDIS_PORT=6379
      

      Save and close the file when you are finished editing.

      You are now ready to create the files with your object specs. kompose offers multiple options for translating your resources. You can:

      • Create yaml files based on the service definitions in your docker-compose.yml file with kompose convert.
      • Create Kubernetes objects directly with kompose up.
      • Create a Helm chart with kompose convert -c.

      For now, we will convert our service definitions to yaml files and then add to and revise the files that kompose creates.

      Convert your service definitions to yaml files with the following command:

      After you run this command, kompose will output information about the files it has created:

      Output

      INFO Kubernetes file "app-service.yaml" created INFO Kubernetes file "database-service.yaml" created INFO Kubernetes file "redis-service.yaml" created INFO Kubernetes file "app-deployment.yaml" created INFO Kubernetes file "env-configmap.yaml" created INFO Kubernetes file "database-deployment.yaml" created INFO Kubernetes file "db-data-persistentvolumeclaim.yaml" created INFO Kubernetes file "redis-deployment.yaml" created INFO Kubernetes file "sidekiq-deployment.yaml" created

      These include yaml files with specs for the Rails application Service, Deployment, and ConfigMap, as well as for the db-data PersistentVolumeClaim and PostgreSQL database Deployment. Also included are files for Redis and Sidekiq respectively.

      To keep these manifests out of the main directory for your Rails project, create a new directory called k8s-manifests and then use the mv command to move the generated files into it:

      • mkdir k8s-manifests
      • mv *.yaml k8s-manifests

      Finally, cd into the k8s-manifests directory. We’ll work from inside this directory from now on to keep things tidy:

      These files are a good starting point, but in order for our application’s functionality to match the setup described in Containerizing a Ruby on Rails Application for Development with Docker Compose we will need to make a few additions and changes to the files that kompose has generated.

      Step 4 — Creating Kubernetes Secrets

      In order for our application to function in the way we expect, we will need to make a few modifications to the files that kompose has created. The first of these changes will be generating a Secret for our database user and password and adding it to our application and database Deployments. Kubernetes offers two ways of working with environment variables: ConfigMaps and Secrets. kompose has already created a ConfigMap with the non-confidential information we included in our .env file, so we will now create a Secret with our confidential information: our database name, username and password.

      The first step in manually creating a Secret will be to convert the data to base64, an encoding scheme that allows you to uniformly transmit data, including binary data.

      First convert the database name to base64 encoded data:

      • echo -n 'your_database_name' | base64

      Note down the encoded value.

      Next convert your database username:

      • echo -n 'your_database_username' | base64

      Again record the value you see in the output.

      Finally, convert your password:

      • echo -n 'your_database_password' | base64

      Take note of the value in the output here as well.

      Open a file for the Secret:

      Note: Kubernetes objects are typically defined using YAML, which strictly forbids tabs and requires two spaces for indentation. If you would like to check the formatting of any of your yaml files, you can use a linter or test the validity of your syntax using kubectl create with the --dry-run and --validate flags:

      • kubectl create -f your_yaml_file.yaml --dry-run --validate=true

      In general, it is a good idea to validate your syntax before creating resources with kubectl.

      Add the following code to the file to create a Secret that will define your DATABASE_NAME, DATABASE_USER and DATABASE_PASSWORD using the encoded values you just created. Be sure to replace the highlighted placeholder values here with your encoded database name, username and password:

      ~/rails_project/k8s-manifests/secret.yaml

      apiVersion: v1
      kind: Secret
      metadata:
        name: database-secret
      data:
        DATABASE_NAME: your_database_name
        DATABASE_PASSWORD: your_encoded_password
        DATABASE_USER: your_encoded_username
      

      We have named the Secret object database-secret, but you are free to name it anything you would like.

      These secrets are used with the Rails application so that it can connect to PostgreSQL. However, the database itself needs to be initialized with these same values. So next, copy the three lines and paste them at the end of the file. Edit the last three lines and change the DATABASE prefix for each variable to POSTGRES. Finally change the POSTGRES_NAME variable to read POSTGRES_DB.

      Your final secret.yaml file should contain the following:

      ~/rails_project/k8s-manifests/secret.yaml

      apiVersion: v1
      kind: Secret
      metadata:
        name: database-secret
      data:
        DATABASE_NAME: your_database_name
        DATABASE_PASSWORD: your_encoded_password
        DATABASE_USER: your_encoded_username
        POSTGRES_DB: your_database_name
        POSTGRES_PASSWORD: your_encoded_password
        POSTGRES_USER: your_encoded_username
      

      Save and close this file when you are finished editing. As you did with your .env file, be sure to add secret.yaml to your .gitignore file to keep it out of version control.

      With secret.yaml written, our next step will be to ensure that our application and database Deployments both use the values that we added to the file. Let’s start by adding references to the Secret to our application Deployment.

      Open the file called app-deployment.yaml:

      The file’s container specifications include the following environment variables defined under the env key:

      ~/rails_project/k8s-manifests/app-deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      . . .
          spec:
            containers:
              - env:
                  - name: DATABASE_HOST
                    valueFrom:
                      configMapKeyRef:
                        key: DATABASE_HOST
                        name: env
                  - name: DATABASE_PORT
                    valueFrom:
                      configMapKeyRef:
                        key: DATABASE_PORT
                        name: env
                  - name: RAILS_ENV
                    value: development
                  - name: REDIS_HOST
                    valueFrom:
                      configMapKeyRef:
                        key: REDIS_HOST
                        name: env
                  - name: REDIS_PORT
                    valueFrom:
                      configMapKeyRef:
                        key: REDIS_PORT
                        name: env
      . . .
      

      We will need to add references to our Secret so that our application will have access to those values. Instead of including a configMapKeyRef key to point to our env ConfigMap, as is the case with the existing values, we’ll include a secretKeyRef key to point to the values in our database-secret secret.

      Add the following Secret references after the - name: REDIS_PORT variable section:

      ~/rails_project/k8s-manifests/app-deployment.yaml

      . . .
          spec:
            containers:
              - env:
              . . .  
                  - name: REDIS_PORT
                    valueFrom:
                      configMapKeyRef:
                        key: REDIS_PORT
                        name: env
                  - name: DATABASE_NAME
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: DATABASE_NAME
                  - name: DATABASE_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: DATABASE_PASSWORD
                  - name: DATABASE_USER
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: DATABASE_USER
      . . .
      
      

      Save and close the file when you are finished editing. As with your secrets.yaml file, be sure to validate your edits using kubectl to ensure there are no issues with spaces, tabs, and indentation:

      • kubectl create -f app-deployment.yaml --dry-run --validate=true

      Output

      deployment.apps/app created (dry run)

      Next, we’ll add the same values to the database-deployment.yaml file.

      Open the file for editing:

      • nano database-deployment.yaml

      In this file, we will add references to our Secret for following variable keys: POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD. The postgres image makes these variables available so that you can modify the initialization of your database instance. The POSTGRES_DB creates a default database that is available when the container starts. The POSTGRES_USER and POSTGRES_PASSWORD together create a privileged user that can access the created database.

      Using the these values means that the user we create has access to all of the administrative and operational privileges of that role in PostgreSQL. When working in production, you will want to create a dedicated application user with appropriately scoped privileges.

      Under the POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD variables, add references to the Secret values:

      ~/rails_project/k8s-manifests/database-deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      . . .
          spec:
            containers:
              - env:
                  - name: PGDATA
                    value: /var/lib/postgresql/data/pgdata
                  - name: POSTGRES_DB
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: POSTGRES_DB
                  - name: POSTGRES_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: POSTGRES_PASSWORD        
                  - name: POSTGRES_USER
                    valueFrom:
                      secretKeyRef:
                        name: database-secret
                        key: POSTGRES_USER
      . . .
      

      Save and close the file when you are finished editing. Again be sure to lint your edited file using kubectl with the --dry-run --validate=true arguments.

      With your Secret in place, you can move on to creating the database Service and ensuring that your application container only attempts to connect to the database once it is fully set up and initialized.

      Step 5 — Modifying the PersistentVolumeClaim and Exposing the Application Frontend

      Before running our application, we will make two final changes to ensure that our database storage will be provisioned properly and that we can expose our application frontend using a LoadBalancer.

      First, let’s modify the storage resource defined in the PersistentVolumeClaim that kompose created for us. This Claim allows us to dynamically provision storage to manage our application’s state.

      To work with PersistentVolumeClaims, you must have a StorageClass created and configured to provision storage resources. In our case, because we are working with DigitalOcean Kubernetes, our default StorageClass provisioner is set to dobs.csi.digitalocean.com — DigitalOcean Block Storage.

      We can check this by typing:

      If you are working with a DigitalOcean cluster, you will see the following output:

      Output

      NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE do-block-storage (default) dobs.csi.digitalocean.com Delete Immediate true 76m

      If you are not working with a DigitalOcean cluster, you will need to create a StorageClass and configure a provisioner of your choice. For details about how to do this, please see the official documentation.

      When kompose created db-data-persistentvolumeclaim.yaml, it set the storage resource to a size that does not meet the minimum size requirements of our provisioner. We will therefore need to modify our PersistentVolumeClaim to use the minimum viable DigitalOcean Block Storage unit: 1GB. Please feel free to modify this to meet your storage requirements.

      Open db-data-persistentvolumeclaim.yaml:

      • nano db-data-persistentvolumeclaim.yaml

      Replace the storage value with 1Gi:

      ~/rails_project/k8s-manifests/db-data-persistentvolumeclaim.yaml

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        creationTimestamp: null
        labels:
          io.kompose.service: db-data
        name: db-data
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
      status: {}
      

      Also note the accessMode: ReadWriteOnce means that the volume provisioned as a result of this Claim will be read-write only by a single node. Please see the documentation for more information about different access modes.

      Save and close the file when you are finished.

      Next, open app-service.yaml:

      We are going to expose this Service externally using a DigitalOcean Load Balancer. If you are not using a DigitalOcean cluster, please consult the relevant documentation from your cloud provider for information about their load balancers. Alternatively, you can follow the official Kubernetes documentation on setting up a highly available cluster with kubeadm, but in this case you will not be able to use PersistentVolumeClaims to provision storage.

      Within the Service spec, specify LoadBalancer as the Service type:

      ~/rails_project/k8s-manifests/app-service.yaml

      apiVersion: v1
      kind: Service
      . . .
      spec:
        type: LoadBalancer
        ports:
      . . .
      

      When we create the app Service, a load balancer will be automatically created, providing us with an external IP where we can access our application.

      Save and close the file when you are finished editing.

      With all of our files in place, we are ready to start and test the application.

      Note:
      If you would like to compare your edited Kubernetes manifests to a set of reference files to be certain that your changes match this tutorial, the companion Github repository contains a set of tested manifests. You can compare each file individually, or you can also switch your local git branch to use the kubernetes-workflow branch.

      If you opt to switch branches, be sure to copy your secrets.yaml file into the new checked out version since we added it to .gitignore earlier in the tutorial.

      Step 6 — Starting and Accessing the Application

      It’s time to create our Kubernetes objects and test that our application is working as expected.

      To create the objects we’ve defined, we’ll use kubectl create with the -f flag, which will allow us to specify the files that kompose created for us, along with the files we wrote. Run the following command to create the Rails application and PostgreSQL database, Redis cache, and Sidekiq Services and Deployments, along with your Secret, ConfigMap, and PersistentVolumeClaim:

      • kubectl create -f app-deployment.yaml,app-service.yaml,database-deployment.yaml,database-service.yaml,db-data-persistentvolumeclaim.yaml,env-configmap.yaml,redis-deployment.yaml,redis-service.yaml,secret.yaml,sidekiq-deployment.yaml

      You receive the following output, indicating that the objects have been created:

      Output

      deployment.apps/app created service/app created deployment.apps/database created service/database created persistentvolumeclaim/db-data created configmap/env created deployment.apps/redis created service/redis created secret/database-secret created deployment.apps/sidekiq created

      To check that your Pods are running, type:

      You don’t need to specify a Namespace here, since we have created our objects in the default Namespace. If you are working with multiple Namespaces, be sure to include the -n flag when running this kubectl create command, along with the name of your Namespace.

      You will see output similar to the following while your database container is starting (the status will be either Pending or ContainerCreating):

      Output

      NAME READY STATUS RESTARTS AGE app-854d645fb9-9hv7w 1/1 Running 0 23s database-c77d55fbb-bmfm8 0/1 Pending 0 23s redis-7d65467b4d-9hcxk 1/1 Running 0 23s sidekiq-867f6c9c57-mcwks 1/1 Running 0 23s

      Once the database container is started, you will have output like this:

      Output

      NAME READY STATUS RESTARTS AGE app-854d645fb9-9hv7w 1/1 Running 0 30s database-c77d55fbb-bmfm8 1/1 Running 0 30s redis-7d65467b4d-9hcxk 1/1 Running 0 30s sidekiq-867f6c9c57-mcwks 1/1 Running 0 30s

      The Running STATUS indicates that your Pods are bound to nodes and that the containers associated with those Pods are running. READY indicates how many containers in a Pod are running. For more information, please consult the documentation on Pod lifecycles.

      Note:
      If you see unexpected phases in the STATUS column, remember that you can troubleshoot your Pods with the following commands:

      • kubectl describe pods your_pod
      • kubectl logs your_pod

      Now that your application is up and running, the last step that is required is to run Rails’ database migrations. This step will load a schema into the PostgreSQL database for the demo application.

      To run pending migrations you’ll exec into the running application pod and then call the rake db:migrate command.

      First, find the name of the application pod with the following command:

      Find the pod that corresponds to your application like the highlighted pod name in the following output:

      Output

      NAME READY STATUS RESTARTS AGE app-854d645fb9-9hv7w 1/1 Running 0 30s database-c77d55fbb-bmfm8 1/1 Running 0 30s redis-7d65467b4d-9hcxk 1/1 Running 0 30s sidekiq-867f6c9c57-mcwks 1/1 Running 0 30s

      With that pod name noted down, you can now run the kubectl exec command to complete the database migration step.

      Run the migrations with this command:

      • kubectl exec your_app_pod_name -- rake db:migrate

      You should receive output similar to the following, which indicates that the database schema has been loaded:

      Output

      == 20190927142853 CreateSharks: migrating ===================================== -- create_table(:sharks) -> 0.0190s == 20190927142853 CreateSharks: migrated (0.0208s) ============================ == 20190927143639 CreatePosts: migrating ====================================== -- create_table(:posts) -> 0.0398s == 20190927143639 CreatePosts: migrated (0.0421s) ============================= == 20191120132043 CreateEndangereds: migrating ================================ -- create_table(:endangereds) -> 0.8359s == 20191120132043 CreateEndangereds: migrated (0.8367s) =======================

      With your containers running and data loaded, you can now access the application. To get the IP for the app LoadBalancer, type:

      You will receive output like the following:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE app LoadBalancer 10.245.73.142 your_lb_ip 3000:31186/TCP 21m database ClusterIP 10.245.155.87 <none> 5432/TCP 21m kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 21m redis ClusterIP 10.245.119.67 <none> 6379/TCP 21m

      The EXTERNAL_IP associated with the app service is the IP address where you can access the application. If you see a <pending> status in the EXTERNAL_IP column, this means that your load balancer is still being created.

      Once you see an IP in that column, navigate to it in your browser: http://your_lb_ip:3000.

      You should see the following landing page:

      Application Landing Page

      Click on the Get Shark Info button. You will have a page with a button to create a new shark:

      Shark Info Form

      Click it and when prompted, enter the username and password from earlier in the tutorial series. If you did not change these values then the defaults are sammy and shark respectively.

      In the form, add a shark of your choosing. To demonstrate, we will add Megalodon Shark to the Shark Name field, and Ancient to the Shark Character field:

      Filled Shark Form

      Click on the Submit button. You will see a page with this shark information displayed back to you:

      Shark Output

      You now have a single instance setup of a Rails application with a PostgreSQL database running on a Kubernetes cluster. You also have a Redis cache and a Sidekiq worker to process data that users submit.

      Conclusion

      The files you have created in this tutorial are a good starting point to build from as you move toward production. As you develop your application, you can work on implementing the following:



      Source link

      How To Bootstrap a New Laravel Application with Docker Compose



      Part of the Series:
      How To Build a Links Landing Page in PHP with Laravel and Docker Compose

      Laravel is an open-source PHP framework that provides a set of tools and resources to build modern PHP applications. In this project-based tutorial series, you’ll build a Links Landing Page application with the Laravel framework, using a containerized PHP development environment managed by Docker Compose.

      At the end, you’ll have a one-page website built with Laravel and managed via Artisan commands where you can share relevant links to an audience on social channels and presentations.

      To get started, you’ll need to create a containerized environment able to execute PHP and Composer, the PHP dependency management tool. Then, you’ll be able to bootstrap the new Laravel application from scratch, without the need to have a local PHP environment installed on your local machine or development server.

      In this guide, we’ll provide streamlined instructions on how to set this environment up based on our tutorial on How To Install Laravel with Docker Compose on Ubuntu 20.04. Please refer to that tutorial for more detailed instructions on each of the options used within the Docker Compose file that will be provided in this guide.

      Create a new directory for your application in your home folder:

      • mkdir ~/landing-laravel
      • cd ~/landing-laravel

      Next, you’ll create the docker-compose.yml file that will define the containerized environment. In this file, you’ll set up a service named app, which will be based on a custom Docker image built with a Dockerfile you’ll set up later on.

      The build arguments user and uid, both defined in the docker-compose.yml file and used in the Dockerfile at build time, should be changed to reflect your own username and uid on your local machine or development server. To find out your current user’s uid, type:

      Output

      1000

      The user and uid variables will be available at build time and will be used in the Dockerfile to create a new user in the app service with the same username and uid as your current system user on your local machine or development server. This will avoid permission and ownership issues when working with application files both from the container as well as from the host that executes Docker.

      Create a new docker-compose.yml file using your text editor of choice. Here, we’re using nano:

      Copy the following content to this file, and don’t forget to replace the highlighted values with appropriate values depending on your own username and uid on the system that runs Docker:

      ~/landing-laravel/docker-compose.yml

      version: "3.7"
      services:
        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: landing-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - landing
      
      networks:
        landing:
          driver: bridge
      

      Save and close the file when you are done. If you are using nano, you can do that by pressing CTRL+X, then Y and ENTER to confirm.

      Next, you’ll set up the Dockerfile that is referenced in the docker-compose.yml file, which will set up a custom image for the app service:

      This Dockerfile extends from the default php:7.4-fpm Docker image. It uses the user and uid variables to create a new user able to execute Artisan and Composer commands. It also installs a few PHP dependencies that are required by Laravel, and the Composer executable.

      Copy the following content to your Dockerfile:

      ~/my-todo-list/Dockerfile

      FROM php:7.4-fpm
      
      # Arguments defined in docker-compose.yml
      ARG user
      ARG uid
      
      # Install system dependencies
      RUN apt-get update && apt-get install -y 
          git 
          curl 
          libpng-dev 
          libonig-dev 
          libxml2-dev 
          zip 
          unzip
      
      # Clear cache
      RUN apt-get clean && rm -rf /var/lib/apt/lists/*
      
      # Install PHP extensions
      RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
      
      # Get latest Composer
      COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
      
      # Create system user to run Composer and Artisan Commands
      RUN useradd -G www-data,root -u $uid -d /home/$user $user
      RUN mkdir -p /home/$user/.composer && 
          chown -R $user:$user /home/$user
      
      # Set working directory
      WORKDIR /var/www
      
      USER $user
      

      Save and close the file when you’re done. Next, you can bring your environment up with:

      This command will execute Docker Compose in detached mode, which means it will run in the background. The first time you bring an environment up with a custom image, Docker Compose will automatically build the image for you before creating the required containers. This might take a few moments to finish. You’ll see output similar to this:

      Output

      Creating network "landing-laravel_landing" with driver "bridge" Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a ... Step 10/11 : WORKDIR /var/www ---> Using cache ---> 769afd5d44d8 Step 11/11 : USER $user ---> Using cache ---> 841eb5852b69 Successfully built 841eb5852b69 Successfully tagged landing-app:latest WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating landing-laravel_app_1 ... done

      You can verify that your environment is up and running with:

      Output

      Name Command State Ports ------------------------------------------------------------------------ landing-laravel_app_1 docker-php-entrypoint php-fpm Up 9000/tcp

      Once the app service is up, you can run Composer, the PHP dependency management tool, to bootstrap a new Laravel application. In order to do that, you’ll use docker compose exec to run commands on the app service, where PHP is installed.

      The following command will use Docker Compose to execute composer create-project, which will bootstrap a fresh installation of Laravel based on the laravel/laravel package:

      • docker-compose exec app composer create-project laravel/laravel --prefer-dist application
      Creating a "laravel/laravel" project at "./application"
      Installing laravel/laravel (v8.4.0)
        - Downloading laravel/laravel (v8.4.0)
        - Installing laravel/laravel (v8.4.0): Extracting archive
      Created project in /var/www/application
      > @php -r "file_exists('.env') || copy('.env.example', '.env');"
      Loading composer repositories with package information
      Updating dependencies
      Lock file operations: 104 installs, 0 updates, 0 removals
      …
      Package manifest generated successfully.
      71 packages you are using are looking for funding.
      Use the `composer fund` command to find out more!
      > @php artisan key:generate --ansi
      Application key set successfully.
      

      This installation creates a new .env file based on the default .env.example file that comes with Laravel. The .env file contains database credentials and other sensitive application settings, and should be unique per environment where the app runs. You’ll come back to edit this file after you finish setting up the development environment.

      Next, copy the application files to the same directory as the docker-compose.yml file, so that you can share Laravel’s environment variables file with Docker Compose. Then, you can remove the application directory created by Composer:

      cp -rT application .
      rm -rfv application
      

      Your application is now bootstrapped, but you’ll need to include a couple services in the Docker Compose file in order to be able to access the app from a browser. An nginx service will serve the application using the Nginx web server, and a db service will host the application’s MySQL database.

      First, bring your environment down with:

      Output

      Stopping landing-laravel_app_1 ... done Removing landing-laravel_app_1 ... done Removing network landing-laravel_landing

      This will remove all containers and networks associated with this environment. Before editing your docker-compose.yml file to add the new services, create a new directory to share configuration files with containers. You’ll need this to properly set up Nginx to handle the Laravel PHP application.

      • mkdir -p docker-compose/nginx

      Next, create a new landing-laravel.conf file containing a custom Nginx server block. Later on, you’ll set up a volume to share this file within the nginx service container.

      Open a new Nginx configuration file with:

      • nano docker-compose/nginx/landing-laravel.conf

      The following server block configures Nginx to serve a Laravel application using an external service (app) to handle PHP code. Copy this content to your own Nginx configuration file:

      docker-compose/nginx/landing-laravel.conf

      server {
          listen 80;
          index index.php index.html;
          error_log  /var/log/nginx/error.log;
          access_log /var/log/nginx/access.log;
          root /var/www/public;
          location ~ .php$ {
              try_files $uri =404;
              fastcgi_split_path_info ^(.+.php)(/.+)$;
              fastcgi_pass app:9000;
              fastcgi_index index.php;
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_param PATH_INFO $fastcgi_path_info;
          }
          location / {
              try_files $uri $uri/ /index.php?$query_string;
              gzip_static on;
          }
      }
      

      Save and close the file when you’re done.

      Next, open your docker-compose.yml file:

      Include the following configuration for the nginx service, at the same level as the previously configured app service. This will create a new service based on the nginx:alpine image, and all requests on port 8000 of the host where Docker is running will be redirected to port 80 in the service container. In addition to the application files, you’ll also share a volume containing Nginx’s configuration file for a Laravel application:

        nginx:
          image: nginx:alpine
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d/
          networks:
            - landing
      
      

      Then, include the following configuration block for the db service. This will create a service based on the default MySQL 8 image, and pull in the values defined in Laravel’s environment file to set up database access:

        db:
          image: mysql:8
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
          networks:
            - landing
      

      This is how your updated docker-compose.yml file should look like once you’re finished:

      ~/landing-laravel/docker-compose.yml

      version: "3.7"
      services:
        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: landing-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - landing
      
        nginx:
          image: nginx:alpine
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d/
          networks:
            - landing
        db:
          image: mysql:8
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
          networks:
            - landing
      
      networks:
        landing:
          driver: bridge
      
      

      Note: for more detailed information about containerizing Laravel environments, including explanations about shared volumes and networks, please refer to our full guide on How To Install Laravel with Docker Compose on Ubuntu 20.04.

      Save and close the file when you’re done editing. Lastly, update your Laravel dot env file (.env) to point the MySQL database host configuration to the host where the MySQL service will be running, called db:

      The .env file that is automatically generated by Composer upon installation comes with some default values that you might want to change, such as the APP_NAME and the APP_URL. The database DB_HOST variable must be changed to point to the service where MySQL will be running, and you can reference it by its service name, as defined in the docker-compose.yml file. In this example, we’ve used db as name for the database service, so this will be available in the containerized network as a host named db.

      Change your .env accordingly, using the following example as base. The highlighted values were updated here to reflect the state of the application under development:

      ~/landing-laravel/.env

      APP_NAME=LandingLaravel
      APP_ENV=local
      APP_KEY=base64:ffYPNP8kPeQDf8gE/qh3kWjk59p6gFY66kCKhhKUa2w=
      APP_DEBUG=true
      APP_URL=http://localhost:8000
      
      LOG_CHANNEL=stack
      LOG_LEVEL=debug
      
      DB_CONNECTION=mysql
      DB_HOST=db
      DB_PORT=3306
      DB_DATABASE=landing-db
      DB_USERNAME=landing-user
      DB_PASSWORD=dev-password
      
      ...
      

      You don’t need to change any other sections of this file, but feel free to tweak to your specific use case.

      Save and close the file when you’re done editing its contents.

      You can now bring the updated environment up with:

      Output

      Creating network "landing-laravel_landing" with driver "bridge" Creating landing-laravel_app_1 ... done Creating landing-laravel_db_1 ... done Creating landing-laravel_nginx_1 ... done

      With the full environment up, you can now point your browser to localhost or your remote server’s IP address, on port 8000:

      http://localhost:8000
      

      If everything works as expected, you’ll see a page like this:

      Laravel Landing Links - basic app

      In the next part of this series, you’ll create a database migration to set up a links table.



      Source link

      Cara Menginstal dan Menyiapkan Laravel dengan Docker Compose pada Ubuntu 20.04


      Pengantar

      Kontainerisasi aplikasi mengacu pada suatu proses adaptasi aplikasi dan komponennya dengan tujuan agar dapat menjalankannya dalam lingkungan ringan yang dikenal sebagai kontainer. Lingkungan tersebut terisolasi dan dapat dibuang, serta dapat dimanfaatkan lebih lanjut untuk pengembangan, pengujian, dan penyebaran aplikasi hingga produksi.

      Dalam panduan ini, kita akan menggunakan Docker Compose untuk melakukan kontainerisasi aplikasi Laravel sebagai pengembangan. Ketika selesai, Anda akan memiliki aplikasi Laravel demo yang berjalan di tiga kontainer layanan terpisah:

      • Layanan app yang menjalankan PHP7.4-FPM;
      • Layanan db yang menjalankan MySQL 5.7;
      • Layanan nginx yang menggunakan layanan app untuk mengurai kode PHP sebelum menyajikan aplikasi Laravel ke pengguna akhir.

      Untuk mengizinkan proses pengembangan yang lebih efisien dan memfasilitasi pengawakutuan aplikasi, kita akan membuat berkas aplikasi tetap sinkron menggunakan volume bersama. Kita juga akan melihat cara menggunakan perintah docker-compose exec untuk menjalankan Composer dan Artisan di kontainer app.

      Prasyarat

      Langkah 1 — Memperoleh Aplikasi Demo

      Untuk memulai, kita akan mengambil aplikasi Laravel demo dari repositori Github. Kita tertarik dengan cabang tutorial-01, yang mengandung aplikasi Laravel dasar yang telah kita ciptakan dalam panduan pertama dari seri ini.

      Untuk memperoleh kode aplikasi yang kompatibel dengan tutorial ini, unduh rilis tutorial-1.0.1 ke direktori rumah dengan:

      • cd ~
      • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

      Kita akan memerlukan perintah unzip untuk membongkar kode aplikasi. Jika Anda belum menginstal paket ini sebelumnya, lakukan sekarang dengan:

      • sudo apt update
      • sudo apt install unzip

      Sekarang, lakukan unzip konten aplikasi dan ganti nama direktori yang belum dibongkar untuk memudahkan akses:

      • unzip travellist.zip
      • mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

      Bernavigasilah ke direktori travellist-demo:

      Dalam langkah selanjutnya, kita akan menciptakan berkas konfigurasi .env untuk menyiapkan aplikasi.

      Langkah 2 — Menyiapkan Berkas .env Aplikasi

      Berkas konfigurasi Laravel terletak di dalam direktori yang bernama config, di dalam direktori root aplikasi. Sebagai tambahan, berkas .env digunakan untuk menyiapkan konfigurasi dependen lingkungan, seperti kredensial dan informasi apa pun yang mungkin bervariasi di antara penyebaran. Berkas ini tidak disertakan dalam kontrol revisi.

      Peringatan: Berkas konfigurasi lingkungan berisi informasi sensitif tentang server Anda, termasuk kredensial basis data dan kunci keamanan. Dengan alasan itu, Anda jangan pernah membagikan berkas ini secara publik.

      Nilai-nilai yang terkandung di dalam berkas .env akan lebih diutamakan daripada nilai-nilai yang ditetapkan dalam berkas konfigurasi reguler yang terletak di direktori config. Setiap instalasi pada lingkungan baru membutuhkan berkas lingkungan yang dibuat khusus untuk menentukan hal-hal seperti pengaturan koneksi basis data, opsi awakutu, URL aplikasi, di antara hal-hal lainnya yang mungkin dapat bervariasi tergantung pada lingkungan aplikasi yang berjalan.

      Sekarang kita akan menciptakan berkas .env baru untuk menyesuaikan opsi konfigurasi lingkungan pengembangan yang kita siapkan. Laravel disertakan dengan berkas .env contoh yang dapat kita salin untuk menciptakan berkas sendiri:

      Buka berkas ini menggunakan nano atau editor teks pilihan Anda:

      Berkas .env saat ini dari aplikasi demo travellist berisi pengaturan untuk menggunakan basis data MySQL lokal, dengan 127.0.0.1 sebagai hos basis data. Kita perlu memperbarui variabel DB_HOST, sehingga mengarah ke layanan basis data yang akan kita ciptakan dalam lingkungan Docker. Dalam panduan ini, kita akan menyebut layanan basis data kita db. Lanjutkan dan ganti nilai yang tercantum di DB_HOST dengan nama layanan basis data:

      .env

      APP_NAME=Travellist
      APP_ENV=dev
      APP_KEY=
      APP_DEBUG=true
      APP_URL=http://localhost:8000
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=db
      DB_PORT=3306
      DB_DATABASE=travellist
      DB_USERNAME=travellist_user
      DB_PASSWORD=password
      ...
      

      Silakan mengubah nama basis data, nama pengguna, dan kata sandi, jika Anda ingin. Variabel-variabel ini akan digunakan dalam langkah selanjutnya saat kita akan menyiapkan berkas docker-compose.yml untuk mengonfigurasi layanan kita.

      Simpan berkas itu saat Anda selesai mengedit. Jika menggunakan nano, Anda dapat melakukannya dengan menekan Ctrl+x, lalu Y, dan Enter untuk mengonfirmasi.

      Langkah 3 — Menyiapkan Dockerfile Aplikasi

      Meskipun kedua layanan MySQL dan Nginx akan berdasarkan citra asali yang diperoleh dari Docker Hub, kita tetap perlu membangun citra khusus untuk kontainer aplikasi. Kita akan menciptakan Dockerfile yang baru untuk itu.

      Citra travellist kita akan berdasarkan citra PHP resmi php:7.4-fpm dari Docker Hub. Selain lingkungan PHP-FPM dasar tersebut, kita akan menginstal beberapa modul PHP tambahan dan alat manajemen dependensi Composer.

      Kita juga akan menciptakan pengguna sistem baru; ini diperlukan untuk mengeksekusi perintah artisan dan composer ketika mengembangkan aplikasi. Pengaturan uid memastikan bahwa pengguna di dalam kontainer memiliki uid yang sama seperti pengguna sistem di mesin hos, tempat Anda menjalankan Docker. Dengan cara ini, berkas apa pun yang diciptakan oleh perintah-perintah ini direplikasi di dalam hos dengan izin yang benar. Ini juga berarti Anda dapat menggunakan editor kode pilihan Anda di mesin hos untuk mengembangkan aplikasi yang berjalan di dalam kontainer.

      Buat Dockerfile yang baru dengan:

      Salin konten berikut ke Dockerfile Anda:

      Dockerfile

      FROM php:7.4-fpm
      
      # Arguments defined in docker-compose.yml
      ARG user
      ARG uid
      
      # Install system dependencies
      RUN apt-get update && apt-get install -y 
          git 
          curl 
          libpng-dev 
          libonig-dev 
          libxml2-dev 
          zip 
          unzip
      
      # Clear cache
      RUN apt-get clean && rm -rf /var/lib/apt/lists/*
      
      # Install PHP extensions
      RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
      
      # Get latest Composer
      COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
      
      # Create system user to run Composer and Artisan Commands
      RUN useradd -G www-data,root -u $uid -d /home/$user $user
      RUN mkdir -p /home/$user/.composer && 
          chown -R $user:$user /home/$user
      
      # Set working directory
      WORKDIR /var/www
      
      USER $user
      
      

      Jangan lupa simpan berkas itu saat Anda selesai.

      Dockerfile dimulai dengan menentukan citra dasar yang kita gunakan: php:7.4-fpm.

      Setelah menginstal paket sistem dan ekstensi PHP, kita menginstal Composer dengan menyalin perintah composer yang dapat dieksekusi dari citra resmi terbarunya ke citra aplikasi kita.

      Pengguna sistem baru kemudian diciptakan dan disiapkan dengan menggunakan argumen user dan uid yang dinyatakan di awal Dockerfile. Nilai-nilai ini akan dimasukkan oleh Docker Compose saat pembangunan.

      Terakhir, kita mengatur dir kerja asali sebagai /var/www dan mengganti ke pengguna yang baru diciptakan. Ini akan memastikan Anda terhubung sebagai pengguna reguler, dan berada di direktori yang tepat, saat menjalankan perintah composer dan artisan di kontainer aplikasi.

      Langkah 4 — Menyiapkan Konfigurasi Nginx dan Berkas Buangan Basis Data

      Ketika menciptakan lingkungan pengembangan dengan Docker Compose, seringkali diperlukan untuk berbagi berkas konfigurasi atau inisialisasi dengan kontainer layanan, yang bertujuan menyiapkan atau melakukan bootstrap terhadap layanan tersebut. Praktik ini memfasilitasi pembuatan perubahan terhadap berkas konfigurasi untuk menyetel lingkungan saat Anda mengembangkan aplikasi.

      Sekarang kita akan menyiapkan folder dengan berkas yang akan digunakan untuk mengonfigurasi dan menginisialisasi kontainer layanan.

      Untuk menyiapkan Nginx, kita akan berbagi berkas travellist.conf yang akan mengonfigurasi cara penyajian aplikasi. Buat folder docker-compose/nginx dengan:

      • mkdir -p docker-compose/nginx

      Buka berkas baru yang bernama travellist.conf di dalam direktori itu:

      • nano docker-compose/nginx/travellist.conf

      Salin konfigurasi Nginx berikut ke berkas itu:

      docker-compose/nginx/travellist.conf

      
      server {
          listen 80;
          index index.php index.html;
          error_log  /var/log/nginx/error.log;
          access_log /var/log/nginx/access.log;
          root /var/www/public;
          location ~ .php$ {
              try_files $uri =404;
              fastcgi_split_path_info ^(.+.php)(/.+)$;
              fastcgi_pass app:9000;
              fastcgi_index index.php;
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_param PATH_INFO $fastcgi_path_info;
          }
          location / {
              try_files $uri $uri/ /index.php?$query_string;
              gzip_static on;
          }
      }
      

      Berkas ini akan mengonfigurasi Nginx untuk mendengarkan porta 80 dan menggunakan index.php sebagai laman indeks asali. Ini akan menetapkan root dokumen menjadi /var/www/public, lalu mengonfigurasi Nginx untuk menggunakan layanan app pada porta 9000 yang memproses berkas *.php.

      Simpan dan tutup berkas setelah Anda selesai mengedit.

      Untuk menyiapkan basis data MySQL, kita akan berbagi buangan basis data yang akan diimpor saat kontainer diinisialisasi. Ini adalah fitur yang yang disediakan oleh citra MySQL 5.7 yang akan kita gunakan pada kontainer itu.

      Buat folder baru untuk berkas inisialisasi MySQL Anda di dalam folder docker-compose:

      • mkdir docker-compose/mysql

      Buka berkas .sql yang baru:

      • nano docker-compose/mysql/init_db.sql

      Buangan MySQL berikut ini berdasarkan basis data yang telah kita siapkan dalam panduan Laravel pada LEMP dari kami. Ini akan menciptakan tabel baru yang bernama places. Lalu, tabel akan terisi dengan serangkaian tempat sampel.

      Tambahkan kode berikut ke berkas:

      docker-compose/mysql/db_init.sql

      DROP TABLE IF EXISTS `places`;
      
      CREATE TABLE `places` (
        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
        `visited` tinyint(1) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
      
      INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);
      

      Tabel places berisi tiga bidang: id, name, dan visited. Bidang visited adalah bendera yang digunakan untuk mengidentifikasi tempat-tempat yang masih harus* dikunjungi*. Silakan ganti tempat sampel atau masukkan tempat yang baru. Simpan dan tutup berkas setelah Anda selesai.

      Kita telah selesai menyiapkan Dockerfile aplikasi dan berkas konfigurasi layanan. Selanjutnya, kita akan menyiapkan Docker Compose untuk menggunakan berkas-berkas ini saat menciptakan layanan kita.

      Langkah 5 — Menciptakan Lingkungan Multi-Kontainer dengan Docker Compose

      Docker Compose memungkinkan Anda menciptakan lingkungan multi-kontainer untuk aplikasi yang berjalan pada Docker. Ini menggunakan definisi layanan untuk sepenuhnya membangun lingkungan yang dapat disesuaikan dengan beberapa kontainer yang dapat berbagi volume data dan jaringan. Ini memungkinkan integrasi tanpa hambatan di antara komponen aplikasi.

      Untuk menyiapkan definisi layanan, kita akan menciptakan berkas baru yang bernama docker-compose.yml. Biasanya, berkas ini berada di root dari folder aplikasi, dan mendefinisikan lingkungan terkontainerisasi, termasuk citra dasar yang Anda gunakan untuk membangun kontainer, dan bagaimana layanan Anda berinteraksi.

      Kita akan mendefinisikan tiga layanan berbeda dalam berkas docker-compose.yml: app, db, dan nginx.

      Layanan app akan membangun citra yang disebut travellist, berdasarkan Dockerfile yang kita ciptakan sebelumnya. Kontainer yang didefinisikan oleh layanan ini akan menjalankan server php-fpm untuk mengurai kode PHP dan mengirim hasilnya kembali ke layanan nginx, yang akan berjalan pada kontainer terpisah. Layanan mysql mendefinisikan kontainer yang menjalankan server MySQL 5.7. Layanan kita akan berbagi jaringan jembatan bernama travellist.

      Berkas aplikasi akan disinkronkan baik pada layanan app dan nginx melalui bind mounts. Bind mounts berguna dalam lingkungan pengembangan karena mengizinkan sinkronisasi dua arah dengan performa yang baik antara mesin hos dan kontainer.

      Buat berkas docker-compose.yml baru di root dari folder aplikasi:

      Berkas docker-compose.yml khusus dimulai dengan definisi versi, diikuti oleh node services, yang mendefinisikan semua layanan. Jaringan bersama biasanya didefinisikan di bagian bawah berkas itu.

      Untuk memulai, salin kode boilerplate ini ke berkas docker-compose.yml:

      docker-compose.yml

      version: "3.7"
      services:
      
      
      networks:
        travellist:
          driver: bridge
      

      Sekarang kita akan mengedit node services untuk menyertakan layanan app, db, dan nginx.

      Layanan app

      Layanan app akan menyiapkan kontainer bernama travellist-app. Ini membangun citra Docker baru berdasarkan Dockerfile yang terletak di jalur yang sama seperti berkas docker-compose.yml. Citra baru akan disimpan secara lokal dengan nama travellist.

      Meskipun root dokumen yang disajikan sebagai aplikasi berada di dalam kontainer nginx, kita juga memerlukan berkas aplikasi di suatu tempat di dalam kontainer app, sehingga kita dapat melaksanakan tugas baris perintah dengan alat Laravel Artisan.

      Salin definisi layanan berikut di bawah node services, di dalam berkas docker-compose.yml:

      docker-compose.yml

        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      

      Pengaturan ini melakukan yang berikut:

      • build: Konfigurasi ini memberi tahu Docker Compose untuk membangun citra lokal layanan app, menggunakan jalur yang ditentukan (context) dan Dockerfile untuk instruksi. Argumen user dan uid dimasukkan ke Dockerfile untuk menyesuaikan perintah penciptaan pengguna saat pembangunan.
      • image: Nama yang akan digunakan untuk citra yang sedang dibangun.
      • container_name: Menyiapkan nama kontainer untuk layanan ini.
      • restart: Selalu mulai ulang, kecuali layanan dihentikan.
      • working_dir: Menetapkan direktori asali untuk layanan ini sebagai /var/www.
      • volumes: Menciptakan volume bersama yang akan menyinkronkan konten dari direktori saat ini dengan /var/www di dalam kontainer. Perhatikan bahwa ini bukan root dokumen Anda, karena itu akan hidup di dalam kontainer nginx.
      • networks: Menyiapkan layanan ini untuk menggunakan jaringan yang bernama travellist.

      Layanan db

      Layanan db menggunakan citra MySQL 5.7 yang telah disiapkan sebelumnya dari Docker Hub. Karena Docker Compose secara otomatis memuat berkas variabel .env yang terletak di dalam direktori yang sama dengan berkas docker-compose.yml, kita dapat memperoleh pengaturan basis data dari berkas .env Laravel yang kita ciptakan dalam langkah sebelumnya.

      Sertakan definisi layanan berikut di node services Anda, tepat setelah layanan app:

      docker-compose.yml

        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      

      Pengaturan ini melakukan yang berikut:

      • image: Menetapkan citra Docker yang harus digunakan untuk kontainer ini. Dalam kasus ini, kita menggunakan citra MySQL 5.7 dari Docker Hub.
      • container_name: Menyiapkan nama kontainer untuk layanan ini: travellist-db.
      • restart: Selalu mulai ulang layanan ini, kecuali jika layanan secara eksplisit dihentikan.
      • environment: Menetapkan variabel lingkungan di dalam kontainer baru. Kita menggunakan nilai yang diperoleh dari berkas .env Laravel untuk menyiapkan layanan MySQL, yang secara otomatis akan menciptakan pengguna dan basis data baru berdasarkan variabel lingkungan yang disediakan.
      • volumes: Menciptakan volume untuk berbagi buangan basis data .sql yang akan digunakan untuk menginisialisasi basis data aplikasi. Citra MySQL akan secara otomatis mengimpor berkas .sql yang ditempatkan di direktori /docker-entrypoint-initdb.d di dalam kontainer.
      • networks: Menyiapkan layanan ini untuk menggunakan jaringan yang bernama travellist.

      Layanan nginx

      Layanan nginx menggunakan citra Nginx yang telah disiapkan sebelumnya di atas Alpine, distribusi Linux yang ringan. Ini menciptakan kontainer bernama travellist-nginx, dan menggunakan definisi ports untuk menciptakan pengarahan ulang dari porta 8000 pada sistem hos ke porta 80 di dalam kontainer.

      Sertakan definisi layanan berikut di node services, tepat setelah layanan db:

      docker-compose.yml

        nginx:
          image: nginx:1.17-alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d
          networks:
            - travellist
      

      Pengaturan ini melakukan yang berikut:

      • image: Menetapkan citra Docker yang harus digunakan untuk kontainer ini. Dalam kasus ini, kita menggunakan citra Alpine Nginx 1.17.
      • container_name: Menyiapkan nama kontainer untuk layanan ini: travellist-nginx.
      • restart: Selalu mulai ulang layanan ini, kecuali jika layanan secara eksplisit dihentikan.
      • ports: Menyiapkan pengarahan ulang porta yang akan mengizinkan akses eksternal melalui porta 8000 ke server web yang berjalan di porta 80 di dalam kontainer.
      • volumes: Menciptakan dua volume bersama. Yang pertama akan melakukan sinkronisasi konten dari direktori saat ini dengan /var/www di dalam kontainer. Dengan cara ini, saat Anda melakukan perubahan lokal pada berkas aplikasi, itu akan segera tercermin dengan cepat di aplikasi yang disajikan oleh Nginx di dalam kontainer. Volume kedua akan memastikan berkas konfigurasi Nginx, yang terletak di docker-compose/nginx/travellist.conf, disalin ke folder konfigurasi Nginx di dalam kontainer.
      • networks: Menyiapkan layanan ini untuk menggunakan jaringan bernama travellist.

      Berkas docker-compose.yml yang Sudah Selesai

      Berkas docker-compose.yml yang sudah selesai akan terlihat seperti ini:

      docker-compose.yml

      version: "3.7"
      services:
        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      
        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      
        nginx:
          image: nginx:alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d/
          networks:
            - travellist
      
      networks:
        travellist:
          driver: bridge
      

      Pastikan menyimpan berkas itu saat Anda selesai.

      Langkah 6 — Menjalankan Aplikasi dengan Docker Compose

      Sekarang kita akan menggunakan perintah docker-compose untuk membangun citra aplikasi dan menjalankan layanan yang kita tetapkan dalam penyiapan.

      Bangun citra app dengan perintah berikut:

      Perintah ini mungkin membutuhkan waktu beberapa menit untuk selesai. Anda akan melihat keluaran yang serupa dengan ini:

      Output

      Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a Step 2/11 : ARG user ---> Running in f71eb33b7459 Removing intermediate container f71eb33b7459 ---> 533c30216f34 Step 3/11 : ARG uid ---> Running in 60d2d2a84cda Removing intermediate container 60d2d2a84cda ---> 497fbf904605 Step 4/11 : RUN apt-get update && apt-get install -y git curl libpng-dev libonig-dev ... Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer ---> e499f74896e3 Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user ---> Running in 232ef9c7dbd1 Removing intermediate container 232ef9c7dbd1 ---> 870fa3220ffa Step 9/11 : RUN mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user ---> Running in 7ca8c0cb7f09 Removing intermediate container 7ca8c0cb7f09 ---> 3d2ef9519a8e Step 10/11 : WORKDIR /var/www ---> Running in 4a964f91edfa Removing intermediate container 4a964f91edfa ---> 00ada639da21 Step 11/11 : USER $user ---> Running in 9f8e874fede9 Removing intermediate container 9f8e874fede9 ---> fe176ff4702b Successfully built fe176ff4702b Successfully tagged travellist:latest

      Ketika proses pembangunan selesai, Anda dapat menjalankan lingkungan dalam mode latar belakang dengan:

      Output

      Creating travellist-db ... done Creating travellist-app ... done Creating travellist-nginx ... done

      Ini akan menjalankan kontainer Anda di latar belakang. Untuk menunjukkan informasi tentang kondisi layanan aktif, jalankan:

      Anda akan melihat keluaran seperti ini:

      Output

      Name Command State Ports -------------------------------------------------------------------------------- travellist-app docker-php-entrypoint php-fpm Up 9000/tcp travellist-db docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp travellist-nginx /docker-entrypoint.sh ngin ... Up 0.0.0.0:8000->80/tcp

      Lingkungan Anda kini sudah aktif dan berjalan, tetapi kita masih perlu mengeksekusi beberapa perintah untuk menyelesaikan pengaturan aplikasi. Anda dapat menggunakan perintah docker-compose exec untuk mengeksekusi perintah di dalam kontainer layanan, seperti ls -l untuk menampilkan informasi detail tentang berkas di dalam direktori aplikasi:

      • docker-compose exec app ls -l

      Output

      total 260 -rw-rw-r-- 1 sammy sammy 737 Jun 9 11:19 Dockerfile -rw-rw-r-- 1 sammy sammy 101 Jan 7 08:05 README.md drwxrwxr-x 6 sammy sammy 4096 Jan 7 08:05 app -rwxr-xr-x 1 sammy sammy 1686 Jan 7 08:05 artisan drwxrwxr-x 3 sammy sammy 4096 Jan 7 08:05 bootstrap -rw-rw-r-- 1 sammy sammy 1501 Jan 7 08:05 composer.json -rw-rw-r-- 1 sammy sammy 179071 Jan 7 08:05 composer.lock drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 config drwxrwxr-x 5 sammy sammy 4096 Jan 7 08:05 database drwxrwxr-x 4 sammy sammy 4096 Jun 9 11:19 docker-compose -rw-rw-r-- 1 sammy sammy 965 Jun 9 11:27 docker-compose.yml -rw-rw-r-- 1 sammy sammy 1013 Jan 7 08:05 package.json -rw-rw-r-- 1 sammy sammy 1405 Jan 7 08:05 phpunit.xml drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 public -rw-rw-r-- 1 sammy sammy 273 Jan 7 08:05 readme.md drwxrwxr-x 6 sammy sammy 4096 Jan 7 08:05 resources drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 routes -rw-rw-r-- 1 sammy sammy 563 Jan 7 08:05 server.php drwxrwxr-x 5 sammy sammy 4096 Jan 7 08:05 storage drwxrwxr-x 4 sammy sammy 4096 Jan 7 08:05 tests drwxrwxr-x 41 sammy sammy 4096 Jun 9 11:32 vendor -rw-rw-r-- 1 sammy sammy 538 Jan 7 08:05 webpack.mix.js

      Sekarang kita akan menjalankan composer install untuk menginstal dependensi aplikasi:

      • docker-compose exec app composer install

      Anda akan melihat keluaran seperti ini:

      Output

      Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 85 installs, 0 updates, 0 removals - Installing doctrine/inflector (1.3.1): Downloading (100%) - Installing doctrine/lexer (1.2.0): Downloading (100%) - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%) - Installing erusev/parsedown (1.7.4): Downloading (100%) - Installing symfony/polyfill-ctype (v1.13.1): Downloading (100%) - Installing phpoption/phpoption (1.7.2): Downloading (100%) - Installing vlucas/phpdotenv (v3.6.0): Downloading (100%) - Installing symfony/css-selector (v5.0.2): Downloading (100%) … Generating optimized autoload files > IlluminateFoundationComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.

      Hal terakhir yang perlu kita lakukan sebelum menguji aplikasi ini adalah menghasilkan kunci aplikasi unik dengan alat baris perintah Laravel artisan. Kunci ini digunakan untuk mengenkripsi sesi pengguna dan data sensitif lainnya:

      • docker-compose exec app php artisan key:generate

      Output

      Application key set successfully.

      Sekarang, buka peramban Anda dan akses nama domain server atau alamat IP Anda pada porta 8000:

      http://server_domain_or_IP:8000
      

      Catatan: Jika Anda menjalankan demo ini pada mesin lokal, gunakan http://localhost:8000 untuk mengakses aplikasi dari peramban Anda.

      Anda akan melihat sebuah laman seperti ini:

      Aplikasi Laravel Demo

      Anda dapat menggunakan perintah logs untuk memeriksa log yang dihasilkan oleh layanan Anda:

      • docker-compose logs nginx
      Attaching to travellist-nginx
      …
      travellist-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      travellist-nginx | /docker-entrypoint.sh: Configuration complete; ready for start up
      travellist-nginx | 192.168.0.1 - - [09/Jun/2020:11:46:34 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
      travellist-nginx | 192.168.0.1 - - [09/Jun/2020:11:46:35 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
      

      Jika Anda ingin menghentikan sementara lingkungan Docker Compose sembari tetap mempertahankan kondisi semua layanan, jalankan:

      Output

      Pausing travellist-db ... done Pausing travellist-nginx ... done Pausing travellist-app ... done

      Kemudian, Anda dapat melanjutkan layanan dengan:

      Output

      Unpausing travellist-app ... done Unpausing travellist-nginx ... done Unpausing travellist-db ... done

      Untuk mematikan lingkungan Docker Compose dan menghapus semua kontainer, jaringan, dan volumenya, jalankan:

      Output

      Stopping travellist-nginx ... done Stopping travellist-db ... done Stopping travellist-app ... done Removing travellist-nginx ... done Removing travellist-db ... done Removing travellist-app ... done Removing network travellist-laravel-demo_travellist

      Untuk ikhtisar dari semua perintah Docker Compose, lihat referensi baris perintah Docker Compose.

      Kesimpulan

      Dalam panduan ini, kita telah menyiapkan lingkungan Docker dengan tiga kontainer yang menggunakan Docker Compose untuk mendefinisikan infrastruktur dalam berkas YAML.

      Mulai dari sekarang, Anda dapat bekerja di aplikasi Laravel tanpa perlu menginstal dan menyiapkan server web lokal untuk pengembangan dan pengujian. Lebih jauh lagi, Anda akan bekerja dengan lingkungan yang dapat dibuang serta mudah direplikasi dan didistribusikan, yang dapat membantu saat mengembangkan aplikasi Anda dan menjelang pembuatan suatu lingkungan.



      Source link