<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Carlos Ribeiro on Medium]]></title>
        <description><![CDATA[Stories by Carlos Ribeiro on Medium]]></description>
        <link>https://medium.com/@caduribeiro?source=rss-35e349b8ec68------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Q5POYDY8MyTeuVsu1J6PUQ.png</url>
            <title>Stories by Carlos Ribeiro on Medium</title>
            <link>https://medium.com/@caduribeiro?source=rss-35e349b8ec68------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 12:52:47 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@caduribeiro/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Creating a Docker image with a preloaded database]]></title>
            <link>https://caduribeiro.medium.com/creating-a-docker-image-with-a-preloaded-database-fcc35ff2da56?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/fcc35ff2da56</guid>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Fri, 29 Jan 2021 15:00:00 GMT</pubDate>
            <atom:updated>2021-01-29T20:09:39.151Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/284/0*dVB0-23TFabzTtz1.png" /></figure><p>Imagine that we have the following Postgresql database dump:</p><pre><br>--<br>-- PostgreSQL database dump<br>--<br><br>-- Dumped from database version 11.5<br>-- Dumped by pg_dump version 11.3<br><br>SET statement_timeout = 0;<br>SET lock_timeout = 0;<br>SET idle_in_transaction_session_timeout = 0;<br>SET client_encoding = &#39;UTF8&#39;;<br>SET standard_conforming_strings = on;<br>SELECT pg_catalog.set_config(&#39;search_path&#39;, &#39;&#39;, false);<br>SET check_function_bodies = false;<br>SET xmloption = content;<br>SET client_min_messages = warning;<br>SET row_security = off;<br><br>--<br>-- Name: my_db; Type: DATABASE; Schema: -; Owner: postgres<br>--<br><br>CREATE DATABASE my_db WITH TEMPLATE = template0 ENCODING = &#39;UTF8&#39; LC_COLLATE = &#39;en_US.utf8&#39; LC_CTYPE = &#39;en_US.utf8&#39;;<br><br>ALTER DATABASE my_db OWNER TO postgres;<br><br>\connect my_db<br><br>SET statement_timeout = 0;<br>SET lock_timeout = 0;<br>SET idle_in_transaction_session_timeout = 0;<br>SET client_encoding = &#39;UTF8&#39;;<br>SET standard_conforming_strings = on;<br>SELECT pg_catalog.set_config(&#39;search_path&#39;, &#39;&#39;, false);<br>SET check_function_bodies = false;<br>SET xmloption = content;<br>SET client_min_messages = warning;<br>SET row_security = off;<br><br>SET default_tablespace = &#39;&#39;;<br><br>SET default_with_oids = false;<br><br>--<br>-- Name: clients; Type: TABLE; Schema: public; Owner: postgres<br>--<br><br>CREATE TABLE public.clients (<br>    id integer NOT NULL,<br>    name character varying(150) NOT NULL<br>);<br><br>ALTER TABLE public.clients OWNER TO postgres;<br><br>--<br>-- Name: clients_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres<br>--<br><br>CREATE SEQUENCE public.clients_id_seq<br>    AS integer<br>    START WITH 1<br>    INCREMENT BY 1<br>    NO MINVALUE<br>    NO MAXVALUE<br>    CACHE 1;<br><br>ALTER TABLE public.clients_id_seq OWNER TO postgres;<br><br>--<br>-- Name: clients_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres<br>--<br><br>ALTER SEQUENCE public.clients_id_seq OWNED BY public.clients.id;<br><br>--<br>-- Name: clients id; Type: DEFAULT; Schema: public; Owner: postgres<br>--<br><br>ALTER TABLE ONLY public.clients ALTER COLUMN id SET DEFAULT nextval(&#39;public.clients_id_seq&#39;::regclass);<br><br>--<br>-- Data for Name: clients; Type: TABLE DATA; Schema: public; Owner: postgres<br>--<br><br>COPY public.clients (id, name) FROM stdin;<br>1   Client 1<br>2   Client 2<br>\.<br><br>--<br>-- Name: clients_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres<br>--<br><br>SELECT pg_catalog.setval(&#39;public.clients_id_seq&#39;, 2, true);<br><br>--<br>-- Name: clients clients_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres<br>--<br><br>ALTER TABLE ONLY public.clients<br>    ADD CONSTRAINT clients_pkey PRIMARY KEY (id);<br><br>--<br>-- PostgreSQL database dump complete<br>--</pre><p>It is a simple database with a Clients table and 2 records.</p><p>If we want to start a Postgresql Docker container with this dump loaded to share with our team, we can add this SQL file into the <em>/docker-entrypoint-initdb.d/</em> folder inside the container, like <a href="https://hub.docker.com/_/postgres">explained into the Postgresql Image docs from DockerHub</a>.</p><blockquote><em>Initialization scripts If you would like to do additional initialization in an image derived from this one, add one or more *.sql, *.sql.gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary). After the entrypoint calls initdb to create the default postgres user and database, it will run any *.sql files, run any executable *.sh scripts, and source any non-executable *.sh scripts found in that directory to do further initialization before starting the service.</em></blockquote><p>The following Dockerfile uses <em>postgres:11-alpine</em> as base image and copies <em>test_dump.sql</em> file to the entrypoint folder.</p><pre>FROM postgres:11-alpine<br><br>COPY test_dump.sql /docker-entrypoint-initdb.d/</pre><p>If we build this image</p><pre>$ docker image build . -t preloaded_db:latest</pre><p>and start a container with the generated image</p><pre>$ docker container run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres --name test_preloaded_db preloaded_db:latest</pre><p>we can see in our database that the database was created. (password is postgres)</p><pre>$ psql -h localhost -U postgres<br>postgres=# \c my_db<br>psql (11.3, server 11.5)<br>You are now connected to database “my_db” as user “postgres”.<br>my_db=# SELECT * FROM clients;<br> id | name<br> — — + — — — — —<br> 1 | Client 1<br> 2 | Client 2<br>(2 rows)</pre><p>Awesome. Now we have a docker image that has our database loaded. But if we check the log of this container</p><pre>$ docker container logs test_preloaded_db</pre><p>we can see CREATE DATABASE and CREATE TABLE commands.</p><pre>/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/test_dump.sql<br>SET<br>SET<br>SET<br>SET<br>SET<br> set_config<br>-----------------<br><br>(1 row)<br><br>SET<br>SET<br>SET<br>SET<br>CREATE DATABASE<br>ALTER DATABASE<br>...</pre><p>This tell us that the dump is being processed every time we create the container. If we destroy this container and create a new one, the dump will be processed again. This works fine but if we have a big database with a big dump file, the startup process of the container will be slow because it can take some time to process the whole dump. We can fix it by keeping the database preloaded in the image.</p><p>Before we moving on, let’s destroy the container we created</p><pre>$ docker container rm -f test_preloaded_db</pre><h3>Preloading the database in the image</h3><p>To preload the database in the image, we need to tell our Dockerfile to execute the same entrypoint of the original PostgreSQL image so it can execute the dump in the build step. Let&#39;s use <a href="https://docs.docker.com/develop/develop-images/multistage-build">Multi-Stage build</a> to divide our build in two steps. The first one will execute the entrypoint with the dump file and the second one will copy the data folder to the resulting image.</p><pre># dump build stage<br>FROM postgres:11-alpine as dumper<br><br>COPY test_dump.sql /docker-entrypoint-initdb.d/<br><br>RUN [&quot;sed&quot;, &quot;-i&quot;, &quot;s/exec \&quot;$@\&quot;/echo \&quot;skipping...\&quot;/&quot;, &quot;/usr/local/bin/docker-entrypoint.sh&quot;]<br><br>ENV POSTGRES_USER=postgres<br>ENV POSTGRES_PASSWORD=postgres<br>ENV PGDATA=/data<br><br>RUN [&quot;/usr/local/bin/docker-entrypoint.sh&quot;, &quot;postgres&quot;]<br><br># final build stage<br>FROM postgres:11-alpine<br><br>COPY --from=dumper /data $PGDATA</pre><p>In the first step, we have the following instructions:</p><ul><li><strong>FROM postgres:11-alpine as dumper</strong> We define the base image our step will use. postgres with the 11-alpine tag in this case.</li><li><strong>COPY test_dump.sql /docker-entrypoint-initdb.d/</strong> Copy the test_dump.sql file to the /docker-entrypoint-initdb.d/ folder.</li><li><strong>RUN [“sed”, “-i”, “s/exec “$@”/echo “skipping…”/”, “/usr/local/bin/docker-entrypoint.sh”]</strong> We need to execute this sed command in order to remove the exec &quot;$@&quot; content that exists in the docker-entrypoint.sh file so it will not start the PostgreSQL daemon (we don&#39;t need it on this step).</li><li><strong>ENV POSTGRES_USER=postgres; ENV POSTGRES_PASSWORD=postgres; ENV PGDATA=/data</strong> Sets environment variables to define user and password and tell PostgreSQL to use /data as data folder, so we can copy it in the next step</li><li><strong>RUN [“/usr/local/bin/docker-entrypoint.sh”, “postgres”]</strong> Execute the entrypoint itself. It will execute the dump and load the data into /data folder. Since we executed the sed command to remove the $@ content it will not run the PostgreSQL daemon</li></ul><p>The second step contains only this instruction:</p><p>If we build this Dockerfile</p><pre>$ docker image build . -t preloaded_db:new</pre><p>We can see in the output the dump being processed and after everything is finished, the image is built.</p><p>and we can start the container with this new image</p><pre>$ docker container run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres --name test_preloaded_db preloaded_db:latest</pre><p>and our database is loaded</p><pre>$ psql -h localhost -U postgres<br>psql (11.3, server 11.5)<br>Type “help” for help.<br>postgres=# \c my_db<br>psql (11.3, server 11.5)<br>You are now connected to database “my_db” as user “postgres”.<br>my_db=# SELECT * FROM clients;<br> id | name<br> — — + — — — — —<br> 1 | Client 1<br> 2 | Client 2<br>(2 rows)</pre><p>But if we check the logs now, the dump is not being processed every time we create the container</p><pre>$ docker container logs test_preloaded_db<br>2019–09–16 01:42:22.458 UTC [1] LOG: listening on IPv4 address “0.0.0.0”, port 5432<br>2019–09–16 01:42:22.458 UTC [1] LOG: listening on IPv6 address “::”, port 5432<br>2019–09–16 01:42:22.460 UTC [1] LOG: listening on Unix socket “/var/run/postgresql/.s.PGSQL.5432”<br>2019–09–16 01:42:22.470 UTC [18] LOG: database system was shut down at 2019–09–16 01:41:02 UTC<br>2019–09–16 01:42:22.473 UTC [1] LOG: database system is ready to accept connections</pre><p>We can see that only the PostgreSQL startup is being done. No dump is being executed because it was executed in the build image step.</p><h3>Creating a Makefile to make the process easier</h3><p>I like to create a Makefile to make easier the process of making a database dump and creating an image. This <strong>Makefile</strong> will contain commands to create the dump the database, create an image and tag it by date allowing me to have daily dumps on my registry to download.</p><pre>default: all<br><br>.PHONY: default all fetch_dump<br><br>date := `date &#39;+%Y-%m-%d&#39;`<br>TARGET_IMAGE ?= my_app<br><br>all: check_vars fetch_dump generate_image push_to_registry clean finished<br><br>check_vars:<br>    @test -n &quot;$(DB_ENDPOINT)&quot; || (echo &quot;You need to set DB_ENDPOINT environment variable&quot; &gt;&amp;2 &amp;&amp; exit 1)<br>    @test -n &quot;$(DB_NAME)&quot; || (echo &quot;You need to set DB_NAME environment variable&quot; &gt;&amp;2 &amp;&amp; exit 1)<br>    @test -n &quot;$(DESTINATION_REPOSITORY)&quot; || (echo &quot;You need to set DESTINATION_REPOSITORY environment variable&quot; &gt;&amp;2 &amp;&amp; exit 1)<br><br>fetch_dump: DB_USER ?= postgres<br>fetch_dump:<br>    @echo &quot;&quot;<br>    @echo &quot;====== Fetching remote dump ======&quot;<br>    @PGPASSWORD=&quot;$(DB_PASSWORD)&quot; pg_dump -h $(DB_ENDPOINT) -d $(DB_NAME) -U $(DB_USER) &gt; dump.sql<br><br>generate_image:<br>generate_image:<br>    @docker build . -t $(TARGET_IMAGE):latest -t $(DESTINATION_REPOSITORY)/$(TARGET_IMAGE):latest -t $(DESTINATION_REPOSITORY)/$(TARGET_IMAGE):$(date)<br><br>push_to_registry:<br>    @echo &quot;&quot;<br>    @echo &quot;====== Pushing image to repository ======&quot;<br>    @docker push $(DESTINATION_REPOSITORY)/$(TARGET_IMAGE)<br><br>clean:<br>    @echo &quot;&quot;<br>    @echo &quot;====== Cleaning used files ======&quot;<br>    @rm -f dump.sql<br><br>finished:<br>    @echo &quot;&quot;<br>    @echo &quot;Finished with success. Pushed image to $(DESTINATION_REPOSITORY)/$(TARGET_IMAGE)&quot;</pre><p>And I can execute the following command to generate my image with a new dump</p><pre>$ make DB_ENDPOINT=127.0.0.1 DB_USER=postgres DB_PASSWORD=postgres DB_NAME=my_db TARGET_IMAGE=myapp-data DESTINATION_REPOSITORY=gcr.io/my_project</pre><p>This command usually is integrated in a Cron job in some server to be executed daily. With this I can have on my image registry dumps from each day.</p><p>Another interesting thing to do is to add some SQL script to obfuscate users data. <a href="https://blog.taadeem.net/english/2018/10/29/Introducing-PostgreSQL-Anonymizer">This article can be helpful if you want to achive this</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*-441zqOQ4mkmbBmK.png" /></figure><p>Thanks ☕️</p><p><em>Originally published at </em><a href="https://cadu.dev/creating-a-docker-image-with-database-preloaded/"><em>https://cadu.dev</em></a><em> on January 29, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fcc35ff2da56" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando uma imagem Docker com o banco de dados pré carregado]]></title>
            <link>https://medium.com/opensanca/criando-uma-imagem-docker-com-o-banco-de-dados-pr%C3%A9-carregado-5b97b7802007?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/5b97b7802007</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Tue, 17 Sep 2019 12:41:53 GMT</pubDate>
            <atom:updated>2019-09-17T21:37:39.306Z</atom:updated>
            <content:encoded><![CDATA[<h3>Criando uma imagem Docker com o banco de dados pré-carregado</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/284/1*76BLzQhFKsywrztd5GKaYw.png" /></figure><p>Imagine que você possua o seguinte dump de uma base de dados Postgres.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ce04012d1603f0a7fbb60a0b97074f2e/href">https://medium.com/media/ce04012d1603f0a7fbb60a0b97074f2e/href</a></iframe><p>Uma base simples com uma tabela <strong>Clients</strong> que contém 2 registros.</p><p>Se desejarmos iniciar um container Docker Postgres com essa base já pré-carregada para poder distribuir para as pessoas do time, podemos colocar esse arquivo sql na pasta <strong>/docker-entrypoint-initdb.d/</strong> dentro do container, <a href="https://hub.docker.com/_/postgres">como informado na documentação da página da imagem no DockerHub</a>:</p><blockquote>Initialization scripts<br>If you would like to do additional initialization in an image derived from this one, add one or more *.sql, *.sql.gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary). After the entrypoint calls initdb to create the default postgres user and database, it will run any *.sql files, run any executable *.sh scripts, and source any non-executable *.sh scripts found in that directory to do further initialization before starting the service.</blockquote><p>O seguinte Dockerfile usa a imagem <strong>postgres:11-alpine</strong> como base e realiza a cópia do arquivo <strong>test_dump.sql</strong> para a pasta indicada:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/45fa272ed92f600122b638c82b87eba1/href">https://medium.com/media/45fa272ed92f600122b638c82b87eba1/href</a></iframe><p>Se realizarmos o build desse arquivo:</p><pre>$ docker image build . -t preloaded_db:latest</pre><p>E inicializarmos um container da imagem gerada:</p><pre>$ docker container run -d --rm -p 5432:5432 -e POSTGRES_USER=postgres --name test_preloaded_db preloaded_db:latest</pre><p>Podemos verificar no client do Postgres que o banco foi criado e inicializado:</p><pre>$ psql -h localhost -U postgres</pre><pre>postgres=# \c my_db<br>psql (11.3, server 11.5)<br>You are now connected to database “my_db” as user “postgres”.<br>my_db=# SELECT * FROM clients;<br> id | name<br> — — + — — — — — <br> 1 | Client 1<br> 2 | Client 2<br>(2 rows)</pre><p>Se analisarmos o log desse container:</p><pre>$ docker container logs test_preloaded_db</pre><p>Vemos vários comandos do Postgres sendo executados. O que nos indica que o dump está sendo processado ao criar o container.</p><p>Ok. Nosso banco está sendo carregado, porém o dump está sendo executado toda vez que o container é criado. Se destruirmos o container e criarmos outro, o arquivo .sql vai ser executado novamente. Essa abordagem é ok, porém se tivermos um dump muito grande, a inicialização do container é mais lenta, pois a execução do dump pode demorar para ser completada. Para resolvermos isso, podemos gerar uma imagem com a base de dados já pré carregada na imagem</p><p>Antes de continuarmos, vamos parar o container que criamos:</p><pre>$ docker container rm -f test_preloaded_db</pre><h3>Pré-carregando o banco na imagem</h3><p>Para pré-carregar a imagem, precisamos fazer nosso Dockerfile executar o mesmo entrypoint da imagem original do Postgres, para ele realizar a inicialização do banco no processo de build. Vamos utilizar <a href="https://docs.docker.com/develop/develop-images/multistage-build">Multi-Stage build</a> para dividirmos o build em duas etapas. A primeira irá executar o entrypoint com o arquivo de dump, e a segunda irá copiar a pasta de dados do Postgres para a imagem final</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c5eadd77545800245cb16a4b8af2e2ab/href">https://medium.com/media/c5eadd77545800245cb16a4b8af2e2ab/href</a></iframe><p>No primeira etapa temos as seguintes instruções:<br>- <strong>FROM postgres:11-alpine as dumper</strong>: Definimos a imagem base que nossa etapa irá usar. Nesse caso, imagem `postgres` com a tag `11-alpine`.</p><p>- <strong>COPY test_dump.sql /docker-entrypoint-initdb.d/</strong>: Copiamos o arquivo `test_dump.sql`, que contém o dump do nosso banco de dados para pasta `/docker-entrypoint-initdb.d/`</p><p>- <strong>RUN [&quot;sed&quot;, &quot;-i&quot;, &quot;s/exec \&quot;$@\&quot;/echo &quot;skipping…\&quot;/&quot;, &quot;/usr/local/bin/docker-entrypoint.sh&quot;]</strong>: Nessa linha, executamos o <strong>sed</strong> para trocar o conteúdo <strong>exec &quot;$@&quot;</strong> para <strong>skipping…</strong> do arquivo <strong>/usr/local/bin/docker-entrypoint.sh</strong>. Fazendo isso, quando executarmos o entrypoint, ele irá carregar o script sql da pasta porém não irá iniciar o daemon do Postgres, pois não precisamos. Só queremos que ele faça a pré-carga dos dados para podermos copiar para a etapa posterior.</p><p>- <strong>ENV PG_USER=postgres ; ENV PGDATA=/data</strong>: Nessas duas linhas configuramos duas variáveis de ambientes. Uma para definir que o usuário será o `postgres` e outra para informar que a pasta de dados do postgres será `/data`.</p><p>- <strong>RUN [&quot;/usr/local/bin/docker-entrypoint.sh&quot;, &quot;postgres&quot;]</strong>: Aqui executamos o entrypoint em si. Ele irá executar o dump e carregar os dados na pasta `/data`. E como executamos o`sed` para remover o `$@`, ele não irá executar o daemon do postgres.</p><p>Todas essas alterações de arquivos e variáveis de ambientes só ficam disponíveis nessa etapa de build. Na próxima, como veremos, a etapa vai se basear novamente da imagem base do Postgres, sem nenhuma configuração modificada que fizemos nessa etapa.</p><p>Na segunda etapa temos apenas a seguinte instrução:</p><p>- <strong>COPY — from=dumper /data $PGDATA</strong>: Essa instrução copia todos os arquivos da pasta `/data` da etapa `dumper` para a pasta `$PGDATA` dessa etapa, fazendo nossos dados do dump serem já carregados na imagem.</p><p>Se realizarmos um build desse Dockerfile:</p><pre>$ docker image build . -t preloaded_db:new</pre><p>Vemos no output que ele executa o script sql e gera a imagem.</p><p>Se criarmos um container com essa imagem:</p><pre>$ docker container run -d --rm -p 5432:5432 -e POSTGRES_USER=postgres --name test_preloaded_db preloaded_db:new</pre><p>Vemos que nosso banco de dados está carregado:</p><pre>$ psql -h localhost -U postgres</pre><pre>psql (11.3, server 11.5)<br>Type “help” for help.</pre><pre>postgres=# \c my_db<br>psql (11.3, server 11.5)<br>You are now connected to database “my_db” as user “postgres”.<br>my_db=# SELECT * FROM clients;<br> id | name<br> — — + — — — — — <br> 1 | Client 1<br> 2 | Client 2<br>(2 rows)</pre><p>E ao analisar os logs, vemos que o dump não é executado ao criar o container:</p><pre>$ docker container logs test_preloaded_db</pre><pre>2019–09–16 01:42:22.458 UTC [1] LOG: listening on IPv4 address “0.0.0.0”, port 5432<br>2019–09–16 01:42:22.458 UTC [1] LOG: listening on IPv6 address “::”, port 5432<br>2019–09–16 01:42:22.460 UTC [1] LOG: listening on Unix socket “/var/run/postgresql/.s.PGSQL.5432”<br>2019–09–16 01:42:22.470 UTC [18] LOG: database system was shut down at 2019–09–16 01:41:02 UTC<br>2019–09–16 01:42:22.473 UTC [1] LOG: database system is ready to accept connections</pre><p>Vemos que apenas a inicialização do Postgres é feita. Não há nenhum comando da execução do dump em si, isso foi realizado no build da imagem.</p><h3>Criando um Makefile para facilitar o processo</h3><p>Para facilitar o processo de dump e criação da imagem customizada, eu gosto de criar um Makefile que contém uma série de passos para criar a imagem e tagear ela pela data, me possibilitando ter dumps diários no meu registry para poder baixar.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5163e8df9522d21486e7423a185f0aa4/href">https://medium.com/media/5163e8df9522d21486e7423a185f0aa4/href</a></iframe><p>E eu consigo executar o seguinte comando para gerar a imagem com o dump presente (apenas trocando as variáveis para a configuração certa)</p><pre>make DB_ENDPOINT=127.0.0.1 DB_USER=postgres DB_PASSWORD=password DB_NAME=my_db TARGET_IMAGE=myapp DESTINATION_REPOSITORY=gcr.io/my_project</pre><p>Esse comando é integrado em um Cron para ser executado diariamente, então eu tenho em meu repositório imagens com dump de Dia - 1.</p><p>Outro ponto interessante é você colocar mais um script SQL para ser executado que irá realizar a ofuscação dos dados dos usuários. <a href="https://blog.taadeem.net/english/2018/10/29/Introducing-PostgreSQL-Anonymizer">Esse artigo pode ser interessante para saber mais sobre</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SCM5BMNrvsiC4rPa.png" /></figure><p>Thanks ☕️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5b97b7802007" width="1" height="1" alt=""><hr><p><a href="https://medium.com/opensanca/criando-uma-imagem-docker-com-o-banco-de-dados-pr%C3%A9-carregado-5b97b7802007">Criando uma imagem Docker com o banco de dados pré carregado</a> was originally published in <a href="https://medium.com/opensanca">Opensanca</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reduce your Docker images (an example with Ruby)]]></title>
            <link>https://medium.com/code-pub/reduce-your-docker-images-an-example-with-ruby-564735f4388c?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/564735f4388c</guid>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Tue, 26 Feb 2019 15:14:06 GMT</pubDate>
            <atom:updated>2019-02-26T15:14:06.488Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*oZtQbUoanv_gIHLX.jpg" /></figure><p>(Portuguese version <a href="https://medium.com/opensanca/reduzindo-o-tamanho-de-suas-imagens-docker-exemplo-c-ruby-2b5ce5ca6dfa">here</a>)</p><p>A big problem that we face when deploying Docker into production is the image size. Large images take longer to download, consume much of your cloud network traffic quota, cost more money to be stored on the repository and don’t bring any good value.</p><p>In most situations, when we create a Docker image, we add steps and dependencies that sometimes we don’t need in the final image that will run in production.</p><p>I will use the following application as an example:<br><a href="https://github.com/opensanca/opensanca/_jobs">https://github.com/opensanca/opensanca_jobs</a></p><p>This is the Dockerfile that generates our image</p><pre>FROM ruby:2.5.0-alpine<br>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;<br>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;<br>ENV SECRET_KEY_BASE=dumb<br>RUN apk update \<br>&amp;&amp; apk add \<br>openssl \<br>tar \<br>build-base \<br>tzdata \<br>postgresql-dev \<br>postgresql-client \<br>nodejs \<br>&amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>&amp;&amp; mkdir -p /opt/yarn \<br>&amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>&amp;&amp; mkdir -p /var/app<br>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=2 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}<br>COPY . /var/app<br>WORKDIR /var/app<br>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile<br>CMD rails s -b 0.0.0.0</pre><p>And the command used to build it:</p><pre>docker build -t openjobs:latest --build-arg build_without=&quot;development test&quot; --build-arg rails_env=&quot;production&quot; .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*RujRaeSrBXgUJHVNWjQMIg.png" /></figure><p>This build generated an image with almost 1GB!!! 😱.</p><p>This image has some unnecessary stuff, like node but yarn (we only need them to precompile the assets but not to execute the application itself).</p><h3>Multi-Stage build</h3><p>Docker introduced the concept of <a href="https://docs.docker.com/develop/develop-images/multistage-build/">Multi-Stage build</a> in version 17.05. This build technic allows us to split our Dockerfile into several statements FROM. Each statement can use a different base image and you can copy artifacts from one stage to another, without bringing stuff that you don’t want in the final image. Our final image will only contain the build wrote in the last stage.</p><p>Now we have a Dockerfile divided into two stages. Pre-build and Final-Build.</p><pre># pre-build stage<br>FROM ruby:2.5.0-alpine AS pre-builder<br>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;<br>ENV SECRET_KEY_BASE=dumb<br>RUN apk add --update --no-cache \<br>openssl \<br>tar \<br>build-base \<br>tzdata \<br>postgresql-dev \<br>postgresql-client \<br>nodejs \<br>&amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>&amp;&amp; mkdir -p /opt/yarn \<br>&amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>&amp;&amp; mkdir -p /var/app<br>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=2 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}<br>COPY . /var/app<br>WORKDIR /var/app<br>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile</pre><pre># final build stage<br>FROM ruby:2.5.0-alpine<br>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;<br>RUN apk add --update --no-cache \<br>openssl \<br>tzdata \<br>postgresql-dev \<br>postgresql-client<br>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app<br>ENV RAILS_LOG_TO_STDOUT true<br>WORKDIR /var/app<br>EXPOSE 3000<br>CMD rails s -b 0.0.0.0</pre><p>In the pre-build stage we install node and yarn, all dependencies and precompile the assets. In the final stage, we use an alpine image (which is very small) with ruby, we install only the necessary dependencies to run the application and we then copy the libraries and assets generated in the build-stage with the following command:</p><pre>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app</pre><p>Doing the build with this Dockerfile, we have now a 562MB image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*G7h0VTW1JD9tKZ7DHnqM9w.png" /></figure><p>We have already reduced almost half the image size, but can we reduce it further?? 🤔</p><p>Yes. We can do some actions to reduce more this image.</p><h3>Removing unnecessary files</h3><p>We can delete files that are not necessary from the image, like cache and temporary files used by the installed libraries. We can add a .dockerignore file, telling the build what not to send to the image.</p><pre># build stage<br>FROM ruby:2.5.0-alpine AS pre-builder<br>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;<br>ENV SECRET_KEY_BASE=dumb<br>RUN apk add --update --no-cache \<br>openssl \<br>tar \<br>build-base \<br>tzdata \<br>postgresql-dev \<br>postgresql-client \<br>nodejs \<br>&amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>&amp;&amp; mkdir -p /opt/yarn \<br>&amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>&amp;&amp; mkdir -p /var/app<br>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=4 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}<br>COPY . /var/app<br>WORKDIR /var/app<br>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile \<br>&amp;&amp; rm -rf /gems/cache/*.gem \<br>&amp;&amp; find /gems/gems/ -name &quot;*.c&quot; -delete \<br>&amp;&amp; find /gems/gems/ -name &quot;*.o&quot; -delete</pre><pre># final stage<br>FROM ruby:2.5.0-alpine<br>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;<br>RUN apk add --update --no-cache \<br>openssl \<br>tzdata \<br>postgresql-dev \<br>postgresql-client<br>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app<br>ENV RAILS_LOG_TO_STDOUT true<br>WORKDIR /var/app<br>EXPOSE 3000<br>CMD rails s -b 0.0.0.0</pre><p>In this new Dockerfile, we added this part that removes caches and temporary C files used to build the libraries:</p><pre>&amp;&amp; rm -rf /gems/cache/*.gem \<br>&amp;&amp; find /gems/gems/ -name &quot;*.c&quot; -delete \<br>&amp;&amp; find /gems/gems/ -name &quot;*.o&quot; -delete</pre><p>We also included our .dockerignore to tell the build process the files that we don’t want in the image:</p><pre>.env*<br>.git<br>.gitignore<br>.codeclimate.yml<br>.dockerignore<br>.gitlab-ci.yml<br>.hound.yml<br>.travis.yml<br>LICENSE.md<br>README.md<br>docker-compose.*<br>Dockerfile<br>log/*<br>node_modules/*<br>public/assets/*<br>storage/*<br>public/packs/*<br>public/packs-test/*<br>tmp/*</pre><p>With these two steps, now our image has 272MB.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*eZJTGWQQHJTdqKyvfyvFjw.png" /></figure><p>We can reduce it even more. For production, we don’t need test folders, npm raw folder (they are already included on the asset pipeline), no precompiled assets and caches.</p><p>To remove this files, we can include a strategy of passing an argument to build (we will call it: to_remove)</p><pre>...<br>ARG to_remove<br>...<br>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile  \<br>&amp;&amp; rm -rf /usr/local/bundle/cache/*.gem \<br> &amp;&amp; find /usr/local/bundle/gems/ -name &quot;*.c&quot; -delete \<br> &amp;&amp; find /usr/local/bundle/gems/ -name &quot;*.o&quot; -delete \<br> &amp;&amp; rm -rf $to_remove   # Here we remove all files that we passed as an argument to the build.<br>...</pre><p>In this argument, we will pass all the files that we don’t want in production:</p><pre>docker build -t openjobs:reduced --build-arg build_without=&quot;development test&quot; --build-arg rails_env=&quot;production&quot; . --build-arg to_remove=&quot;spec node_modules app/assets vendor/assets lib/assets tmp/cache&quot;</pre><p>Notice the `— build-arg to_remove=”spec node_modules app/assets vendor/assets lib/assets tmp/cache”`. These are the folders that we want to remove from our build process. We don’t need them to run in production.</p><p>Removing these files, now we have an image with 164MB, almost 6 times smaller than the original one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*dOtqxq0ssllOzV6v_iVjmw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*Lt65Wab0jeBX6OSkgUFcAQ.gif" /></figure><p>If you still don’t believe me and want to see it, this is the PR that generates this reduction: <a href="https://github.com/opensanca/opensanca_jobs/pull/164">https://github.com/opensanca/opensanca_jobs/pull/164</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*eqsPaN0ft0DkhHczXD5vJA.png" /></figure><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=564735f4388c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/reduce-your-docker-images-an-example-with-ruby-564735f4388c">Reduce your Docker images (an example with Ruby)</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reduzindo o tamanho de suas imagens Docker (exemplo c/ Ruby)]]></title>
            <link>https://medium.com/opensanca/reduzindo-o-tamanho-de-suas-imagens-docker-exemplo-c-ruby-2b5ce5ca6dfa?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/2b5ce5ca6dfa</guid>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Fri, 15 Feb 2019 14:14:34 GMT</pubDate>
            <atom:updated>2019-02-15T14:14:34.781Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/570/0*oZtQbUoanv_gIHLX.jpg" /></figure><p>Um grande problema de fazer deploy em produção utilizando docker é tamanho que as imagens geradas podem ocupar. Imagens muito grandes demoram para ser baixadas, consomem parte da sua cota de tráfego de rede no seu provedor de nuvem, custam para ser guardadas no seu repositório e não trazer nenhuma valor adicional.</p><p>Na maioria das situações, quando criamos uma imagem docker, colocamos alguns passos e dependências que as vezes não precisamos na imagem final que iremos rodar em produção</p><p>Vou usar como exemplo a seguinte aplicação: <a href="https://github.com/opensanca/opensanca_jobs">https://github.com/opensanca/opensanca_jobs</a></p><p>e esse é o Dockerfile que nos gera a imagem.</p><pre>FROM ruby:2.5.0-alpine</pre><pre>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;</pre><pre>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;</pre><pre>ENV SECRET_KEY_BASE=dumb</pre><pre>RUN apk update \<br>  &amp;&amp; apk add \<br>    openssl \<br>    tar \<br>    build-base \<br>    tzdata \<br>    postgresql-dev \<br>    postgresql-client \<br>    nodejs \<br>  &amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>  &amp;&amp; mkdir -p /opt/yarn \<br>  &amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>  &amp;&amp; mkdir -p /var/app</pre><pre>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=2 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}</pre><pre>COPY . /var/app<br>WORKDIR /var/app</pre><pre>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile<br>CMD rails s -b 0.0.0.0</pre><p>E o comando utilizado para realizar o build:</p><pre>docker build -t openjobs:latest --build-arg build_without=&quot;development test&quot; --build-arg rails_env=&quot;production&quot; .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/734/1*RujRaeSrBXgUJHVNWjQMIg.png" /></figure><p>Esse build nos gerou uma imagem de quase 1GB!!! 😱.</p><p>Essa imagem tem algumas coisas desnecessárias, como o node e o yarn (só precisamos deles para precompilar os assets e não para a execução da aplicação).</p><h3><strong>Multi-Stage build</strong></h3><p>Na versão 17.05 o docker introduziu o conceito de <a href="https://docs.docker.com/develop/develop-images/multistage-build/">Multi-stage Builds</a>. Essa técnica de build nos permite dividir o nosso Dockerfile em várias declarações FROM. Cada declaração pode usar imagens base diferentes e você pode copiar artefatos de um estágio para outro, sem trazer as coisas que você não quer na imagem final. Nossa imagem final só contará com o build escrito no ultimo estágio.</p><p>Nosso Dockerfile separado em dois estágios: Pré Build e Final Build</p><pre># pre-build stage<br>FROM ruby:2.5.0-alpine AS pre-builder</pre><pre>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;</pre><pre>ENV SECRET_KEY_BASE=dumb</pre><pre>RUN apk add --update --no-cache \<br>    openssl \<br>    tar \<br>    build-base \<br>    tzdata \<br>    postgresql-dev \<br>    postgresql-client \<br>    nodejs \<br>  &amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>  &amp;&amp; mkdir -p /opt/yarn \<br>  &amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>  &amp;&amp; mkdir -p /var/app</pre><pre>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=2 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}</pre><pre>COPY . /var/app<br>WORKDIR /var/app</pre><pre>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile</pre><pre># final build stage<br>FROM ruby:2.5.0-alpine<br>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;</pre><pre>RUN apk add --update --no-cache \<br>    openssl \<br>    tzdata \<br>    postgresql-dev \<br>    postgresql-client</pre><pre>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app</pre><pre>ENV RAILS_LOG_TO_STDOUT true</pre><pre>WORKDIR /var/app</pre><pre>EXPOSE 3000</pre><pre>CMD rails s -b 0.0.0.0</pre><p>No estágio de build, instalamos o node e o yarn, instalamos as dependências e precompilamos os <em>assets</em>. No estágio final, utilizamos uma imagem <em>alpine</em> (que é bem leve) com ruby, instalamos apenas dependências necessárias para rodar a aplicação e copiamos as bibliotecas e assets gerados no estágio anterior com o seguinte comando:</p><pre>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app  </pre><p>Fazendo o build desse Dockerfile, agora temos uma imagem de 562mb.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/739/1*G7h0VTW1JD9tKZ7DHnqM9w.png" /></figure><p>Já diminuímos quase pela metade, mas será que ainda podemos reduzir ainda mais o tamanho da imagem?? 🤔</p><p>Sim. Podemos fazer algumas outras ações para reduzir ainda mais essa imagem.</p><h3>Removendo arquivos desnecessários</h3><p>Podemos apagar da imagem arquivos que não são necessários, como cache e os arquivos utilizados temporariamente das bibliotecas instaladas. E também podemos adicionar um arquivo .dockerignore, dizendo para o build o que não enviar para a imagem.</p><pre># build stage<br>FROM ruby:2.5.0-alpine AS pre-builder</pre><pre>ARG rails_env=&quot;development&quot;<br>ARG build_without=&quot;&quot;</pre><pre>ENV SECRET_KEY_BASE=dumb</pre><pre>RUN apk add --update --no-cache \<br>    openssl \<br>    tar \<br>    build-base \<br>    tzdata \<br>    postgresql-dev \<br>    postgresql-client \<br>    nodejs \<br>  &amp;&amp; wget <a href="https://yarnpkg.com/latest.tar.gz">https://yarnpkg.com/latest.tar.gz</a> \<br>  &amp;&amp; mkdir -p /opt/yarn \<br>  &amp;&amp; tar -xf latest.tar.gz -C /opt/yarn --strip 1 \<br>  &amp;&amp; mkdir -p /var/app</pre><pre>ENV PATH=&quot;$PATH:/opt/yarn/bin&quot; BUNDLE_PATH=&quot;/gems&quot; BUNDLE_JOBS=4 RAILS_ENV=${rails_env} BUNDLE_WITHOUT=${bundle_without}</pre><pre>COPY . /var/app<br>WORKDIR /var/app</pre><pre>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile \<br> &amp;&amp; rm -rf /gems/cache/*.gem \<br> &amp;&amp; find /gems/gems/ -name &quot;*.c&quot; -delete \<br> &amp;&amp; find /gems/gems/ -name &quot;*.o&quot; -delete</pre><pre># final stage<br>FROM ruby:2.5.0-alpine<br>LABEL maintainer=&quot;<a href="/service/mailto:contato@opensanca.com.br">contato@opensanca.com.br</a>&quot;</pre><pre>RUN apk add --update --no-cache \<br>    openssl \<br>    tzdata \<br>    postgresql-dev \<br>    postgresql-client</pre><pre>COPY --from=pre-builder /gems/ /gems/<br>COPY --from=pre-builder /var/app /var/app</pre><pre>ENV RAILS_LOG_TO_STDOUT true</pre><pre>WORKDIR /var/app</pre><pre>EXPOSE 3000</pre><pre>CMD rails s -b 0.0.0.0</pre><p>Nesse novo Dockerfile, adicionamos esse trecho que remove caches e arquivos C temporários utilizados para buildar as bibliotecas.:</p><pre> &amp;&amp; rm -rf /gems/cache/*.gem \<br> &amp;&amp; find /gems/gems/ -name &quot;*.c&quot; -delete \<br> &amp;&amp; find /gems/gems/ -name &quot;*.o&quot; -delete</pre><p>Além de incluir tambem o nosso .dockerignore para informar ao processo de build os arquivos que não precisam ser enviados para a imagem:</p><pre>.env*<br>.git<br>.gitignore<br>.codeclimate.yml<br>.dockerignore<br>.gitlab-ci.yml<br>.hound.yml<br>.travis.yml<br>LICENSE.md<br>README.md<br>docker-compose.*<br>Dockerfile<br>log/*<br>node_modules/*<br>public/assets/*<br>storage/*<br>public/packs/*<br>public/packs-test/*<br>tmp/*</pre><p>Com esses dois passos, agora nossa imagem tem 272MB.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/740/1*eZJTGWQQHJTdqKyvfyvFjw.png" /></figure><p>Podemos dar um passo a mais. Para produção, não precisamos das pastas de teste, dependências do npm (pois elas já foram incluídas no asset pipeline), assets não precompilados, e caches.</p><p>Pra remover esses arquivos, podemos incluir uma estratégia de passar um argumento para o build, que iremos chamar de to_remove .</p><pre>...</pre><pre>ARG to_remove</pre><pre>...</pre><pre>RUN bundle install &amp;&amp; yarn &amp;&amp; bundle exec rake assets:precompile  \<br>&amp;&amp; rm -rf /usr/local/bundle/cache/*.gem \<br> &amp;&amp; find /usr/local/bundle/gems/ -name &quot;*.c&quot; -delete \<br> &amp;&amp; find /usr/local/bundle/gems/ -name &quot;*.o&quot; -delete \<br> &amp;&amp; rm -rf $to_remove   # Aqui removemos todos arquivos que passamos como argumento no build</pre><pre>...</pre><p>Nesse argumento, passaremos os arquivos que não desejamos para produção:</p><pre>docker build -t openjobs:reduced --build-arg build_without=&quot;development test&quot; --build-arg rails_env=&quot;production&quot; . --build-arg to_remove=&quot;spec node_modules app/assets vendor/assets lib/assets tmp/cache&quot;</pre><p>Perceba o --build-arg to_remove=&quot;spec node_modules app/assets vendor/assets lib/assets tmp/cache&quot; Essas são as pastas que iremos remover do nosso processo de build, pois não necessitamos delas para rodar em produção.</p><p>Removendo esses arquivos, ficamos com uma imagem de 164MB, quase 6 vezes menor que a original.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/747/1*dOtqxq0ssllOzV6v_iVjmw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Lt65Wab0jeBX6OSkgUFcAQ.gif" /></figure><p>Se você ainda não acreditou em mim e quer ver com seus próprios olhos, deixo aqui o pull request pra alteração que gerou essa redução em um projeto que estou trabalhando: <a href="https://github.com/opensanca/opensanca_jobs/pull/164">https://github.com/opensanca/opensanca_jobs/pull/164</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*eqsPaN0ft0DkhHczXD5vJA.png" /></figure><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2b5ce5ca6dfa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/opensanca/reduzindo-o-tamanho-de-suas-imagens-docker-exemplo-c-ruby-2b5ce5ca6dfa">Reduzindo o tamanho de suas imagens Docker (exemplo c/ Ruby)</a> was originally published in <a href="https://medium.com/opensanca">Opensanca</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An Introduction into Spacemacs from a Vim user]]></title>
            <link>https://medium.com/code-pub/an-introduction-into-spacemacs-from-a-vim-user-f0d9c860911e?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/f0d9c860911e</guid>
            <category><![CDATA[emacs]]></category>
            <category><![CDATA[spacemacs]]></category>
            <category><![CDATA[vim]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Fri, 23 Nov 2018 12:10:41 GMT</pubDate>
            <atom:updated>2018-11-23T12:10:41.185Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*1WlehGOk0peCQhbx.jpg" /></figure><p>I’m a Vim user. I use it in my workflow all the time, but I enjoy trying other editors sometimes and I will share my experience playing with Spacemacs.</p><h3>What is Spacemacs?</h3><p>Spacemacs is a starter kit for Emacs that makes it easier for anyone to use it. It has the <a href="https://github.com/emacs-evil/evil">Evil</a> plugin by default. Evil is a layer for Emacs that emulates Vim. I always try vim mode in all other editors and they all are not so good, Evil is one of the best Vim emulators that I’ve already seen. It makes it easier for me to use Emacs, since it emulates Vim commands that I already know.</p><p>Spacemacs has easy to remember keybindings. Everything starts with the SPC key (it is like the leader key from Vim) and all belongs to some group. For example: SPC b for all buffers commands and SPC p for all project commands. If you don’t remember the command, press SPC SPC and it brings an autocomplete buffer to search for commands.</p><h3>How do I experiment another editor?</h3><p>It’s hard for me to learn all the stuffs from a tool in a day or two. So when I want to learn more about an editor, I force myself to experiment it for at least one week, trying to do everything on the new editor without opening others. To use an editor I need to learn how to search files, how to navigate through files and how split the window, only this.</p><h3>Installation</h3><p>First, you should install Emacs.<br>You can use Homebrew to install emacs on mac:</p><pre>brew install emacs --use-git-head --cocoa --srgb</pre><p>To install on Linux, you can follow this <a href="https://www.gnu.org/software/emacs/manual/html_node/efaq/Installing-Emacs.html">documentation</a>. <br>Now you can install the Spacemacs distribution:</p><pre>git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d</pre><p>Now open Emacs..</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*isSOE_FDRGXQ0Yth.png" /><figcaption>Spacemacs Startup Screen</figcaption></figure><h3>The Basics</h3><p>Spacemacs has the concept oflayers . The idea is to have a collection of packages with some configurations. The documentation says:</p><blockquote>Layer: A collected unit of configuration that can be enabled (or disabled) in Spacemacs. A layer typically brings together one or more packages, as well as the glue configuration code required to make them play well with each other and Spacemacs in general.</blockquote><p>Once you open Emacs, enter SPC f e d, this will take you to Spacemacs configuration file (~/.spacemacs). Here you can add layers inside dotspacemacs-configuration-layers. For example:</p><pre>dotspacemacs-configuration-layers<br>   &#39;(<br>     elixir<br>     (ruby :variables ruby-version-manager &#39;rvm)<br>     ruby-on-rails<br>     )</pre><p>This adds layers for working with Elixir, Ruby and Ruby on Rails</p><h3>Navigating through a file</h3><p>To navigate on the file, I use the Vim commands.</p><pre>gg  =&gt; goes to the top of the file<br>G   =&gt; goes to the bottom<br>h   =&gt; left<br>j   =&gt; down<br>k   =&gt; up<br>l   =&gt; right</pre><p>(to learn more about navigation, <a href="http://vim.wikia.com/wiki/Moving_around">read here</a>)</p><h3>Loading your project</h3><p>After loaded, you can use SPC a d (or File -&gt; Open Directory) to open your project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/352/1*f2aEVrVO424eAKVfUN8XhA.png" /></figure><p>It will load this buffer and you can find your project file. Hit Enter to load it</p><p>Spacemacs includes <a href="https://github.com/bbatsov/projectile">Projectile</a>, an Emacs library for handling projects. So, after you added your project the first time with SPC a d , you can use SPC p p to Switch between projects</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*G6tl43SpJFd1sDuu7EtwyA.png" /></figure><h3>Terminology</h3><p>I like to use this image to understand Emacs terminology</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*V790P2DvSbUyLai3.jpg" /><figcaption><a href="https://emacs.stackexchange.com/questions/13583/whats-the-difference-between-a-buffer-a-file-a-window-and-a-frame">https://emacs.stackexchange.com/questions/13583/whats-the-difference-between-a-buffer-a-file-a-window-and-a-frame</a></figcaption></figure><p>Usually in most applications we call the running instance as window, but on Emacs we call it the Frame . A frame can contain multiple Windows and inside each window we have a Buffer loaded (on the image above, the buffer loaded is indicated by the yellow circle). A buffer can be the content of a loaded file or the output of some command execution. Every time you open a file, it changes the active buffer to this file, but the buffer with the past file opened is still alive. You can use SPC b b to see all buffers and switch them</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g6xsZuwBoh-7bLpRhbuD9g.gif" /></figure><p>You can easily switch between the last used buffer with SPC TAB</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1OWdL-LXoWbS5C8w5YQoYA.gif" /></figure><h3>Searching for a file</h3><p>The first thing that I learn in a new editor is: How can I search for a file?</p><p>In Spacemacs we can do a fuzzy file search pressing SPC p f</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eeEs8GYQu-IJ2pOjUyiXVA.gif" /><figcaption>Fuzzy file search</figcaption></figure><p>Another way is to search for Classes and methods using Tags. SPC p G will generate a TAGS file for your project. After generated, you can search through tags with SPC p g</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AkzXGmwPbIx1mvNkYM0tog.gif" /><figcaption>Searching by Class/Method Name using Tags</figcaption></figure><p>You can also open a tree explorer using SPC p t</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ykdIGDHb5hKUN55v7ptXMA.gif" /><figcaption>SPC p t will open the tree explorer</figcaption></figure><h3>Splitting window</h3><p>Another feature that I use a lot is splitting the window. You can use the Emacs keybinding to do it:</p><pre>SPC w -   =&gt; Split window into two windows, one above the other.<br>SPC w /   =&gt; Split window into two windows, side by side.</pre><p>Or using the vim commands:</p><pre>:sp    =&gt; Split window into two windows, one above the other.<br>:vsp   =&gt; Split window into two windows, side by side.</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4HZiedewGWiJNSNhE5I46g.gif" /><figcaption>Splitting the window</figcaption></figure><p>After splitting, you will notice that a number is defined in the window</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K02Mq9n_eMlv8ABamsD8nw.png" /></figure><p>To jump into a window, you can press SPC NUMBER to go to that window (for example, SPC 3 take me to the right window on the image)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qgxRsfzVDJU4dzLYg75m7A.gif" /><figcaption>Switching between windows</figcaption></figure><p>You can kill a window with SPC w d or :q .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*buC_BQKmCPKUoA-6rFKkmA.gif" /></figure><p>Sometimes when working on more than one window in the same frame (for example, having a class and a test file opened together), I like to maximize one of them. You can use SPC w m to maximize and use it again to return it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bc0ZGbmD9gjSB17zVILoXg.gif" /></figure><h3>My Workflow working with it</h3><p>Usually, my workflow with Vim involves Tmux too. I typically have 3 Tmux panes open. One for the editor (Vim), another for the terminal (to run tests, ssh, etc), and one for the server (and the logs).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i_-7O6wOGJ_R68ezpzNbzA.gif" /><figcaption>My workflow with Vim and tmux</figcaption></figure><p>I also like to use <a href="https://github.com/janko-m/vim-test">vim-test</a> to run my tests inside Vim</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mb3axdT9givXhxTqp7lhXA.gif" /></figure><p>Emacs for another side, have the idea that you can do everything on the Emacs itself. You can start your server on it, open a terminal, etc, without having the needing to have multiple terminals for each thing (although I still have they open when working on Emacs 😄)</p><p>My next examples use the rails layer on Spacemacs.</p><p>I can run bundle install (to install my ruby dependencies) inside it with SPC m b i</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xvFEvzNsI5xh6Qunk36dFQ.gif" /></figure><p>I can run the rails server inside it and check the logs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xek3DjksVemXCqtavhSswg.gif" /></figure><p>and also run the tests directly from Emacs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vEjoFzZNxuPcZ8ads4H62Q.gif" /></figure><p>Follow a simple mindmap that I created for these basic commands:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VKqD5q2PaAuZj4UZnBZf7A.png" /></figure><p>And this is all the basics that I need to use it and that I want to share.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eqsPaN0ft0DkhHczXD5vJA.png" /></figure><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f0d9c860911e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/an-introduction-into-spacemacs-from-a-vim-user-f0d9c860911e">An Introduction into Spacemacs from a Vim user</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploy de aplicações Docker na AWS usando ECS e Fargate]]></title>
            <link>https://medium.com/opensanca/deploy-de-aplica%C3%A7%C3%B5es-docker-na-aws-usando-ecs-e-fargate-9e8713c775db?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/9e8713c775db</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[aws-ecs]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Wed, 31 Jan 2018 17:03:58 GMT</pubDate>
            <atom:updated>2018-07-13T22:13:28.882Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*L_1-B_5m036MTLsyQ135cw.png" /></figure><p><em>Publicado originalmente em </em><a href="https://thecode.pub/easy-deploy-your-docker-applications-to-aws-using-ecs-and-fargate-a988a1cc842f"><em>thecode.pub</em></a><em> em 31 de janeiro de 2018.</em></p><p>Nesse post irei demonstrar como você pode fazer o Deploy de sua aplicação Docker na AWS usando ECS e Fargate.</p><p>Como exemplo, eu irei fazer o deploy da aplicação do <a href="https://openjobs.me/">Openjobs</a>. O código fonte pode ser encontrado <a href="https://github.com/opensanca/opensanca_jobs/">nesse link</a>.</p><p>Eu irei usar o <a href="https://www.terraform.io/">Terraform</a> para criar a infraestrutura, assim consigo controlar todos os recursos criados como código. Se você deseja saber o básico sobre Terraform, <a href="https://thecode.pub/creating-your-cloud-servers-with-terraform-bfa01a499bad">leia meu post sobre (inglês)</a>.</p><h3>ECS</h3><p><strong>O que é ECS?</strong></p><p>O Elastic Container Service (ECS) é um serviço da AWS que cuida da orquestração de containers Docker no seu cluster de instâncias EC2. É uma alternativa para o Kubernetes, Docker Swarm, e outros.</p><h4>ECS Terminology</h4><p>Para entender o que é o ECS, nós precisamos entender seus termos e definições, que diferem um pouco do mundo Docker.</p><ul><li><strong>Cluster: </strong>É um group de instâncias EC2 que irão hospedar os containers.</li><li><strong>Task definition: </strong>É uma especificação de como o ECS deve rodar sua aplicação. Aqui se define qual imagem usar, mapeamento de portas, configuração de memória, variáveis de ambiente, etc.</li><li><strong>Service: </strong>Services executam e mantêm as tasks rodando dentro do cluster. O Service irá auto recuperar qualquer task que estiver parada, tentando deixar o total delas em execução com o mesmo valor que especificado.</li></ul><h3>Fargate</h3><p>Fargate é a tecnologia que nos permite rodar containers no ECS sem a necessidade de gerenciarmos o cluster de servidores EC2. Apenas fazemos o deploy da aplicação Docker e definimos as regras de escalabilidade. Fargate é um método de execução do ECS.</p><p><strong><em>Show me the code</em></strong></p><p>O exemplo completo está no <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example">Github</a>.</p><h3>A estrutura do projeto</h3><p>Nosso projeto do Terraform é composto pela seguinte estrutura:</p><p>├── modules<br>│ └── code_pipeline <br>│ └── ecs <br>│ └── networking <br>│ └── rds<br>├── pipeline.tf<br>├── production.tf<br>├── production_key.pub<br>├── terraform.tfvars<br>└── variables.tf</p><ul><li><strong>Modules</strong> é onde iremos armazenar o código que cuida da criação de um grupo de recurso. Ele pode ser reutilizado por todos os ambientes (Produção, Staging, QA, etc) sem a necessidade de duplicação de código.</li><li><strong>production.tf </strong>é o arquivo que definimos nosso ambiente de produção. Ele executa os módulos passando as variáveis para definir as configurações.</li><li><strong>pipeline.tf </strong>O pipeline pode ser um recurso global sem a necessidade de ser isolado por ambiente. Esse arquivo irá cuidar da criação do pipeline usando o módulo code_pipeline.</li></ul><h3>A primeira parte, a rede</h3><p>A branch para essa parte pode ser encontrada <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/01_networking">aqui</a>.</p><p>A primeira coisa que precisamos criar é a VPC com 2 subnets (1 pública e 1 privada) em cada Availability Zone. Cada Availability Zone fica em uma região isolada geograficamente. Mantendo nossos recursos em mais de uma Availability Zone é o primeiro item para termos High Availability. Se um desses locais físicos falham por algum motivo, sua aplicação pode responder dos outros locais.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*Cvu1YNJdfezuVfU8kAPgNA.png" /><figcaption>Nossa rede</figcaption></figure><p>Mantendo nosso cluster na subnet privada protege nossa infraestrutura de acesso externo. A subnet privada é configurada para permitir acesso apenas da subnet pública (Apenas o Load Balancer no nosso caso).</p><p>Esse é o código para criar essa parte (é praticamente o mesmo do meu post de introdução ao Terraform):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3000fa3a6cfa0ccdb9678d3e4660424d/href">https://medium.com/media/3000fa3a6cfa0ccdb9678d3e4660424d/href</a></iframe><p>O código acima cria a VPC, 4 subnets (2 públicas e 2 privadas) em cada Availability Zone. Também cria o NAT para permitir a subnet privada acessar a internet.</p><p><strong>O Banco de dados</strong></p><p>A branch para essa parte pode ser encontrada <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/02_database">aqui</a>.</p><p>Iremos criar uma instância de banco de dados no RDS. Ele estará localizado na subnet privada, permitindo apenas a public subnet de accessar.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59d7deafef35b800131d6d8061ad1e82/href">https://medium.com/media/59d7deafef35b800131d6d8061ad1e82/href</a></iframe><p>Com esse código criamos o recurso do RDS com os valores recebidos das variáveis. Também criamos o Security Group que deverá ser usado pelos recursos que se comunicam com o banco de dados (no nosso caso, o cluster ECS).</p><p>Ok. Agora temos o banco de dados. Vamos finalmente criar nosso cluster ECS para fazer o deploy da aplicação \o/.</p><h3>O ECS</h3><p>A branch para essa parte pode ser encontrada <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/03_ecs">aqui</a>.</p><p>Estamos nos aproximando dos passos finais. Agora é a parte que definimos os recursos do ECS necessários para nossa aplicação.</p><h4>O repositório ECR</h4><p>A primeira coisa que devemos fazer é criar o repositório para armazenar nossas imagens Docker.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/356acc348a4487a6434c6e60919f7c62/href">https://medium.com/media/356acc348a4487a6434c6e60919f7c62/href</a></iframe><h4>O cluster ECS</h4><p>Agora precisamos de nosso cluster ECS. Mesmo usando Fargate (que não precisa de nenhuma instância EC2), nós devemos definir o cluster ECS para a aplicação.</p><h4>As Tasks Definitions</h4><p>Vamos definir 2 Task Definitions</p><ul><li>Web: Contem a definição da aplicação Web.</li><li>Db Migrate: Essa tarefa executará apenas o comando para migrar nosso banco de dados e irá morrer. Como é uma tarefa de execução única, não precisamos de um Service para ela.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cbba359b380baf0de6398ebbf29d66c0/href">https://medium.com/media/cbba359b380baf0de6398ebbf29d66c0/href</a></iframe><p>As Tasks Definitions são configuradas em um arquivo JSON e renderizadas como um template no Terraform.<br>Essa é a Task Definition para a aplicação Web:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cd914e0e8af12d8d338da2acd81e85d2/href">https://medium.com/media/cd914e0e8af12d8d338da2acd81e85d2/href</a></iframe><p>No arquivo acima estamos definindo a Task Definition para o ECS. Nós passamos o repositório ECR criado como uma variável. Também configuramos outras variáveis para que o ECS consiga iniciar nossa aplicação Rails. <br>A definição da migração de banco de dados é praticamente a mesma. Nós apenas mudamos o comando que será executado.</p><h4>O Load Balancer</h4><p>Antes de criarmos os Serviços, nós precisamos criar o Load Balancer. Ele será colocado na subnet pública e irá enviar as requests para o Service no ECS.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3033baa23cf2903207443201d7762619/href">https://medium.com/media/3033baa23cf2903207443201d7762619/href</a></iframe><p>No arquivo acima nós definimos que nosso Target Group irá usar a porta 80 com o protocolo HTTP. Nós também criamos um Security Group para permitir acessos vindos da internet apenas na porta 80. Depois criamos o Application Load Balancer e o Listener. É necessário usar o Application Load Balancer no lugar do Elastic Load Balancer para usar o Fargate.</p><h4>O ECS Service finalmente</h4><p>Agora iremos criar o Service. Para usar o Fargate, precisamos especificar o launch_typecom o valorFargate.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4631b7af611d3710c0f6dafe8a5b898/href">https://medium.com/media/b4631b7af611d3710c0f6dafe8a5b898/href</a></iframe><p>O Fargate nos permite escalar nossa aplicação facilmente. Para isso, precisamos apenas criar as métricas no CloudWatch e ativar os gatilhos para escalar para cima e para baixo.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4c8f2760cd44d935b704e7aa750bb5ab/href">https://medium.com/media/4c8f2760cd44d935b704e7aa750bb5ab/href</a></iframe><p>Nós criamos 2 politicas de Auto Scaling. Uma para escalar para cima e outra para escalar para baixo a quantidade desejada de Tasks rodando no nosso Service Web do ECS.</p><p>Depois criamos uma métrica no CloudWatch baseada no uso de CPU. Se o uso de CPU está maior que 85% por 2 períodos, a trigger de alarm_actionexecuta a política de Scale Up. Se retornar para o estado Ok, irá executar a política de Scale Down.</p><h3>O Pipeline para o deploy da aplicação</h3><p>Nossa infraestrutura para fazer o deploy da aplicação Docker está pronta. Mas ainda é meio tedioso fazer o deploy para o ECS. No nosso caso, precisamos fazer manualmente o envio da imagem para o repositório ECR e atualizar a Task Definition com a nova imagem. Podemos executar isso através do Terraform, mas pode ser melhor se conseguirmos fazer um push no nosso código no Github na master branch e o deploy for feito automaticamente para nós.</p><p><em>Apresentando, </em><a href="https://aws.amazon.com/codepipeline"><em>CodePipeline</em></a><em> e </em><a href="https://aws.amazon.com/codebuild/"><em>CodeBuild</em></a><em>.</em></p><p>CodePipeline é o serviço gerenciado de Continuous Integration e Continuous Delivery da AWS.</p><p>CodeBuild é um serviço gerenciado de build que pode executar testes e gerar pacotes para nós (no nosso caso, uma imagem Docker).</p><p>Com eles nós podemos criar pipelines para entregar nossa aplicação para o ECS. O fluxo será:</p><ul><li>Você faz o push do código para a master branch.</li><li>CodePipeline irá pegar o código no passo de Source e executará o passo de Build (CodeBuild).</li><li>O passo de Build irá processar nosso Dockerfile, construindo e enviando a imagem para o ECR and chamando o passo de Deploy.</li><li>O passo de Deploy atualiza nosso cluster ECS com a nova imagem.</li></ul><p>Vamos definir nosso Pipeline com o Terraform:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/68275343cb24ec50baf32ce6cc1002e3/href">https://medium.com/media/68275343cb24ec50baf32ce6cc1002e3/href</a></iframe><p>No código acima nós criamos um projeto CodeBuild, usando o seguinte buildspec (arquivo de especificação de build):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/40583c74d4308338360de35bb99a1921/href">https://medium.com/media/40583c74d4308338360de35bb99a1921/href</a></iframe><p>Nós definimos algumas fases no arquivo acima..</p><ul><li>pre_build: Atualiza o aws-cli, configura algumas variáveis de ambiente: REPOSITORY_URL com o repositório ECR e IMAGE_TAG com versão de source do CodeBuild. O repositório ECR é passado como variável pelo Terraform.</li><li>build: Constrói usando o Dockerfile e gera uma imagem Docker e cria uma tag LATEST no repositório.</li><li>post_build: Envia a imagem gerada no passo anterior para o repositório. Cria um arquvio chamado imagedefinitions.json com o seguinte conteúdo: <br>‘[{“name”:”web”,”imageUri”:REPOSITORY_URL”}]’<br>Esse arquivo é usado pelo CodePipeline para atualizar nosso cluster ECS no passo de Deploy.</li><li>artifacts: Pega o arquivo imagedefinitions.jsongerado no passo anterior e usa como o artefato.</li></ul><p>Depois é criado o CodePipeline com 3 estagágios:</p><ul><li>Source: Pega o repositório do Github e executa o próximo passo.</li><li>Build: Executa o CodeBuild que criamos.</li><li>Production: Pega o artefato gerado pelo passo de Build (imagedefinitions.json) e faz o deploy para o ECS.</li></ul><p>Vamos ver tudo trabalhando junto?</p><h3>Executando tudo</h3><p>O código com o exemplo completo pode ser encontrado <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example">aqui</a>.</p><p>Clone ele. Como usamos o Github como provider de código no CodePipeline, você precisa gerar o token de acesso para o repositório. (<a href="https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/">https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/</a>)</p><p>Depois de gerar o token, exporte como uma variável de ambiente.</p><pre>$ export GITHUB_TOKEN=YOUR_TOKEN</pre><p>Agora precisamos importar os módulos e bibliotecas de providers do Terraform.</p><pre>$ terraform init</pre><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*6HQQkkurojqHSIw3ywQTSw.png" /></figure><p>Agora deixe a magia começar!</p><pre>$ terraform apply</pre><p>Ele irá mostrar que o Terraform irá criar alguns recursos e se você deseja continuar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*N_Ce_3nRV-hk4QFgd-J3sw.png" /></figure><p>Digite yes.</p><p>Terraform irá começar a criar nossa infraestrutura.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*lZ7NXzq0NMQmObgMoyoJIg.gif" /></figure><p>Sério, pegue um café até ele terminar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*hl4G1GBfzMSBuBJ2axn0LQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*-w-oLVLxuyebsaUStamQHg.png" /></figure><p>AWESOME!. Nossa infraestrutura está pronta!!. Se você entrar no seu CodePipeline no Dashboard da AWS você ira ver que ele ativou nossa primeira build.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*XfmkQU8ae8v9Pbp6iaJQIA.png" /></figure><p>Espere até todos passos ficarem verdes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*ZdNh3pr5qIdU-vMwlQfi4Q.png" /></figure><p>Pegue o DNS do seu Load Balancer e verifique a aplicação.</p><pre>$ terraform output alb_dns_name</pre><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*WW07XGYH37It1xq5tUnGrQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*wJmKvO2Pd36jtJMZePd-3Q.png" /><figcaption>It is working \o/</figcaption></figure><p>Finalmente, a aplicação está rodando. Magica!</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*mPUc2fU1VPbW6gjbw1DjeQ.gif" /></figure><p>Se tiver alguma dúvida, entre em contato comigo. Esse post foi apenas uma introdução para o ECS usando Fargate e Terraform.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*dsHpznpcd482MHT1fvyc3Q.png" /></figure><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9e8713c775db" width="1" height="1" alt=""><hr><p><a href="https://medium.com/opensanca/deploy-de-aplica%C3%A7%C3%B5es-docker-na-aws-usando-ecs-e-fargate-9e8713c775db">Deploy de aplicações Docker na AWS usando ECS e Fargate</a> was originally published in <a href="https://medium.com/opensanca">Opensanca</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Easy deploy your Docker applications to AWS using ECS and Fargate]]></title>
            <link>https://medium.com/code-pub/easy-deploy-your-docker-applications-to-aws-using-ecs-and-fargate-a988a1cc842f?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/a988a1cc842f</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[aws-ecs]]></category>
            <category><![CDATA[aws]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Wed, 31 Jan 2018 17:03:58 GMT</pubDate>
            <atom:updated>2018-01-31T22:13:58.424Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L_1-B_5m036MTLsyQ135cw.png" /></figure><p>In this post, I will try to demonstrate how you can deploy your Docker application into AWS using ECS and Fargate.</p><p>As an example, I will deploy <a href="http://openjobs.me/">this app</a> to ECS. The source can be found <a href="https://github.com/opensanca/opensanca_jobs/">here</a>.</p><p>I will use <a href="https://www.terraform.io/">Terraform</a> to spin the infrastructure so I can easily track everything that I create as a code. If you want to learn the basics of Terraform, please read my <a href="https://thecode.pub/creating-your-cloud-servers-with-terraform-bfa01a499bad">post about it</a>.</p><h3>ECS</h3><p>What is ECS?</p><p>The Elastic Container Service (ECS) is an AWS Service that handles the Docker containers orchestration in your EC2 cluster. It is an alternative for Kubernetes, Docker Swarm, and others.</p><h4>ECS Terminology</h4><p>To start understanding what ECS is, we need to understand its terms and definitions that differs from the Docker world.</p><ul><li><strong>Cluster: </strong>It is a group of EC2 instances hosting containers.</li><li><strong>Task definition: </strong>It is the specification of how ECS should run your app. Here you define which image to use, port mapping, memory, environments variables, etc.</li><li><strong>Service: </strong>Services launches and maintains tasks running inside the cluster. A Service will auto-recover any stopped tasks keeping the number of tasks running as you specified.</li></ul><h3>Fargate</h3><p>Fargate is a technology that allows running containers in ECS without needing to manage the EC2 servers for cluster. You only deploy your Docker applications and set the scaling rules for it. Fargate is an execution method from ECS.</p><p><strong><em>Show me the code</em></strong></p><p>The full example is on <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example">Github</a>.</p><h3>The project structure</h3><p>Our Terraform project is composed of the following structure:</p><p>├── modules<br>│ └── code_pipeline <br>│ └── ecs <br>│ └── networking <br>│ └── rds<br>├── pipeline.tf<br>├── production.tf<br>├── production_key.pub<br>├── terraform.tfvars<br>└── variables.tf</p><ul><li><strong>Modules</strong> are where we will store the code that handles the creation of a group of resources. It can be reused by all environments (Production, Staging, QA, etc.) without needing to duplicate a lot of code.</li><li><strong>production.tf </strong>is the file that defines the environment itself. It calls the modules passing variables to it.</li><li><strong>pipeline.tf </strong>Since the pipeline can be a global resource without needing to isolate per environment. This file will handle the creation of this pipeline using the `code_pipeline` module.</li></ul><h3>First part, the networking</h3><p>The branch with this part can be found <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/01_networking">here</a>.</p><p>The first thing that we need to create is the VPC with 2 subnets (1 public and 1 private) in each Availability Zone. Each Availability Zone is a geographically isolated region. Keeping our resources in more than one zone is the first thing to achieve high availability. If one physical zone fails for some reason, your application can answer from the others.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cvu1YNJdfezuVfU8kAPgNA.png" /><figcaption>Our networking</figcaption></figure><p>Keeping the cluster on the private subnet protects your infrastructure from external access. The private subnet is allowed only to be accessed from resources inside the public network (In our case, will be the Load Balancer only).</p><p>This is the code to create this structure (it is practically the same from my introduction post of Terraform):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3000fa3a6cfa0ccdb9678d3e4660424d/href">https://medium.com/media/3000fa3a6cfa0ccdb9678d3e4660424d/href</a></iframe><p>The above code creates the VPC, 4 subnets (2 public and 2 private) in each Availability zone. It also creates a NAT to allow the private network access the internet.</p><p><strong>The Database</strong></p><p>The branch with this part can be found <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/02_database">here</a>.</p><p>We will create a RDS database. It will be located on the private subnet. Allowing only the public subnet to access it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59d7deafef35b800131d6d8061ad1e82/href">https://medium.com/media/59d7deafef35b800131d6d8061ad1e82/href</a></iframe><p>With this code, we create the RDS resource with values received from the variables. We also create the security group that should be used by resources that want to connect to the database (in our case, the ECS cluster).</p><p>Ok. Now we have the database. Let’s finally create our ECS to deploy our app \o/.</p><h3>Take Three: The ECS</h3><p>The branch with this part can be found <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example/tree/03_ecs">here</a>.</p><p>We are approaching the final steps. Now, it is the part that we define the ECS resources needed for our app.</p><h4>The ECR repository</h4><p>The first thing is to create the repository to store our built images.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/356acc348a4487a6434c6e60919f7c62/href">https://medium.com/media/356acc348a4487a6434c6e60919f7c62/href</a></iframe><h4>The ECS cluster</h4><p>Next, we need our ECS cluster. Even using Fargate (that doesn’t need any EC2), we need to define a cluster for the application.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/007965a6291dc6a49086c14471cdd92f/href">https://medium.com/media/007965a6291dc6a49086c14471cdd92f/href</a></iframe><h4>The tasks definitions</h4><p>Now, we will define 2 task definitions.</p><ul><li>Web: Contains the definition of the web app itself.</li><li>Db Migrate: This task will only run the command to migrate our database and will die. Since it is a single run task, we don’t need a service for it.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cbba359b380baf0de6398ebbf29d66c0/href">https://medium.com/media/cbba359b380baf0de6398ebbf29d66c0/href</a></iframe><p>The tasks definitions are configured in a JSON file and rendered as a template in Terraform. <br>This is the task definition of the web app:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cd914e0e8af12d8d338da2acd81e85d2/href">https://medium.com/media/cd914e0e8af12d8d338da2acd81e85d2/href</a></iframe><p>In the file above, we are defining the task to ECS. We pass the created ECR image repository as variable to it. We also configure other variables so ECS can start our Rails app. <br>The definition of the DB migration task is almost the same. We only change the command that will be executed.</p><h4>The load balancers</h4><p>Before creating the Services, we need to create the load balancers. They will be on the public subnet and will forward the requests to the ECS service.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3033baa23cf2903207443201d7762619/href">https://medium.com/media/3033baa23cf2903207443201d7762619/href</a></iframe><p>In the file above we define that our target group will use HTTP on port 80. We also create a security group to allow access into the port 80 from the internet. After, we create the Application Load Balancer and the listener. To use Fargate, you should use an Application Load Balancer instead an Elastic Load Balancer.</p><h4>Finally, the ECS service</h4><p>Now we will create the service. To use Fargate, we need to specify the lauch_type as Fargate.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a7598bcb9d94c58ca13ca1be88a7d69/href">https://medium.com/media/9a7598bcb9d94c58ca13ca1be88a7d69/href</a></iframe><h4>Auto-scaling</h4><p>Fargate allows us to auto-scale our app easily. We only need to create the metrics in CloudWatch and trigger to scale it up or down.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0163c1beacb8ade34e9f09209dd80916/href">https://medium.com/media/0163c1beacb8ade34e9f09209dd80916/href</a></iframe><p>We create 2 auto scaling policies. One to scale up and other to scale down the desired count of running tasks from our ECS service.</p><p>After, we create a CloudWatch metric based on the CPU. If the CPU usage is greater than 85% from 2 periods, we trigger the alarm_action that calls the scale-up policy. If it returns to the Ok state, it will trigger the scale-down policy.</p><h3>The Pipeline to deploy our app</h3><p>Our infrastructure to run our Docker app is ready. But it is still boring to deploy it to ECS. We need to manually push our image to the repository and update the task definition with the new image and update the new task definition. We can run it through Terraform, but it could be better if we have a way to push our code to Github in the master branch and it deploys automatically for us.</p><p><em>Entering, </em><a href="https://aws.amazon.com/codepipeline"><em>CodePipeline</em></a><em> and </em><a href="https://aws.amazon.com/codebuild/"><em>CodeBuild</em></a><em>.</em></p><p>CodePipeline is a Continuous Integration and Continuous Delivery service hosted by AWS.</p><p>CodeBuild is a managed build service that can execute tests and generate packages for us (in our case, a Docker image).</p><p>With it, we can create pipelines to delivery our code to ECS. The flow will be:</p><ul><li>You push the code to master’s branch</li><li>CodePipeline gets the code in the Source stage and calls the Build stage (CodeBuild).</li><li>Build stage process our Dockerfile building and pushing the Image to ECR and triggers the Deploy stage</li><li>Deploy stage updates our ECS with the new image</li></ul><p>Let’s define our Pipeline with Terraform:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/68275343cb24ec50baf32ce6cc1002e3/href">https://medium.com/media/68275343cb24ec50baf32ce6cc1002e3/href</a></iframe><p>In the above code, we create a CodeBuild project, using the following buildspec (build specifications file):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/40583c74d4308338360de35bb99a1921/href">https://medium.com/media/40583c74d4308338360de35bb99a1921/href</a></iframe><p>We defined some phases in the above file.</p><ul><li>pre_build: Upgrade aws-cli, set some environment variables: REPOSITORY_URL with the ECR repository and IMAGE_TAG with the CodeBuild source version. The ECR repository is passed as a variable by Terraform.</li><li>build: Build the Dockerfile from the repository tagging it as LATEST in the repository URL.</li><li>post_build: Push the image to the repository. Creates a file named imagedefinitions.json with the following content: <br>‘[{“name”:”web”,”imageUri”:REPOSITORY_URL”}]’<br>This file is used by CodePipeline to upgrade your ECS cluster in the Deployment stage.</li><li>artifacts: Get the file created in the last phase and uses as the artifact.</li></ul><p>After, we create a CodePipeline resource with 3 stages:</p><ul><li>Source: Gets the repository from Github (change it by your repository information) and pass it to the next stage.</li><li>Build: Calls the CodeBuild project that we created in the step before.</li><li>Production: Gets the artifact from Build stage (imagedefinitions.json) and deploy to ECS.</li></ul><p>Let’s see they working together?</p><h3>Running all together</h3><p>The code with the full example is <a href="https://github.com/duduribeiro/terraform_ecs_fargate_example">here</a>.</p><p>Clone it. Also, since we use Github as the CodePipeline source provider, you need to generate a token to access the repositories. [Read here](<a href="https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/">https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/</a>) to generate yours.</p><p>After generating your token, export it as an environment variable.</p><pre>$ export GITHUB_TOKEN=YOUR_TOKEN</pre><p>Now, we need to import the modules and the provider library.</p><pre>$ terraform init</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6HQQkkurojqHSIw3ywQTSw.png" /></figure><p>Now, let the magic begin!</p><pre>$ terraform apply</pre><p>it will display that Terraform will create some resources, and if you want to continue</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/812/1*N_Ce_3nRV-hk4QFgd-J3sw.png" /></figure><p>Type yes.</p><p>Terraform will start create our infraestructure.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/260/1*lZ7NXzq0NMQmObgMoyoJIg.gif" /></figure><p>Seriously, get a coffee until it finishes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hl4G1GBfzMSBuBJ2axn0LQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-w-oLVLxuyebsaUStamQHg.png" /></figure><p>AWESOME!. Our infrastructure is ready!!. If you enter in your CodePipeline at AWS Dashboard, you can see that it also triggered the first build:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*XfmkQU8ae8v9Pbp6iaJQIA.png" /></figure><p>Wait until all the Stages are green.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/456/1*ZdNh3pr5qIdU-vMwlQfi4Q.png" /></figure><p>Get your Load Balancer DNS and check the deployed application:</p><pre>$ terraform output alb_dns_name</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WW07XGYH37It1xq5tUnGrQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wJmKvO2Pd36jtJMZePd-3Q.png" /><figcaption>It is working \o/</figcaption></figure><p>Finally, the app is running. Almost magic!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/275/1*mPUc2fU1VPbW6gjbw1DjeQ.gif" /></figure><p>If you have any questions about it, contact me. This was just an introduction post about ECS with Fargate using Terraform.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dsHpznpcd482MHT1fvyc3Q.png" /></figure><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a988a1cc842f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/easy-deploy-your-docker-applications-to-aws-using-ecs-and-fargate-a988a1cc842f">Easy deploy your Docker applications to AWS using ECS and Fargate</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Evaluate your ruby code directly from VIM]]></title>
            <link>https://medium.com/code-pub/evaluate-your-ruby-code-directly-from-vim-5f12a83c1163?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/5f12a83c1163</guid>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[vim]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Mon, 07 Aug 2017 21:41:45 GMT</pubDate>
            <atom:updated>2017-08-08T13:29:27.210Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/476/1*-t37W7RSogM604xODOKokw.png" /></figure><p>When I am writing code, usually I want to evaluate some piece of code. I used to do the following actions:</p><ul><li>Copy and paste my code to IRB (or run my ruby script file directly from the terminal).</li><li>When using tmux, send my code directly from vim to tmux with the <a href="https://github.com/christoomey/vim-tmux-runner">vim-tmux-runner</a> plugin.</li></ul><p>The first option needs an extra work of copying and pasting content. I prefer the second option, but sometimes I forgot to attach the VTR pane and get errors.</p><p>Now I’m using <a href="https://github.com/JoshCheek/seeing_is_believing">https://github.com/JoshCheek/seeing_is_believing</a> along with <a href="https://github.com/t9md/vim-ruby-xmpfilter">https://github.com/t9md/vim-ruby-xmpfilter</a> plugin</p><p>I set my .vimrc with the following content (I use Plug to manage my dependencies):</p><pre>Plug &#39;t9md/vim-ruby-xmpfilter&#39;</pre><pre>&quot; Enable seeing-is-believing mappings only for Ruby<br>let g:xmpfilter_cmd = &quot;seeing_is_believing&quot;</pre><pre>autocmd FileType ruby nmap &lt;buffer&gt; &lt;F4&gt; &lt;Plug&gt;(seeing_is_believing-mark)<br>autocmd FileType ruby xmap &lt;buffer&gt; &lt;F4&gt; &lt;Plug&gt;(seeing_is_believing-mark)<br>autocmd FileType ruby imap &lt;buffer&gt; &lt;F4&gt; &lt;Plug&gt;(seeing_is_believing-mark)</pre><pre>autocmd FileType ruby nmap &lt;buffer&gt; &lt;F6&gt; &lt;Plug&gt;(seeing_is_believing-clean)<br>autocmd FileType ruby xmap &lt;buffer&gt; &lt;F6&gt; &lt;Plug&gt;(seeing_is_believing-clean)<br>autocmd FileType ruby imap &lt;buffer&gt; &lt;F6&gt; &lt;Plug&gt;(seeing_is_believing-clean)</pre><pre>autocmd FileType ruby nmap &lt;buffer&gt; &lt;F5&gt; &lt;Plug&gt;(seeing_is_believing-run)<br>autocmd FileType ruby xmap &lt;buffer&gt; &lt;F5&gt; &lt;Plug&gt;(seeing_is_believing-run)<br>autocmd FileType ruby imap &lt;buffer&gt; &lt;F5&gt; &lt;Plug&gt;(seeing_is_believing-run)</pre><p>Now I can visual select my code, use F4 to mark and that line will be evaluated, press F5 and get the result of that code. After, I can clean all marks with F6 .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/922/1*7gjSHyVfzMhsoa038YicQg.gif" /></figure><p>Cheers,<br>🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5f12a83c1163" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/evaluate-your-ruby-code-directly-from-vim-5f12a83c1163">Evaluate your ruby code directly from VIM</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating review apps per pull requests]]></title>
            <link>https://medium.com/code-pub/creating-review-apps-per-pull-requests-c8c9dfa02ffb?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8c9dfa02ffb</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[pull-request]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Tue, 04 Jul 2017 11:44:00 GMT</pubDate>
            <atom:updated>2018-07-20T15:29:43.885Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*JKrc7lJZGa3_R1jA8yXxuQ.png" /></figure><p>In this post I will show a simple example about how to create apps for each pull request (or, creating your own Gitlab/Heroku Review App).</p><p>Let’s imagine the following scenario of a development team:</p><ul><li>Developer creates a new feature branch.</li><li>Developer pushes the branch</li><li>Developer opens a pull request so other developers can check his code and test the feature</li><li>CI runs the tests and makes the branch green if it passes</li></ul><p>Now the developer should send the feature to someone else to test it. The feature should not be merged into master so it can be tested. They should be tested isolated because one feature can interfere with another and master should only have deployable code. With this in mind, we will create an environment for each pull request using Docker.</p><p>I will use Jenkins as CI because I want to use an open source tool to demonstrate this, but you can use any CI tool that you prefer.</p><p>This post will assume that you already have Jenkins installed. I will use <a href="https://github.com/opensanca/opensanca_jobs">this</a> opensource Rails application as an example. Fork this repo into your account and clone it in your computer.</p><pre>git clone <a href="https://github.com/duduribeiro/openjobs_experiment.git">https://github.com/yourgithubusername/opensanca_jobs.git</a></pre><p>You can get the full code repo that I used on <a href="https://github.com/duduribeiro/openjobs_jenkins_test">https://github.com/duduribeiro/openjobs_jenkins_test</a></p><h3>Configuring Docker</h3><p>The first step that we need is to configuring our app with docker.</p><p>A Dockerfile is a file that has instructions to build an image that contains our application. You can learn more about Docker in their <a href="https://docs.docker.com/">documentation</a>.</p><p>Create a file named Dockerfile in the app folder:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/003fceac9920828ca28ed4512189df82/href">https://medium.com/media/003fceac9920828ca28ed4512189df82/href</a></iframe><p>This is our instructions to build an image. The first command FROM specifies to docker that we will use the image <a href="https://hub.docker.com/r/_/ruby/">ruby:2.4.1</a> as our base image.</p><p>After, the first RUN command installs all dependencies the app needs: yarn, imagemagick and node (to precompile the assets. A fancy solution is to use a different container only with node and sprockets to precompile the assets). The second RUN command, creates a folder /var/app that will be responsible to store the application. The COPY command moves the current folder to the container in the /var/app folder. The next RUN command installs all dependencies from Rails and yarn. The CMD specify the command that the container should execute when it runs the image. You can learn more about Dockerfile <a href="https://docs.docker.com/engine/reference/builder/">here</a>.</p><p>With this file we can build our image.</p><pre>docker build -t myimage .</pre><p>With this command, we are building our Dockerfile and generating an image with the tag myimage .</p><p>Running docker image ls we can check our image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*q2LJXXVz4fAXwOD2wp2jjA.png" /></figure><p>Now we can start our application:</p><pre>docker run -d -p 3000:3000 myimage</pre><p>The -d option tells to docker that this container will run in background and -p 3000:3000 will connect the local port 3000 with the exposed 3000 port from the container.</p><p>We can navigate now to <a href="http://localhost:3000/">http://localhost:3000</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fT58lZr0Pz8zUfEnn43y1Q.png" /></figure><p>And we receive this error. This is because our app requires a database connection. We need to run a new container with the database and link both containers so they can communicate with each other. Each container should have only one purpose, so, you should not run more than 1 service in a single container (ie: a container with the application and the database).</p><p>Instead of manually run each container, we will use <a href="https://docs.docker.com/compose/">docker compose</a> , a tool to help us to run multi containers applications.</p><p>Use docker ps and docker kill to destroy your application container/</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/414/1*M2HI_2fGRmo8knwM9VGQgQ.png" /><figcaption>Get the id of the container</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/374/1*3GpKXZWWfJIR5Qa603m_0w.png" /><figcaption>Kill the container</figcaption></figure><p>Create a docker-compose.yml file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e51014a8240e0aae94472eb7c41940b5/href">https://medium.com/media/e51014a8240e0aae94472eb7c41940b5/href</a></iframe><p>In this file, we are creating 2 services. One for the database using the postgres image, and another one with our web application using our Dockerfile image.</p><p>In the environment item on web service, I’m setting the DATABASE_URL environment variable. If this environment variable is set, rails will use it replacing the loaded configurations from config/database.yml. Read more about this <a href="http://edgeguides.rubyonrails.org/configuring.html#configuring-a-database">here</a>. DATABASE_URL follows this pattern for postgres:<br> postgres://user:password@host/database_name.Since our database user does not have password, we leave empty after the : . We don’t fill the database_name because we want the configured one in config/database.yml (one database name per environment).</p><p>Create the databases and run the migrations:</p><pre>docker-compose run --rm web rake db:create db:migrate</pre><p>In this command, we will run a container with the web service and execute the command rake db:create db:migrate . The --rm option is to remove the container after the execution.</p><p>Run the tests:</p><pre>docker-compose run --rm -e RAILS_ENV=test web rake db:drop db:create db:migrate</pre><pre>docker-compose run --rm web rspec</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qzMCx1X0ZNlKkKNYSsN3MA.png" /><figcaption>Success</figcaption></figure><p>Now we can start the application:</p><pre>docker-compose up</pre><p>Access <a href="http://localhost:3000/">http://localhost:3000</a> again and now our app is working.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RlLvjbA3ZZL6CR5U2hBKeQ.png" /></figure><h3>Configuring our pipeline</h3><p>We will use Jenkins pipeline as code to configure our pipeline. Read more about it <a href="https://jenkins.io/solutions/pipeline/">here</a>. We need to have <a href="https://www.docker.com/">docker</a>, <a href="https://stedolan.github.io/jq/">jq</a> and Jenkins installed on CI server.</p><p>We will use the following Jenkins plugins: <a href="https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin">Blue Ocean</a>, <a href="https://wiki.jenkins-ci.org/display/JENKINS/BlueOcean+Pipeline+Editor+Plugin">Blue Ocean Pipeline Editor</a>, <a href="https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin">GitHub Pipeline for Blue Ocean</a></p><p>Create a file named Jenkinsfile in the project root with the following content:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/facd69ee31f3101f4a74cad00a85ab60/href">https://medium.com/media/facd69ee31f3101f4a74cad00a85ab60/href</a></iframe><p>We have 4 stages on this pipeline:</p><ul><li><strong>Build</strong>: It will build our Dockerfile and generate an image from this build tagged with openjobs with version named latest . It will use docker-compose to build, install dependencies, create and migrate the database created with compose .</li><li><strong>Tests</strong>: It will run 2 parallel steps, one to run unit tests, and another to run feature tests.</li><li><strong>Deploy to staging</strong>: If it is the master branch, it will deploy the app to staging.domain . We will cover this in the next steps.</li><li><strong>Create feature environment</strong>: If it isn’t the master branch, it will deploy the app to branchname.domain . We will cover this in the next steps.</li></ul><p>Let’s create our pipeline on Jenkins. Push your code, go to your Jenkins, and access the blue ocean interface and click in the New Pipeline button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eXA3HCPFSBQ-mvUNWHV8gw.png" /></figure><p>In the next screen, select Github, choose you account and find the repository. Click in Create Pipeline</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_2gQbUaarl7LFVDOKNcqxw.png" /></figure><p>And our build is passing 🎉</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*--GrJVBvaS8b7WMavEazew.png" /></figure><p>The red icon on the build is because this step will not run, since the branch built was master .</p><p>Now we need to create our environments after the build and dynamic route a domain to a specific container.</p><p>Entering, <a href="https://traefik.io/">Traefik</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/340/1*0A8y77LMVEQtxLSQ_J0zJA.png" /></figure><p>Traefik is a tool that will help us make the dynamic routing and act as a load balancer. Example: If I access http://mybranch.mydomain.com , I want to access the container containing the app with mybranch that should be started by Jenkins.</p><h3>Creating dynamic environments</h3><p>The following steps should be executed on CI server.</p><p>We will use <a href="https://docs.docker.com/engine/swarm/">Docker Swarm</a>. It is very helpful to create a docker cluster. I will use only one server to demonstrate, but you are able also to create a cluster.</p><p>Initialize swarm cluster:</p><pre>docker swarm init --advertise-addr=10.10.0.5</pre><p>This will initialize our server as the swarm master. It will generate a command that you can use to join the cluster in other servers.</p><p>Create a network that we can use with traefik and our containers:</p><pre>docker network create --driver=overlay --attachable traefik-net</pre><p>Initialize traefik:</p><pre>docker service create \<br>--name traefik \<br>--constraint &#39;node.role==manager&#39; \<br>--publish 80:80 \<br>--publish 8081:8080 \<br>--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \<br>--network traefik-net \<br>traefik \<br>--docker \<br>--docker.swarmmode \<br>--docker.domain=apps.carlosribeiro.me \<br>--docker.watch \<br>--logLevel=DEBUG \<br>--web</pre><p>This will initialize traefik. You should treat this initialization on some startup script on your server.</p><p>If we access our server in 8081 port, we can access traefik dashboard.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Let1rhq73CWwjCyWSZgKLw.png" /></figure><p>The docker.domain specify that we will access the apps through appname.apps.carlosribeiro.me . To allow this, I created two alias on my DNS server pointing to the CI server’s IP:</p><ul><li>apps.carlosribeiro.me</li><li>*.apps.carlosribeiro.me</li></ul><h3>Editing Jenkinsfile to create the environments</h3><p>Lets add a method on Jenkinsfile that will create a environment when the build is triggered.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8315e300d1449968162a8236ce1e1ea0/href">https://medium.com/media/8315e300d1449968162a8236ce1e1ea0/href</a></iframe><p>This method will accept an argument to inform the name of the environment. This name will be used to prefix the services name. It will stop all docker-compose containers that are still running. After, it will remove if exist, services with the same name (This is to recreate the environment when we push again to the branch). After, we create 3 services. One for Postgres, another for Redis, and the app itself using the imaged that we built on build step. The last command, runs a temporary docker container to create and migrate the database and precompile the assets (you can fetch your previously dump from production too).</p><p>Look that in the app service, we specify some environment variables.</p><ul><li><strong>REDIS_URL, DATABASE_URL</strong>: Endpoint to connect in both services using the previously created services hosts.</li><li><strong>RAILS_ENV: </strong>We set it to production . It will enforce that our app should behave like a production environment.</li><li><strong>RAILS_SERVE_STATIC_FILES</strong>: Since we will not have a nginx in front of the app server, we need to set this to tell rails to service static files for us.</li></ul><p>— label ‘traefik.port’ will inform traefik what is the container’s exposed port. In our case, is the 3000.</p><p>— name inform the service name that traefik will identify as the prefix on domain.</p><p>Now, lets call this method on our pipeline’s stages.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/75f30f7e1b15363b2f3b5ba14aa9b5ff/href">https://medium.com/media/75f30f7e1b15363b2f3b5ba14aa9b5ff/href</a></iframe><p>If it is master branch, we will deploy into an app called staging . If not, it will call the app with the same name of branch.</p><p>Look the final Jenkinsfile:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/75bc614142f13ad02f8259736f7dd458/href">https://medium.com/media/75bc614142f13ad02f8259736f7dd458/href</a></iframe><p>Push master and let’s wait Jenkins create our staging for us.</p><p>Our build passes…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dXnyUalMlB61BcsFeyDApA.png" /></figure><p>… and our app is deployed on staging:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*160K0xkSl3dJ83duQ5Mq8g.png" /></figure><p>Now let’s open a pull request to change this button color.</p><pre>git checkout -b 01-change-button-color</pre><p>Edit app/assets/stylesheets/application.scss</p><pre>.btn-register-vacancy {<br>  background-color: #2769ff;<br>  width: 300px;<br>  height: 35px;<br>  border-radius: 5px;<br>}</pre><p>Push this branch, and open a pull request.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eLQPHGOVDArlDDrvPmnLXQ.png" /></figure><p>Github will tell us that the build is pending.</p><p>…</p><p>Job is passing</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vZwLnuEbj8egwCvUQ8cAIg.png" /></figure><p>and Github is informed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W6y5kHRubVrYJgFeACdRIA.png" /></figure><p>And now we have our dynamic environment up and running</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M_n7-CK4mquX42LkpM_pmQ.png" /></figure><p>I can send this URL to a QA or a PO review it before merging. If something is wrong, it can be validated before it is on master.</p><p>That’s all</p><p>Cheers 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8c9dfa02ffb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/creating-review-apps-per-pull-requests-c8c9dfa02ffb">Creating review apps per pull requests</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating your cloud servers with Terraform]]></title>
            <link>https://medium.com/code-pub/creating-your-cloud-servers-with-terraform-bfa01a499bad?source=rss-35e349b8ec68------2</link>
            <guid isPermaLink="false">https://medium.com/p/bfa01a499bad</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[infrastructure-as-code]]></category>
            <category><![CDATA[aws]]></category>
            <dc:creator><![CDATA[Carlos Ribeiro]]></dc:creator>
            <pubDate>Tue, 04 Apr 2017 14:56:49 GMT</pubDate>
            <atom:updated>2017-04-04T14:56:49.819Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*2QvMO07P4smlK-GE45EROA.jpeg" /><figcaption>Terraforming Mars</figcaption></figure><p>Sometimes when you handle a lot of servers in the cloud, it is pretty easy to get lost on your infrastructure. “Where is that freaking server that I can’t find?”, or even “Why is this instance for?”.</p><p>In this post, I will introduce one tool that I found myself liking a lot: To have a bootstrap of an Infrastructure as Code flow in my applications. <a href="https://www.terraform.io/">Terraform</a>. Infrastructure as Code (IaC) allows us to have a repository with code that describes our infrastructure. This way, we can avoid reminding how rebuild the entire infrastructure for an application. It will be on the code and it can be versioned and tested. And if something goes wrong, we can revert it. It is very useful having a continuous integration of the infrastructure. The whole team knows how it was built and all the pieces. You can apply the same flow that you use in you app code to your infrastructure, i.e: Someone makes a change, open a Pull Request, someone reviews it and after it is approved, you merge and your CI tools apply the changes in your environment.</p><h4>Why Terraform? What is the difference between Chef, Puppet or Ansible?</h4><p>Chef, Puppet and Ansible are IaC tools too, but they focus on <strong>configuring</strong> operating system and applications. They are called Configuration Management Tools and they also can build infrastructure on the cloud with a help of plugins, but usually it is hard to configure and sometimes it is limited. With Terraform you can build from the services to the networking part. You can use Terraform to create the infrastructure and a configuration management tool to configure the applications. Terraform can’t replace your configuration management tool, but it’s made to work together with it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*sS6MVPyxzhn3O8pA3nw0kg.jpeg" /></figure><h3>A practical example</h3><p>In order to follow this article, you’ll need an AWS account.</p><p>Let’s create the servers for our web application. Take a look at this diagram of the resources that we’ll build.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nYWHvlp87BBsE6gI2TbKYA.png" /></figure><p>We will create 2 subnets (one for public access and another private). We have Elastic Load Balancer in the public subnet to handle the traffic to our web servers. Our web servers will be on the private subnet and it will only be accessible through the Load Balancer. This mean that we won’t have direct access to make connections (for example, SSH) on the server. In order to access via SSH an instance on a private subnet, you’ll need a bastion host and connect to the web server through it. Thus, we will create the bastion host on the public subnet.</p><p>Before getting your hands dirty, you need to install Terraform. Follow the instructions of <a href="https://www.terraform.io/intro/getting-started/install.html">this link</a> to install it in your machine.</p><h4>Directory structure</h4><p>Let’s create a folder to handle our infrastructure code.</p><pre>mkdir ~/terraform</pre><p>I like to follow this pattern when working with Terraform:</p><pre>├── modules<br>│   ├── networking<br>│   └── web<br>├── production<br>└── staging</pre><p>Let’s create this folder structure</p><pre>cd ~/terraform<br>mkdir -p modules/{networking,web} production staging</pre><p>Our modules folder contains all the <strong>shared code</strong> to create the pieces of the infrastructure (web servers, app servers, databases, vpc, etc). Each folder inside the modules folder is related to a specific module. <br>Next, I have folders for my environments (staging, production, qa, development, etc). Each of this folder contains code to use our shared modules and create a different architecture for each environment (This is my personal approach using Terraform, but feel free to work on a different way).</p><h4>Our first module: Networking</h4><p>Let’s create our networking module. This will be responsible for creating the networking pieces of our infrastructure, like VPC, subnets, routing table, NAT server and the bastion instance.</p><p>Before we get deep in the code, I wanna explain how terraform works:</p><p>Terraform will provide us with some commands. Some of them are:</p><p><strong>plan</strong>: Displays all the changes that Terraform makes on our infrastructure<br><strong>apply</strong>:<strong> </strong>Executes all the changes to the infrastructure<br><strong>destroy</strong>: Destroys everything that was created with Terraform</p><p>When you run Terraform inside a directory, it loads ALL .tf files from the directory and execute them (will not load on subfolders). Terraform will first create a graph of the resources to apply only in the final phase, so you don’t need to specify the resources in any specific order. The graph will determine the relations between the resources and ensure that Terraform creates they in the right order.</p><h4>Continuing on our networking module</h4><p>Enter in the networking module</p><pre>cd modules/networking</pre><p>Let’s create our first tf file. The one that specifies all variables needed for our module.</p><pre>touch variables.tf</pre><p>Insert the following content on the variables.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b7e4d1ba69d4475fee457b86c4dab162/href">https://medium.com/media/b7e4d1ba69d4475fee457b86c4dab162/href</a></iframe><p>These are all variables that our networking module needs in order to create all resources. We need the CIDR for the VPC and the subnets, the AWS region that we will use, the key name and the environment that we are building.<br>This is the way that you specify a variable in Terraform</p><pre>variable &quot;variable_name&quot; {<br>  description = &quot;The description of the variable&quot;<br>  default = &quot;A default value if this isn&#39;t set<br>}</pre><p>Ok. Now we have our variables.tf file to specify the interface of our module. Let’s create the file that will create networking stuffs for our module. Create the main.tf file in the networking folder. (you can specify any name that you want. Remember, all tf files will be loaded).</p><pre>touch main.tf</pre><p>Insert the following content on the main.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4a31095d5f547a058e6a12c95e4a8fd/href">https://medium.com/media/b4a31095d5f547a058e6a12c95e4a8fd/href</a></iframe><p>Here we are creating all the networking part of our infrastructure based on the diagram that we saw. A VPC, both subnets (public and private), the Internet Gateway to the public subnet, the NAT server for the private subnet, the bastion host and all security group for the VPC, allowing inbound and outbound inside the VPC, and the security group for the bastion host, allowing the SSH on the Port 22. You can check <a href="http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html">this link from AWS</a> for more details.</p><p>This is how we create a resource on Terraform.</p><pre>resource &quot;resource&quot; &quot;name&quot; {<br>  attribute = &quot;value&quot;<br>}</pre><p>The resource is the name of the resource that we want to build. Each cloud provider has different resources. For example, <a href="https://www.terraform.io/docs/providers/aws/index.html">these resources</a> from AWS. We can also concatenate values. Remember that we created the variables file? We use them here with the var.variable_name . Like in this part of the code, which we use the key_name variable that we specified in the variables file:</p><pre>resource &quot;aws_instance&quot; &quot;bastion&quot; {<br>  ...<br>  key_name = &quot;${var.key_name}&quot;<br>  ...<br>}</pre><p>This is how we created the instance and called its bastion. You can also get property of this resource in other parts of the code. Example:</p><pre>resource &quot;someresource&quot; &quot;somename&quot; {<br>  attribute = &quot;${aws_instance.bastion.id}&quot;<br>}</pre><p>We use the same idea of var concatenation. But we specify ${resource_type.resource_name.property} .</p><p>Our networking module is almost ready. We need to output some variables after the module build the resources, so we can use it in other parts of the code. Terraform has the <a href="https://www.terraform.io/intro/getting-started/outputs.html">output command,</a> allowing us to expose variables.</p><p>Create the output.tf file inside the networking folder.</p><pre>touch output.tf</pre><p>Insert the following content on the output.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9ab6de1cc65f4da4045c7b13600f027d/href">https://medium.com/media/9ab6de1cc65f4da4045c7b13600f027d/href</a></iframe><p>This is how we output a variable from our module:</p><pre>output &quot;variable_name&quot; {<br>  value = &quot;variable value&quot;<br>}</pre><p>This will allow us to get these variables outside the module.</p><h4>Using our module to build the networking from our environment</h4><p>Now that our networking module is ready, we can use it to build our networking from our environment (i.e, staging).<br>This is how our terraform folder looks now:</p><pre>├── modules<br>│   ├── networking<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── web<br>├── production<br>└── staging</pre><p>Go into staging folder.</p><pre>cd ~/terraform/staging</pre><p>First, create the public key for the staging servers</p><pre>ssh-keygen -t rsa -C &quot;staging_key&quot; -f ./staging_key</pre><p>Let’s create our main file. In this file, we will specify information of the AWS provider.</p><pre>touch _main.tf</pre><p>(We use _ at the beginning of the name because since terraform loads all files alphabetically, we need this to be loaded first, since it will create the keypair)</p><p>You can specify things like Access and secret key in some ways:</p><ul><li>Specify it directly in the provider (not recommended)</li></ul><pre>provider &quot;aws&quot; {<br>  region     = &quot;us-west-1&quot;<br>  access_key = &quot;myaccesskey&quot;<br>  secret_key = &quot;mysecretkey&quot;<br>}</pre><ul><li>Using the AWS_ACCESS_KEY and AWS_SECRET_KEY environment variables</li></ul><pre>$ export AWS_ACCESS_KEY_ID=&quot;myaccesskey&quot;<br>$ export AWS_SECRET_ACCESS_KEY=&quot;mysecretkey&quot;</pre><pre>terraform plan</pre><p>The second option is recommended because you don’t need to expose your secrets on the file. <strong>Bonus point</strong>: If you have the <a href="https://aws.amazon.com/pt/cli/">AWS cli</a> you don’t need to export these variables. Only run the aws configure command and terraform will use the variables that you set on it.</p><p>Insert the following content on the _main.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cdc58319738177cc97837db280daa355/href">https://medium.com/media/cdc58319738177cc97837db280daa355/href</a></iframe><p>We will only specify the region to the provider. Both access and secret key, we will rely on the AWS cli .</p><p>Now create the networking.tf file. It will use our module to create the resources.</p><pre>touch networking.tf</pre><p>Insert the following content on the networking.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8cbdd8aa51f47ad77e3ef6bd5be9d78c/href">https://medium.com/media/8cbdd8aa51f47ad77e3ef6bd5be9d78c/href</a></iframe><p>This is how we use <a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=1&amp;cad=rja&amp;uact=8&amp;ved=0ahUKEwjYr97GxIHTAhVCkJAKHWi8AsQQFggaMAA&amp;url=https%3A%2F%2Fwww.terraform.io%2Fdocs%2Fmodules%2Fusage.html&amp;usg=AFQjCNHaKs5YpQe2IsNwXa9ikVF3N67CRw&amp;bvm=bv.151426398,d.Y2I">modules on Terraform</a>.</p><pre>module &quot;name&quot; {<br>  source    = &quot;location_path&quot;<br>  attribute = &quot;value&quot;<br>}</pre><p>The module attributes are all variables that we specified before in the variables.tf file from our networking module. Look that we are passing more variables to our module attributes. We need our environment to require these variables too. Create the variables.tf file for our staging environment.</p><pre>touch variables.tf</pre><p>Add the following content to the variables.tf file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/839acf0f491f2c5940c4efee955ced99/href">https://medium.com/media/839acf0f491f2c5940c4efee955ced99/href</a></iframe><p>This file follows the same pattern of the module’s variables file. These are all variables that we need to build our staging networking piece.</p><p>Ok… I think that we are ready to go. Your terraform’s folder structure should be like this:</p><pre>├── modules<br>│   ├── networking<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── web<br>├── production<br>└── staging<br>    ├── _main.tf<br>    ├── networking.tf<br>    └── variables.tf</pre><p>Run this command on staging folder: (Terraform’s commands should be run on the environments folder).</p><pre>cd ~/terraform/staging<br>terraform get<br>terraform plan</pre><p>*the terraform get command only syncs all modules.</p><p>After executing the terraform plan command, it will ask you a lot of informations (our variables). Answer they:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/646/1*CKnvWOjniPmXHpbI8u6oNA.png" /></figure><p>It will output a lot of things. Resources that will be created. You can analyze it to check if everything is ok. terraform plan will output only the planned change of the infrastructure.</p><p>Now, let’s apply these modifications.</p><pre>terraform apply</pre><p>But it is asking for all that information again. To avoid this, you can follow 2 ways to automatically inject variable’s value to Terraform</p><ul><li>Using a terraform.tfvars file and remember to ignore this file in your VCS.</li><li>Specifying variables in Environment variables. TF_VAR_environment=staging terraform apply (this can be useful when you run through some CI tool)</li></ul><p>We will follow the first way. So, create a terraform.tfvars file</p><pre>touch terraform.tfvars</pre><p>Insert the following content on terraform.tfvars :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e4696a8d950d82075de33e57604cfd4d/href">https://medium.com/media/e4696a8d950d82075de33e57604cfd4d/href</a></iframe><p>Now you can run the apply command without being asked for variable’s value.</p><pre>terraform apply</pre><p>You should receive the following message:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/868/1*Lb4nvDoCX_hOyDckORfLOg.png" /></figure><p>Congratulations..</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*6447MjaXTKhttJ5WlqTVpQ.gif" /></figure><p>This will generate 2 files on the staging folder: terraform.tfstate and terraform.tfstate.backup</p><p>Terraform controls all their resources on this terraform.tfstate file. You should <strong>NEVER </strong>delete this file. If you do, terraform will think that it needs to create new resources and will lose tracking with the others that it has been already created.</p><p>And how can I make my team in sync with the state?</p><p>You have 2 ways to keep the team with the remote in sync. You can commit this .tfstate file to your VCS repository, or use <a href="https://www.terraform.io/docs/state/remote.html">Terraform Remote State</a>. If you rely on terraform’s remote state, I really recommend you to use some wrapper tool for Terraform like <a href="https://github.com/gruntwork-io/terragrunt">Terragrunt</a>. It can handle the remote state locking and initializing it for you.</p><h4>Creating the web servers</h4><p>This is our folder structure until now.</p><pre>├── modules<br>│   ├── networking<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── web<br>├── production<br>└── staging<br>    ├── _main.tf<br>    ├── networking.tf<br>    ├── staging_key<br>    ├── staging_key.pub<br>    ├── terraform.tfstate<br>    ├── terraform.tfstate.backup<br>    ├── terraform.tfvars<br>    └── variables.tf</pre><p>Go into our web module</p><pre>cd ~/terrform/modules/web</pre><p>Following the same flow we used to create the networking module, let’s create our variables file to specify the interface to our module:</p><pre>touch variables.tf</pre><p>Insert the following content on variables.tf :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/732217908f7d46161103837e6d4a53ac/href">https://medium.com/media/732217908f7d46161103837e6d4a53ac/href</a></iframe><p>Create the main.tf file to handle the creation of the resources.</p><pre>touch main.tf</pre><p>Insert the following content on main.tf :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/77a1072b408e91c51feeef4467f208ad/href">https://medium.com/media/77a1072b408e91c51feeef4467f208ad/href</a></iframe><p>This file is practically the same from our networking’s main.tf, we are only creating different resources. <br>Some differences that you can note:</p><ul><li>In this example we are using the count attribute. It specifies to Terraform, to create N times this resource. If we pass 5 on the value, it will create 5 instances.</li></ul><pre>resource &quot;aws_instance&quot; &quot;web&quot; {<br>  count = &quot;${var.web_instance_count}&quot;<br>  ...<br>}</pre><p>and you can create dynamic names with the counting number using the count.index property, for example:</p><pre>tags {<br>  Name = &quot;web-server-${count.index + 1}&quot;<br>}</pre><ul><li>We are passing a file to the user_data property. In order to execute some code on the instance initialization, we need to pass the user_data attribute to our instance and we are specifying a file.</li></ul><pre>user_data = &quot;${file(&quot;${path.module}/files/user_data.sh&quot;)}&quot;</pre><p>This will load the content of the user_data.sh file and pass to the attribute.</p><p>But we haven’t created this file yet, let’s do it. On the web module folder, create the files folder and the user_data.sh file.</p><pre>mkdir files<br>touch files/user_data.sh</pre><p>Insert the following content on the files/user_data.sh :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69db10a86575efc84ab07cfa2a438b72/href">https://medium.com/media/69db10a86575efc84ab07cfa2a438b72/href</a></iframe><p>This will install the nginx when the instance is created.</p><p>Now, let’s create the output.tf file to get the load balancer’s DNS after the execution.</p><pre>touch output.tf</pre><p>Insert the following content on the output.tf :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/52dd7676eaac9c6d1878eccb8bed66cc/href">https://medium.com/media/52dd7676eaac9c6d1878eccb8bed66cc/href</a></iframe><p>Our web module is done.</p><p>This is how our terraform structure is now:</p><pre>├── modules<br>│   ├── networking<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── web<br>│       ├── files<br>│       │   └── user_data.sh<br>│       ├── main.tf<br>│       ├── output.tf<br>│       └── variables.tf<br>├── production<br>└── staging<br>    ├── _main.tf<br>    ├── networking.tf<br>    ├── staging_key<br>    ├── staging_key.pub<br>    ├── terraform.tfstate<br>    ├── terraform.tfstate.backup<br>    ├── terraform.tfvars<br>    └── variables.tf</pre><h4>Using our web module</h4><p>Let’s back to our staging folder</p><pre>cd ~/terraform<br>cd staging</pre><p>Now, we will use our recent created web module. Create a web.tf file</p><pre>touch web.tf</pre><p>Insert the following content on the web.tf :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/359044103c61f2e62130308cd3cf517e/href">https://medium.com/media/359044103c61f2e62130308cd3cf517e/href</a></iframe><p>This is pretty much the same way we used in our networking module. We are using almost the same variables that we already specified (except for web_instance_count . And some variables, we pass the output from our networking module.</p><pre>&quot;${module.networking.public_subnet_id}&quot;</pre><p>This way we get the public_subnet_id output created on the networking module.</p><p>Let’s add the web_instance_count variable to our variables.tf file and terraform.tfvars . This variable represents the number of web instances that we will be created.</p><p>Your variables.tf from staging folder should be like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ad6f0db1f0e752cf587942f5a3a1c8fe/href">https://medium.com/media/ad6f0db1f0e752cf587942f5a3a1c8fe/href</a></iframe><p>And your terraform.tfvars should be like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/81473661340b5972f7b67cddca88ccdf/href">https://medium.com/media/81473661340b5972f7b67cddca88ccdf/href</a></iframe><p>Let’s create an output.tf for our staging environment. With this, we can get the ELB hostname from our web module.</p><pre>touch output.tf</pre><p>Add the following content to output.tf :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/765f3e052832c63972696848a743d5de/href">https://medium.com/media/765f3e052832c63972696848a743d5de/href</a></iframe><p>This is the final directory structure that we have:</p><pre>├── modules<br>│   ├── networking<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── web<br>│       ├── files<br>│       │   └── user_data.sh<br>│       ├── main.tf<br>│       ├── output.tf<br>│       └── variables.tf<br>├── production<br>└── staging<br>    ├── _main.tf<br>    ├── networking.tf<br>    ├── output.tf<br>    ├── staging_key<br>    ├── staging_key.pub<br>    ├── terraform.tfstate<br>    ├── terraform.tfstate.backup<br>    ├── terraform.tfvars<br>    ├── variables.tf<br>    └── web.tf</pre><p>You can now run terraform plan to check which resources will be created. (before, run terraform get to update the modules)</p><pre>terraform get<br>terraform plan</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/724/1*_3j03t04L3BbcAM9B3t5Tg.png" /></figure><p>Done. Terraform will create 5 new resources from our web module.</p><p>Let’s apply it.</p><pre>terraform apply</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*zojlSrGMXN93qiHdoZVPhQ.png" /></figure><p>Yay.. our instances was created. Let’s get our Load Balancer DNS and try to open it on a Browser:</p><pre><a href="http://$(terraform/">terraform</a> output elb_hostname</pre><p>This command will return the DNS from our Load Balancer. Open it on a browser:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*KAFGuGGUKWTWISGD2dqmBQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X4llUCSctCV1RBczcOPusw.png" /></figure><p>It’s working!!! With this, you finished the creation of your infrastructure.</p><h4>Final Notes:</h4><p>Your web instances don’t have public ips:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SipX4XhQdT5_Z0zYySLmNg.png" /></figure><p>In order to SSH then, you need to use the bastion host created on the networking module.</p><p>Get the bastion host public IP,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HctNFDW39zAagy7iRyiirQ.png" /></figure><p>and SSH on it with the -A flag to enable agent forwarding:</p><pre>chmod 400 staging_key.pub</pre><pre>ssh-add -K staging_key</pre><pre>ssh -A ubuntu@52.53.227.241</pre><p>Now, inside the Bastion host, you can connect into your web server private IP:</p><pre>ssh ubuntu@10.0.2.113</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jxxGXtybRz43Rw47BacdxA.png" /></figure><p>Now, destroy everything</p><pre>terraform destroy</pre><p>You can get the full code of the example <a href="https://github.com/duduribeiro/terraform_example">here</a>.</p><p>That’s all.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/499/1*PlGYD6UUSY3wbNzGRAkHSw.gif" /></figure><p>Cheers,</p><p>🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bfa01a499bad" width="1" height="1" alt=""><hr><p><a href="https://medium.com/code-pub/creating-your-cloud-servers-with-terraform-bfa01a499bad">Creating your cloud servers with Terraform</a> was originally published in <a href="https://medium.com/code-pub">Code Pub</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>