转自:https://www.poeticoding.com/running-elixir-in-docker-containers/

One of the wonderful things about Docker Containers is that you can easily freeze your environment and application in a Docker Image, and deploy it in production without worrying about installing dependencies on your servers. It just works.
This is also great for debugging. If something doesn’t work in production, you can run exactly the same image locally in matter of seconds.
The features and advantages of docker obviously don’t stop here.
In this article we see how to use Docker to run our development Elixir environment and to run multiple Elixir Nodes over a Docker bridge network.


In docker hub we find the official Elixir docker image. There are mainly two branches: the defacto image (1Gb) and the one based on alpine (80Mb)For production I would definitively go with alpine, which is much lighter.
But, for development, the main one brings many more tools and is based on Debian stretch. The image itself is based on erlang:21 that, in turn, is based on buildpack-deps:stretch, which is Debian stretch + a collection of common build dependencies.

Dowloading the image is easy.

  1. $ docker image pull elixir:1.7.4

Where elixir is the image (elixir in this case) and 1.7.4 (the elixir version) is the tag of the image. To check its size

  1. $ docker image inspect elixir:1.7.4 --format '{{ .Size}}'
  2. 1072576831

To download elixir 1.7.4 based on alpine, we just need to use a different tag

  1. $ docker image pull elixir:1.7.4-alpine
  2. $ docker image inspect elixir:1.7.4-alpine --format '{{ .Size}}'
  3. 86796332

Both images run iex if no command is passed.

  1. $ docker run -it elixir:1.7.4
  2. Erlang/OTP 21 [erts-10.1.3] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [hipe]
  3. Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)
  4. iex(1)>

We use docker run here to create a new container from the image elixir:1.7.4.
The -it option allocates a pseudo-TTY connected to the container stdin, which is what gives you interaction with the iex running in the container.

Leave the terminal with iex open and take another terminal trying this command

  1. $ docker container ls
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. bb667cecf5da elixir "iex" 3 minutes ago Up 3 minutes dazzling_rosalind

We see the container id and the name, dazzling_rosalind. The name, when not specified is randomly set by docker. If we quit from iex we see that the container stops.

  1. $ docker container ls -a
  2. ...
  3. bb667cecf5da elixir "iex" 12 minutes ago Exited (0) 8 seconds ago dazzling_rosalind

With the -a options we list all the containers, both stopped and running. Our dazzling_rosalind container stopped once we quit from iex. We can recover the same container starting it and attacching to its terminal session

  1. $ docker container start dazzling_rosalind
  2. dazzling_rosalind
  3. $ docker container attach dazzling_rosalind
  4. PRESS RETURN
  5. iex(2)>

To stop and remove the container we just use rm with -f option, which forces the removal when the container is running.

  1. docker container rm -f dazzling_rosalind

We can save files on a container and until this container is removed, these files are saved in its file system.
Containers should be treated as ephemeral though. It means that containers are thought to be stopped, removed and recreated without having to rely on configurations and files we save in them. To automatically remove the container when stopped we use the --rm option

  1. $ docker container run -it --rm elixir:1.7.4

In general, when we want some files to stay persistent, we use volumes. There are different type of volumes, docker local volumes, cloud volumes connected to a docker container etc.. In this way we can destroy and recreate our containers without loosing important data.

In our case, since we want to use docker for development, the best idea to make a container see the project’s files is to use bind mounting. In this way we mount the directory of our local machine into the container filesystem. All the changes made by the container reflect in our local machine filesystem.

Bind Mounting

We use then the -v local_path:container_path option, where both paths need to be absolute. Let’s try to create a new elixir project using mix new and bind mounting a local directory.

  1. $ docker container run --rm -v $PWD:/data -w /data elixir:1.7.4 mix new crypto
  2. * creating README.md
  3. * creating .formatter.exs
  4. * creating .gitignore
  5. * creating mix.exs
  6. * creating config
  7. ...
  8. $ ls -l
  9. drwxr-xr-x 9 alvise staff 288 8 Dec 19:54 crypto

We’ve mounted the current directory into the contasiner’s /data path, using $PWD environment variable. We’ve also added a -w /data option which tells the container to start the command into the /data working directory. We can see that the project directory crypto is now in our current directory.

Let’s move inside the crypto directory and add a dependency into mix.exs

  1. defp deps do
  2. [ {:poison, "~> 4.0"} ]
  3. end
  1. $ docker container run --rm -v $PWD:/app -w /app -it elixir:1.7.4 mix deps.get
  2. Could not find Hex, which is needed to build dependency :poison
  3. Shall I install Hex? (if running non-interactively, use "mix local.hex --force") [Yn] Y
  4. * creating /root/.mix/archives/hex-0.18.2
  5. ...

After the dependency is downloaded and built, the container is destroyed and all the data in /root/.mix is lost. If we run again the same commands we will have again to install hex. To solve this, an similar issues, we can create a local volume

  1. $ docker volume create elixir-mix
  2. elixir-mix

To use it we need to add another option to the command -v volume-name:container_mount_point.

  1. $ docker container run --rm -v elixir-mix:/root/.mix -v $PWD:/app -w /app -it elixir:1.7.4 mix deps.get
  2. Could not find Hex, which is needed to build dependency :poison
  3. Shall I install Hex? (if running non-interactively, use "mix local.hex --force") [Yn] Y
  4. * creating /root/.mix/archives/hex-0.18.2
  5. ...
  6. $ docker container run --rm -v elixir-mix:/root/.mix -v $PWD:/app -w /app -it elixir:1.7.4 mix deps.get
  7. Resolving Hex dependencies...
  8. Dependency resolution completed:
  9. ...

The first time, Hex needs to be installed. The second time the command is working properly because hex is found in the /root/.mix directory, where elixir-mix volume is mounted.

We can now run our iex loading our crypto project

  1. $ docker container run --rm -v elixir-mix:/root/.mix -v $PWD:/app -w /app -it elixir:1.7.4 iex -S mix
  2. ...
  3. ==> poison
  4. Compiling 4 files (.ex)
  5. Generated poison app
  6. ...
  7. iex(1)> Poison.encode! %{hello: :world}
  8. "{"hello":"world"}"

The dependencies are downloaded in deps and compiled in build project’s directory. Since these changes are reflected in our project directory via bind mounting, if we run the same command again we shouldn’t have any dependencies compilation.

Running Multiple Containers

Let’s see now how to use docker to run multiple containers in a virtual network and then run multiple Elixir Nodes.

We first need to create our bridge network, to which we will link our containers to.

  1. $ docker network create elixir-net
  2. $ docker network inspect elixir-net
  3. ...
  4. "Config": [ {
  5. "Subnet": "172.18.0.0/16",
  6. ...

With inspect we see that our elixir-net network has 172.18.0.0/16 subnet.

To run a container and to make it join the network we’ve just created, we use the --network option

  1. $ docker run -it --rm --network elixir-net elixir:1.7.4
  2. iex>

Let’s use another terminal to see first the id of the container we started, and get then its IP inside elixir-net.

  1. $ docker container ls
  2. 0c219f042eb8 elixir:1.7.4 ...
  3. $ docker container inspect 0c219f042eb8 | jq
  4. '.[] .NetworkSettings .Networks ."elixir-net" .IPAddress'
  5. "172.18.0.2"

inspect prints all the details of the container. With jq we filter them to get the IP address.

Let’s run another container in the same network, starting the session in bashinstead of iex. The main image has the ping command we can use to ping the other container.

  1. $ docker run -it --rm --network elixir-net elixir:1.7.4 bash
  2. root@aeb8101fbb9b:/# ping 172.18.0.2
  3. PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
  4. 64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.095 ms

Even if it works, we mustn’t use the IPs directly: each container has its own IP and if a container is destroyed and re-created the IP could not be the same.
Hopefully we can rely on names. To make them work we need to start our container with the --name option. We also add the -h option to set the hostname (which will be useful later running multiple Elixir nodes).
Remove first all the running containers so we can make a fresh start. Then run these two containers in two seperate terminals

  1. # in terminal 1
  2. $ docker run -it --rm --network elixir-net --name elixir-1
  3. -h elixir-1 elixir:1.7.4 bash
  4. root@elixir-1:/#
  5. # in terminal 2
  6. $ docker run -it --rm --network elixir-net --name elixir-2
  7. -h elixir-2 elixir:1.7.4 bash
  8. root@elixir-2:/# ping elixir-1
  9. PING elixir-1 (172.18.0.2) 56(84) bytes of data.
  10. 64 bytes from elixir-1.elixir-net (172.18.0.2): icmp_seq=1 ttl=64 ..

Now we have all we need to start iex in two separate containers and to connect the two nodes. To make them able to connect we need to use the --sname iex option, which assigns a short name to the node. In our case the name can be the same since we have different hostnames (-h option in docker).

  1. # Terminal 1
  2. $ docker run -it --rm --network elixir-net --name elixir-1
  3. -h elixir-1 elixir:1.7.4 iex --sname docker
  4. iex(docker@elixir-1)1>
  5. # Terminal 2
  6. $ docker run -it --rm --network elixir-net --name elixir-2
  7. -h elixir-2 elixir:1.7.4 iex --sname docker
  8. iex(docker@elixir-2)1> Node.connect :"docker@elixir-1"
  9. false

And we see an error in the terminal-1

  1. iex(docker@elixir-1)1>
  2. Connection attempt from disallowed node :"docker@elixir-2"

Something went wrong. What’s missing? The secret cookie needs to be the same. The best way to pass a secret in general is using environment variables. In a docker/kubernetes production environment we can safely store secrets and map them to environment variables. Right now we set the environment variable passing the plain cookie.
Note we are passing '$COOKIE' with the quotes. doing so we avoid that our shell substitutes $COOKIE with our system $COOKIE environment variable.

  1. # Terminal 1
  2. $ docker run -it --rm -e COOKIE=secret --network elixir-net --name elixir-1
  3. -h elixir-1 elixir:1.7.4 iex --sname docker --cookie '$COOKIE'
  4. # Terminal 2
  5. $ docker run -it --rm -e COOKIE=secret --network elixir-net --name elixir-2
  6. -h elixir-2 elixir:1.7.4 iex --sname docker --cookie '$COOKIE'
  7. iex(docker@elixir-2)1> Node.connect :"docker@elixir-1"
  8. true
  9. iex(docker@elixir-2)2> Node.list
  10. [:"docker@elixir-1"]

Fantastic! We can now have fun running distributed elixir code

  1. # Terminal 2
  2. iex(docker@elixir-2)3> Agent.start_link(
  3. fn -> {:hello, :world} end,
  4. name: {:global, MyAgent})
  5. {:ok, #PID<0.118.0>}

And in the other container, in Terminal 1, we can now access to the MyAgentprocess.

  1. # Terminal 1
  2. iex(docker@elixir-1)1> Agent.get({:global, MyAgent}, fn state-> state end)
  3. {:hello, :world}

Wrap Up

We saw how easy it is to use docker containers to develop and run locally our Elixir’s projects, also distributed across multiple nodes. This is just the foundation. where to start adding other services, like a postgres container linked to our elixir-netnetwork, so we can start working with Ecto in matter of seconds.

 
 
 
 

Running Elixir in Docker Containers的更多相关文章

  1. [Docker] Run Short-Lived Docker Containers

    Learn the benefits of running one-off, short-lived Docker containers. Short-Lived containers are use ...

  2. Configuring and Running Django + Celery in Docker Containers

    Configuring and Running Django + Celery in Docker Containers  Justyna Ilczuk  Oct 25, 2016  0 Commen ...

  3. Understanding how uid and gid work in Docker containers

    转自:https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf Un ...

  4. Removing Docker Containers and Images

    Removing Docker Containers and Images In a recent post aboutDocker, we looked into some things that ...

  5. [Docker] Run, Stop and Remove Docker Containers

    In this lesson, we'll find out the basics of running Docker containers. We'll go over how to downloa ...

  6. [Docker] Prune Old Unused Docker Containers and Images

    In this lesson, we will look at docker container prune to remove old docker containers. We can also ...

  7. docker log 批量删除报错: find: `/var/lib/docker/containers/': 没有那个文件或目录

    问题描述: 服务器上面docker log太多,打算用之前写的批量清理shell脚本清理掉,但是发现报错. find: `/var/lib/docker/containers/': 没有那个文件或目录 ...

  8. Docker目录/var/lib/docker/containers文件太大

    Docker在不重建容器的情况下,日志文件默认会一直追加,时间一长会逐渐占满服务器的硬盘的空间,内存消耗也会一直增加,本篇来了解一些控制日志文件的方法. 查出占用磁盘较大的文件 Docker 的日志文 ...

  9. A workaround to change shared memory size for Docker containers in AWS ECS

    Issue Because of not supporting to specify the following docker run parameter, containers in ECS can ...

随机推荐

  1. 二. Python基础(2)--语法

    二. Python基础(2)--语法 1.实现一个简单的登录系统 '''# 形式1 n = 1 while n < 4:     name = input("请输入姓名\n" ...

  2. html、xhtml、html5的区别

    1.HTML:HyperText Mark-up Language(超文本标记语言)构成网页的主要语言  常指:HTML 4.012.XHTLM:EXtensible HyperText Mark-u ...

  3. SQL3-查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号dept_no

    题目描述 查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号dept_noCREATE TABLE `dept_manager` (`dept_no` ch ...

  4. xilinx 高速收发器Serdes深入研究-Comma码(转)

    一.为什么要用Serdes 传统的源同步传输,时钟和数据分离.在速率比较低时(<1000M),没有问题. 在速率越来越高时,这样会有问题 由于传输线的时延不一致和抖动存在,接收端不能正确的采样数 ...

  5. android 应用程序中执行Linux 命令

    ADB 无线调试命令son = "setprop service.adb.tcp.port 5555\n" + "stop adbd\n" + "st ...

  6. SharePoint REST API - 基本操作(二)

    博客地址:http://blog.csdn.net/FoxDave 上一节讲了SharePoint REST API的一些基本操作,本节将继续介绍一些关于SharePoint REST API的内容. ...

  7. AssetBundle打包详解

    Unity5.x AssetBundle打包详解 在网上查看了很多资料,想详细搞清楚AssetBundle的原理.以实现符合项目需求的打包工具和加载逻辑 1. AssetBundle是什么? Asse ...

  8. Delphi 10.2.3 新增的TMimeTypes类

    Delphi 10.2.3 新增的TMimeTypes类,利用url中的文件扩展名,可以轻松得到url需要返回的HTTP content-type类型,可以参考这里,现在查看AddDefTypes方法 ...

  9. excel单元格内容连接

    1.连接符号: & 举例子:C1= A1&B1 2.生成sql: CONCATENATE("(seq_table.nextval,sysdate, 'test',sysdat ...

  10. sql 按字段指定值排序

    这个需要在排序语句中使用条件判断 例如:表[Table_temp]中列[col1]为字符,属性为varchar(10),排序时需要按照B.A.C的顺序显示,则可按照以下SQL语句: select * ...