Docker on macOS: Getting Started

In this Docker tutorial, you’ll learn Docker vocabulary and the commands for creating, inspecting and removing containers, networks and data volumes. You’ll learn how to run Docker containers in the background or foreground, and switch between the two; how to publish ports; how to connect a database app and a web app running in separate containers; and how to share directories between containers and your Mac and among containers. By Audrey Tam.

4.6 (23) · 3 Reviews

Download materials
Save for later
Share
You are currently viewing page 3 of 6 of this article. Click here to view the first page.

Running a Web App With a Database

In this section, you’ll first run a database app in a container, accessed by a web app running on the host system. You’ll need to publish the database app’s port, so the web app can access it.

If you want to run the web app on multiple ports, it’s easier if it runs in a container, too. But you’ll have to do some extra work to give the web app access to the database app.

Running CouchDB in a Docker Container

Web apps usually store data in a database app, like PostgreSQL or CouchDB. Running the database app in Docker containers lets you avoid version problems or test different versions or user types.

In your Docker run window, enter this command to run CouchDB in a Docker container:

docker run --name couchdb -p 5984:5984 -d couchdb 

The CouchDB image always exposes port 5984. You publish this to a local host port number so the web server app can access it.

Note: To specify user and password information when you run CouchDB in a container, use the --env (-e) option — for example:
docker run -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password --name couchdb -p 5984:5984 -d couchdb
docker run -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password --name couchdb -p 5984:5984 -d couchdb

While you wait for CouchDB, build a web server app that uses it. Don’t worry — it’s just a couple of Unix commands!

Open another terminal window — this is where you’ll enter commands that must run in the EmojiJournalServer directory. In Finder, locate the EmojiJournalServer folder in the Download Materials, then run these commands in the terminal window:

cd <drag the EmojiJournalServer folder from Finder to the terminal window> 
 
swift build 
.build/debug/EmojiJournalServer
Note: You’ll get some deprecation warnings as the app builds. Ignore them.

First, you change to the EmojiJournalServer directory, which contains a Package.swift file. The second command uses this file to build the web server app, and the third command runs it. The output looks like this:

[2018-10-22T14:56:58.919+11:00] [INFO] [Application.swift:45 
  connectionProperties] Running on MacOS - using local database
[2018-10-22T14:56:58.932+11:00] [WARNING] [ConnectionProperties.swift:57 
  init(host:port:secured:username:password:)] Initializing a CouchDB connection 
  without a username or password.
[2018-10-22T14:56:59.018+11:00] [INFO] [Application.swift:94 createNewDatabase()] 
  Database does not exist - creating new database
[2018-10-22T14:56:59.112+11:00] [INFO] [EntryRoutes.swift:43 
  initializeEntryRoutes(app:)] Journal entry routes created
[Mon Oct 22 14:56:59 2018] com.ibm.diagnostics.healthcenter.loader INFO: Swift 
  Application Metrics
[2018-10-22T14:56:59.206+11:00] [INFO] [Metrics.swift:47 
  initializeMetrics(app:)] Initialized metrics.
[2018-10-22T14:56:59.208+11:00] [INFO] [WebClientRoutes.swift:43 
  initializeWebClientRoutes(app:)] Web client routes created
[2018-10-22T14:56:59.218+11:00] [INFO] [HTTPServer.swift:124 listen(on:)] 
  Listening on port 8080

The last line of the output says it’s “listening on port 8080”, so open localhost:8080 in a browser to see the Kitura welcome page.

The “Journal entry route” is client, so add /client to the location URL to see the Emoji Journal app:

Click the smiley face to open the emoji menu. Select an emoji, then click the big + sign. This adds your chosen emoji to the journal:

Note: EmojiJournalServer is the sample web server app from our free video course Server Side Swift with Kitura. I’ve edited it a little, so it works in a Docker container.

This web server app is running on your host system, not in a Docker container. To run another server alongside, you must either edit and rebuild the Xcode project to expose a different port, or run the server in a Docker container. This is a Docker tutorial, so of course, you’re going to do the second thing!

Press Control-C in the EmojiJournalServer terminal window, to stop the server.

Running CouchDB & Server in Docker Containers

The EmojiJournalServer folder also contains Dockerfile, Dockerfile-tools and runDocker.sh files.

Enter these commands in the EmojiJournalServer terminal window:

cat Dockerfile
cat runDocker.sh 

You’re just looking at Dockerfile and runDocker.sh. Dockerfile-tools looks similar to Dockerfile. These files contain instructions for building the Docker images needed to run EmojiJournalServer. The runDocker.sh shell script contains the commands to build these images and run the app.

Enter this command to run the shell script — be sure to type the period and space, before runDocker.sh:

. runDocker.sh

This takes a while — go get something to drink, rack up another hour for your activity ring … you’ll end up with something like this:

[2018-10-24T05:25:20.387Z] [INFO] [Application.swift:50 connectionProperties] 
  No cloud credentials - running in Docker
[2018-10-24T05:25:20.390Z] [WARNING] [ConnectionProperties.swift:57 
  init(host:port:secured:username:password:)] Initializing a CouchDB connection 
  without a username or password.
[2018-10-24T05:25:20.432Z] [ERROR] [ClientRequest.swift:344 end(close:)] 
  ClientRequest Error, Failed to invoke HTTP request. CURL Return 
  code=CURLcode(rawValue: 6)
[2018-10-24T05:25:20.432Z] [INFO] [Application.swift:98 createNewDatabase()] 
  Database does not exist - creating new database
[2018-10-24T05:25:20.445Z] [ERROR] [ClientRequest.swift:344 end(close:)] 
  ClientRequest Error, Failed to invoke HTTP request. CURL Return 
  code=CURLcode(rawValue: 6)
[2018-10-24T05:25:20.446Z] [ERROR] [Application.swift:104 createNewDatabase()] 
  Could not create new database: (Optional("Internal Error")) - journal 
  entry routes not created
[2018-10-24T05:25:20.451Z] [INFO] [HTTPServer.swift:124 listen(on:)] 
  Listening on port 8080

This output looks similar to what you got from the .build/debug/EmojiJournalServer command, but “Database does not exist” seems wrong — why doesn’t it see the database you already created? And it gets worse, with “Could not create new database” and “journal entry routes not created”!

The last command in runDocker.sh published 8080 to 8090, so refresh or open localhost:8090 in a browser: the Kitura welcome page is there. But localhost:8090/client isn’t:

Cannot GET /client.

What’s happened? Both containers are running on the default bridge network. In your Docker run terminal window, enter this command to inspect it:

docker network inspect bridge

The output lists information about the emojijournal and couchdb containers:

"Containers": {
    "029114b903dd55ad4cce2160bd0c956439e939a8f6cfb81bb9a5f400ef953727": {
        "Name": "emojijournal",
        "EndpointID": "32ed8052f7e598b9fd027f5a227d5b277b53d66237b44018648e79dc22125c8c",
        "MacAddress": "02:42:ac:11:00:03",
        "IPv4Address": "172.17.0.3/16",
        "IPv6Address": ""
    },
    "2aaecbcb834ae405f4a5046f5195c1b2026b4122d1d05401b87fc6442085acf0": {
        "Name": "couchdb",
        "EndpointID": "043c1ec8eb3521caad9ec8ebacea2bf0a876e1c21cbfe3144f04be63ed3d9986",
        "MacAddress": "02:42:ac:11:00:02",
        "IPv4Address": "172.17.0.2/16",
        "IPv6Address": ""
    }
}

The IP address of couchdb is 172.17.0.2 in my output but might be different on your Mac or the next time I run CouchDB. You could edit and rebuild the web app to access CouchDB’s 5984 port as 172.17.0.2, but the IP address will probably be different when you run it. Ideally, you want the web app to automatically discover CouchDB’s IP address, whatever it is this time, from the host name couchdb.

The trick is to create a user-defined bridge network, then connect both containers to this network. Then each container can use the other’s host name to access its ports. An additional benefit is that entities outside the network cannot access these ports.

In your Docker cleanup terminal window, enter this command to stop and remove both containers:

docker rm $(docker stop $(docker ps -q))

Back in the EmojiJournalServer terminal window, run the following commands:

docker network create emoji-net

You’ve created a Docker network named emoji-net.

docker run --network emoji-net -d --name couchdb couchdb

You’ve started a CouchDB container on this network. You don’t need to publish a local port for CouchDB — the web app will run on the same network, so it will be able to see the port that CouchDB exposes to the network.

docker run --network emoji-net --name emojijournal -it -p 8090:8080 -v $PWD:/root/project -w /root/project emojijournal-run sh -c .build-ubuntu/release/EmojiJournalServer

And now you’ve started an EmojiJournalServer container on this network. This command is the same as the last command in runDocker.sh, with the network option added. Examining the rest of this command:

  • --name emojijournal names the container
  • -it ... sh -c .build-ubuntu/release/EmojiJournalServer creates an interactive terminal, and executes a shell command to run EmojiJournalServer
  • -p 8090:8080 publishes the container’s 8080 port to localhost:8090
  • -v $PWD:/root/project mounts the current directory in the container as /root/project — it bind your current directory to /root/project inside the container, so changes to the host files affect the container’s files and vice versa. Mounting Volumes has its own section later in this tutorial.
  • -w /root/project sets the container’s working directory to /root/project. -w is short for --workdir.

This time, it successfully creates a new database and journal entry routes:

...
[2018-10-25T22:53:32.642Z] [INFO] [Application.swift:98 createNewDatabase()] 
  Database does not exist - creating new database
[2018-10-25T22:53:32.702Z] [INFO] [EntryRoutes.swift:43 
  initializeEntryRoutes(app:)] Journal entry routes created
...
Note: The instructions for some Docker Hub images tell you to use --link to connect containers. Docker documentation says it’s “a legacy feature [that] may eventually be removed”, and you should use user-defined networks and --network.

The server is running in the foreground — press Control-P-Q to detach this process without stopping it. The Unix shell prompt returns.

Enter this command to inspect your network:

docker network inspect emoji-net

Scroll down to see both emojijournal and couchdb are listed in “Containers”:

"Containers": {
    "5fb6ef216c90489fec958c860915760f8d9b801800c6d0f8a70c9c8304c128d5": {
        "Name": "emojijournal",
        "EndpointID": "95871482d549441b22de602e5d27de21912cb85977c1ba4f5b74f5cb2fdc6fb6",
        "MacAddress": "02:42:ac:14:00:03",
        "IPv4Address": "172.20.0.3/16",
        "IPv6Address": ""
    },
    "686f1e8b59f1d95d482991bbc38e85d14bf1fd22bc7b87148f65fc899e834f2a": {
        "Name": "couchdb",
        "EndpointID": "1d7c116dae6d9039329de2db788cb27429330519257c24a5ec4fcac0764ab426",
        "MacAddress": "02:42:ac:14:00:02",
        "IPv4Address": "172.20.0.2/16",
        "IPv6Address": ""
    }
Note: The CouchDB container must be named “couchdb”, because this is what EmojiJournalServer expects. It’s hard-coded in Application.swift as the host name in the connectionProperties property of the App class.

In your browser, refresh or open localhost:8090/client and add an emoji or three. Then enter this command in the EmojiJournalServer terminal window — note the name is emojijournal2 and the published port number is 8070:

docker run --network emoji-net --name emojijournal2 -it -p 8070:8080 -v $PWD:/root/project -w /root/project emojijournal-run sh -c .build-ubuntu/release/EmojiJournalServer

This new emojijournal2 container finds the CouchDB database created by the first EmojiJournalServer:

[2018-10-25T23:38:14.632Z] [INFO] [Application.swift:80 postInit()] Journal 
  entries database located - loading...

Open localhost:8070/client in a browser — it displays the same emoji you added on port 8090. Add another emoji on port 8070, then refresh port 8090 to see it appear.

Contributors

Over 300 content creators. Join our team.