Saturday, June 11, 2022

Docker-compose for Oracle APEX

 
Hello everyone and welcome back to our blog...


In the previous post, we showed you how to run an Oracle XE instance in a docker container and then run ORDS in another container to use it to connect to APEX. 

We first downloaded the Images we needed, then we had to write some codes in the console/Terminal to configure the networks and volumes and for each time we wanted to spin up a DB container or an ORDS container, we had to write a docker-console command and pass all the parameters. 

There is an easier way that we can use to run all the containers with the configurations in just one command, and that tool is docker-compose. In this post, we will show you how to use docker-compose and how to run APEX in docker just as we did last time.

In docker-compose, we use a YAML file to define the services we want to run. By services we mean containers, so each container is represented as a service in docker-compose. Under each service, we can list all the configurations that are related to that service. One advantage of using docker-compose is to make your commands structured, easy to read, and maintain or change. For example, see how the command for running a docker container looks like we you want to write it in the terminal:

    1
    2
    3
    4
    5
    6
    docker run -d --name db-container\
    >          -p 1521:1521\
    >          -e ORACLE_PWD=1230123\
    >          -v db-demo-volume:/opt/oracle/oradata\
    >          --network=demo-network\
    >          --hostname database
    >          oracle-xe-21.3

    It is not easy to read right :). Now let's see how docker-compose will help us make this more efficient. 

    For using docker-compose we just need to create a YAML file wherever we want, so let's create a folder on the desktop and name it demo. In that folder create a file with the name docker-compose.yml

    Oracle XE Service

    We will first define the Oracle XE service with its related configurations in our compose file:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    version: '3.9'
    
    services:
      devdb:
        container_name: devdb
        image: container-registry.oracle.com/database/express:latest
        ports: 
          - 1521:1521
        environment:
          - ORACLE_PWD=1230123
        volumes:
          - dev-vol:/opt/oracle/oradata
        hostname: oracledev
    

    Keep in mind that the first line of every docker-compose file is for specifying the Compose file format version. In our example, we are using version 3.9.

    • The third line is the services we want this file to run, by services we mean containers. Under the services, we will list the containers we want to run. 
    • devdb is the name for our first service (container), the name can be anything but you better give meaningful names. Under this service, we will list the configurations related to it.
    • container_name This is equivalent to the --name tag when using the terminal. 
    • image is the image to be used for spinning up the container 
    • ports to map the ports between the container and the host. The first port is the host port and the second specifies the container port. This is equivalent to -p.
    • environment under this we pass the environment variable to the container, in our example, we are passing the oracle_pwd. This is equivalent to -e (You can create an env file and save all environment variables in it).
    • volumes to specify the paths to mount on the host and the container. The first path is the host and the second is the containers' path. This is equivalent to -v (At the end of the file we will define our volumes). 
    • hostname giving a name to the DB host machine. This is equivalent to --hostname.
    As you see this looks much cleaner than before :). 

    ORDS Service

    We will continue by defining the next service in the document. But before we do that, we need the connection string to tell ORDS how to connect to the database. So, we will create a new folder where the compose file is located, name the folder variables and inside the folder create text file conn_string and write the following in it.
    CONN_STRING=sys/1230123@oracledev:1521/XEPDB1
    Now back to the compose file, under the services we continue by adding the following:

    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ords:
        container_name: ords
        restart: always
        depends_on:
          - devdb
        volumes:
          - ./variables:/opt/oracle/variables
        ports:
          - 8181:8181
        image: container-registry.oracle.com/database/ords:latest
    

    As you see, it is almost the same as the first service, we have changed the name of the service and container, ports, volume, and the image to use. We just added three lines 16, 17, and 18.

    • restart this will restart the service every time it fails. The database will take some time to be ready to use, in this time ORDS will try to connect to the database and it will fail, so we are retrying to connect every time until the connection is established.
    • depends_on we don't want ORDS service to start before the database service starts. So we are telling the compose that this service has to start after the devdb service. 

    Network and Volumes

    Now we will define the Networks and Volumes that we want to use. As we saw in line 12 of the compose file, we are mounting dev-vol volume to the path in the database container where the data are saved. In this way, we are saving the data on the host machine to prevent losing it when the container gets deleted. We will define this volume and give it a meaningful name, so we don't delete it later by mistake. Then we will define the network where the services will live, this is to make the communication between the services possible. Please note, if you use docker-compose, it will take care of the network configuration but it will give a random name, what we are doing is just giving a name to the network.
    In the same compose file add the following:

    24
    25
    26
    27
    28
    29
    30
    volumes:
      dev-vol:
        name: db-vol
        external: false
    networks:
      default:
        name: demo-network
    

    Final Compose file

    In the end, you should have a docker-compose file that looks like this:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    version: '3.9'
    
    services:
      devdb:
        container_name: devdb
        image: container-registry.oracle.com/database/express:latest
        ports: 
          - 1521:1521
        environment:
          - ORACLE_PWD=1230123
        volumes:
          - dev-vol:/opt/oracle/oradata
        hostname: oracledev
      ords:
        container_name: ords
        restart: always
        depends_on:
          - devdb
        volumes:
          - ./variables:/opt/oracle/variables
    ports: - 8282:8181 image: container-registry.oracle.com/database/ords:latest volumes: dev-vol: name: db-vol external: false networks: default: name: demo-network

    Keep in mind, that the indentation in the docker-compose file is important.

    Docker-compose commands

    Up

    After we have done with the compose file, it is time to run it. Open a command line in the same folder where the compose file is located and write the following:

    1
    docker-compose up  
    

    This command will take care of running the containers, mapping the ports, and configuring the volumes and networks. You will use this command every time you want to run the containers.

    Down

    This command will stop the running containers, removing them and remove the networks associated, but it will keep the volumes:

    1
    docker-compose down       

    Stop

    This command will stop the running containers without removing anything:

    1
    docker-compose stop       

    P.s: If you don't have the images locally, you have to log in to the oracle container registry before running the Up command. Execute the following command in the Terminal and give your credentials

    1
    docker login container-registry.oracle.com       


    So that's it for docker-compose, we hope you found this useful :).

    References:
    • https://docs.docker.com/compose/

    Labels: , , , ,

    Tuesday, June 7, 2022

    Running APEX in Docker container


    Hello everyone and welcome back to our blog...


    In this blog, we are going to show you how to install Oracle XE in a Docker container, then install ORDS and APEX. It is recommended that you have a basic knowledge of Docker and how it works.

    First of all, you need to install docker on your machine, you can easily download and install it from the docker homepage.

    Then you will need to sign up for a free account on the oracle container registry, from this registry we will pull all the docker images we will need later.

    After you have Docker installed and signed up, you can follow throw with us :).

    Starting Docker

    After you installed Docker, you need to start docker desktop, to be able to use the docker commands. 

    Docker Network

    We will configure a docker network, to allow communication between the DB and ORDS container. For creating a network, we simply run the following command in the Terminal or Command line:

    1
    docker network create demo-network
     
    Now, to list the networks you have, run the following command:

    1
    docker network ls

    Docker Volume

    When we run our DB in a docker container all data changes are saved in the container, meaning we will lose all the data when the container gets deleted. To store the data independent from the container we can attach a volume. In this case, the data is saved on the host machine and will not be affected if we remove the container. We can later spin up a new container and mount the same volume to it.
    To create a volume we run the following command:

    1
    docker volume create db-demo-volume

    To list all the volumes, run the following:

    1
    docker volume ls
     

    Download and Build Oracle XE

    After installing docker, we need to download the Oracle XE docker-image. Afterward, we can spin up a container from that image, and our DB instance is ready to use. 

    In most cases, you will find the Docker image you need on Docker-Hub. In some cases, the image provider hosts the images on its registry, like in our example, the images are hosted on an Oracle registry.

    To pull the Oracle XE image, sign in to the Oracle registry using the account you created. You can do that by running the following command:

    1
    docker login container-registry.oracle.com
    

    After a successful login, you will see the following message


    Then we run the following command to pull the latest Oracle XE image (As of today 21c 21.3.0):

    1
    docker pull container-registry.oracle.com/database/express:latest


    To make things easy we will tag this image and give it a short name (oracle-xe-21.3):

    1
    docker image tag container-registry.oracle.com/database/express:latest oracle-xe-21.3

    1
    docker rmi container-registry.oracle.com/database/express:latest

    You can list the images to see what images you have:

    1
    docker images


    Now we can spin up a container from that image by running the following command:

    1
    2
    3
    4
    5
    6
    docker run -d --name db-container\
    >          -p 1521:1521\
    >          -e ORACLE_PWD=1230123\
    >          -v db-demo-volume:/opt/oracle/oradata\
    >          --network=demo-network\
    >          --hostname database
    >          oracle-xe-21.3
    • The first flag -d will run the container in a detached mode.
    • The parameter --name specifies the container name.
    • -p maps the port 1521 on the host machine to the port 1521 in the container, so we can connect to the database.
    • With -e we can pass environment variables. In this case, ORACLE_PWD set the password for the SYY, SYSTEM, and PDB_ADMIN users.
    • -v mounts the volume we created for the oradata files in the container.
    • --network connects the container to the network we created.
    • --hostname give a name to the DB server. 
    • The last parameter is the image we want to use to spin up the container.
    After the command finishes, you should see this message:


    Now if you go to SQL Developer, you can connect to the Database just like shown below:


    The password in our example is 1230123.

    Download and build ORDS and APEX

    Now we will pull the ORDS image from the Oracle registry, to do that run the following command:

    1

    docker pull container-registry.oracle.com/database/ords:latest


    We will tag the image to make the name shorter:

    1
    docker image tag container-registry.oracle.com/database/ords:latest ords-21.4

    1

    docker rmi container-registry.oracle.com/database/ords:latest



    For ORDS to connect to the database, it has to know the hostname where the DB is running, the password to use when connecting, and the service name. So, we will create a file on our host machine and save the connection string variable in it. When we spin up an ORDS container, we will mount this file to the container, and ORDS will use the connection string variable to connect to the DB. In this demo, we will create a folder on the desktop and name it ORDS, create a file in the ORDS folder and name it conn_string, in the file, we will store the connection string variable.


    1
    echo 'CONN_STRING=sys/1230123@database:1521/XEPDB1'>conn_string.txt

    Please note that the connection string variable has to be in the following format:
    conn_string=username/password@hostname:port/service
    The hostname of our DB is database, which we gave it as we initialized the container and the password is 1230123.

    For Windows Users, you need to remove the quote signs from the conn_string variable. Create a file and write the connection string variable like this:

    CONN_STRING=sys/1230123@database:1521/XEPDB1

    Now, we spin up an ORDS container by running the following command (You have to modify the 4th line so you point to where you saved the connection string file):

    1
    2
    3
    4
    5
    docker run -d --name ords 
    --network=demo-network 
    -p 8181:8181 
    -v /Users/mohamadbouchi/Desktop/ords:/opt/oracle/variables 
    ords-21.4
    

    If you are using Windows then the command line then the command would be:

    1
    2
    3
    4
    5
    docker run -d --name ords 
    --network=demo-network 
    -p 8181:8181 
    -v C:\Users\mohamadbouchi\Desktop\ords:/opt/oracle/variables 
    ords-21.4
    • The first flag -d will run the container in a detached mode.
    • The parameter --name specifies the container name.
    • -p maps the port 8181 on the host machine to the port 8181 in the container, so we can connect to APEX.
    • -v moves the connection string file to /opt/oracle/variables in the container.
    • --network connects the container to the network we created.
    • The last parameter is the image we want to use to spin up the container.
    Please notice, that we are attaching our network to the ORDS container, so both the DB container and ORDS container are running in the same network. And also we are mounting the folder where our connection string is stored to the container.
    This will take a few minutes because it will install APEX in the DB, you can monitor the installation by running the following command in a new Console/Terminal window:

    1
    docker exec -it ords tail -f /tmp/install_container.log
    

    When the APEX installation finishes,  you can open APEX in your browser using the following link:
    localhost:8181/ords/


    Use the following credentials to connect to the Internal Workspace:
    • Workspace: Internal
    • Username: Admin
    • Password: Welcome_1

    Removing the Containers

    After you finish, you may want to stop or remove the containers to free up system resources.
    To stop the container you can simply run the following command

    1
    docker stop <container name>

    At a later point you can start the container again with:

    1
    docker start <container name>

    You can remove the container completely by running

    1
    docker rm <container name>

    Since we are using volumes, our data will remain and we can mount it on different containers, or you can delete the volume, you can do that by running

    1
    docker volume rm <volume name>

    Finally to remove the network

    1
    docker network rm <network name>


    We hope you found this post useful :).

    References:

    Labels: , ,